diff options
author | Havoc Pennington <hp@redhat.com> | 2003-03-21 02:38:40 +0000 |
---|---|---|
committer | Havoc Pennington <hp@redhat.com> | 2003-03-21 02:38:40 +0000 |
commit | b6ffea177fccb6cc4e65992da7d8b390054277f7 (patch) | |
tree | 5194ad93d495c110c88b7730f05b9265dd6ce73d | |
parent | 056d76d809dc341b0dce160d3f79062604565c77 (diff) |
2003-03-20 Havoc Pennington <hp@redhat.com>
* dbus/dbus-connection.c (dbus_connection_set_unix_user_function):
new function
(dbus_connection_get_unix_user): new function
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | bus/bus.c | 47 | ||||
-rw-r--r-- | dbus/dbus-connection.c | 58 | ||||
-rw-r--r-- | dbus/dbus-connection.h | 12 | ||||
-rw-r--r-- | dbus/dbus-sysdeps.c | 16 | ||||
-rw-r--r-- | dbus/dbus-transport-protected.h | 5 | ||||
-rw-r--r-- | dbus/dbus-transport.c | 111 | ||||
-rw-r--r-- | dbus/dbus-transport.h | 60 | ||||
-rw-r--r-- | doc/config-file.txt | 28 |
9 files changed, 296 insertions, 47 deletions
@@ -1,3 +1,9 @@ +2003-03-20 Havoc Pennington <hp@redhat.com> + + * dbus/dbus-connection.c (dbus_connection_set_unix_user_function): + new function + (dbus_connection_get_unix_user): new function + 2003-03-20 Havoc Pennington <hp@pobox.com> * bus/connection.c (bus_connection_send_oom_error): assert that @@ -27,16 +27,23 @@ #include "connection.h" #include "services.h" #include "utils.h" +#include "policy.h" +#include <dbus/dbus-list.h> +#include <dbus/dbus-hash.h> #include <dbus/dbus-internals.h> struct BusContext { int refcount; - char *address; + char *address; DBusServer *server; BusConnections *connections; BusActivation *activation; BusRegistry *registry; + DBusList *default_rules; /**< Default policy rules */ + DBusList *override_rules; /**< Override policy rules */ + DBusHashTable *rules_by_uid; /**< per-UID policy rules */ + DBusHashTable *rules_by_gid; /**< per-GID policy rules */ }; static dbus_bool_t @@ -109,6 +116,14 @@ new_connection_callback (DBusServer *server, /* on OOM, we won't have ref'd the connection so it will die. */ } +static void +free_rule_func (void *data) +{ + BusPolicyRule *rule = data; + + bus_policy_rule_unref (rule); +} + BusContext* bus_context_new (const char *address, const char **service_dirs, @@ -164,6 +179,24 @@ bus_context_new (const char *address, goto failed; } + context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_INT, + NULL, + free_rule_func); + if (context->rules_by_uid == NULL) + { + BUS_SET_OOM (error); + goto failed; + } + + context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_INT, + NULL, + free_rule_func); + if (context->rules_by_gid == NULL) + { + BUS_SET_OOM (error); + goto failed; + } + dbus_server_set_new_connection_function (context->server, new_connection_callback, context, NULL); @@ -260,6 +293,18 @@ bus_context_unref (BusContext *context) dbus_server_unref (context->server); context->server = NULL; } + + if (context->rules_by_uid) + { + _dbus_hash_table_unref (context->rules_by_uid); + context->rules_by_uid = NULL; + } + + if (context->rules_by_gid) + { + _dbus_hash_table_unref (context->rules_by_gid); + context->rules_by_gid = NULL; + } dbus_free (context->address); dbus_free (context); 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 @@ -2211,6 +2211,64 @@ dbus_connection_handle_watch (DBusConnection *connection, } /** + * 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 * registered with dbus_connection_register_handler(). 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; diff --git a/doc/config-file.txt b/doc/config-file.txt index c10cd7ad..ae581924 100644 --- a/doc/config-file.txt +++ b/doc/config-file.txt @@ -94,6 +94,8 @@ Elements: own="servicename" send_to="servicename" receive_from="servicename" + user="username" + group="groupname" Examples: <deny send="org.freedesktop.System.Reboot"/> @@ -101,6 +103,8 @@ Elements: <deny own="org.freedesktop.System"/> <deny send_to="org.freedesktop.System"/> <deny receive_from="org.freedesktop.System"/> + <deny user="john"/> + <deny group="enemies"/> send_to and receive_from mean that messages may not be sent to or received from the *owner* of the given service, not that @@ -108,24 +112,32 @@ Elements: a connection owns services A, B, C, and sending to A is denied, sending to B or C will not work either. - For "servicename" or "messagename" the character "*" can be - substituted, meaning "any." Complex globs like "foo.bar.*" aren't - allowed for now because they'd be work to implement and maybe - encourage sloppy security anyway. + user and group denials mean that the given user or group may + not connect to the message bus. - FIXME should we allow send/send_to and receive/receive_from - to both be specified, in which case they would be ANDed together? + For "servicename" or "messagename" or "username" or "groupname" + the character "*" can be substituted, meaning "any." Complex globs + like "foo.bar.*" aren't allowed for now because they'd be work to + implement and maybe encourage sloppy security anyway. + + It does not make sense to deny a user or group inside a <policy> + for a user or group; user/group denials can only be inside + context="default" or context="required" policies. + + A single <deny> rule may specify both send and send_to, OR both + receive and receive_from. In this case, the denial applies only if + both attributes match the message being denied. e.g. <deny send="foo.bar" send_to="foo.blah"/> would deny messages of the given name AND to the given service. - Probably need to see how hard/slow all this will be to implement. - <allow> send="messagename" receive="messagename" own="servicename" send_to="servicename" receive_from="servicename" + user="username" + group="groupname" Makes an exception to previous <deny> statements. Works just like <deny> but with the inverse meaning. |