From a26607ab68bf0878f23d2dbddec781b4b760d034 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sun, 23 Mar 2003 07:41:54 +0000 Subject: 2003-03-23 Havoc Pennington * 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 --- bus/bus.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- bus/bus.h | 24 ++++--- bus/connection.c | 123 +++++++++++++++++++++++++++++++++ bus/connection.h | 7 ++ bus/policy.c | 20 +++++- bus/policy.h | 24 +++++-- 6 files changed, 381 insertions(+), 22 deletions(-) (limited to 'bus') diff --git a/bus/bus.c b/bus/bus.c index 4b5b23b5..304faa99 100644 --- a/bus/bus.c +++ b/bus/bus.c @@ -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; +} diff --git a/bus/bus.h b/bus/bus.h index eed279e6..56ed5156 100644 --- a/bus/bus.h +++ b/bus/bus.h @@ -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 @@ -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 #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 */ -- cgit