summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-03-23 07:41:54 +0000
committerHavoc Pennington <hp@redhat.com>2003-03-23 07:41:54 +0000
commita26607ab68bf0878f23d2dbddec781b4b760d034 (patch)
treefe9153c14712ce893b605a7b23b47e4e7e8d17d8
parentb6ffea177fccb6cc4e65992da7d8b390054277f7 (diff)
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
-rw-r--r--ChangeLog10
-rw-r--r--bus/bus.c205
-rw-r--r--bus/bus.h24
-rw-r--r--bus/connection.c123
-rw-r--r--bus/connection.h7
-rw-r--r--bus/policy.c20
-rw-r--r--bus/policy.h24
-rw-r--r--configure.in2
-rw-r--r--dbus/dbus-connection.c10
-rw-r--r--dbus/dbus-hash.c143
-rw-r--r--dbus/dbus-hash.h96
-rw-r--r--dbus/dbus-sysdeps.c183
-rw-r--r--dbus/dbus-sysdeps.h8
-rw-r--r--dbus/dbus-transport.c8
-rw-r--r--doc/config-file.txt8
15 files changed, 796 insertions, 75 deletions
diff --git a/ChangeLog b/ChangeLog
index a96b97ff..aad1a71f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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):
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 <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
+