diff options
Diffstat (limited to 'dbus')
-rw-r--r-- | dbus/dbus-auth.c | 268 | ||||
-rw-r--r-- | dbus/dbus-auth.h | 43 | ||||
-rw-r--r-- | dbus/dbus-server-unix.c | 2 | ||||
-rw-r--r-- | dbus/dbus-sysdeps.c | 350 | ||||
-rw-r--r-- | dbus/dbus-sysdeps.h | 26 | ||||
-rw-r--r-- | dbus/dbus-transport-protected.h | 5 | ||||
-rw-r--r-- | dbus/dbus-transport-unix.c | 62 | ||||
-rw-r--r-- | dbus/dbus-transport.c | 49 |
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; } } |