From b6ffea177fccb6cc4e65992da7d8b390054277f7 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Fri, 21 Mar 2003 02:38:40 +0000 Subject: 2003-03-20 Havoc Pennington * dbus/dbus-connection.c (dbus_connection_set_unix_user_function): new function (dbus_connection_get_unix_user): new function --- dbus/dbus-connection.c | 58 +++++++++++++++++++++ dbus/dbus-connection.h | 12 ++++- dbus/dbus-sysdeps.c | 16 ++++++ dbus/dbus-transport-protected.h | 5 ++ dbus/dbus-transport.c | 111 ++++++++++++++++++++++++++++++++++++---- dbus/dbus-transport.h | 60 +++++++++++++--------- 6 files changed, 224 insertions(+), 38 deletions(-) (limited to 'dbus') diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index d70ff716..db621405 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -2210,6 +2210,64 @@ dbus_connection_handle_watch (DBusConnection *connection, return retval; } +/** + * Gets the UNIX user ID of the connection if any. + * Returns #TRUE if the uid is filled in. + * Always returns #FALSE on non-UNIX platforms. + * + * @param connection the connection + * @param uid return location for the user ID + * @returns #TRUE if uid is filled in with a valid user ID + */ +dbus_bool_t +dbus_connection_get_unix_user (DBusConnection *connection, + unsigned long *uid) +{ + dbus_bool_t result; + + dbus_mutex_lock (connection->mutex); + result = _dbus_transport_get_unix_user (connection->transport, + uid); + dbus_mutex_unlock (connection->mutex); + + return result; +} + +/** + * Sets a predicate function used to determine whether a given user ID + * is allowed to connect. When an incoming connection has + * authenticated with a particular user ID, this function is called; + * if it returns #TRUE, the connection is allowed to proceed, + * otherwise the connection is disconnected. + * + * If the function is set to #NULL (as it is by default), then + * only the same UID as the server process will be allowed to + * connect. + * + * @param connection the connection + * @param function the predicate + * @param data data to pass to the predicate + * @param free_data_function function to free the data + */ +void +dbus_connection_set_unix_user_function (DBusConnection *connection, + DBusAllowUnixUserFunction function, + void *data, + DBusFreeFunction free_data_function) +{ + void *old_data = NULL; + DBusFreeFunction old_free_function = NULL; + + dbus_mutex_lock (connection->mutex); + _dbus_transport_set_unix_user_function (connection->transport, + function, data, free_data_function, + &old_data, &old_free_function); + dbus_mutex_unlock (connection->mutex); + + if (old_free_function != NULL) + (* old_free_function) (old_data); +} + /** * Adds a message filter. Filters are handlers that are run on * all incoming messages, prior to the normal handlers diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h index 9b135a5d..21a4a15a 100644 --- a/dbus/dbus-connection.h +++ b/dbus/dbus-connection.h @@ -69,13 +69,16 @@ typedef void (* DBusWatchToggledFunction) (DBusWatch *watch, void *data); typedef void (* DBusRemoveWatchFunction) (DBusWatch *watch, void *data); -typedef void (* DBusWakeupMainFunction) (void *data); typedef dbus_bool_t (* DBusAddTimeoutFunction) (DBusTimeout *timeout, void *data); typedef void (* DBusTimeoutToggledFunction) (DBusTimeout *timeout, void *data); typedef void (* DBusRemoveTimeoutFunction) (DBusTimeout *timeout, void *data); +typedef void (* DBusWakeupMainFunction) (void *data); +typedef dbus_bool_t (* DBusAllowUnixUserFunction) (DBusConnection *connection, + unsigned long uid, + void *data); DBusConnection* dbus_connection_open (const char *address, DBusResultCode *result); @@ -123,7 +126,12 @@ void dbus_connection_set_wakeup_main_function (DBusConnection dbus_bool_t dbus_connection_handle_watch (DBusConnection *connection, DBusWatch *watch, unsigned int condition); - +dbus_bool_t dbus_connection_get_unix_user (DBusConnection *connection, + unsigned long *uid); +void dbus_connection_set_unix_user_function (DBusConnection *connection, + DBusAllowUnixUserFunction function, + void *data, + DBusFreeFunction free_data_function); int dbus_watch_get_fd (DBusWatch *watch); unsigned int dbus_watch_get_flags (DBusWatch *watch); diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index d653b868..cd4a82a5 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -651,6 +651,14 @@ _dbus_read_credentials_unix_socket (int client_fd, struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem; #endif + /* The POSIX spec certainly doesn't promise this, but + * we need these assertions to fail as soon as we're wrong about + * it so we can do the porting fixups + */ + _dbus_assert (sizeof (pid_t) <= sizeof (credentials->pid)); + _dbus_assert (sizeof (uid_t) <= sizeof (credentials->uid)); + _dbus_assert (sizeof (gid_t) <= sizeof (credentials->gid)); + credentials->pid = -1; credentials->uid = -1; credentials->gid = -1; @@ -1353,6 +1361,14 @@ _dbus_credentials_from_uid_string (const DBusString *uid_str, void _dbus_credentials_from_current_process (DBusCredentials *credentials) { + /* The POSIX spec certainly doesn't promise this, but + * we need these assertions to fail as soon as we're wrong about + * it so we can do the porting fixups + */ + _dbus_assert (sizeof (pid_t) <= sizeof (credentials->pid)); + _dbus_assert (sizeof (uid_t) <= sizeof (credentials->uid)); + _dbus_assert (sizeof (gid_t) <= sizeof (credentials->gid)); + credentials->pid = getpid (); credentials->uid = getuid (); credentials->gid = getgid (); diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h index 8ee605c0..31668389 100644 --- a/dbus/dbus-transport-protected.h +++ b/dbus/dbus-transport-protected.h @@ -86,6 +86,11 @@ struct DBusTransport long max_live_messages_size; /**< Max total size of received messages. */ DBusCounter *live_messages_size; /**< Counter for size of all live messages. */ + + + DBusAllowUnixUserFunction unix_user_function; /**< Function for checking whether a user is authorized. */ + void *unix_user_data; /**< Data for unix_user_function */ + DBusFreeFunction free_unix_user_data; /**< Function to free unix_user_data */ 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 */ diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 8087f5b0..b6ab8c0a 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -127,6 +127,10 @@ _dbus_transport_init_base (DBusTransport *transport, transport->receive_credentials_pending = server; transport->is_server = server; + transport->unix_user_function = NULL; + transport->unix_user_data = NULL; + transport->free_unix_user_data = NULL; + /* Try to default to something that won't totally hose the system, * but doesn't impose too much of a limitation. */ @@ -155,6 +159,9 @@ _dbus_transport_finalize_base (DBusTransport *transport) { if (!transport->disconnected) _dbus_transport_disconnect (transport); + + if (transport->free_unix_user_data != NULL) + (* transport->free_unix_user_data) (transport->unix_user_data); _dbus_message_loader_unref (transport->loader); _dbus_auth_unref (transport->auth); @@ -334,6 +341,8 @@ _dbus_transport_get_is_connected (DBusTransport *transport) * Returns #TRUE if we have been authenticated. Will return #TRUE * even if the transport is disconnected. * + * @todo needs to drop connection->mutex when calling the unix_user_function + * * @param transport the transport * @returns whether we're authenticated */ @@ -363,23 +372,45 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport) 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)) + + if (transport->unix_user_function != NULL) { - _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; + /* FIXME we hold the connection lock here and should drop it */ + if (!(* transport->unix_user_function) (transport->connection, + auth_identity.uid, + transport->unix_user_data)) + { + _dbus_verbose ("Client UID %d was rejected, disconnecting\n", + auth_identity.uid); + _dbus_transport_disconnect (transport); + return FALSE; + } + else + { + _dbus_verbose ("Client UID %d authorized\n", auth_identity.uid); + } } else { - _dbus_verbose ("Client authorized as UID %d matching our UID %d\n", - auth_identity.uid, our_identity.uid); + DBusCredentials our_identity; + + _dbus_credentials_from_current_process (&our_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); + } } } @@ -737,4 +768,62 @@ _dbus_transport_get_max_live_messages_size (DBusTransport *transport) return transport->max_live_messages_size; } +/** + * See dbus_connection_get_unix_user(). + * + * @param transport the transport + * @param uid return location for the user ID + * @returns #TRUE if uid is filled in with a valid user ID + */ +dbus_bool_t +_dbus_transport_get_unix_user (DBusTransport *transport, + unsigned long *uid) +{ + DBusCredentials auth_identity; + + *uid = _DBUS_INT_MAX; /* better than some root or system user in + * case of bugs in the caller. Caller should + * never use this value on purpose, however. + */ + + if (!transport->authenticated) + return FALSE; + + _dbus_auth_get_identity (transport->auth, &auth_identity); + + if (auth_identity.uid >= 0) + { + *uid = auth_identity.uid; + return TRUE; + } + else + return FALSE; +} + +/** + * See dbus_connection_set_unix_user_function(). + * + * @param transport the transport + * @param function the predicate + * @param data data to pass to the predicate + * @param free_data_function function to free the data + * @param old_data the old user data to be freed + * @param old_free_data_function old free data function to free it with + */ +void +_dbus_transport_set_unix_user_function (DBusTransport *transport, + DBusAllowUnixUserFunction function, + void *data, + DBusFreeFunction free_data_function, + void **old_data, + DBusFreeFunction *old_free_data_function) +{ + *old_data = transport->unix_user_data; + *old_free_data_function = transport->free_unix_user_data; + + transport->unix_user_function = function; + transport->unix_user_data = data; + transport->free_unix_user_data = free_data_function; +} + /** @} */ diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h index 639a7d50..c016412f 100644 --- a/dbus/dbus-transport.h +++ b/dbus/dbus-transport.h @@ -30,31 +30,41 @@ DBUS_BEGIN_DECLS; typedef struct DBusTransport DBusTransport; -DBusTransport* _dbus_transport_open (const char *address, - DBusResultCode *result); -void _dbus_transport_ref (DBusTransport *transport); -void _dbus_transport_unref (DBusTransport *transport); -void _dbus_transport_disconnect (DBusTransport *transport); -dbus_bool_t _dbus_transport_get_is_connected (DBusTransport *transport); -dbus_bool_t _dbus_transport_get_is_authenticated (DBusTransport *transport); -dbus_bool_t _dbus_transport_handle_watch (DBusTransport *transport, - DBusWatch *watch, - unsigned int condition); -dbus_bool_t _dbus_transport_set_connection (DBusTransport *transport, - DBusConnection *connection); -void _dbus_transport_messages_pending (DBusTransport *transport, - int queue_length); -void _dbus_transport_do_iteration (DBusTransport *transport, - unsigned int flags, - int timeout_milliseconds); -DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport); -dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport); -void _dbus_transport_set_max_message_size (DBusTransport *transport, - long size); -long _dbus_transport_get_max_message_size (DBusTransport *transport); -void _dbus_transport_set_max_live_messages_size (DBusTransport *transport, - long size); -long _dbus_transport_get_max_live_messages_size (DBusTransport *transport); +DBusTransport* _dbus_transport_open (const char *address, + DBusResultCode *result); +void _dbus_transport_ref (DBusTransport *transport); +void _dbus_transport_unref (DBusTransport *transport); +void _dbus_transport_disconnect (DBusTransport *transport); +dbus_bool_t _dbus_transport_get_is_connected (DBusTransport *transport); +dbus_bool_t _dbus_transport_get_is_authenticated (DBusTransport *transport); +dbus_bool_t _dbus_transport_handle_watch (DBusTransport *transport, + DBusWatch *watch, + unsigned int condition); +dbus_bool_t _dbus_transport_set_connection (DBusTransport *transport, + DBusConnection *connection); +void _dbus_transport_messages_pending (DBusTransport *transport, + int queue_length); +void _dbus_transport_do_iteration (DBusTransport *transport, + unsigned int flags, + int timeout_milliseconds); +DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport); +dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport); +void _dbus_transport_set_max_message_size (DBusTransport *transport, + long size); +long _dbus_transport_get_max_message_size (DBusTransport *transport); +void _dbus_transport_set_max_live_messages_size (DBusTransport *transport, + long size); +long _dbus_transport_get_max_live_messages_size (DBusTransport *transport); +dbus_bool_t _dbus_transport_get_unix_user (DBusTransport *transport, + unsigned long *uid); +void _dbus_transport_set_unix_user_function (DBusTransport *transport, + DBusAllowUnixUserFunction function, + void *data, + DBusFreeFunction free_data_function, + void **old_data, + DBusFreeFunction *old_free_data_function); + + DBUS_END_DECLS; -- cgit