Commit bc983ecf authored by Havoc Pennington's avatar Havoc Pennington

2003-04-12 Havoc Pennington <hp@pobox.com>

	* bus/policy.h: change BusPolicy to be the thing from the config
	file, and rename old BusPolicy to BusClientPolicy

	* bus/bus.c, bus/connection.c, bus/config-parser.c: change to
	match change in how policy works

	* dbus/dbus-internals.h: mark assert_not_reached as
	__attribute((noreturn))__
parent 4b773b4a
2003-04-12 Havoc Pennington <hp@pobox.com>
* bus/policy.h: change BusPolicy to be the thing from the config
file, and rename old BusPolicy to BusClientPolicy
* bus/bus.c, bus/connection.c, bus/config-parser.c: change to
match change in how policy works
* dbus/dbus-internals.h: mark assert_not_reached as
__attribute((noreturn))__
2003-04-11 Havoc Pennington <hp@redhat.com>
* configure.in: add another directory to look for qt in.
......
......@@ -43,10 +43,7 @@ struct BusContext
BusConnections *connections;
BusActivation *activation;
BusRegistry *registry;
DBusList *default_rules; /**< Default policy rules */
DBusList *mandatory_rules; /**< Mandatory policy rules */
DBusHashTable *rules_by_uid; /**< per-UID policy rules */
DBusHashTable *rules_by_gid; /**< per-GID policy rules */
BusPolicy *policy;
int activation_timeout; /**< How long to wait for an activation to time out */
int auth_timeout; /**< How long to wait for an authentication to time out */
int max_completed_connections; /**< Max number of authorized connections */
......@@ -213,27 +210,6 @@ new_connection_callback (DBusServer *server,
/* on OOM, we won't have ref'd the connection so it will die. */
}
static void
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);
}
static void
free_server_data (void *data)
{
......@@ -565,25 +541,10 @@ bus_context_new (const DBusString *config_file,
BUS_SET_OOM (error);
goto failed;
}
context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
NULL,
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_ULONG,
NULL,
free_rule_list_func);
if (context->rules_by_gid == NULL)
{
BUS_SET_OOM (error);
goto failed;
}
context->policy = bus_config_parser_steal_policy (parser);
_dbus_assert (context->policy != NULL);
/* Now become a daemon if appropriate */
if (bus_config_parser_get_fork (parser))
{
......@@ -744,18 +705,12 @@ bus_context_unref (BusContext *context)
}
_dbus_list_clear (&context->servers);
if (context->rules_by_uid)
{
_dbus_hash_table_unref (context->rules_by_uid);
context->rules_by_uid = NULL;
}
if (context->rules_by_gid)
if (context->policy)
{
_dbus_hash_table_unref (context->rules_by_gid);
context->rules_by_gid = NULL;
bus_policy_unref (context->policy);
context->policy = NULL;
}
if (context->loop)
{
_dbus_loop_unref (context->loop);
......@@ -821,184 +776,18 @@ bus_context_get_loop (BusContext *context)
return context->loop;
}
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;
return bus_policy_allow_user (context->policy, uid);
}
BusPolicy*
bus_context_create_connection_policy (BusContext *context,
DBusConnection *connection)
BusClientPolicy*
bus_context_create_client_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;
return bus_policy_create_client_policy (context->policy, connection);
}
int
......
......@@ -30,31 +30,33 @@
#include <dbus/dbus-string.h>
#include <dbus/dbus-mainloop.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;
typedef struct BusActivation BusActivation;
typedef struct BusConnections BusConnections;
typedef struct BusContext BusContext;
typedef struct BusPolicy BusPolicy;
typedef struct BusClientPolicy BusClientPolicy;
typedef struct BusPolicyRule BusPolicyRule;
typedef struct BusRegistry BusRegistry;
typedef struct BusService BusService;
typedef struct BusTransaction BusTransaction;
BusContext* bus_context_new (const DBusString *config_file,
int print_addr_fd,
DBusError *error);
void bus_context_shutdown (BusContext *context);
void bus_context_ref (BusContext *context);
void bus_context_unref (BusContext *context);
const char* bus_context_get_type (BusContext *context);
const char* bus_context_get_address (BusContext *context);
BusRegistry* bus_context_get_registry (BusContext *context);
BusConnections* bus_context_get_connections (BusContext *context);
BusActivation* bus_context_get_activation (BusContext *context);
DBusLoop* bus_context_get_loop (BusContext *context);
dbus_bool_t bus_context_allow_user (BusContext *context,
unsigned long uid);
BusClientPolicy* bus_context_create_client_policy (BusContext *context,
DBusConnection *connection);
int bus_context_get_activation_timeout (BusContext *context);
BusContext* bus_context_new (const DBusString *config_file,
int print_addr_fd,
DBusError *error);
void bus_context_shutdown (BusContext *context);
void bus_context_ref (BusContext *context);
void bus_context_unref (BusContext *context);
const char* bus_context_get_type (BusContext *context);
const char* bus_context_get_address (BusContext *context);
BusRegistry* bus_context_get_registry (BusContext *context);
BusConnections* bus_context_get_connections (BusContext *context);
BusActivation* bus_context_get_activation (BusContext *context);
DBusLoop* bus_context_get_loop (BusContext *context);
dbus_bool_t bus_context_allow_user (BusContext *context,
unsigned long uid);
BusPolicy* bus_context_create_connection_policy (BusContext *context,
DBusConnection *connection);
int bus_context_get_activation_timeout (BusContext *context);
#endif /* BUS_BUS_H */
......@@ -23,6 +23,7 @@
#include "config-parser.h"
#include "test.h"
#include "utils.h"
#include "policy.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-internals.h>
#include <string.h>
......@@ -93,6 +94,8 @@ struct BusConfigParser
DBusList *mechanisms; /**< Auth mechanisms */
DBusList *service_dirs; /**< Directories to look for services in */
BusPolicy *policy; /**< Security policy */
unsigned int fork : 1; /**< TRUE to fork into daemon mode */
......@@ -262,7 +265,8 @@ bus_config_parser_new (const DBusString *basedir)
return NULL;
}
if (!_dbus_string_copy (basedir, 0, &parser->basedir, 0))
if (((parser->policy = bus_policy_new ()) == NULL) ||
!_dbus_string_copy (basedir, 0, &parser->basedir, 0))
{
_dbus_string_free (&parser->basedir);
dbus_free (parser);
......@@ -317,6 +321,9 @@ bus_config_parser_unref (BusConfigParser *parser)
_dbus_list_clear (&parser->mechanisms);
_dbus_string_free (&parser->basedir);
if (parser->policy)
bus_policy_unref (parser->policy);
dbus_free (parser);
}
......@@ -627,7 +634,7 @@ start_busconfig_child (BusConfigParser *parser,
return FALSE;
}
if (!locate_attributes (parser, "include",
if (!locate_attributes (parser, "policy",
attribute_names,
attribute_values,
error,
......@@ -637,7 +644,51 @@ start_busconfig_child (BusConfigParser *parser,
NULL))
return FALSE;
/* FIXME */
if (((context && user) ||
(context && group)) ||
(user && group) ||
!(context || user || group))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"<policy> element must have exactly one of (context|user|group) attributes");
return FALSE;
}
if (context != NULL)
{
if (strcmp (context, "default") == 0)
{
}
else if (strcmp (context, "mandatory") == 0)
{
}
else
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"context attribute on <policy> must have the value \"default\" or \"mandatory\", not \"%s\"",
context);
return FALSE;
}
/* FIXME */
}
else if (user != NULL)
{
/* FIXME */
}
else if (group != NULL)
{
/* FIXME */
}
else
{
_dbus_assert_not_reached ("all <policy> attributes null and we didn't set error");
}
return TRUE;
}
......@@ -1269,6 +1320,20 @@ bus_config_parser_get_pidfile (BusConfigParser *parser)
return parser->pidfile;
}
BusPolicy*
bus_config_parser_steal_policy (BusConfigParser *parser)
{
BusPolicy *policy;
_dbus_assert (parser->policy != NULL); /* can only steal the policy 1 time */
policy = parser->policy;
parser->policy = NULL;
return policy;
}
#ifdef DBUS_BUILD_TESTS
#include <stdio.h>
......
......@@ -29,6 +29,7 @@
#include <dbus/dbus.h>
#include <dbus/dbus-string.h>
#include <dbus/dbus-list.h>
#include "bus.h"
/* Whatever XML library we're using just pushes data into this API */
......@@ -62,6 +63,7 @@ DBusList** bus_config_parser_get_mechanisms (BusConfigParser *parser);
dbus_bool_t bus_config_parser_get_fork (BusConfigParser *parser);
const char* bus_config_parser_get_pidfile (BusConfigParser *parser);
DBusList** bus_config_parser_get_service_dirs (BusConfigParser *parser);
BusPolicy* bus_config_parser_steal_policy (BusConfigParser *parser);
/* Loader functions (backended off one of the XML parsers). Returns a
* finished ConfigParser.
......
......@@ -50,7 +50,7 @@ typedef struct
DBusPreallocatedSend *oom_preallocated;
unsigned long *group_ids;
int n_group_ids;
BusPolicy *policy;
BusClientPolicy *policy;
} BusConnectionData;
#define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot))
......@@ -306,7 +306,7 @@ free_connection_data (void *data)
dbus_message_unref (d->oom_message);
if (d->policy)
bus_policy_unref (d->policy);
bus_client_policy_unref (d->policy);
dbus_free (d->group_ids);
......@@ -541,7 +541,7 @@ bus_connection_is_in_group (DBusConnection *connection,
return FALSE;
}
BusPolicy*
BusClientPolicy*
bus_connection_get_policy (DBusConnection *connection)
{
BusConnectionData *d;
......@@ -562,8 +562,8 @@ bus_connection_get_policy (DBusConnection *connection)
if (d->policy == NULL)
{
d->policy =
bus_context_create_connection_policy (d->connections->context,
connection);
bus_context_create_client_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
......
......@@ -70,12 +70,12 @@ 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);
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);
BusClientPolicy* bus_connection_get_policy (DBusConnection *connection);
/* transaction API so we can send or not send a block of messages as a whole */
......
/* -*- mode: C; c-file-style: "gnu" -*- */
/* policy.c Policies for what a connection can do
/* policy.c Bus security policy
*
* Copyright (C) 2003 Red Hat, Inc.
*
......@@ -25,6 +25,7 @@
#include "services.h"
#include "test.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-hash.h>
#include <dbus/dbus-internals.h>
BusPolicyRule*
......@@ -88,9 +89,33 @@ struct BusPolicy
{
int refcount;
DBusList *rules;
DBusList *default_rules; /**< Default policy rules */
DBusList *mandatory_rules; /**< Mandatory policy rules */
DBusHashTable *rules_by_uid; /**< per-UID policy rules */
DBusHashTable *rules_by_gid; /**< per-GID policy rules */
};
static void
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);
}
BusPolicy*
bus_policy_new (void)
{
......@@ -101,8 +126,24 @@ bus_policy_new (void)
return NULL;
policy->refcount = 1;
policy->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
NULL,
free_rule_list_func);
if (policy->rules_by_uid == NULL)
goto failed;
policy->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
NULL,
free_rule_list_func);
if (policy->rules_by_gid == NULL)
goto failed;
return policy;
failed:
bus_policy_unref (policy);
return NULL;
}
void
......@@ -113,6 +154,240 @@ bus_policy_ref (BusPolicy *policy)
policy->refcount += 1;
}
void
bus_policy_unref (BusPolicy *policy)
{
_dbus_assert (policy->refcount > 0);
policy->refcount -= 1;
if (policy->refcount == 0)
{
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)
{
BusClientPolicy *client;
unsigned long uid;
DBusList **list;
_dbus_assert (dbus_connection_get_is_authenticated (connection));
client = bus_client_policy_new ();
if (client == NULL)
return NULL;
if (!add_list_to_client (&policy->default_rules,
client))
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 (policy->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 (policy->rules_by_gid,
groups[i]);
if (list != NULL)
{
if (!add_list_to_client (list, client))
goto failed;
}
++i;
}
}
if (!dbus_connection_get_unix_user (connection, &uid))
goto failed;
list = _dbus_hash_table_lookup_ulong (policy->rules_by_uid,
uid);
if (!add_list_to_client (list, client))
goto failed;
if (!add_list_to_client (&policy->mandatory_rules,
client))
goto failed;
bus_client_policy_optimize (client);
return client;
failed:
bus_client_policy_unref (client);
return NULL;
}