diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | bus/bus.c | 205 | ||||
-rw-r--r-- | bus/bus.h | 24 | ||||
-rw-r--r-- | bus/connection.c | 123 | ||||
-rw-r--r-- | bus/connection.h | 7 | ||||
-rw-r--r-- | bus/policy.c | 20 | ||||
-rw-r--r-- | bus/policy.h | 24 | ||||
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | dbus/dbus-connection.c | 10 | ||||
-rw-r--r-- | dbus/dbus-hash.c | 143 | ||||
-rw-r--r-- | dbus/dbus-hash.h | 96 | ||||
-rw-r--r-- | dbus/dbus-sysdeps.c | 183 | ||||
-rw-r--r-- | dbus/dbus-sysdeps.h | 8 | ||||
-rw-r--r-- | dbus/dbus-transport.c | 8 | ||||
-rw-r--r-- | doc/config-file.txt | 8 |
15 files changed, 796 insertions, 75 deletions
@@ -1,3 +1,13 @@ +2003-03-23 Havoc Pennington <hp@pobox.com> + + * bus/policy.c, bus/bus.c, bus/connection.c: implement allow/deny + policies code + + * dbus/dbus-hash.h: add ULONG hash keys + + * dbus/dbus-sysdeps.c (_dbus_get_groups): new + (_dbus_get_group_id): new function + 2003-03-20 Havoc Pennington <hp@redhat.com> * dbus/dbus-connection.c (dbus_connection_set_unix_user_function): @@ -41,7 +41,7 @@ struct BusContext BusActivation *activation; BusRegistry *registry; DBusList *default_rules; /**< Default policy rules */ - DBusList *override_rules; /**< Override policy rules */ + DBusList *mandatory_rules; /**< Mandatory policy rules */ DBusHashTable *rules_by_uid; /**< per-UID policy rules */ DBusHashTable *rules_by_gid; /**< per-GID policy rules */ }; @@ -117,13 +117,26 @@ new_connection_callback (DBusServer *server, } static void -free_rule_func (void *data) +free_rule_func (void *data, + void *user_data) { BusPolicyRule *rule = data; bus_policy_rule_unref (rule); } +static void +free_rule_list_func (void *data) +{ + DBusList **list = data; + + _dbus_list_foreach (list, free_rule_func, NULL); + + _dbus_list_clear (list); + + dbus_free (list); +} + BusContext* bus_context_new (const char *address, const char **service_dirs, @@ -179,18 +192,18 @@ bus_context_new (const char *address, goto failed; } - context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_INT, + context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG, NULL, - free_rule_func); + free_rule_list_func); if (context->rules_by_uid == NULL) { BUS_SET_OOM (error); goto failed; } - context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_INT, + context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG, NULL, - free_rule_func); + free_rule_list_func); if (context->rules_by_gid == NULL) { BUS_SET_OOM (error); @@ -328,3 +341,183 @@ bus_context_get_activation (BusContext *context) { return context->activation; } + +static dbus_bool_t +list_allows_user (dbus_bool_t def, + DBusList **list, + unsigned long uid, + const unsigned long *group_ids, + int n_group_ids) +{ + DBusList *link; + dbus_bool_t allowed; + + allowed = def; + + link = _dbus_list_get_first_link (list); + while (link != NULL) + { + BusPolicyRule *rule = link->data; + link = _dbus_list_get_next_link (list, link); + + if (rule->type == BUS_POLICY_RULE_USER) + { + if (rule->d.user.uid != uid) + continue; + } + else if (rule->type == BUS_POLICY_RULE_GROUP) + { + int i; + + i = 0; + while (i < n_group_ids) + { + if (rule->d.group.gid == group_ids[i]) + break; + ++i; + } + + if (i == n_group_ids) + continue; + } + else + continue; + + allowed = rule->allow; + } + + return allowed; +} + +dbus_bool_t +bus_context_allow_user (BusContext *context, + unsigned long uid) +{ + dbus_bool_t allowed; + unsigned long *group_ids; + int n_group_ids; + + /* On OOM or error we always reject the user */ + if (!_dbus_get_groups (uid, &group_ids, &n_group_ids)) + { + _dbus_verbose ("Did not get any groups for UID %lu\n", + uid); + return FALSE; + } + + allowed = FALSE; + + allowed = list_allows_user (allowed, + &context->default_rules, + uid, + group_ids, n_group_ids); + + allowed = list_allows_user (allowed, + &context->mandatory_rules, + uid, + group_ids, n_group_ids); + + dbus_free (group_ids); + + return allowed; +} + +static dbus_bool_t +add_list_to_policy (DBusList **list, + BusPolicy *policy) +{ + DBusList *link; + + link = _dbus_list_get_first_link (list); + while (link != NULL) + { + BusPolicyRule *rule = link->data; + link = _dbus_list_get_next_link (list, link); + + switch (rule->type) + { + case BUS_POLICY_RULE_USER: + case BUS_POLICY_RULE_GROUP: + /* These aren't per-connection policies */ + break; + + case BUS_POLICY_RULE_OWN: + case BUS_POLICY_RULE_SEND: + case BUS_POLICY_RULE_RECEIVE: + /* These are per-connection */ + if (!bus_policy_append_rule (policy, rule)) + return FALSE; + break; + } + } + + return TRUE; +} + +BusPolicy* +bus_context_create_connection_policy (BusContext *context, + DBusConnection *connection) +{ + BusPolicy *policy; + unsigned long uid; + DBusList **list; + + _dbus_assert (dbus_connection_get_is_authenticated (connection)); + + policy = bus_policy_new (); + if (policy == NULL) + return NULL; + + if (!add_list_to_policy (&context->default_rules, + policy)) + goto failed; + + /* we avoid the overhead of looking up user's groups + * if we don't have any group rules anyway + */ + if (_dbus_hash_table_get_n_entries (context->rules_by_gid) > 0) + { + const unsigned long *groups; + int n_groups; + int i; + + if (!bus_connection_get_groups (connection, &groups, &n_groups)) + goto failed; + + i = 0; + while (i < n_groups) + { + list = _dbus_hash_table_lookup_ulong (context->rules_by_gid, + groups[i]); + + if (list != NULL) + { + if (!add_list_to_policy (list, policy)) + goto failed; + } + + ++i; + } + } + + if (!dbus_connection_get_unix_user (connection, &uid)) + goto failed; + + list = _dbus_hash_table_lookup_ulong (context->rules_by_uid, + uid); + + if (!add_list_to_policy (list, policy)) + goto failed; + + if (!add_list_to_policy (&context->mandatory_rules, + policy)) + goto failed; + + bus_policy_optimize (policy); + + return policy; + + failed: + bus_policy_unref (policy); + return NULL; +} @@ -32,19 +32,25 @@ typedef struct BusActivation BusActivation; typedef struct BusConnections BusConnections; typedef struct BusContext BusContext; +typedef struct BusPolicy BusPolicy; +typedef struct BusPolicyRule BusPolicyRule; typedef struct BusRegistry BusRegistry; typedef struct BusService BusService; typedef struct BusTransaction BusTransaction; -BusContext* bus_context_new (const char *address, - const char **service_dirs, - DBusError *error); -void bus_context_shutdown (BusContext *context); -void bus_context_ref (BusContext *context); -void bus_context_unref (BusContext *context); -BusRegistry* bus_context_get_registry (BusContext *context); -BusConnections* bus_context_get_connections (BusContext *context); -BusActivation* bus_context_get_activation (BusContext *context); +BusContext* bus_context_new (const char *address, + const char **service_dirs, + DBusError *error); +void bus_context_shutdown (BusContext *context); +void bus_context_ref (BusContext *context); +void bus_context_unref (BusContext *context); +BusRegistry* bus_context_get_registry (BusContext *context); +BusConnections* bus_context_get_connections (BusContext *context); +BusActivation* bus_context_get_activation (BusContext *context); +dbus_bool_t bus_context_allow_user (BusContext *context, + unsigned long uid); +BusPolicy* bus_context_create_connection_policy (BusContext *context, + DBusConnection *connection); #endif /* BUS_BUS_H */ diff --git a/bus/connection.c b/bus/connection.c index b01cbde6..773ed5ef 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -23,6 +23,7 @@ #include "connection.h" #include "dispatch.h" #include "loop.h" +#include "policy.h" #include "services.h" #include "utils.h" #include <dbus/dbus-list.h> @@ -48,6 +49,9 @@ typedef struct DBusList *transaction_messages; /**< Stuff we need to send as part of a transaction */ DBusMessage *oom_message; DBusPreallocatedSend *oom_preallocated; + unsigned long *group_ids; + int n_group_ids; + BusPolicy *policy; } BusConnectionData; #define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot)) @@ -231,6 +235,20 @@ remove_connection_timeout (DBusTimeout *timeout, bus_loop_remove_timeout (timeout, connection_timeout_callback, connection); } +static dbus_bool_t +allow_user_function (DBusConnection *connection, + unsigned long uid, + void *data) +{ + BusConnectionData *d; + + d = BUS_CONNECTION_DATA (connection); + + _dbus_assert (d != NULL); + + return bus_context_allow_user (d->connections->context, uid); +} + static void free_connection_data (void *data) { @@ -246,6 +264,11 @@ free_connection_data (void *data) if (d->oom_message) dbus_message_unref (d->oom_message); + + if (d->policy) + bus_policy_unref (d->policy); + + dbus_free (d->group_ids); dbus_free (d->name); @@ -333,6 +356,9 @@ bus_connections_setup_connection (BusConnections *connections, } retval = FALSE; + + d->n_group_ids = 0; + d->group_ids = NULL; if (!dbus_connection_set_watch_functions (connection, (DBusAddWatchFunction) add_connection_watch, @@ -387,6 +413,103 @@ bus_connections_setup_connection (BusConnections *connections, return retval; } +dbus_bool_t +bus_connection_get_groups (DBusConnection *connection, + const unsigned long **groups, + int *n_groups) +{ + BusConnectionData *d; + + d = BUS_CONNECTION_DATA (connection); + + _dbus_assert (d != NULL); + + *groups = NULL; + *n_groups = 0; + + /* we do a lazy lookup on groups a user is in for two reasons: + * 1) we can't do it on connection setup since the user + * hasn't authenticated and 2) it might be expensive + * and we don't need to do it if there are no group-based + * rules in the config file + */ + + if (d->n_group_ids == 0) + { + unsigned long uid; + + if (dbus_connection_get_unix_user (connection, &uid)) + { + if (!_dbus_get_groups (uid, &d->group_ids, &d->n_group_ids)) + { + _dbus_verbose ("Did not get any groups for UID %lu\n", + uid); + return FALSE; + } + } + } + + *groups = d->group_ids; + *n_groups = d->n_group_ids; + + return TRUE; +} + +dbus_bool_t +bus_connection_is_in_group (DBusConnection *connection, + unsigned long gid) +{ + int i; + const unsigned long *group_ids; + int n_group_ids; + + if (!bus_connection_get_groups (connection, &group_ids, &n_group_ids)) + return FALSE; + + i = 0; + while (i < n_group_ids) + { + if (group_ids[i] == gid) + return TRUE; + ++i; + } + + return FALSE; +} + +BusPolicy* +bus_connection_get_policy (DBusConnection *connection) +{ + BusConnectionData *d; + + d = BUS_CONNECTION_DATA (connection); + + _dbus_assert (d != NULL); + + if (!dbus_connection_get_is_authenticated (connection)) + { + _dbus_verbose ("Tried to get policy for unauthenticated connection!\n"); + return NULL; + } + + /* We do lazy creation of the policy because + * it can only be done post-authentication. + */ + if (d->policy == NULL) + { + d->policy = + bus_context_create_connection_policy (d->connections->context, + connection); + + /* we may have a NULL policy on OOM or error getting list of + * groups for a user. In the latter case we don't handle it so + * well currently, just keep pretending we're out of memory, + * which is kind of bizarre. + */ + } + + return d->policy; +} /** * Calls function on each connection; if the function returns diff --git a/bus/connection.h b/bus/connection.h index f78c3ac1..0d64e987 100644 --- a/bus/connection.h +++ b/bus/connection.h @@ -66,6 +66,13 @@ const char *bus_connection_get_name (DBusConnection *connection); /* called by dispatch.c when the connection is dropped */ void bus_connection_disconnected (DBusConnection *connection); +dbus_bool_t bus_connection_is_in_group (DBusConnection *connection, + unsigned long gid); +dbus_bool_t bus_connection_get_groups (DBusConnection *connection, + const unsigned long **groups, + int *n_groups); +BusPolicy* bus_connection_get_policy (DBusConnection *connection); + /* transaction API so we can send or not send a block of messages as a whole */ BusTransaction* bus_transaction_new (BusContext *context); BusContext* bus_transaction_get_context (BusTransaction *transaction); diff --git a/bus/policy.c b/bus/policy.c index f916383c..015757a0 100644 --- a/bus/policy.c +++ b/bus/policy.c @@ -156,7 +156,7 @@ remove_rules_by_type_up_to (BusPolicy *policy, } } -static void +void bus_policy_optimize (BusPolicy *policy) { DBusList *link; @@ -175,6 +175,9 @@ bus_policy_optimize (BusPolicy *policy) * file. */ + _dbus_verbose ("Optimizing policy with %d rules\n", + _dbus_list_get_length (&policy->rules)); + link = _dbus_list_get_first (&policy->rules); while (link != NULL) { @@ -208,6 +211,21 @@ bus_policy_optimize (BusPolicy *policy) link = next; } + + _dbus_verbose ("After optimization, policy has %d rules\n", + _dbus_list_get_length (&policy->rules)); +} + +dbus_bool_t +bus_policy_append_rule (BusPolicy *policy, + BusPolicyRule *rule) +{ + if (!_dbus_list_append (policy->rules, rule)) + return FALSE; + + bus_policy_rule_ref (rule); + + return TRUE; } dbus_bool_t diff --git a/bus/policy.h b/bus/policy.h index 00d60baa..680ad581 100644 --- a/bus/policy.h +++ b/bus/policy.h @@ -28,14 +28,13 @@ #include <dbus/dbus-string.h> #include "bus.h" -typedef struct BusPolicy BusPolicy; -typedef struct BusPolicyRule BusPolicyRule; - typedef enum { BUS_POLICY_RULE_SEND, BUS_POLICY_RULE_RECEIVE, - BUS_POLICY_RULE_OWN + BUS_POLICY_RULE_OWN, + BUS_POLICY_RULE_USER, + BUS_POLICY_RULE_GROUP } BusPolicyRuleType; struct BusPolicyRule @@ -68,6 +67,18 @@ struct BusPolicyRule char *service_name; } own; + struct + { + char *user; + unsigned long uid; + } user; + + struct + { + char *group; + unsigned long gid; + } group; + } d; }; @@ -90,7 +101,8 @@ dbus_bool_t bus_policy_check_can_receive (BusPolicy *policy, dbus_bool_t bus_policy_check_can_own (BusPolicy *policy, DBusConnection *connection, const DBusString *service_name); - - +dbus_bool_t bus_policy_append_rule (BusPolicy *policy, + BusPolicyRule *rule); +void bus_policy_optimize (BusPolicy *policy); #endif /* BUS_POLICY_H */ diff --git a/configure.in b/configure.in index 920c7708..40557732 100644 --- a/configure.in +++ b/configure.in @@ -134,7 +134,7 @@ AC_C_BIGENDIAN AC_CHECK_LIB(socket,socket) AC_CHECK_LIB(nsl,gethostbyname) -AC_CHECK_FUNCS(vsnprintf vasprintf nanosleep usleep poll setenv socketpair) +AC_CHECK_FUNCS(vsnprintf vasprintf nanosleep usleep poll setenv socketpair getgrouplist) AC_CHECK_HEADERS(execinfo.h, [AC_CHECK_FUNCS(backtrace)]) diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index db621405..ad8a1724 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -2214,6 +2214,8 @@ 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. + * Always returns #FALSE prior to authenticating the + * connection. * * @param connection the connection * @param uid return location for the user ID @@ -2226,8 +2228,12 @@ dbus_connection_get_unix_user (DBusConnection *connection, dbus_bool_t result; dbus_mutex_lock (connection->mutex); - result = _dbus_transport_get_unix_user (connection->transport, - uid); + + if (!_dbus_transport_get_is_authenticated (connection->transport)) + result = FALSE; + else + result = _dbus_transport_get_unix_user (connection->transport, + uid); dbus_mutex_unlock (connection->mutex); return result; diff --git a/dbus/dbus-hash.c b/dbus/dbus-hash.c index a53c5020..ff3f3b08 100644 --- a/dbus/dbus-hash.c +++ b/dbus/dbus-hash.c @@ -313,6 +313,7 @@ _dbus_hash_table_new (DBusHashType type, { case DBUS_HASH_INT: case DBUS_HASH_POINTER: + case DBUS_HASH_ULONG: table->find_function = find_direct_function; break; case DBUS_HASH_STRING: @@ -644,6 +645,25 @@ _dbus_hash_iter_get_int_key (DBusHashIter *iter) /** * Gets the key for the current entry. + * Only works for hash tables of type #DBUS_HASH_ULONG. + * + * @param iter the hash table iterator. + */ +unsigned long +_dbus_hash_iter_get_ulong_key (DBusHashIter *iter) +{ + DBusRealHashIter *real; + + real = (DBusRealHashIter*) iter; + + _dbus_assert (real->table != NULL); + _dbus_assert (real->entry != NULL); + + return (unsigned long) real->entry->key; +} + +/** + * Gets the key for the current entry. * Only works for hash tables of type #DBUS_HASH_STRING * @param iter the hash table iterator. */ @@ -963,6 +983,7 @@ rebuild_table (DBusHashTable *table) idx = string_hash (entry->key) & table->mask; break; case DBUS_HASH_INT: + case DBUS_HASH_ULONG: case DBUS_HASH_POINTER: idx = RANDOM_INDEX (table, entry->key); break; @@ -1060,6 +1081,31 @@ _dbus_hash_table_lookup_pointer (DBusHashTable *table, } /** + * Looks up the value for a given integer in a hash table + * of type #DBUS_HASH_ULONG. Returns %NULL if the value + * is not present. (A not-present entry is indistinguishable + * from an entry with a value of %NULL.) + * @param table the hash table. + * @param key the integer to look up. + * @returns the value of the hash entry. + */ +void* +_dbus_hash_table_lookup_ulong (DBusHashTable *table, + unsigned long key) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_ULONG); + + entry = (* table->find_function) (table, (void*) key, FALSE, NULL); + + if (entry) + return entry->value; + else + return NULL; +} + +/** * Removes the hash entry for the given key. If no hash entry * for the key exists, does nothing. * @@ -1145,6 +1191,34 @@ _dbus_hash_table_remove_pointer (DBusHashTable *table, /** + * Removes the hash entry for the given key. If no hash entry + * for the key exists, does nothing. + * + * @param table the hash table. + * @param key the hash key. + * @returns #TRUE if the entry existed + */ +dbus_bool_t +_dbus_hash_table_remove_ulong (DBusHashTable *table, + unsigned long key) +{ + DBusHashEntry *entry; + DBusHashEntry **bucket; + + _dbus_assert (table->key_type == DBUS_HASH_ULONG); + + entry = (* table->find_function) (table, (void*) key, FALSE, &bucket); + + if (entry) + { + remove_entry (table, bucket, entry); + return TRUE; + } + else + return FALSE; +} + +/** * Creates a hash entry with the given key and value. * The key and value are not copied; they are stored * in the hash table by reference. If an entry with the @@ -1267,6 +1341,48 @@ _dbus_hash_table_insert_pointer (DBusHashTable *table, return TRUE; } + +/** + * Creates a hash entry with the given key and value. + * The key and value are not copied; they are stored + * in the hash table by reference. If an entry with the + * given key already exists, the previous key and value + * are overwritten (and freed if the hash table has + * a key_free_function and/or value_free_function). + * + * Returns #FALSE if memory for the new hash entry + * can't be allocated. + * + * @param table the hash table. + * @param key the hash entry key. + * @param value the hash entry value. + */ +dbus_bool_t +_dbus_hash_table_insert_ulong (DBusHashTable *table, + unsigned long key, + void *value) +{ + DBusHashEntry *entry; + + _dbus_assert (table->key_type == DBUS_HASH_ULONG); + + entry = (* table->find_function) (table, (void*) key, TRUE, NULL); + + if (entry == NULL) + return FALSE; /* no memory */ + + if (table->free_key_function && entry->key != (void*) key) + (* table->free_key_function) (entry->key); + + if (table->free_value_function && entry->value != value) + (* table->free_value_function) (entry->value); + + entry->key = (void*) key; + entry->value = value; + + return TRUE; +} + /** * Gets the number of hash entries in a hash table. * @@ -1316,6 +1432,7 @@ _dbus_hash_test (void) int i; DBusHashTable *table1; DBusHashTable *table2; + DBusHashTable *table3; DBusHashIter iter; #define N_HASH_KEYS 5000 char **keys; @@ -1352,6 +1469,11 @@ _dbus_hash_test (void) if (table2 == NULL) goto out; + table3 = _dbus_hash_table_new (DBUS_HASH_ULONG, + NULL, dbus_free); + if (table3 == NULL) + goto out; + /* Insert and remove a bunch of stuff, counting the table in between * to be sure it's not broken and that iteration works */ @@ -1379,9 +1501,18 @@ _dbus_hash_test (void) if (!_dbus_hash_table_insert_int (table2, i, value)) goto out; + + value = _dbus_strdup (keys[i]); + if (value == NULL) + goto out; + + if (!_dbus_hash_table_insert_ulong (table3, + i, value)) + goto out; _dbus_assert (count_entries (table1) == i + 1); _dbus_assert (count_entries (table2) == i + 1); + _dbus_assert (count_entries (table3) == i + 1); value = _dbus_hash_table_lookup_string (table1, keys[i]); _dbus_assert (value != NULL); @@ -1390,6 +1521,10 @@ _dbus_hash_test (void) value = _dbus_hash_table_lookup_int (table2, i); _dbus_assert (value != NULL); _dbus_assert (strcmp (value, keys[i]) == 0); + + value = _dbus_hash_table_lookup_ulong (table3, i); + _dbus_assert (value != NULL); + _dbus_assert (strcmp (value, keys[i]) == 0); ++i; } @@ -1402,19 +1537,25 @@ _dbus_hash_test (void) _dbus_hash_table_remove_int (table2, i); + _dbus_hash_table_remove_ulong (table3, i); + _dbus_assert (count_entries (table1) == i); _dbus_assert (count_entries (table2) == i); + _dbus_assert (count_entries (table3) == i); --i; } _dbus_hash_table_ref (table1); _dbus_hash_table_ref (table2); + _dbus_hash_table_ref (table3); _dbus_hash_table_unref (table1); _dbus_hash_table_unref (table2); + _dbus_hash_table_unref (table3); _dbus_hash_table_unref (table1); _dbus_hash_table_unref (table2); - + _dbus_hash_table_unref (table3); + table3 = NULL; /* Insert a bunch of stuff then check * that iteration works correctly (finds the right diff --git a/dbus/dbus-hash.h b/dbus/dbus-hash.h index dc60679b..b9efcebb 100644 --- a/dbus/dbus-hash.h +++ b/dbus/dbus-hash.h @@ -52,53 +52,61 @@ typedef struct DBusHashIter DBusHashIter; */ typedef enum { - DBUS_HASH_STRING, /**< Hash keys are strings. */ - DBUS_HASH_INT, /**< Hash keys are integers. */ - DBUS_HASH_POINTER /**< Hash keys are pointers. */ + DBUS_HASH_STRING, /**< Hash keys are strings. */ + DBUS_HASH_INT, /**< Hash keys are integers. */ + DBUS_HASH_POINTER, /**< Hash keys are pointers. */ + DBUS_HASH_ULONG /**< Hash keys are unsigned long. */ } DBusHashType; -DBusHashTable* _dbus_hash_table_new (DBusHashType type, - DBusFreeFunction key_free_function, - DBusFreeFunction value_free_function); -void _dbus_hash_table_ref (DBusHashTable *table); -void _dbus_hash_table_unref (DBusHashTable *table); +DBusHashTable* _dbus_hash_table_new (DBusHashType type, + DBusFreeFunction key_free_function, + DBusFreeFunction value_free_function); +void _dbus_hash_table_ref (DBusHashTable *table); +void _dbus_hash_table_unref (DBusHashTable *table); +void _dbus_hash_iter_init (DBusHashTable *table, + DBusHashIter *iter); +dbus_bool_t _dbus_hash_iter_next (DBusHashIter *iter); +void _dbus_hash_iter_remove_entry (DBusHashIter *iter); +void* _dbus_hash_iter_get_value (DBusHashIter *iter); +void _dbus_hash_iter_set_value (DBusHashIter *iter, + void *value); +int _dbus_hash_iter_get_int_key (DBusHashIter *iter); +const char* _dbus_hash_iter_get_string_key (DBusHashIter *iter); +unsigned long _dbus_hash_iter_get_ulong_key (DBusHashIter *iter); +dbus_bool_t _dbus_hash_iter_lookup (DBusHashTable *table, + void *key, + dbus_bool_t create_if_not_found, + DBusHashIter *iter); +void* _dbus_hash_table_lookup_string (DBusHashTable *table, + const char *key); +void* _dbus_hash_table_lookup_int (DBusHashTable *table, + int key); +void* _dbus_hash_table_lookup_pointer (DBusHashTable *table, + void *key); +void* _dbus_hash_table_lookup_ulong (DBusHashTable *table, + unsigned long key); +dbus_bool_t _dbus_hash_table_remove_string (DBusHashTable *table, + const char *key); +dbus_bool_t _dbus_hash_table_remove_int (DBusHashTable *table, + int key); +dbus_bool_t _dbus_hash_table_remove_pointer (DBusHashTable *table, + void *key); +dbus_bool_t _dbus_hash_table_remove_ulong (DBusHashTable *table, + unsigned long key); +dbus_bool_t _dbus_hash_table_insert_string (DBusHashTable *table, + char *key, + void *value); +dbus_bool_t _dbus_hash_table_insert_int (DBusHashTable *table, + int key, + void *value); +dbus_bool_t _dbus_hash_table_insert_pointer (DBusHashTable *table, + void *key, + void *value); +dbus_bool_t _dbus_hash_table_insert_ulong (DBusHashTable *table, + unsigned long key, + void *value); +int _dbus_hash_table_get_n_entries (DBusHashTable *table); -void _dbus_hash_iter_init (DBusHashTable *table, - DBusHashIter *iter); -dbus_bool_t _dbus_hash_iter_next (DBusHashIter *iter); - -void _dbus_hash_iter_remove_entry (DBusHashIter *iter); -void* _dbus_hash_iter_get_value (DBusHashIter *iter); -void _dbus_hash_iter_set_value (DBusHashIter *iter, - void *value); -int _dbus_hash_iter_get_int_key (DBusHashIter *iter); -const char* _dbus_hash_iter_get_string_key (DBusHashIter *iter); -dbus_bool_t _dbus_hash_iter_lookup (DBusHashTable *table, - void *key, - dbus_bool_t create_if_not_found, - DBusHashIter *iter); -void* _dbus_hash_table_lookup_string (DBusHashTable *table, - const char *key); -void* _dbus_hash_table_lookup_int (DBusHashTable *table, - int key); -void* _dbus_hash_table_lookup_pointer (DBusHashTable *table, - void *key); -dbus_bool_t _dbus_hash_table_remove_string (DBusHashTable *table, - const char *key); -dbus_bool_t _dbus_hash_table_remove_int (DBusHashTable *table, - int key); -dbus_bool_t _dbus_hash_table_remove_pointer (DBusHashTable *table, - void *key); -dbus_bool_t _dbus_hash_table_insert_string (DBusHashTable *table, - char *key, - void *value); -dbus_bool_t _dbus_hash_table_insert_int (DBusHashTable *table, - int key, - void *value); -dbus_bool_t _dbus_hash_table_insert_pointer (DBusHashTable *table, - void *key, - void *value); -int _dbus_hash_table_get_n_entries (DBusHashTable *table); DBUS_END_DECLS; diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index cd4a82a5..200fbddd 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -43,6 +43,7 @@ #include <sys/wait.h> #include <netinet/in.h> #include <netdb.h> +#include <grp.h> #ifdef HAVE_WRITEV #include <sys/uio.h> @@ -1217,6 +1218,20 @@ _dbus_credentials_from_username (const DBusString *username, return get_user_info (username, -1, credentials, NULL, NULL); } +/** + * Gets the credentials corresponding to the given user ID. + * + * @param user_id the user ID + * @param credentials credentials to fill in + * @returns #TRUE if the username existed and we got some credentials + */ +dbus_bool_t +_dbus_credentials_from_user_id (unsigned long user_id, + DBusCredentials *credentials) +{ + return get_user_info (NULL, user_id, credentials, NULL, NULL); +} + static DBusMutex *user_info_lock = NULL; /** * Initializes the global mutex for the process's user information. @@ -1399,6 +1414,174 @@ _dbus_credentials_match (const DBusCredentials *expected_credentials, } /** + * Gets group ID from group name. + * + * @param group_name name of the group + * @param gid location to store group ID + * @returns #TRUE if group was known + */ +dbus_bool_t +_dbus_get_group_id (const DBusString *group_name, + unsigned long *gid) +{ + const char *group_c_str; + + _dbus_string_get_const_data (group_name, &group_c_str); + + /* For now assuming that the getgrnam() and getgrgid() flavors + * always correspond to the pwnam flavors, if not we have + * to add more configure checks. + */ + +#if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R) + { + struct group *g; + int result; + char buf[1024]; + struct group g_str; + + g = NULL; +#ifdef HAVE_POSIX_GETPWNAME_R + + result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf), + &g); +#else + p = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf)); + result = 0; +#endif /* !HAVE_POSIX_GETPWNAME_R */ + if (result == 0 && g == &g_str) + { + *gid = g->gr_gid; + return TRUE; + } + else + { + _dbus_verbose ("Group %s unknown\n", group_c_str); + return FALSE; + } + } +#else /* ! HAVE_GETPWNAM_R */ + { + /* I guess we're screwed on thread safety here */ + struct group *g; + + g = getgrnam (group_c_str); + + if (g != NULL) + { + *gid = g->gr_gid; + return TRUE; + } + else + { + _dbus_verbose ("Group %s unknown\n", group_c_str); + return FALSE; + } + } +#endif /* ! HAVE_GETPWNAM_R */ +} + +/** + * Gets all groups for a particular user. Returns #FALSE + * if no memory, or user isn't known, but always initializes + * group_ids to a NULL array. + * + * @todo failing to distinguish "out of memory" from + * "unknown user" is kind of bogus and would probably + * result in a failure in a comprehensive test suite. + * + * @param uid the user ID + * @param group_ids return location for array of group IDs + * @param n_group_ids return location for length of returned array + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_get_groups (unsigned long uid, + unsigned long **group_ids, + int *n_group_ids) +{ + DBusCredentials creds; + DBusString username; + const char *username_c; + dbus_bool_t retval; + + *group_ids = NULL; + *n_group_ids = 0; + + retval = FALSE; + + if (!_dbus_string_init (&username, _DBUS_INT_MAX)) + return FALSE; + + if (!get_user_info (NULL, uid, &creds, + NULL, &username) || + creds.gid < 0) + goto out; + + _dbus_string_get_const_data (&username, &username_c); + +#ifdef HAVE_GETGROUPLIST + { + gid_t *buf; + int buf_count; + int i; + + buf_count = 17; + buf = dbus_new (gid_t, buf_count); + if (buf == NULL) + goto out; + + if (getgrouplist (username_c, + creds.gid, + buf, &buf_count) < 0) + { + gid_t *new = dbus_realloc (buf, buf_count * sizeof (buf[0])); + if (new == NULL) + { + dbus_free (buf); + goto out; + } + + buf = new; + + getgrouplist (username_c, creds.gid, buf, &buf_count); + } + + *group_ids = dbus_new (unsigned long, buf_count); + if (*group_ids == NULL) + { + dbus_free (buf); + goto out; + } + + for (i = 0; i < buf_count; ++i) + (*group_ids)[i] = buf[i]; + + *n_group_ids = buf_count; + + dbus_free (buf); + } +#else /* HAVE_GETGROUPLIST */ + { + /* We just get the one group ID */ + *group_ids = dbus_new (unsigned long, 1); + if (*group_ids == NULL) + goto out; + + *n_group_ids = 1; + + (*group_ids)[0] = creds.gid; + } +#endif /* HAVE_GETGROUPLIST */ + + retval = TRUE; + + out: + _dbus_string_free (&username); + return retval; +} + +/** * Appends the uid of the current process to the given string. * * @param str the string to append to diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 0c24d0c5..672d23a8 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -96,6 +96,8 @@ dbus_bool_t _dbus_send_credentials_unix_socket (int server_fd, dbus_bool_t _dbus_credentials_from_username (const DBusString *username, DBusCredentials *credentials); +dbus_bool_t _dbus_credentials_from_user_id (unsigned long user_id, + DBusCredentials *credentials); dbus_bool_t _dbus_credentials_from_uid_string (const DBusString *uid_str, DBusCredentials *credentials); void _dbus_credentials_from_current_process (DBusCredentials *credentials); @@ -110,6 +112,12 @@ dbus_bool_t _dbus_user_info_from_current_process (const DBusString **userna const DBusString **homedir, const DBusCredentials **credentials); +dbus_bool_t _dbus_get_group_id (const DBusString *group_name, + unsigned long *gid); +dbus_bool_t _dbus_get_groups (unsigned long uid, + unsigned long **group_ids, + int *n_group_ids); + typedef int dbus_atomic_t; dbus_atomic_t _dbus_atomic_inc (dbus_atomic_t *atomic); diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index b6ab8c0a..b6ad8f9a 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -353,10 +353,12 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport) return TRUE; else { + dbus_bool_t maybe_authenticated; + if (transport->disconnected) return FALSE; - transport->authenticated = + maybe_authenticated = (!(transport->send_credentials_pending || transport->receive_credentials_pending)) && _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_AUTHENTICATED; @@ -369,7 +371,7 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport) * Or they may give certain identities extra privileges. */ - if (transport->authenticated && transport->is_server) + if (maybe_authenticated && transport->is_server) { DBusCredentials auth_identity; @@ -413,6 +415,8 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport) } } } + + transport->authenticated = maybe_authenticated; return transport->authenticated; } diff --git a/doc/config-file.txt b/doc/config-file.txt index ae581924..093cb413 100644 --- a/doc/config-file.txt +++ b/doc/config-file.txt @@ -60,7 +60,7 @@ Elements: multiple <auth> elements, the last one wins (they are not merged). <policy> - context="(default|required)" one of the context/user/group + context="(default|mandatory)" one of the context/user/group attributes is mandatory user="username or userid" group="group name or gid" @@ -72,8 +72,10 @@ Elements: Policies are applied to a connection as follows: - all context="default" policies are applied - all group="connection's user's group" policies are applied + in undefined order - all user="connection's auth user" policies are applied - - all context="required" policies are applied + in undefined order + - all context="mandatory" policies are applied Policies applied later will override those applied earlier, when the policies overlap. Multiple policies with the same @@ -157,4 +159,4 @@ Elements: -
\ No newline at end of file + |