summaryrefslogtreecommitdiffstats
path: root/dbus
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-01-04 07:28:54 +0000
committerHavoc Pennington <hp@redhat.com>2003-01-04 07:28:54 +0000
commit01af5ff4101e540a6456bca01d56272e701bea78 (patch)
treea5b0fc81b99e3b0564d0b2cc2ac4c20196a051f0 /dbus
parent1ed128b52484d95e30f7437bf87f34d85371f1f8 (diff)
2003-01-04 Havoc Pennington <hp@pobox.com>
* test/watch.c (error_handler): make it safe if the error handler is called multiple times (if we s/error handler/disconnect handler/ we should just guarantee it's called only once) * dbus/dbus-transport.c (_dbus_transport_disconnect): call the error handler on disconnect (it's quite possible we should just change the error handler to a "disconnect handler," I'm not sure we have any other meaningful errors) * configure.in: check for getpwnam_r * dbus/dbus-transport.c, dbus/dbus-transport-unix.c, dbus/dbus-auth.c: add credentials support, add EXTERNAL auth mechanism as in SASL spec, using socket credentials * dbus/dbus-sysdeps.c (_dbus_read_credentials_unix_socket): new function (_dbus_send_credentials_unix_socket): new function * dbus/dbus-sysdeps.c (_dbus_accept_unix_socket): rename just dbus_accept() (_dbus_write): only check errno if <0 returned (_dbus_write_two): ditto
Diffstat (limited to 'dbus')
-rw-r--r--dbus/dbus-auth.c268
-rw-r--r--dbus/dbus-auth.h43
-rw-r--r--dbus/dbus-server-unix.c2
-rw-r--r--dbus/dbus-sysdeps.c350
-rw-r--r--dbus/dbus-sysdeps.h26
-rw-r--r--dbus/dbus-transport-protected.h5
-rw-r--r--dbus/dbus-transport-unix.c62
-rw-r--r--dbus/dbus-transport.c49
8 files changed, 766 insertions, 39 deletions
diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c
index d39a7770..032e49a8 100644
--- a/dbus/dbus-auth.c
+++ b/dbus/dbus-auth.c
@@ -65,6 +65,12 @@ typedef struct
} DBusAuthCommandHandler;
/**
+ * This function appends an initial client response to the given string
+ */
+typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth,
+ DBusString *response);
+
+/**
* This function processes a block of data received from the peer.
* i.e. handles a DATA command.
*/
@@ -97,6 +103,7 @@ typedef struct
DBusAuthEncodeFunction server_encode_func;
DBusAuthDecodeFunction server_decode_func;
DBusAuthShutdownFunction server_shutdown_func;
+ DBusInitialResponseFunction client_initial_response_func;
DBusAuthDataFunction client_data_func;
DBusAuthEncodeFunction client_encode_func;
DBusAuthDecodeFunction client_decode_func;
@@ -116,6 +123,14 @@ struct DBusAuth
const DBusAuthCommandHandler *handlers; /**< Handlers for commands */
const DBusAuthMechanismHandler *mech; /**< Current auth mechanism */
+
+ DBusString identity; /**< Current identity we're authorizing
+ * as.
+ */
+
+ DBusCredentials credentials; /**< Credentials, fields may be -1 */
+
+ DBusCredentials authorized_identity; /**< Credentials that are authorized */
unsigned int needed_memory : 1; /**< We needed memory to continue since last
* successful getting something done
@@ -125,6 +140,7 @@ struct DBusAuth
unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */
unsigned int authenticated_pending_begin : 1; /**< Authenticated once we get BEGIN */
unsigned int already_got_mechanisms : 1; /**< Client already got mech list */
+ unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
};
typedef struct
@@ -228,6 +244,14 @@ _dbus_auth_new (int size)
auth->refcount = 1;
+ auth->credentials.pid = -1;
+ auth->credentials.uid = -1;
+ auth->credentials.gid = -1;
+
+ auth->authorized_identity.pid = -1;
+ auth->authorized_identity.uid = -1;
+ auth->authorized_identity.gid = -1;
+
/* note that we don't use the max string length feature,
* because you can't use that feature if you're going to
* try to recover from out-of-memory (it creates
@@ -244,6 +268,14 @@ _dbus_auth_new (int size)
if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX))
{
+ _dbus_string_free (&auth->incoming);
+ dbus_free (auth);
+ return NULL;
+ }
+
+ if (!_dbus_string_init (&auth->identity, _DBUS_INT_MAX))
+ {
+ _dbus_string_free (&auth->incoming);
_dbus_string_free (&auth->outgoing);
dbus_free (auth);
return NULL;
@@ -278,6 +310,11 @@ shutdown_mech (DBusAuth *auth)
/* Cancel any auth */
auth->authenticated_pending_begin = FALSE;
auth->authenticated = FALSE;
+ auth->already_asked_for_initial_response = FALSE;
+ _dbus_string_set_length (&auth->identity, 0);
+ auth->authorized_identity.pid = -1;
+ auth->authorized_identity.uid = -1;
+ auth->authorized_identity.gid = -1;
if (auth->mech != NULL)
{
@@ -353,6 +390,163 @@ handle_decode_stupid_test_mech (DBusAuth *auth,
return TRUE;
}
+static dbus_bool_t
+do_rejection (DBusAuth *auth)
+{
+ if (_dbus_string_append (&auth->outgoing,
+ "REJECTED\r\n"))
+ {
+ shutdown_mech (auth);
+ _dbus_verbose ("rejected client auth\n");
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static dbus_bool_t
+handle_server_data_external_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+ DBusCredentials desired_identity;
+
+ if (auth->credentials.uid < 0)
+ {
+ _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n");
+ return do_rejection (auth);
+ }
+
+ if (_dbus_string_get_length (data) > 0)
+ {
+ if (_dbus_string_get_length (&auth->identity) > 0)
+ {
+ /* Tried to send two auth identities, wtf */
+ return do_rejection (auth);
+ }
+ else
+ {
+ /* this is our auth identity */
+ if (!_dbus_string_copy (data, 0, &auth->identity, 0))
+ return FALSE;
+ }
+ }
+
+ /* Poke client for an auth identity, if none given */
+ if (_dbus_string_get_length (&auth->identity) == 0 &&
+ !auth->already_asked_for_initial_response)
+ {
+ if (_dbus_string_append (&auth->outgoing,
+ "DATA\r\n"))
+ {
+ _dbus_verbose ("sending empty challenge asking client for auth identity\n");
+ auth->already_asked_for_initial_response = TRUE;
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+
+ desired_identity.pid = -1;
+ desired_identity.uid = -1;
+ desired_identity.gid = -1;
+
+ /* If auth->identity is still empty here, then client
+ * responded with an empty string after we poked it for
+ * an initial response. This means to try to auth the
+ * identity provided in the credentials.
+ */
+ if (_dbus_string_get_length (&auth->identity) == 0)
+ {
+ desired_identity.uid = auth->credentials.uid;
+ }
+ else
+ {
+ if (!_dbus_credentials_from_uid_string (&auth->identity,
+ &desired_identity))
+ return do_rejection (auth);
+ }
+
+ if (desired_identity.uid < 0)
+ {
+ _dbus_verbose ("desired UID %d is no good\n", desired_identity.uid);
+ return do_rejection (auth);
+ }
+
+ if (_dbus_credentials_match (&auth->credentials,
+ &desired_identity))
+ {
+ /* client has authenticated */
+ _dbus_verbose ("authenticated client with UID %d matching socket credentials UID %d\n",
+ desired_identity.uid,
+ auth->credentials.uid);
+
+ if (!_dbus_string_append (&auth->outgoing,
+ "OK\r\n"))
+ return FALSE;
+
+ auth->authorized_identity.uid = desired_identity.uid;
+
+ auth->authenticated_pending_begin = TRUE;
+
+ return TRUE;
+ }
+ else
+ {
+ return do_rejection (auth);
+ }
+}
+
+static void
+handle_server_shutdown_external_mech (DBusAuth *auth)
+{
+
+}
+
+static dbus_bool_t
+handle_client_initial_response_external_mech (DBusAuth *auth,
+ DBusString *response)
+{
+ /* We always append our UID as an initial response, so the server
+ * doesn't have to send back an empty challenge to check whether we
+ * want to specify an identity. i.e. this avoids a round trip that
+ * the spec for the EXTERNAL mechanism otherwise requires.
+ */
+ DBusString plaintext;
+
+ if (!_dbus_string_init (&plaintext, _DBUS_INT_MAX))
+ return FALSE;
+
+ if (!_dbus_string_append_our_uid (&plaintext))
+ goto failed;
+
+ if (!_dbus_string_base64_encode (&plaintext, 0,
+ response,
+ _dbus_string_get_length (response)))
+ goto failed;
+
+ _dbus_string_free (&plaintext);
+
+ return TRUE;
+
+ failed:
+ _dbus_string_free (&plaintext);
+ return FALSE;
+}
+
+static dbus_bool_t
+handle_client_data_external_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+
+ return TRUE;
+}
+
+static void
+handle_client_shutdown_external_mech (DBusAuth *auth)
+{
+
+}
+
/* Put mechanisms here in order of preference.
* What I eventually want to have is:
*
@@ -364,11 +558,21 @@ handle_decode_stupid_test_mech (DBusAuth *auth,
*/
static const DBusAuthMechanismHandler
all_mechanisms[] = {
+ { "EXTERNAL",
+ handle_server_data_external_mech,
+ NULL, NULL,
+ handle_server_shutdown_external_mech,
+ handle_client_initial_response_external_mech,
+ handle_client_data_external_mech,
+ NULL, NULL,
+ handle_client_shutdown_external_mech },
+ /* Obviously this has to die for production use */
{ "DBUS_STUPID_TEST_MECH",
handle_server_data_stupid_test_mech,
handle_encode_stupid_test_mech,
handle_decode_stupid_test_mech,
handle_server_shutdown_stupid_test_mech,
+ NULL,
handle_client_data_stupid_test_mech,
handle_encode_stupid_test_mech,
handle_decode_stupid_test_mech,
@@ -496,8 +700,9 @@ process_auth (DBusAuth *auth,
auth->mech = find_mech (&mech);
if (auth->mech != NULL)
{
- _dbus_verbose ("Trying mechanism %s\n",
- auth->mech->mechanism);
+ _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n",
+ auth->mech->mechanism,
+ _dbus_string_get_length (&decoded_response));
if (!(* auth->mech->server_data_func) (auth,
&decoded_response))
@@ -702,15 +907,30 @@ client_try_next_mechanism (DBusAuth *auth)
{
_dbus_string_free (&auth_command);
return FALSE;
- }
-
+ }
+
if (!_dbus_string_append (&auth_command,
mech->mechanism))
{
_dbus_string_free (&auth_command);
return FALSE;
}
-
+
+ if (mech->client_initial_response_func != NULL)
+ {
+ if (!_dbus_string_append (&auth_command, " "))
+ {
+ _dbus_string_free (&auth_command);
+ return FALSE;
+ }
+
+ if (!(* mech->client_initial_response_func) (auth, &auth_command))
+ {
+ _dbus_string_free (&auth_command);
+ return FALSE;
+ }
+ }
+
if (!_dbus_string_append (&auth_command,
"\r\n"))
{
@@ -1327,4 +1547,42 @@ _dbus_auth_decode_data (DBusAuth *auth,
}
}
+/**
+ * Sets credentials received via reliable means from the operating
+ * system.
+ *
+ * @param auth the auth conversation
+ * @param credentials the credentials received
+ */
+void
+_dbus_auth_set_credentials (DBusAuth *auth,
+ const DBusCredentials *credentials)
+{
+ auth->credentials = *credentials;
+}
+
+/**
+ * Gets the identity we authorized the client as. Apps may have
+ * different policies as to what identities they allow.
+ *
+ * @param auth the auth conversation
+ * @param credentials the credentials we've authorized
+ */
+void
+_dbus_auth_get_identity (DBusAuth *auth,
+ DBusCredentials *credentials)
+{
+ if (auth->authenticated)
+ {
+ *credentials = auth->authorized_identity;
+ }
+ else
+ {
+ credentials->pid = -1;
+ credentials->uid = -1;
+ credentials->gid = -1;
+ }
+}
+
+
/** @} */
diff --git a/dbus/dbus-auth.h b/dbus/dbus-auth.h
index 824426af..7346f99b 100644
--- a/dbus/dbus-auth.h
+++ b/dbus/dbus-auth.h
@@ -26,6 +26,7 @@
#include <dbus/dbus-macros.h>
#include <dbus/dbus-errors.h>
#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
DBUS_BEGIN_DECLS;
@@ -43,26 +44,30 @@ typedef enum
DBusAuth* _dbus_auth_server_new (void);
DBusAuth* _dbus_auth_client_new (void);
-void _dbus_auth_ref (DBusAuth *auth);
-void _dbus_auth_unref (DBusAuth *auth);
-DBusAuthState _dbus_auth_do_work (DBusAuth *auth);
-dbus_bool_t _dbus_auth_get_bytes_to_send (DBusAuth *auth,
- const DBusString **str);
-void _dbus_auth_bytes_sent (DBusAuth *auth,
- int bytes_sent);
-dbus_bool_t _dbus_auth_bytes_received (DBusAuth *auth,
- const DBusString *str);
-dbus_bool_t _dbus_auth_get_unused_bytes (DBusAuth *auth,
- DBusString *str);
-dbus_bool_t _dbus_auth_needs_encoding (DBusAuth *auth);
-dbus_bool_t _dbus_auth_encode_data (DBusAuth *auth,
- const DBusString *plaintext,
- DBusString *encoded);
-dbus_bool_t _dbus_auth_needs_decoding (DBusAuth *auth);
-dbus_bool_t _dbus_auth_decode_data (DBusAuth *auth,
- const DBusString *encoded,
- DBusString *plaintext);
+void _dbus_auth_ref (DBusAuth *auth);
+void _dbus_auth_unref (DBusAuth *auth);
+DBusAuthState _dbus_auth_do_work (DBusAuth *auth);
+dbus_bool_t _dbus_auth_get_bytes_to_send (DBusAuth *auth,
+ const DBusString **str);
+void _dbus_auth_bytes_sent (DBusAuth *auth,
+ int bytes_sent);
+dbus_bool_t _dbus_auth_bytes_received (DBusAuth *auth,
+ const DBusString *str);
+dbus_bool_t _dbus_auth_get_unused_bytes (DBusAuth *auth,
+ DBusString *str);
+dbus_bool_t _dbus_auth_needs_encoding (DBusAuth *auth);
+dbus_bool_t _dbus_auth_encode_data (DBusAuth *auth,
+ const DBusString *plaintext,
+ DBusString *encoded);
+dbus_bool_t _dbus_auth_needs_decoding (DBusAuth *auth);
+dbus_bool_t _dbus_auth_decode_data (DBusAuth *auth,
+ const DBusString *encoded,
+ DBusString *plaintext);
+void _dbus_auth_set_credentials (DBusAuth *auth,
+ const DBusCredentials *credentials);
+void _dbus_auth_get_identity (DBusAuth *auth,
+ DBusCredentials *credentials);
DBUS_END_DECLS;
diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c
index 66494bd4..c9a494e7 100644
--- a/dbus/dbus-server-unix.c
+++ b/dbus/dbus-server-unix.c
@@ -128,7 +128,7 @@ unix_handle_watch (DBusServer *server,
listen_fd = dbus_watch_get_fd (watch);
- client_fd = _dbus_accept_unix_socket (listen_fd);
+ client_fd = _dbus_accept (listen_fd);
if (client_fd < 0)
{
diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c
index fed88144..f85a4a95 100644
--- a/dbus/dbus-sysdeps.c
+++ b/dbus/dbus-sysdeps.c
@@ -33,6 +33,7 @@
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <pwd.h>
#ifdef HAVE_WRITEV
#include <sys/uio.h>
#endif
@@ -151,7 +152,7 @@ _dbus_write (int fd,
bytes_written = write (fd, data, len);
- if (errno == EINTR)
+ if (bytes_written < 0 && errno == EINTR)
goto again;
#if 0
@@ -226,7 +227,7 @@ _dbus_write_two (int fd,
vectors,
data2 ? 2 : 1);
- if (errno == EINTR)
+ if (bytes_written < 0 && errno == EINTR)
goto again;
return bytes_written;
@@ -367,17 +368,184 @@ _dbus_listen_unix_socket (const char *path,
return listen_fd;
}
+/* try to read a single byte and return #TRUE if we read it
+ * and it's equal to nul.
+ */
+static dbus_bool_t
+read_credentials_byte (int client_fd,
+ DBusResultCode *result)
+{
+ char buf[1];
+ int bytes_read;
+
+ again:
+ bytes_read = read (client_fd, buf, 1);
+ if (bytes_read < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+ else
+ {
+ dbus_set_result (result, _dbus_result_from_errno (errno));
+ _dbus_verbose ("Failed to read credentials byte: %s\n",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+ }
+ else if (bytes_read == 0)
+ {
+ dbus_set_result (result, DBUS_RESULT_IO_ERROR);
+ _dbus_verbose ("EOF reading credentials byte\n");
+ return FALSE;
+ }
+ else
+ {
+ _dbus_assert (bytes_read == 1);
+
+ if (buf[0] != '\0')
+ {
+ dbus_set_result (result, DBUS_RESULT_FAILED);
+ _dbus_verbose ("Credentials byte was not nul\n");
+ return FALSE;
+ }
+
+ _dbus_verbose ("read credentials byte\n");
+
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+write_credentials_byte (int server_fd,
+ DBusResultCode *result)
+{
+ int bytes_written;
+ char buf[1] = { '\0' };
+
+ again:
+
+ bytes_written = write (server_fd, buf, 1);
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+ if (bytes_written < 0)
+ {
+ dbus_set_result (result, _dbus_result_from_errno (errno));
+ _dbus_verbose ("Failed to write credentials byte: %s\n",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+ else if (bytes_written == 0)
+ {
+ dbus_set_result (result, DBUS_RESULT_IO_ERROR);
+ _dbus_verbose ("wrote zero bytes writing credentials byte\n");
+ return FALSE;
+ }
+ else
+ {
+ _dbus_assert (bytes_written == 1);
+ _dbus_verbose ("wrote credentials byte\n");
+ return TRUE;
+ }
+}
+
+/**
+ * Reads a single byte which must be nul (an error occurs otherwise),
+ * and reads unix credentials if available. Fills in pid/uid/gid with
+ * -1 if no credentials are available. Return value indicates whether
+ * a byte was read, not whether we got valid credentials. On some
+ * systems, such as Linux, reading/writing the byte isn't actually
+ * required, but we do it anyway just to avoid multiple codepaths.
+ *
+ * Fails if no byte is available, so you must select() first.
+ *
+ * The point of the byte is that on some systems we have to
+ * use sendmsg()/recvmsg() to transmit credentials.
+ *
+ * @param client_fd the client file descriptor
+ * @param credentials struct to fill with credentials of client
+ * @param result location to store result code
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_read_credentials_unix_socket (int client_fd,
+ DBusCredentials *credentials,
+ DBusResultCode *result)
+{
+ credentials->pid = -1;
+ credentials->uid = -1;
+ credentials->gid = -1;
+
+#ifdef SO_PEERCRED
+ if (read_credentials_byte (client_fd, result))
+ {
+ struct ucred cr;
+ int cr_len = sizeof (cr);
+
+ if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
+ cr_len == sizeof (cr))
+ {
+ credentials->pid = cr.pid;
+ credentials->uid = cr.uid;
+ credentials->gid = cr.gid;
+ _dbus_verbose ("Got credentials pid %d uid %d gid %d\n",
+ credentials->pid,
+ credentials->uid,
+ credentials->gid);
+ }
+ else
+ {
+ _dbus_verbose ("Failed to getsockopt() credentials, returned len %d/%d: %s\n",
+ cr_len, (int) sizeof (cr), _dbus_strerror (errno));
+ }
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+#else /* !SO_PEERCRED */
+ _dbus_verbose ("Socket credentials not supported on this OS\n");
+ return TRUE;
+#endif
+}
+
+/**
+ * Sends a single nul byte with our UNIX credentials as ancillary
+ * data. Returns #TRUE if the data was successfully written. On
+ * systems that don't support sending credentials, just writes a byte,
+ * doesn't send any credentials. On some systems, such as Linux,
+ * reading/writing the byte isn't actually required, but we do it
+ * anyway just to avoid multiple codepaths.
+ *
+ * Fails if no byte can be written, so you must select() first.
+ *
+ * The point of the byte is that on some systems we have to
+ * use sendmsg()/recvmsg() to transmit credentials.
+ *
+ * @param server_fd file descriptor for connection to server
+ * @param result return location for error code
+ * @returns #TRUE if the byte was sent
+ */
+dbus_bool_t
+_dbus_send_credentials_unix_socket (int server_fd,
+ DBusResultCode *result)
+{
+ if (write_credentials_byte (server_fd, result))
+ return TRUE;
+ else
+ return FALSE;
+}
+
/**
- * Accepts a connection on a listening UNIX socket.
- * Specific to UNIX domain sockets because we might
- * add extra args to this function later to get client
- * credentials. Handles EINTR for you.
+ * Accepts a connection on a listening socket.
+ * Handles EINTR for you.
*
* @param listen_fd the listen file descriptor
* @returns the connection fd of the client, or -1 on error
*/
int
-_dbus_accept_unix_socket (int listen_fd)
+_dbus_accept (int listen_fd)
{
int client_fd;
@@ -389,7 +557,7 @@ _dbus_accept_unix_socket (int listen_fd)
if (errno == EINTR)
goto retry;
}
-
+
return client_fd;
}
@@ -559,4 +727,168 @@ _dbus_string_parse_double (const DBusString *str,
return TRUE;
}
-/** @} end of DBusString */
+/**
+ * Gets the credentials corresponding to the given username.
+ *
+ * @param username the username
+ * @param credentials credentials to fill in
+ * @returns #TRUE if the username existed and we got some credentials
+ */
+dbus_bool_t
+_dbus_credentials_from_username (const DBusString *username,
+ DBusCredentials *credentials)
+{
+ const char *username_c_str;
+
+ credentials->pid = -1;
+ credentials->uid = -1;
+ credentials->gid = -1;
+
+ _dbus_string_get_const_data (username, &username_c_str);
+
+#ifdef HAVE_GETPWNAM_R
+ {
+ struct passwd *p;
+ int result;
+ char buf[1024];
+ struct passwd p_str;
+
+ p = NULL;
+ result = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf),
+ &p);
+
+ if (result == 0 && p == &p_str)
+ {
+ credentials->uid = p->pw_uid;
+ credentials->gid = p->pw_gid;
+
+ _dbus_verbose ("Username %s has uid %d gid %d\n",
+ username_c_str, credentials->uid, credentials->gid);
+ return TRUE;
+ }
+ else
+ {
+ _dbus_verbose ("User %s unknown\n", username_c_str);
+ return FALSE;
+ }
+ }
+#else /* ! HAVE_GETPWNAM_R */
+ {
+ /* I guess we're screwed on thread safety here */
+ struct passwd *p;
+
+ p = getpwnam (username_c_str);
+
+ if (p != NULL)
+ {
+ credentials->uid = p->pw_uid;
+ credentials->gid = p->pw_gid;
+
+ _dbus_verbose ("Username %s has uid %d gid %d\n",
+ username_c_str, credentials->uid, credentials->gid);
+ return TRUE;
+ }
+ else
+ {
+ _dbus_verbose ("User %s unknown\n", username_c_str);
+ return FALSE;
+ }
+ }
+#endif
+}
+
+/**
+ * Gets credentials from a UID string. (Parses a string to a UID
+ * and converts to a DBusCredentials.)
+ *
+ * @param uid_str the UID in string form
+ * @param credentials credentials to fill in
+ * @returns #TRUE if successfully filled in some credentials
+ */
+dbus_bool_t
+_dbus_credentials_from_uid_string (const DBusString *uid_str,
+ DBusCredentials *credentials)
+{
+ int end;
+ long uid;
+
+ credentials->pid = -1;
+ credentials->uid = -1;
+ credentials->gid = -1;
+
+ if (_dbus_string_get_length (uid_str) == 0)
+ {
+ _dbus_verbose ("UID string was zero length\n");
+ return FALSE;
+ }
+
+ uid = -1;
+ end = 0;
+ if (!_dbus_string_parse_int (uid_str, 0, &uid,
+ &end))
+ {
+ _dbus_verbose ("could not parse string as a UID\n");
+ return FALSE;
+ }
+
+ if (end != _dbus_string_get_length (uid_str))
+ {
+ _dbus_verbose ("string contained trailing stuff after UID\n");
+ return FALSE;
+ }
+
+ credentials->uid = uid;
+
+ return TRUE;
+}
+
+/**
+ * Gets the credentials of the current process.
+ *
+ * @param credentials credentials to fill in.
+ */
+void
+_dbus_credentials_from_current_process (DBusCredentials *credentials)
+{
+ credentials->pid = getpid ();
+ credentials->uid = getuid ();
+ credentials->gid = getgid ();
+}
+
+/**
+ * Checks whether the provided_credentials are allowed to log in
+ * as the expected_credentials.
+ *
+ * @param expected_credentials credentials we're trying to log in as
+ * @param provided_credentials credentials we have
+ * @returns #TRUE if we can log in
+ */
+dbus_bool_t
+_dbus_credentials_match (const DBusCredentials *expected_credentials,
+ const DBusCredentials *provided_credentials)
+{
+ if (provided_credentials->uid < 0)
+ return FALSE;
+ else if (expected_credentials->uid < 0)
+ return FALSE;
+ else if (provided_credentials->uid == 0)
+ return TRUE;
+ else if (provided_credentials->uid == expected_credentials->uid)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * Appends the uid of the current process to the given string.
+ *
+ * @param str the string to append to
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_append_our_uid (DBusString *str)
+{
+ return _dbus_string_append_int (str, getuid ());
+}
+
+/** @} end of sysdeps */
diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h
index 17d96b3d..73482f06 100644
--- a/dbus/dbus-sysdeps.h
+++ b/dbus/dbus-sysdeps.h
@@ -65,12 +65,36 @@ int _dbus_write_two (int fd,
int start2,
int len2);
+typedef struct
+{
+ /* -1 if not available */
+ int pid;
+ int uid;
+ int gid;
+} DBusCredentials;
+
int _dbus_connect_unix_socket (const char *path,
DBusResultCode *result);
int _dbus_listen_unix_socket (const char *path,
DBusResultCode *result);
-int _dbus_accept_unix_socket (int listen_fd);
+int _dbus_accept (int listen_fd);
+
+dbus_bool_t _dbus_read_credentials_unix_socket (int client_fd,
+ DBusCredentials *credentials,
+ DBusResultCode *result);
+dbus_bool_t _dbus_send_credentials_unix_socket (int server_fd,
+ DBusResultCode *result);
+
+
+dbus_bool_t _dbus_credentials_from_username (const DBusString *username,
+ DBusCredentials *credentials);
+dbus_bool_t _dbus_credentials_from_uid_string (const DBusString *uid_str,
+ DBusCredentials *credentials);
+void _dbus_credentials_from_current_process (DBusCredentials *credentials);
+dbus_bool_t _dbus_credentials_match (const DBusCredentials *expected_credentials,
+ const DBusCredentials *provided_credentials);
+dbus_bool_t _dbus_string_append_our_uid (DBusString *str);
DBUS_END_DECLS;
diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h
index 5105165a..b9bbb40d 100644
--- a/dbus/dbus-transport-protected.h
+++ b/dbus/dbus-transport-protected.h
@@ -77,9 +77,14 @@ struct DBusTransport
DBusAuth *auth; /**< Authentication conversation */
+ DBusCredentials credentials; /**< Credentials of other end */
+
unsigned int disconnected : 1; /**< #TRUE if we are disconnected. */
unsigned int authenticated : 1; /**< Cache of auth state; use _dbus_transport_get_is_authenticated() to query value */
unsigned int messages_need_sending : 1; /**< #TRUE if we need to write messages out */
+ unsigned int send_credentials_pending : 1; /**< #TRUE if we need to send credentials */
+ unsigned int receive_credentials_pending : 1; /**< #TRUE if we need to receive credentials */
+ unsigned int is_server : 1; /**< #TRUE if on the server side */
};
dbus_bool_t _dbus_transport_init_base (DBusTransport *transport,
diff --git a/dbus/dbus-transport-unix.c b/dbus/dbus-transport-unix.c
index dd0c6833..ba1528c4 100644
--- a/dbus/dbus-transport-unix.c
+++ b/dbus/dbus-transport-unix.c
@@ -123,7 +123,8 @@ check_write_watch (DBusTransport *transport)
if (_dbus_transport_get_is_authenticated (transport))
need_write_watch = transport->messages_need_sending;
else
- need_write_watch = _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
+ need_write_watch = transport->send_credentials_pending ||
+ _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
if (transport->disconnected)
need_write_watch = FALSE;
@@ -391,15 +392,70 @@ recover_unused_bytes (DBusTransport *transport)
}
static void
+exchange_credentials (DBusTransport *transport,
+ dbus_bool_t do_reading,
+ dbus_bool_t do_writing)
+{
+ DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
+
+ if (do_writing && transport->send_credentials_pending)
+ {
+ if (_dbus_send_credentials_unix_socket (unix_transport->fd,
+ NULL))
+ {
+ transport->send_credentials_pending = FALSE;
+ }
+ else
+ {
+ _dbus_verbose ("Failed to write credentials\n");
+ do_io_error (transport);
+ }
+ }
+
+ if (do_reading && transport->receive_credentials_pending)
+ {
+ if (_dbus_read_credentials_unix_socket (unix_transport->fd,
+ &transport->credentials,
+ NULL))
+ {
+ transport->receive_credentials_pending = FALSE;
+ }
+ else
+ {
+ _dbus_verbose ("Failed to read credentials\n");
+ do_io_error (transport);
+ }
+ }
+
+ if (!(transport->send_credentials_pending ||
+ transport->receive_credentials_pending))
+ {
+ _dbus_auth_set_credentials (transport->auth,
+ &transport->credentials);
+ }
+}
+
+static void
do_authentication (DBusTransport *transport,
dbus_bool_t do_reading,
dbus_bool_t do_writing)
-{
+{
_dbus_transport_ref (transport);
while (!_dbus_transport_get_is_authenticated (transport) &&
_dbus_transport_get_is_connected (transport))
{
+ exchange_credentials (transport, do_reading, do_writing);
+
+ if (transport->send_credentials_pending ||
+ transport->receive_credentials_pending)
+ {
+ _dbus_verbose ("send_credentials_pending = %d receive_credentials_pending = %d\n",
+ transport->send_credentials_pending,
+ transport->receive_credentials_pending);
+ goto out;
+ }
+
switch (_dbus_auth_do_work (transport->auth))
{
case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
@@ -963,7 +1019,7 @@ _dbus_transport_new_for_domain_socket (const char *path,
close (fd);
fd = -1;
}
-
+
return transport;
}
diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c
index 085b0224..110153dd 100644
--- a/dbus/dbus-transport.c
+++ b/dbus/dbus-transport.c
@@ -105,6 +105,13 @@ _dbus_transport_init_base (DBusTransport *transport,
transport->authenticated = FALSE;
transport->messages_need_sending = FALSE;
transport->disconnected = FALSE;
+ transport->send_credentials_pending = !server;
+ transport->receive_credentials_pending = server;
+ transport->is_server = server;
+
+ transport->credentials.pid = -1;
+ transport->credentials.uid = -1;
+ transport->credentials.gid = -1;
return TRUE;
}
@@ -205,8 +212,12 @@ _dbus_transport_disconnect (DBusTransport *transport)
DBUS_TRANSPORT_HOLD_REF (transport);
(* transport->vtable->disconnect) (transport);
-
+
transport->disconnected = TRUE;
+
+ _dbus_connection_transport_error (transport->connection,
+ DBUS_RESULT_DISCONNECTED);
+
DBUS_TRANSPORT_RELEASE_REF (transport);
}
@@ -238,9 +249,45 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport)
return TRUE;
else
{
+ if (transport->disconnected)
+ return FALSE;
+
transport->authenticated =
+ (!(transport->send_credentials_pending ||
+ transport->receive_credentials_pending)) &&
_dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_AUTHENTICATED;
+ /* If we've authenticated as some identity, check that the auth
+ * identity is the same as our own identity. In the future, we
+ * may have API allowing applications to specify how this is
+ * done, for example they may allow connection as any identity,
+ * but then impose restrictions on certain identities.
+ * Or they may give certain identities extra privileges.
+ */
+
+ if (transport->authenticated && transport->is_server)
+ {
+ DBusCredentials auth_identity;
+ DBusCredentials our_identity;
+
+ _dbus_credentials_from_current_process (&our_identity);
+ _dbus_auth_get_identity (transport->auth, &auth_identity);
+
+ if (!_dbus_credentials_match (&our_identity,
+ &auth_identity))
+ {
+ _dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
+ auth_identity.uid, our_identity.uid);
+ _dbus_transport_disconnect (transport);
+ return FALSE;
+ }
+ else
+ {
+ _dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
+ auth_identity.uid, our_identity.uid);
+ }
+ }
+
return transport->authenticated;
}
}