diff options
| author | Havoc Pennington <hp@redhat.com> | 2003-03-23 07:41:54 +0000 | 
|---|---|---|
| committer | Havoc Pennington <hp@redhat.com> | 2003-03-23 07:41:54 +0000 | 
| commit | a26607ab68bf0878f23d2dbddec781b4b760d034 (patch) | |
| tree | fe9153c14712ce893b605a7b23b47e4e7e8d17d8 | |
| parent | b6ffea177fccb6cc4e65992da7d8b390054277f7 (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-- | 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 +    | 
