chiark / gitweb /
bus-proxy: share policy between threads
[elogind.git] / src / bus-proxyd / bus-xml-policy.c
index 8d14828a9cb37225d348587909b2a34a68e0934f..b3daad50177beb0820ace73f3e53502994f174f8 100644 (file)
@@ -26,6 +26,7 @@
 #include "bus-internal.h"
 #include "bus-message.h"
 #include "bus-xml-policy.h"
+#include "sd-login.h"
 
 static void policy_item_free(PolicyItem *i) {
         assert(i);
@@ -62,6 +63,7 @@ static int file_load(Policy *p, const char *path) {
                 STATE_BUSCONFIG,
                 STATE_POLICY,
                 STATE_POLICY_CONTEXT,
+                STATE_POLICY_CONSOLE,
                 STATE_POLICY_USER,
                 STATE_POLICY_GROUP,
                 STATE_POLICY_OTHER_ATTRIBUTE,
@@ -80,6 +82,8 @@ static int file_load(Policy *p, const char *path) {
                 POLICY_CATEGORY_NONE,
                 POLICY_CATEGORY_DEFAULT,
                 POLICY_CATEGORY_MANDATORY,
+                POLICY_CATEGORY_ON_CONSOLE,
+                POLICY_CATEGORY_NO_CONSOLE,
                 POLICY_CATEGORY_USER,
                 POLICY_CATEGORY_GROUP
         } policy_category = POLICY_CATEGORY_NONE;
@@ -156,15 +160,14 @@ static int file_load(Policy *p, const char *path) {
                         if (t == XML_ATTRIBUTE_NAME) {
                                 if (streq(name, "context"))
                                         state = STATE_POLICY_CONTEXT;
+                                else if (streq(name, "at_console"))
+                                        state = STATE_POLICY_CONSOLE;
                                 else if (streq(name, "user"))
                                         state = STATE_POLICY_USER;
                                 else if (streq(name, "group"))
                                         state = STATE_POLICY_GROUP;
                                 else {
-                                        if (streq(name, "at_console"))
-                                                log_debug("Attribute %s of <policy> tag unsupported at %s:%u, ignoring.", name, path, line);
-                                        else
-                                                log_warning("Attribute %s of <policy> tag unknown at %s:%u, ignoring.", name, path, line);
+                                        log_warning("Attribute %s of <policy> tag unknown at %s:%u, ignoring.", name, path, line);
                                         state = STATE_POLICY_OTHER_ATTRIBUTE;
                                 }
                         } else if (t == XML_TAG_CLOSE_EMPTY ||
@@ -217,6 +220,26 @@ static int file_load(Policy *p, const char *path) {
 
                         break;
 
+                case STATE_POLICY_CONSOLE:
+
+                        if (t == XML_ATTRIBUTE_VALUE) {
+                                if (streq(name, "true")) {
+                                        policy_category = POLICY_CATEGORY_ON_CONSOLE;
+                                        state = STATE_POLICY;
+                                } else if (streq(name, "false")) {
+                                        policy_category = POLICY_CATEGORY_NO_CONSOLE;
+                                        state = STATE_POLICY;
+                                } else {
+                                        log_error("at_console= parameter %s unknown for <policy> at %s:%u.", name, path, line);
+                                        return -EINVAL;
+                                }
+                        } else {
+                                log_error("Unexpected token (4.1) at %s:%u.", path, line);
+                                return -EINVAL;
+                        }
+
+                        break;
+
                 case STATE_POLICY_USER:
 
                         if (t == XML_ATTRIBUTE_VALUE) {
@@ -337,6 +360,10 @@ static int file_load(Policy *p, const char *path) {
                                         item_append(i, &p->default_items);
                                 else if (policy_category == POLICY_CATEGORY_MANDATORY)
                                         item_append(i, &p->mandatory_items);
+                                else if (policy_category == POLICY_CATEGORY_ON_CONSOLE)
+                                        item_append(i, &p->on_console_items);
+                                else if (policy_category == POLICY_CATEGORY_NO_CONSOLE)
+                                        item_append(i, &p->no_console_items);
                                 else if (policy_category == POLICY_CATEGORY_USER) {
                                         const char *u = policy_user;
 
@@ -746,7 +773,8 @@ static int policy_check(Policy *p, const struct policy_check_filter *filter) {
          *  1. Check default items
          *  2. Check group items
          *  3. Check user items
-         *  4. Check mandatory items
+         *  4. Check on/no_console items
+         *  5. Check mandatory items
          *
          *  Later rules override earlier rules.
          */
@@ -771,6 +799,13 @@ static int policy_check(Policy *p, const struct policy_check_filter *filter) {
                 }
         }
 
+        if (filter->uid != UID_INVALID && sd_uid_get_seats(filter->uid, -1, NULL) > 0)
+                v = check_policy_items(p->on_console_items, filter);
+        else
+                v = check_policy_items(p->no_console_items, filter);
+        if (v != DUNNO)
+                verdict = v;
+
         v = check_policy_items(p->mandatory_items, filter);
         if (v != DUNNO)
                 verdict = v;
@@ -837,7 +872,8 @@ bool policy_check_recv(Policy *p,
                        const char *name,
                        const char *path,
                        const char *interface,
-                       const char *member) {
+                       const char *member,
+                       bool dbus_to_kernel) {
 
         struct policy_check_filter filter = {
                 .class        = POLICY_ITEM_RECV,
@@ -857,8 +893,9 @@ bool policy_check_recv(Policy *p,
         verdict = policy_check(p, &filter);
 
         log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
-                 "Receive permission check for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s interface=%s path=%s member=%s: %s",
-                 uid, gid, bus_message_type_to_string(message_type), strna(name), strna(path), strna(interface), strna(member), strna(verdict_to_string(verdict)));
+                 "Receive permission check %s for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s path=%s interface=%s member=%s: %s",
+                 dbus_to_kernel ? "dbus-1 to kernel" : "kernel to dbus-1", uid, gid, bus_message_type_to_string(message_type), strna(name),
+                 strna(path), strna(interface), strna(member), strna(verdict_to_string(verdict)));
 
         return verdict == ALLOW;
 }
@@ -870,7 +907,8 @@ bool policy_check_send(Policy *p,
                        const char *name,
                        const char *path,
                        const char *interface,
-                       const char *member) {
+                       const char *member,
+                       bool dbus_to_kernel) {
 
         struct policy_check_filter filter = {
                 .class        = POLICY_ITEM_SEND,
@@ -890,8 +928,9 @@ bool policy_check_send(Policy *p,
         verdict = policy_check(p, &filter);
 
         log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
-                 "Send permission check for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s interface=%s path=%s member=%s: %s",
-                 uid, gid, bus_message_type_to_string(message_type), strna(name), strna(path), strna(interface), strna(member), strna(verdict_to_string(verdict)));
+                 "Send permission check %s for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s path=%s interface=%s member=%s: %s",
+                 dbus_to_kernel ? "dbus-1 to kernel" : "kernel to dbus-1", uid, gid, bus_message_type_to_string(message_type), strna(name),
+                 strna(path), strna(interface), strna(member), strna(verdict_to_string(verdict)));
 
         return verdict == ALLOW;
 }
@@ -939,6 +978,16 @@ void policy_free(Policy *p) {
                 policy_item_free(i);
         }
 
+        while ((i = p->on_console_items)) {
+                LIST_REMOVE(items, p->on_console_items, i);
+                policy_item_free(i);
+        }
+
+        while ((i = p->no_console_items)) {
+                LIST_REMOVE(items, p->no_console_items, i);
+                policy_item_free(i);
+        }
+
         while ((first = hashmap_steal_first(p->user_items))) {
 
                 while ((i = first)) {
@@ -1045,12 +1094,134 @@ void policy_dump(Policy *p) {
         printf("%s User Items:\n", draw_special_char(DRAW_ARROW));
         dump_hashmap_items(p->user_items);
 
+        printf("%s On-Console Items:\n", draw_special_char(DRAW_ARROW));
+        dump_items(p->on_console_items, "\t");
+
+        printf("%s No-Console Items:\n", draw_special_char(DRAW_ARROW));
+        dump_items(p->no_console_items, "\t");
+
         printf("%s Mandatory Items:\n", draw_special_char(DRAW_ARROW));
         dump_items(p->mandatory_items, "\t");
 
         fflush(stdout);
 }
 
+int shared_policy_new(SharedPolicy **out) {
+        SharedPolicy *sp;
+        int r;
+
+        sp = new0(SharedPolicy, 1);
+        if (!sp)
+                return log_oom();
+
+        r = pthread_mutex_init(&sp->lock, NULL);
+        if (r < 0) {
+                log_error_errno(r, "Cannot initialize shared policy mutex: %m");
+                goto exit_free;
+        }
+
+        r = pthread_rwlock_init(&sp->rwlock, NULL);
+        if (r < 0) {
+                log_error_errno(r, "Cannot initialize shared policy rwlock: %m");
+                goto exit_mutex;
+        }
+
+        *out = sp;
+        sp = NULL;
+        return 0;
+
+        /* pthread lock destruction is not fail-safe... meh! */
+exit_mutex:
+        pthread_mutex_destroy(&sp->lock);
+exit_free:
+        free(sp);
+        return r;
+}
+
+SharedPolicy *shared_policy_free(SharedPolicy *sp) {
+        if (!sp)
+                return NULL;
+
+        policy_free(sp->policy);
+        pthread_rwlock_destroy(&sp->rwlock);
+        pthread_mutex_destroy(&sp->lock);
+        free(sp);
+
+        return NULL;
+}
+
+static int shared_policy_reload_unlocked(SharedPolicy *sp, char **configuration) {
+        Policy old, buffer = {};
+        bool free_old;
+        int r;
+
+        assert(sp);
+
+        r = policy_load(&buffer, configuration);
+        if (r < 0)
+                return log_error_errno(r, "Failed to load policy: %m");
+
+        /* policy_dump(&buffer); */
+
+        pthread_rwlock_wrlock(&sp->rwlock);
+        memcpy(&old, &sp->buffer, sizeof(old));
+        memcpy(&sp->buffer, &buffer, sizeof(buffer));
+        free_old = !!sp->policy;
+        sp->policy = &sp->buffer;
+        pthread_rwlock_unlock(&sp->rwlock);
+
+        if (free_old)
+                policy_free(&old);
+
+        return 0;
+}
+
+int shared_policy_reload(SharedPolicy *sp, char **configuration) {
+        int r;
+
+        assert(sp);
+
+        pthread_mutex_lock(&sp->lock);
+        r = shared_policy_reload_unlocked(sp, configuration);
+        pthread_mutex_unlock(&sp->lock);
+
+        return r;
+}
+
+int shared_policy_preload(SharedPolicy *sp, char **configuration) {
+        int r;
+
+        assert(sp);
+
+        pthread_mutex_lock(&sp->lock);
+        if (!sp->policy)
+                r = shared_policy_reload_unlocked(sp, configuration);
+        else
+                r = 0;
+        pthread_mutex_unlock(&sp->lock);
+
+        return r;
+}
+
+Policy *shared_policy_acquire(SharedPolicy *sp) {
+        assert(sp);
+
+        pthread_rwlock_rdlock(&sp->rwlock);
+        if (sp->policy)
+                return sp->policy;
+        pthread_rwlock_unlock(&sp->rwlock);
+
+        return NULL;
+}
+
+void shared_policy_release(SharedPolicy *sp, Policy *p) {
+        assert(sp);
+        assert(!p || sp->policy == p);
+
+        if (p)
+                pthread_rwlock_unlock(&sp->rwlock);
+}
+
 static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = {
         [_POLICY_ITEM_TYPE_UNSET] = "unset",
         [POLICY_ITEM_ALLOW] = "allow",