From ee78f2800f2642b4fff962b736296d87a7f12a17 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 23 Aug 2004 16:09:54 +0000 Subject: 2004-08-23 Colin Walters * 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 * bus/config-parser.c (start_busconfig_child) (bus_config_parser_content): Support SELinux-root relative inclusion. * configure.in : 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 | 25 ++++++++ bus/config-parser.c | 46 +++++++++++++-- bus/selinux.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++--- bus/selinux.h | 2 +- bus/test-main.c | 54 +++++++++++------ configure.in | 2 +- 6 files changed, 258 insertions(+), 35 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2ea0582c..e41de833 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2004-08-23 Colin Walters + + * 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 + + * bus/config-parser.c (start_busconfig_child) + (bus_config_parser_content): Support SELinux-root relative + inclusion. + + * configure.in : 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 * 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 +#include #include #include #include #include #include +#include #include #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, @@ -578,6 +710,20 @@ bus_selinux_id_table_union (DBusHashTable *base, return combined_table; } +/** + * 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. */ 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= -- cgit