2003-03-23 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Sun, 23 Mar 2003 07:41:54 +0000 (07:41 +0000)
committerHavoc Pennington <hp@redhat.com>
Sun, 23 Mar 2003 07:41:54 +0000 (07:41 +0000)
* 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

15 files changed:
ChangeLog
bus/bus.c
bus/bus.h
bus/connection.c
bus/connection.h
bus/policy.c
bus/policy.h
configure.in
dbus/dbus-connection.c
dbus/dbus-hash.c
dbus/dbus-hash.h
dbus/dbus-sysdeps.c
dbus/dbus-sysdeps.h
dbus/dbus-transport.c
doc/config-file.txt

index a96b97f..aad1a71 100644 (file)
--- 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):
index 4b5b23b..304faa9 100644 (file)
--- 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;
+}
index eed279e..56ed515 100644 (file)
--- a/bus/bus.h
+++ b/bus/bus.h
 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 */
index b01cbde..773ed5e 100644 (file)
@@ -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
index f78c3ac..0d64e98 100644 (file)
@@ -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);
index f916383..015757a 100644 (file)
@@ -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
index 00d60ba..680ad58 100644 (file)
 #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 */
index 920c770..4055773 100644 (file)
@@ -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)])
 
index db62140..ad8a172 100644 (file)
@@ -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;
index a53c502..ff3f3b0 100644 (file)
@@ -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
index dc60679..b9efceb 100644 (file)
@@ -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;
index cd4a82a..200fbdd 100644 (file)
@@ -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
index 0c24d0c..672d23a 100644 (file)
@@ -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);
index b6ab8c0..b6ad8f9 100644 (file)
@@ -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;
     }
index ae58192..093cb41 100644 (file)
@@ -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
+