2004-08-23 Colin Walters <walters@redhat.com>
authorColin Walters <walters@verbum.org>
Mon, 23 Aug 2004 16:09:54 +0000 (16:09 +0000)
committerColin Walters <walters@verbum.org>
Mon, 23 Aug 2004 16:09:54 +0000 (16:09 +0000)
* bus/selinux.h: Prototype bus_selinux_get_policy_root.

* bus/selinux.c: Create a thread for policy reload notification.
(bus_selinux_get_policy_root): Implement.

Updated SELinux support from Matthew Rickard <mjricka@epoch.ncsc.mil>

* bus/config-parser.c (start_busconfig_child)
(bus_config_parser_content): Support SELinux-root relative
inclusion.

* configure.in <HAVE_SELINUX>: Add -lpthread.

* bus/test-main.c (test_pre_hook, test_post_hook): New.
(test_post_hook): Move memory checking into here.
(test_pre_hook, test_post_hook): Move SELinux checks in
here, but conditional on a DBUS_TEST_SELINUX environment
variable.  Unfortunately we can't run the SELinux checks
as a normal user, since they won't have any permissions
for /selinux.  So this will have to be tested manually
for now, until we have virtualization for most of
libselinux.

ChangeLog
bus/config-parser.c
bus/selinux.c
bus/selinux.h
bus/test-main.c
configure.in

index 2ea0582..e41de83 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2004-08-23  Colin Walters  <walters@redhat.com>
+
+       * bus/selinux.h: Prototype bus_selinux_get_policy_root.
+
+       * bus/selinux.c: Create a thread for policy reload notification.
+       (bus_selinux_get_policy_root): Implement.
+
+       Updated SELinux support from Matthew Rickard <mjricka@epoch.ncsc.mil>
+       
+       * bus/config-parser.c (start_busconfig_child)
+       (bus_config_parser_content): Support SELinux-root relative
+       inclusion.
+
+       * configure.in <HAVE_SELINUX>: Add -lpthread.
+       
+       * bus/test-main.c (test_pre_hook, test_post_hook): New.
+       (test_post_hook): Move memory checking into here.
+       (test_pre_hook, test_post_hook): Move SELinux checks in
+       here, but conditional on a DBUS_TEST_SELINUX environment
+       variable.  Unfortunately we can't run the SELinux checks
+       as a normal user, since they won't have any permissions
+       for /selinux.  So this will have to be tested manually
+       for now, until we have virtualization for most of
+       libselinux.
+       
 2004-08-23  Havoc Pennington  <hp@redhat.com>
 
        * dbus/dbus-sysdeps.c (_dbus_change_identity): add setgroups() to
index 73d9941..f276fb5 100644 (file)
@@ -73,6 +73,7 @@ typedef struct
     struct
     {
       unsigned int ignore_missing : 1;
+      unsigned int selinux_root_relative : 1;
     } include;
 
     struct
