diff options
| -rw-r--r-- | ChangeLog | 25 | ||||
| -rw-r--r-- | bus/config-parser.c | 46 | ||||
| -rw-r--r-- | bus/selinux.c | 164 | ||||
| -rw-r--r-- | bus/selinux.h | 2 | ||||
| -rw-r--r-- | bus/test-main.c | 54 | ||||
| -rw-r--r-- | configure.in | 2 | 
6 files changed, 258 insertions, 35 deletions
| @@ -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 diff --git a/bus/config-parser.c b/bus/config-parser.c index 73d99418..f276fb52 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -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))            { diff --git a/bus/selinux.c b/bus/selinux.c index 49524624..b5fb6371 100644 --- a/bus/selinux.c +++ b/bus/selinux.c @@ -29,11 +29,13 @@  #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 diff --git a/bus/selinux.h b/bus/selinux.h index 08ea3a02..20803833 100644 --- a/bus/selinux.h +++ b/bus/selinux.h @@ -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, diff --git a/bus/test-main.c b/bus/test-main.c index d92e3fee..4043f6ed 100644 --- a/bus/test-main.c +++ b/bus/test-main.c @@ -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 */ diff --git a/configure.in b/configure.in index 34d69e87..ea25336d 100644 --- a/configure.in +++ b/configure.in @@ -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= | 
