2004-07-24 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / config-parser.c
index 19afe8e..29fade1 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- 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
  *
@@ -24,6 +24,7 @@
 #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>
@@ -44,7 +45,9 @@ typedef enum
   ELEMENT_PIDFILE,
   ELEMENT_SERVICEDIR,
   ELEMENT_INCLUDEDIR,
-  ELEMENT_TYPE
+  ELEMENT_TYPE,
+  ELEMENT_SELINUX,
+  ELEMENT_ASSOCIATE
 } ElementType;
 
 typedef enum
@@ -115,6 +118,10 @@ struct BusConfigParser
 
   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 */
@@ -155,6 +162,10 @@ element_type_to_name (ElementType type)
       return "includedir";
     case ELEMENT_TYPE:
       return "type";
+    case ELEMENT_SELINUX:
+      return "selinux";
+    case ELEMENT_ASSOCIATE:
+      return "associate";
     }
 
   _dbus_assert_not_reached ("bad element type");
@@ -233,6 +244,7 @@ merge_included (BusConfigParser *parser,
                 DBusError       *error)
 {
   DBusList *link;
+  DBusHashTable *table;
 
   if (!bus_policy_merge (parser->policy,
                          included->policy))
@@ -240,6 +252,17 @@ merge_included (BusConfigParser *parser,
       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)
     {
@@ -277,6 +300,24 @@ merge_included (BusConfigParser *parser,
   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,
@@ -297,12 +338,17 @@ bus_config_parser_new (const DBusString      *basedir,
     }
 
   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;
     }
@@ -311,6 +357,10 @@ bus_config_parser_new (const DBusString      *basedir,
     {
       /* 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
     {
@@ -403,6 +453,9 @@ bus_config_parser_unref (BusConfigParser *parser)
 
       if (parser->policy)
         bus_policy_unref (parser->policy);
+
+      if (parser->service_sid_table)
+        _dbus_hash_table_unref (parser->service_sid_table);
       
       dbus_free (parser);
     }
@@ -634,7 +687,7 @@ start_busconfig_child (BusConfigParser   *parser,
           BUS_SET_OOM (error);
           return FALSE;
         }
-
+      
       return TRUE;
     }
   else if (strcmp (element_name, "includedir") == 0)
@@ -819,6 +872,22 @@ start_busconfig_child (BusConfigParser   *parser,
 
       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,
@@ -849,7 +918,8 @@ append_rule_from_element (BusConfigParser   *parser,
   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;
@@ -872,7 +942,8 @@ append_rule_from_element (BusConfigParser   *parser,
                           "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,
@@ -882,7 +953,8 @@ append_rule_from_element (BusConfigParser   *parser,
   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,
@@ -908,8 +980,8 @@ append_rule_from_element (BusConfigParser   *parser,
    *     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
    *
@@ -922,7 +994,7 @@ append_rule_from_element (BusConfigParser   *parser,
        (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)) ||
@@ -933,7 +1005,7 @@ append_rule_from_element (BusConfigParser   *parser,
        (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)) ||
@@ -943,7 +1015,7 @@ append_rule_from_element (BusConfigParser   *parser,
        (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)) ||
@@ -953,7 +1025,7 @@ append_rule_from_element (BusConfigParser   *parser,
        (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)) ||
@@ -963,7 +1035,7 @@ append_rule_from_element (BusConfigParser   *parser,
        (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)) ||
@@ -973,10 +1045,20 @@ append_rule_from_element (BusConfigParser   *parser,
        (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) ||
@@ -996,9 +1078,9 @@ append_rule_from_element (BusConfigParser   *parser,
        (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)) ||
@@ -1019,7 +1101,7 @@ append_rule_from_element (BusConfigParser   *parser,
 #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;
       
@@ -1048,11 +1130,24 @@ append_rule_from_element (BusConfigParser   *parser,
               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);
@@ -1071,7 +1166,7 @@ append_rule_from_element (BusConfigParser   *parser,
         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;
       
@@ -1112,13 +1207,13 @@ append_rule_from_element (BusConfigParser   *parser,
           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;
         }
       
@@ -1129,8 +1224,8 @@ append_rule_from_element (BusConfigParser   *parser,
       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);
@@ -1340,6 +1435,58 @@ start_policy_child (BusConfigParser   *parser,
     }
 }
 
+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,
@@ -1390,6 +1537,12 @@ bus_config_parser_start_element (BusConfigParser   *parser,
                                  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,
@@ -1585,6 +1738,8 @@ bus_config_parser_end_element (BusConfigParser   *parser,
     case ELEMENT_ALLOW:
     case ELEMENT_DENY:
     case ELEMENT_FORK:
+    case ELEMENT_SELINUX:
+    case ELEMENT_ASSOCIATE:
       break;
     }
 
@@ -1635,14 +1790,34 @@ include_file (BusConfigParser   *parser,
    * 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);
@@ -1797,6 +1972,8 @@ bus_config_parser_content (BusConfigParser   *parser,
     case ELEMENT_ALLOW:
     case ELEMENT_DENY:
     case ELEMENT_FORK:
+    case ELEMENT_SELINUX:
+    case ELEMENT_ASSOCIATE:
       if (all_whitespace (content))
         return TRUE;
       else
@@ -2090,6 +2267,20 @@ bus_config_parser_get_limits (BusConfigParser *parser,
   *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>
 
@@ -2207,7 +2398,12 @@ process_test_valid_subdir (const DBusString *test_base_dir,
       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))
@@ -2241,9 +2437,15 @@ process_test_valid_subdir (const DBusString *test_base_dir,
 
       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);
     }
 
@@ -2413,6 +2615,8 @@ config_parsers_equal (const BusConfigParser *a,
   
   /* FIXME: compare policy */
 
+  /* FIXME: compare service selinux ID table */
+
   if (! limits_equal (&a->limits, &b->limits))
     return FALSE;
 
@@ -2458,7 +2662,7 @@ all_are_equiv (const DBusString *target_directory)
       goto finished;
     }
 
-  printf ("Comparing:\n");
+  printf ("Comparing equivalent files:\n");
 
  next:
   while (_dbus_directory_get_next_file (dir, &filename, &error))
@@ -2612,6 +2816,9 @@ bus_config_parser_test (const DBusString *test_data_dir)
   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;