+
+ return policy;
+}
+
+void
+bus_policy_unref (BusPolicy *policy)
+{
+ _dbus_assert (policy->refcount > 0);
+
+ policy->refcount -= 1;
+
+ if (policy->refcount == 0)
+ {
+ _dbus_list_foreach (&policy->default_rules, free_rule_func, NULL);
+ _dbus_list_clear (&policy->default_rules);
+
+ _dbus_list_foreach (&policy->mandatory_rules, free_rule_func, NULL);
+ _dbus_list_clear (&policy->mandatory_rules);
+
+ _dbus_list_foreach (&policy->at_console_true_rules, free_rule_func, NULL);
+ _dbus_list_clear (&policy->at_console_true_rules);
+
+ _dbus_list_foreach (&policy->at_console_false_rules, free_rule_func, NULL);
+ _dbus_list_clear (&policy->at_console_false_rules);
+
+ if (policy->rules_by_uid)
+ {
+ _dbus_hash_table_unref (policy->rules_by_uid);
+ policy->rules_by_uid = NULL;
+ }
+
+ if (policy->rules_by_gid)
+ {
+ _dbus_hash_table_unref (policy->rules_by_gid);
+ policy->rules_by_gid = NULL;
+ }
+
+ dbus_free (policy);
+ }
+}
+
+static dbus_bool_t
+add_list_to_client (DBusList **list,
+ BusClientPolicy *client)
+{
+ 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_client_policy_append_rule (client, rule))
+ return FALSE;
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+BusClientPolicy*
+bus_policy_create_client_policy (BusPolicy *policy,
+ DBusConnection *connection,
+ DBusError *error)
+{
+ BusClientPolicy *client;
+ dbus_uid_t uid;
+ dbus_bool_t at_console;
+
+ _dbus_assert (dbus_connection_get_is_authenticated (connection));
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ client = bus_client_policy_new ();
+ if (client == NULL)
+ goto nomem;
+
+ if (!add_list_to_client (&policy->default_rules,
+ client))
+ goto nomem;
+
+ /* 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 (policy->rules_by_gid) > 0)
+ {
+ unsigned long *groups;
+ int n_groups;
+ int i;
+
+ if (!bus_connection_get_unix_groups (connection, &groups, &n_groups, error))
+ goto failed;
+
+ i = 0;
+ while (i < n_groups)
+ {
+ DBusList **list;
+
+ list = _dbus_hash_table_lookup_ulong (policy->rules_by_gid,
+ groups[i]);
+
+ if (list != NULL)
+ {
+ if (!add_list_to_client (list, client))
+ {
+ dbus_free (groups);
+ goto nomem;
+ }
+ }
+
+ ++i;
+ }
+
+ dbus_free (groups);
+ }
+
+ if (dbus_connection_get_unix_user (connection, &uid))
+ {
+ if (_dbus_hash_table_get_n_entries (policy->rules_by_uid) > 0)
+ {
+ DBusList **list;
+
+ list = _dbus_hash_table_lookup_ulong (policy->rules_by_uid,
+ uid);
+
+ if (list != NULL)
+ {
+ if (!add_list_to_client (list, client))
+ goto nomem;
+ }
+ }
+
+ /* Add console rules */
+ at_console = _dbus_unix_user_is_at_console (uid, error);
+
+ if (at_console)
+ {
+ if (!add_list_to_client (&policy->at_console_true_rules, client))
+ goto nomem;
+ }
+ else if (dbus_error_is_set (error) == TRUE)
+ {
+ goto failed;
+ }
+ else if (!add_list_to_client (&policy->at_console_false_rules, client))
+ {
+ goto nomem;
+ }
+ }
+
+ if (!add_list_to_client (&policy->mandatory_rules,
+ client))
+ goto nomem;
+
+ bus_client_policy_optimize (client);
+
+ return client;
+
+ nomem:
+ BUS_SET_OOM (error);
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (client)
+ bus_client_policy_unref (client);
+ return NULL;
+}
+
+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)
+ {
+ _dbus_verbose ("List %p user rule uid="DBUS_UID_FORMAT"\n",
+ list, rule->d.user.uid);
+
+ if (rule->d.user.uid == DBUS_UID_UNSET)
+ ; /* '*' wildcard */
+ else if (rule->d.user.uid != uid)
+ continue;
+ }
+ else if (rule->type == BUS_POLICY_RULE_GROUP)
+ {
+ _dbus_verbose ("List %p group rule uid="DBUS_UID_FORMAT"\n",
+ list, rule->d.user.uid);
+
+ if (rule->d.group.gid == DBUS_GID_UNSET)
+ ; /* '*' wildcard */
+ else
+ {
+ 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_policy_allow_unix_user (BusPolicy *policy,
+ 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_unix_groups_from_uid (uid, &group_ids, &n_group_ids))
+ {
+ _dbus_verbose ("Did not get any groups for UID %lu\n",
+ uid);
+ return FALSE;
+ }
+
+ /* Default to "user owning bus" can connect */
+ allowed = _dbus_unix_user_is_process_owner (uid);
+
+ allowed = list_allows_user (allowed,
+ &policy->default_rules,
+ uid,
+ group_ids, n_group_ids);
+
+ allowed = list_allows_user (allowed,
+ &policy->mandatory_rules,
+ uid,
+ group_ids, n_group_ids);
+
+ dbus_free (group_ids);
+
+ _dbus_verbose ("UID %lu allowed = %d\n", uid, allowed);
+
+ return allowed;
+}
+
+/* For now this is never actually called because the default
+ * DBusConnection behavior of 'same user that owns the bus can
+ * connect' is all it would do. Set the windows user function in
+ * connection.c if the config file ever supports doing something
+ * interesting here.
+ */
+dbus_bool_t
+bus_policy_allow_windows_user (BusPolicy *policy,
+ const char *windows_sid)
+{
+ /* Windows has no policies here since only the session bus
+ * is really used for now, so just checking that the
+ * connecting person is the same as the bus owner is fine.
+ */
+ return _dbus_windows_user_is_process_owner (windows_sid);
+}
+
+dbus_bool_t
+bus_policy_append_default_rule (BusPolicy *policy,
+ BusPolicyRule *rule)
+{
+ if (!_dbus_list_append (&policy->default_rules, rule))
+ return FALSE;
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_policy_append_mandatory_rule (BusPolicy *policy,
+ BusPolicyRule *rule)
+{
+ if (!_dbus_list_append (&policy->mandatory_rules, rule))
+ return FALSE;
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+}
+
+
+
+static DBusList**
+get_list (DBusHashTable *hash,
+ unsigned long key)
+{
+ DBusList **list;
+
+ list = _dbus_hash_table_lookup_ulong (hash, key);
+
+ if (list == NULL)
+ {
+ list = dbus_new0 (DBusList*, 1);
+ if (list == NULL)
+ return NULL;
+
+ if (!_dbus_hash_table_insert_ulong (hash, key, list))
+ {
+ dbus_free (list);
+ return NULL;
+ }
+ }
+
+ return list;
+}
+
+dbus_bool_t
+bus_policy_append_user_rule (BusPolicy *policy,
+ dbus_uid_t uid,
+ BusPolicyRule *rule)
+{
+ DBusList **list;
+
+ list = get_list (policy->rules_by_uid, uid);
+
+ if (list == NULL)
+ return FALSE;
+
+ if (!_dbus_list_append (list, rule))
+ return FALSE;
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_policy_append_group_rule (BusPolicy *policy,
+ dbus_gid_t gid,
+ BusPolicyRule *rule)
+{
+ DBusList **list;
+
+ list = get_list (policy->rules_by_gid, gid);
+
+ if (list == NULL)
+ return FALSE;
+
+ if (!_dbus_list_append (list, rule))
+ return FALSE;
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_policy_append_console_rule (BusPolicy *policy,
+ dbus_bool_t at_console,
+ BusPolicyRule *rule)
+{
+ if (at_console)
+ {
+ if (!_dbus_list_append (&policy->at_console_true_rules, rule))
+ return FALSE;
+ }
+ else
+ {
+ if (!_dbus_list_append (&policy->at_console_false_rules, rule))
+ return FALSE;
+ }
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+
+}
+
+static dbus_bool_t
+append_copy_of_policy_list (DBusList **list,
+ DBusList **to_append)
+{
+ DBusList *link;
+ DBusList *tmp_list;
+
+ tmp_list = NULL;
+
+ /* Preallocate all our links */
+ link = _dbus_list_get_first_link (to_append);
+ while (link != NULL)
+ {
+ if (!_dbus_list_append (&tmp_list, link->data))
+ {
+ _dbus_list_clear (&tmp_list);
+ return FALSE;
+ }
+
+ link = _dbus_list_get_next_link (to_append, link);
+ }
+
+ /* Now append them */
+ while ((link = _dbus_list_pop_first_link (&tmp_list)))
+ {
+ bus_policy_rule_ref (link->data);
+ _dbus_list_append_link (list, link);
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+merge_id_hash (DBusHashTable *dest,
+ DBusHashTable *to_absorb)
+{
+ DBusHashIter iter;
+
+ _dbus_hash_iter_init (to_absorb, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ unsigned long id = _dbus_hash_iter_get_ulong_key (&iter);
+ DBusList **list = _dbus_hash_iter_get_value (&iter);
+ DBusList **target = get_list (dest, id);
+
+ if (target == NULL)
+ return FALSE;
+
+ if (!append_copy_of_policy_list (target, list))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_policy_merge (BusPolicy *policy,
+ BusPolicy *to_absorb)
+{
+ /* FIXME Not properly atomic, but as used for configuration files we
+ * don't rely on it quite so much.
+ */
+
+ if (!append_copy_of_policy_list (&policy->default_rules,
+ &to_absorb->default_rules))
+ return FALSE;
+
+ if (!append_copy_of_policy_list (&policy->mandatory_rules,
+ &to_absorb->mandatory_rules))
+ return FALSE;
+
+ if (!append_copy_of_policy_list (&policy->at_console_true_rules,
+ &to_absorb->at_console_true_rules))
+ return FALSE;
+
+ if (!append_copy_of_policy_list (&policy->at_console_false_rules,
+ &to_absorb->at_console_false_rules))
+ return FALSE;
+
+ if (!merge_id_hash (policy->rules_by_uid,
+ to_absorb->rules_by_uid))
+ return FALSE;
+
+ if (!merge_id_hash (policy->rules_by_gid,
+ to_absorb->rules_by_gid))
+ return FALSE;
+
+ return TRUE;
+}
+
+struct BusClientPolicy
+{
+ int refcount;
+
+ DBusList *rules;
+};
+
+BusClientPolicy*
+bus_client_policy_new (void)
+{
+ BusClientPolicy *policy;
+
+ policy = dbus_new0 (BusClientPolicy, 1);
+ if (policy == NULL)
+ return NULL;
+
+ policy->refcount = 1;
+
+ return policy;
+}
+
+BusClientPolicy *
+bus_client_policy_ref (BusClientPolicy *policy)
+{
+ _dbus_assert (policy->refcount > 0);
+
+ policy->refcount += 1;
+
+ return policy;