/* -*- mode: C; c-file-style: "gnu" -*- */
/* config-parser.c XML-library-agnostic configuration file parser
*
- * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
*
* Licensed under the Academic Free License version 2.0
*
#include "test.h"
#include "utils.h"
#include "policy.h"
+#include "selinux.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-internals.h>
#include <string.h>
ELEMENT_PIDFILE,
ELEMENT_SERVICEDIR,
ELEMENT_INCLUDEDIR,
- ELEMENT_TYPE
+ ELEMENT_TYPE,
+ ELEMENT_SELINUX,
+ ELEMENT_ASSOCIATE
} ElementType;
typedef enum
char *pidfile; /**< PID file */
+ DBusList *included_files; /**< Included files stack */
+
+ DBusHashTable *service_sid_table; /**< Map service names to SELinux contexts */
+
unsigned int fork : 1; /**< TRUE to fork into daemon mode */
unsigned int is_toplevel : 1; /**< FALSE if we are a sub-config-file inside another one */
return "includedir";
case ELEMENT_TYPE:
return "type";
+ case ELEMENT_SELINUX:
+ return "selinux";
+ case ELEMENT_ASSOCIATE:
+ return "associate";
}
_dbus_assert_not_reached ("bad element type");
DBusError *error)
{
DBusList *link;
+ DBusHashTable *table;
if (!bus_policy_merge (parser->policy,
included->policy))
BUS_SET_OOM (error);
return FALSE;
}
+
+ table = bus_selinux_id_table_union (parser->service_sid_table,
+ included->service_sid_table);
+ if (table == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ _dbus_hash_table_unref (parser->service_sid_table);
+ parser->service_sid_table = table;
if (included->user != NULL)
{
return TRUE;
}
+static dbus_bool_t
+seen_include (BusConfigParser *parser,
+ const DBusString *file)
+{
+ DBusList *iter;
+
+ iter = parser->included_files;
+ while (iter != NULL)
+ {
+ if (! strcmp (_dbus_string_get_const_data (file), iter->data))
+ return TRUE;
+
+ iter = _dbus_list_get_next_link (&parser->included_files, iter);
+ }
+
+ return FALSE;
+}
+
BusConfigParser*
bus_config_parser_new (const DBusString *basedir,
dbus_bool_t is_toplevel,
}
if (((parser->policy = bus_policy_new ()) == NULL) ||
- !_dbus_string_copy (basedir, 0, &parser->basedir, 0))
+ !_dbus_string_copy (basedir, 0, &parser->basedir, 0) ||
+ ((parser->service_sid_table = bus_selinux_id_table_new ()) == NULL))
{
if (parser->policy)
bus_policy_unref (parser->policy);
_dbus_string_free (&parser->basedir);
+
+ if (parser->service_sid_table == NULL)
+ _dbus_hash_table_unref (parser->service_sid_table);
+
dbus_free (parser);
return NULL;
}
{
/* Initialize the parser's limits from the parent. */
parser->limits = parent->limits;
+
+ /* Use the parent's list of included_files to avoid
+ circular inclusions. */
+ parser->included_files = parent->included_files;
}
else
{
if (parser->policy)
bus_policy_unref (parser->policy);
+
+ if (parser->service_sid_table)
+ _dbus_hash_table_unref (parser->service_sid_table);
dbus_free (parser);
}
BUS_SET_OOM (error);
return FALSE;
}
-
+
return TRUE;
}
else if (strcmp (element_name, "includedir") == 0)
return TRUE;
}
+ else if (strcmp (element_name, "selinux") == 0)
+ {
+ Element *e;
+ const char *name;
+
+ if (!check_no_attributes (parser, "selinux", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_SELINUX) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
else
{
dbus_set_error (error, DBUS_ERROR_FAILED,
const char *receive_path;
const char *receive_type;
const char *eavesdrop;
- const char *requested_reply;
+ const char *send_requested_reply;
+ const char *receive_requested_reply;
const char *own;
const char *user;
const char *group;
"receive_path", &receive_path,
"receive_type", &receive_type,
"eavesdrop", &eavesdrop,
- "requested_reply", &requested_reply,
+ "send_requested_reply", &send_requested_reply,
+ "receive_requested_reply", &receive_requested_reply,
"own", &own,
"user", &user,
"group", &group,
if (!(send_interface || send_member || send_error || send_destination ||
send_type || send_path ||
receive_interface || receive_member || receive_error || receive_sender ||
- receive_type || receive_path || eavesdrop || requested_reply ||
+ receive_type || receive_path || eavesdrop ||
+ send_requested_reply || receive_requested_reply ||
own || user || group))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
* interface + member
* error
*
- * base send_ can combine with send_destination, send_path, send_type
- * base receive_ with receive_sender, receive_path, receive_type, eavesdrop, requested_reply
+ * base send_ can combine with send_destination, send_path, send_type, send_requested_reply
+ * base receive_ with receive_sender, receive_path, receive_type, receive_requested_reply, eavesdrop
*
* user, group, own must occur alone
*
(send_interface && receive_error) ||
(send_interface && receive_sender) ||
(send_interface && eavesdrop) ||
- (send_interface && requested_reply) ||
+ (send_interface && receive_requested_reply) ||
(send_interface && own) ||
(send_interface && user) ||
(send_interface && group)) ||
(send_member && receive_error) ||
(send_member && receive_sender) ||
(send_member && eavesdrop) ||
- (send_member && requested_reply) ||
+ (send_member && receive_requested_reply) ||
(send_member && own) ||
(send_member && user) ||
(send_member && group)) ||
(send_error && receive_error) ||
(send_error && receive_sender) ||
(send_error && eavesdrop) ||
- (send_error && requested_reply) ||
+ (send_error && receive_requested_reply) ||
(send_error && own) ||
(send_error && user) ||
(send_error && group)) ||
(send_destination && receive_error) ||
(send_destination && receive_sender) ||
(send_destination && eavesdrop) ||
- (send_destination && requested_reply) ||
+ (send_destination && receive_requested_reply) ||
(send_destination && own) ||
(send_destination && user) ||
(send_destination && group)) ||
(send_type && receive_error) ||
(send_type && receive_sender) ||
(send_type && eavesdrop) ||
- (send_type && requested_reply) ||
+ (send_type && receive_requested_reply) ||
(send_type && own) ||
(send_type && user) ||
(send_type && group)) ||
(send_path && receive_error) ||
(send_path && receive_sender) ||
(send_path && eavesdrop) ||
- (send_path && requested_reply) ||
+ (send_path && receive_requested_reply) ||
(send_path && own) ||
(send_path && user) ||
(send_path && group)) ||
+
+ ((send_requested_reply && receive_interface) ||
+ (send_requested_reply && receive_member) ||
+ (send_requested_reply && receive_error) ||
+ (send_requested_reply && receive_sender) ||
+ (send_requested_reply && eavesdrop) ||
+ (send_requested_reply && receive_requested_reply) ||
+ (send_requested_reply && own) ||
+ (send_requested_reply && user) ||
+ (send_requested_reply && group)) ||
((receive_interface && receive_error) ||
(receive_interface && own) ||
(eavesdrop && user) ||
(eavesdrop && group)) ||
- ((requested_reply && own) ||
- (requested_reply && user) ||
- (requested_reply && group)) ||
+ ((receive_requested_reply && own) ||
+ (receive_requested_reply && user) ||
+ (receive_requested_reply && group)) ||
((own && user) ||
(own && group)) ||
#define IS_WILDCARD(str) ((str) && ((str)[0]) == '*' && ((str)[1]) == '\0')
if (send_interface || send_member || send_error || send_destination ||
- send_path || send_type)
+ send_path || send_type || send_requested_reply)
{
int message_type;
return FALSE;
}
}
+
+ if (send_requested_reply &&
+ !(strcmp (send_requested_reply, "true") == 0 ||
+ strcmp (send_requested_reply, "false") == 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Bad value \"%s\" for %s attribute, must be true or false",
+ "send_requested_reply", send_requested_reply);
+ return FALSE;
+ }
rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, allow);
if (rule == NULL)
goto nomem;
+ if (send_requested_reply)
+ rule->d.send.requested_reply = (strcmp (send_requested_reply, "true") == 0);
+
rule->d.send.message_type = message_type;
rule->d.send.path = _dbus_strdup (send_path);
rule->d.send.interface = _dbus_strdup (send_interface);
goto nomem;
}
else if (receive_interface || receive_member || receive_error || receive_sender ||
- receive_path || receive_type || eavesdrop || requested_reply)
+ receive_path || receive_type || eavesdrop || receive_requested_reply)
{
int message_type;
return FALSE;
}
- if (requested_reply &&
- !(strcmp (requested_reply, "true") == 0 ||
- strcmp (requested_reply, "false") == 0))
+ if (receive_requested_reply &&
+ !(strcmp (receive_requested_reply, "true") == 0 ||
+ strcmp (receive_requested_reply, "false") == 0))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Bad value \"%s\" for %s attribute, must be true or false",
- "requested_reply", requested_reply);
+ "receive_requested_reply", receive_requested_reply);
return FALSE;
}
if (eavesdrop)
rule->d.receive.eavesdrop = (strcmp (eavesdrop, "true") == 0);
- if (requested_reply)
- rule->d.receive.requested_reply = (strcmp (requested_reply, "true") == 0);
+ if (receive_requested_reply)
+ rule->d.receive.requested_reply = (strcmp (receive_requested_reply, "true") == 0);
rule->d.receive.message_type = message_type;
rule->d.receive.path = _dbus_strdup (receive_path);
}
}
+static dbus_bool_t
+start_selinux_child (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error)
+{
+ if (strcmp (element_name, "associate") == 0)
+ {
+ const char *own;
+ const char *context;
+
+ if (!locate_attributes (parser, "associate",
+ attribute_names,
+ attribute_values,
+ error,
+ "own", &own,
+ "context", &context,
+ NULL))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_ASSOCIATE) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (own == NULL || context == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <associate> must have attributes own=\"<servicename>\" and context=\"<selinux context>\"");
+ return FALSE;
+ }
+
+ if (!bus_selinux_id_table_insert (parser->service_sid_table,
+ own, context))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <%s> not allowed inside <%s> in configuration file",
+ element_name, "selinux");
+ return FALSE;
+ }
+}
+
dbus_bool_t
bus_config_parser_start_element (BusConfigParser *parser,
const char *element_name,
attribute_names, attribute_values,
error);
}
+ else if (t == ELEMENT_SELINUX)
+ {
+ return start_selinux_child (parser, element_name,
+ attribute_names, attribute_values,
+ error);
+ }
else
{
dbus_set_error (error, DBUS_ERROR_FAILED,
case ELEMENT_ALLOW:
case ELEMENT_DENY:
case ELEMENT_FORK:
+ case ELEMENT_SELINUX:
+ case ELEMENT_ASSOCIATE:
break;
}
* that the result is the same
*/
BusConfigParser *included;
+ const char *filename_str;
DBusError tmp_error;
dbus_error_init (&tmp_error);
+ filename_str = _dbus_string_get_const_data (filename);
+
+ /* Check to make sure this file hasn't already been included. */
+ if (seen_include (parser, filename))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Circular inclusion of file '%s'",
+ filename_str);
+ return FALSE;
+ }
+
+ if (! _dbus_list_append (&parser->included_files, (void *) filename_str))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
/* Since parser is passed in as the parent, included
inherits parser's limits. */
included = bus_config_load (filename, FALSE, parser, &tmp_error);
+ _dbus_list_pop_last (&parser->included_files);
+
if (included == NULL)
{
_DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
case ELEMENT_ALLOW:
case ELEMENT_DENY:
case ELEMENT_FORK:
+ case ELEMENT_SELINUX:
+ case ELEMENT_ASSOCIATE:
if (all_whitespace (content))
return TRUE;
else
*limits = parser->limits;
}
+DBusHashTable*
+bus_config_parser_steal_service_sid_table (BusConfigParser *parser)
+{
+ DBusHashTable *table;
+
+ _dbus_assert (parser->service_sid_table != NULL); /* can only steal once */
+
+ table = parser->service_sid_table;
+
+ parser->service_sid_table = NULL;
+
+ return table;
+}
+
#ifdef DBUS_BUILD_TESTS
#include <stdio.h>
goto failed;
}
- printf ("Testing:\n");
+ if (validity == VALID)
+ printf ("Testing valid files:\n");
+ else if (validity == INVALID)
+ printf ("Testing invalid files:\n");
+ else
+ printf ("Testing unknown files:\n");
next:
while (_dbus_directory_get_next_file (dir, &filename, &error))
d.full_path = &full_path;
d.validity = validity;
- if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d))
- _dbus_assert_not_reached ("test failed");
+ /* FIXME hackaround for an expat problem, see
+ * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=124747
+ * http://freedesktop.org/pipermail/dbus/2004-May/001153.html
+ */
+ /* if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) */
+ if (!check_loader_oom_func (&d))
+ _dbus_assert_not_reached ("test failed");
+
_dbus_string_free (&full_path);
}
/* FIXME: compare policy */
+ /* FIXME: compare service selinux ID table */
+
if (! limits_equal (&a->limits, &b->limits))
return FALSE;
goto finished;
}
- printf ("Comparing:\n");
+ printf ("Comparing equivalent files:\n");
next:
while (_dbus_directory_get_next_file (dir, &filename, &error))
if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID))
return FALSE;
+ if (!process_test_valid_subdir (test_data_dir, "invalid-config-files", INVALID))
+ return FALSE;
+
if (!process_test_equiv_subdir (test_data_dir, "equiv-config-files"))
return FALSE;