@@ -717,6 +718,7 @@ start_busconfig_child (BusConfigParser   *parser,
     {
       Element *e;
       const char *ignore_missing;
+      const char *selinux_root_relative;
 
       if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
         {
@@ -725,12 +727,14 @@ start_busconfig_child (BusConfigParser   *parser,
         }
 
       e->d.include.ignore_missing = FALSE;
+      e->d.include.selinux_root_relative = FALSE;
 
       if (!locate_attributes (parser, "include",
                               attribute_names,
                               attribute_values,
                               error,
                               "ignore_missing", &ignore_missing,
+                              "selinux_root_relative", &selinux_root_relative,
                               NULL))
         return FALSE;
 
@@ -747,6 +751,21 @@ start_busconfig_child (BusConfigParser   *parser,
               return FALSE;
             }
         }
+      
+      if (selinux_root_relative != NULL)
+        {
+          if (strcmp (selinux_root_relative, "yes") == 0)
+            e->d.include.selinux_root_relative = TRUE;
+          else if (strcmp (selinux_root_relative, "no") == 0)
+            e->d.include.selinux_root_relative = FALSE;
+          else
+            {
+              dbus_set_error (error, DBUS_ERROR_FAILED,
+                              "selinux_root_relative attribute must have value"
+                              " \"yes\" or \"no\"");
+              return FALSE;
+           }
+        }
 
       return TRUE;
     }
@@ -1994,19 +2013,36 @@ bus_config_parser_content (BusConfigParser   *parser,
 
     case ELEMENT_INCLUDE:
       {
-        DBusString full_path;
-        
+        DBusString full_path, selinux_policy_root;
+
         e->had_content = TRUE;
 
         if (!_dbus_string_init (&full_path))
           goto nomem;
-        
-        if (!make_full_path (&parser->basedir, content, &full_path))
+
+        if (e->d.include.selinux_root_relative)
+         {
+            if (!bus_selinux_get_policy_root ())
+             {
+               dbus_set_error (error, DBUS_ERROR_FAILED,
+                               "Could not determine SELinux policy root for relative inclusion");
+               _dbus_string_free (&full_path);
+               return FALSE;
+             }
+            _dbus_string_init_const (&selinux_policy_root,
+                                     bus_selinux_get_policy_root ());
+            if (!make_full_path (&selinux_policy_root, content, &full_path))
+              {
+                _dbus_string_free (&full_path);
+                goto nomem;
+              }
+          }
+        else if (!make_full_path (&parser->basedir, content, &full_path))
           {
             _dbus_string_free (&full_path);
             goto nomem;
           }
-        
+
         if (!include_file (parser, &full_path,
                            e->d.include.ignore_missing, error))
           {
index 4952462..b5fb637 100644 (file)
 
 #ifdef HAVE_SELINUX
 #include <errno.h>
+#include <pthread.h>
 #include <syslog.h>
 #include <selinux/selinux.h>
 #include <selinux/avc.h>
 #include <selinux/av_permissions.h>
 #include <selinux/flask.h>
+#include <signal.h>
 #include <stdarg.h>
 #endif /* HAVE_SELINUX */
 
@@ -47,7 +49,44 @@ static dbus_bool_t selinux_enabled = FALSE;
 /* Store an avc_entry_ref to speed AVC decisions. */
 static struct avc_entry_ref aeref;
 
+/* Store the SID of the bus itself to use as the default. */
 static security_id_t bus_sid = SECSID_WILD;
+
+/* Thread to listen for SELinux status changes via netlink. */
+static pthread_t avc_notify_thread;
+
+/* Prototypes for AVC callback functions.  */
+static void log_callback (const char *fmt, ...);
+static void *avc_create_thread (void (*run) (void));
+static void avc_stop_thread (void *thread);
+static void *avc_alloc_lock (void);
+static void avc_get_lock (void *lock);
+static void avc_release_lock (void *lock);
+static void avc_free_lock (void *lock);
+
+/* AVC callback structures for use in avc_init.  */
+static const struct avc_memory_callback mem_cb =
+{
+  .func_malloc = dbus_malloc,
+  .func_free = dbus_free
+};
+static const struct avc_log_callback log_cb =
+{
+  .func_log = log_callback,
+  .func_audit = NULL
+};
+static const struct avc_thread_callback thread_cb =
+{
+  .func_create_thread = avc_create_thread,
+  .func_stop_thread = avc_stop_thread
+};
+static const struct avc_lock_callback lock_cb =
+{
+  .func_alloc_lock = avc_alloc_lock,
+  .func_get_lock = avc_get_lock,
+  .func_release_lock = avc_release_lock,
+  .func_free_lock = avc_free_lock
+};
 #endif /* HAVE_SELINUX */
 
 /**
@@ -67,8 +106,89 @@ log_callback (const char *fmt, ...)
   vsyslog (LOG_INFO, fmt, ap);
   va_end(ap);
 }
-#endif /* HAVE_SELINUX */
 
+/**
+ * On a policy reload we need to reparse the SELinux configuration file, since
+ * this could have changed.  Send a SIGHUP to reload all configs.
+ */
+static int
+policy_reload_callback (u_int32_t event, security_id_t ssid, 
+                        security_id_t tsid, security_class_t tclass, 
+                        access_vector_t perms, access_vector_t *out_retained)
+{
+  if (event == AVC_CALLBACK_RESET)
+    return raise (SIGHUP);
+  
+  return 0;
+}
+
+/**
+ * Create thread to notify the AVC of enforcing and policy reload
+ * changes via netlink.
+ *
+ * @param run the thread run function
+ * @return pointer to the thread
+ */
+static void *
+avc_create_thread (void (*run) (void))
+{
+  int rc;
+
+  rc = pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL);
+  if (rc != 0)
+    {
+      _dbus_warn ("Failed to start AVC thread: %s\n", _dbus_strerror (rc));
+      exit (1);
+    }
+  return &avc_notify_thread;
+}
+
+/* Stop AVC netlink thread.  */
+static void
+avc_stop_thread (void *thread)
+{
+  pthread_cancel (*(pthread_t *) thread);
+}
+
+/* Allocate a new AVC lock.  */
+static void *
+avc_alloc_lock (void)
+{
+  pthread_mutex_t *avc_mutex;
+
+  avc_mutex = dbus_new (pthread_mutex_t, 1);
+  if (avc_mutex == NULL)
+    {
+      _dbus_warn ("Could not create mutex: %s\n", _dbus_strerror (errno));
+      exit (1);
+    }
+  pthread_mutex_init (avc_mutex, NULL);
+
+  return avc_mutex;
+}
+
+/* Acquire an AVC lock.  */
+static void
+avc_get_lock (void *lock)
+{
+  pthread_mutex_lock (lock);
+}
+
+/* Release an AVC lock.  */
+static void
+avc_release_lock (void *lock)
+{
+  pthread_mutex_unlock (lock);
+}
+
+/* Free an AVC lock.  */
+static void
+avc_free_lock (void *lock)
+{
+  pthread_mutex_destroy (lock);
+  dbus_free (lock);
+}
+#endif /* HAVE_SELINUX */
 
 /**
  * Initialize the user space access vector cache (AVC) for D-BUS and set up
@@ -78,7 +198,6 @@ dbus_bool_t
 bus_selinux_init (void)
 {
 #ifdef HAVE_SELINUX
-  struct avc_log_callback log_cb = {(void*)log_callback, NULL};
   int r;
   char *bus_context;
 
@@ -104,7 +223,7 @@ bus_selinux_init (void)
   _dbus_verbose ("SELinux is enabled in this kernel.\n");
 
   avc_entry_ref_init (&aeref);
-  if (avc_init ("avc", NULL, &log_cb, NULL, NULL) < 0)
+  if (avc_init ("avc", &mem_cb, &log_cb, &thread_cb, &lock_cb) < 0)
     {
       _dbus_warn ("Failed to start Access Vector Cache (AVC).\n");
       return FALSE;
@@ -115,6 +234,15 @@ bus_selinux_init (void)
       _dbus_verbose ("Access Vector Cache (AVC) started.\n");
     }
 
+  if (avc_add_callback (policy_reload_callback, AVC_CALLBACK_RESET,
+                       NULL, NULL, 0, 0) < 0)
+    {
+      _dbus_warn ("Failed to add policy reload callback: %s\n",
+                  _dbus_strerror (errno));
+      avc_destroy ();
+      return FALSE;
+    }
+
   bus_context = NULL;
   bus_sid = SECSID_WILD;
 
@@ -141,7 +269,6 @@ bus_selinux_init (void)
 #endif /* HAVE_SELINUX */
 }
 
-
 /**
  * Decrement SID reference count.
  * 
@@ -198,7 +325,7 @@ bus_selinux_check (BusSELinuxID        *sender_sid,
 {
   if (!selinux_enabled)
     return TRUE;
-  
+
   /* Make the security check.  AVC checks enforcing mode here as well. */
   if (avc_has_perm (SELINUX_SID_FROM_BUS (sender_sid),
                     override_sid ?
@@ -367,7 +494,8 @@ bus_selinux_init_connection_id (DBusConnection *connection,
 }
 
 
-/* Function for freeing hash table data.  These SIDs
+/**
+ * Function for freeing hash table data.  These SIDs
  * should no longer be referenced.
  */
 static void
@@ -466,9 +594,6 @@ bus_selinux_id_table_insert (DBusHashTable *service_table,
  * constant time operation.  If SELinux support is not available,
  * always return NULL.
  *
- * @todo This should return a const security_id_t since we don't
- *       want the caller to mess with it.
- *
  * @param service_table the hash table to check for service name.
  * @param service_name the name of the service to look for.
  * @returns the SELinux ID associated with the service
@@ -503,6 +628,13 @@ bus_selinux_id_table_lookup (DBusHashTable    *service_table,
   return NULL;
 }
 
+/**
+ * Copy security ID table mapping from one table into another.
+ *
+ * @param dest the table to copy into
+ * @param override the table to copy from
+ * @returns #FALSE if out of memory
+ */
 #ifdef HAVE_SELINUX
 static dbus_bool_t
 bus_selinux_id_table_copy_over (DBusHashTable    *dest,
@@ -579,6 +711,20 @@ bus_selinux_id_table_union (DBusHashTable    *base,
 }
 
 /**
+ * Get the SELinux policy root.  This is used to find the D-BUS
+ * specific config file within the policy.
+ */
+const char *
+bus_selinux_get_policy_root (void)
+{
+#ifdef HAVE_SELINUX
+  return selinux_policy_root ();
+#else
+  return NULL;
+#endif /* HAVE_SELINUX */
+} 
+
+/**
  * For debugging:  Print out the current hash table of service SIDs.
  */
 void
index 08ea3a0..2080383 100644 (file)
@@ -42,7 +42,7 @@ dbus_bool_t    bus_selinux_id_table_insert (DBusHashTable    *service_table,
 DBusHashTable* bus_selinux_id_table_union  (DBusHashTable    *base,
                                             DBusHashTable    *override);
 void           bus_selinux_id_table_print  (DBusHashTable    *service_table);
-
+const char*    bus_selinux_get_policy_root (void);
 
 
 dbus_bool_t bus_selinux_allows_acquire_service (DBusConnection *connection,
index d92e3fe..4043f6e 100644 (file)
@@ -52,6 +52,23 @@ check_memleaks (const char *name)
 }
 #endif /* DBUS_BUILD_TESTS */
 
+static void
+test_pre_hook (void)
+{
+  
+  if (_dbus_getenv ("DBUS_TEST_SELINUX") && !bus_selinux_init ())
+    die ("could not init selinux support");
+}
+
+static char *progname = "";
+static void
+test_post_hook (void)
+{
+  if (_dbus_getenv ("DBUS_TEST_SELINUX"))
+    bus_selinux_shutdown ();
+  check_memleaks (progname);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -59,6 +76,8 @@ main (int argc, char **argv)
   const char *dir;
   DBusString test_data_dir;
 
+  progname = argv[0];
+
   if (argc > 1)
     dir = argv[1];
   else
@@ -70,9 +89,6 @@ main (int argc, char **argv)
       return 1;
     }
 
-  if (!bus_selinux_init ())
-    die ("could not init selinux support");
-
   _dbus_string_init_const (&test_data_dir, dir);
 
 #if 0
@@ -81,50 +97,50 @@ main (int argc, char **argv)
     die ("initializing debug threads");
 #endif
  
+  test_pre_hook ();
   printf ("%s: Running expire list test\n", argv[0]);
   if (!bus_expire_list_test (&test_data_dir))
     die ("expire list");
-  
-  check_memleaks (argv[0]);
-  
+  test_post_hook ();
+  test_pre_hook ();
   printf ("%s: Running config file parser test\n", argv[0]);
   if (!bus_config_parser_test (&test_data_dir))
     die ("parser");
-  
-  check_memleaks (argv[0]);  
-  
+  test_post_hook ();
+
+  test_pre_hook ();
   printf ("%s: Running policy test\n", argv[0]);
   if (!bus_policy_test (&test_data_dir))
     die ("policy");
+  test_post_hook ();
 
-  check_memleaks (argv[0]);
-
+  test_pre_hook ();
   printf ("%s: Running signals test\n", argv[0]);
   if (!bus_signals_test (&test_data_dir))
     die ("signals");
+  test_post_hook ();
 
-  check_memleaks (argv[0]);
-  
+  test_pre_hook ();
   printf ("%s: Running SHA1 connection test\n", argv[0]);
   if (!bus_dispatch_sha1_test (&test_data_dir))
     die ("sha1");
+  test_post_hook ();
 
-  check_memleaks (argv[0]);
+  test_pre_hook ();
   printf ("%s: Running message dispatch test\n", argv[0]);
   if (!bus_dispatch_test (&test_data_dir)) 
     die ("dispatch");
+  test_post_hook ();
 
-  check_memleaks (argv[0]);
-
+  test_pre_hook ();
   printf ("%s: Running service files reloading test\n", argv[0]);
   if (!bus_activation_service_reload_test (&test_data_dir))
     die ("service reload");
+  test_post_hook ();
 
-  check_memleaks (argv[0]);
-  
   printf ("%s: Success\n", argv[0]);
 
-  bus_selinux_shutdown ();
   
   return 0;
 #else /* DBUS_BUILD_TESTS */
index 34d69e8..ea25336 100644 (file)
@@ -724,7 +724,7 @@ fi
 AM_CONDITIONAL(HAVE_SELINUX, test x$have_selinux = xyes)
 
 if test x$have_selinux = xyes ; then
-    SELINUX_LIBS=-lselinux
+    SELINUX_LIBS="-lselinux -lpthread"
     AC_DEFINE(HAVE_SELINUX,1,[SELinux support])
 else
     SELINUX_LIBS=