chiark / gitweb /
remove unused includes
[elogind.git] / src / bus-proxyd / bus-xml-policy.c
index 8d14828a9cb37225d348587909b2a34a68e0934f..497bce7b5699805d1b87968cb9a8c88040d1fb89 100644 (file)
 #include "xml.h"
 #include "fileio.h"
 #include "strv.h"
+#include "set.h"
 #include "conf-files.h"
 #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;
@@ -830,14 +865,14 @@ bool policy_check_hello(Policy *p, uid_t uid, gid_t gid) {
         return verdict == ALLOW;
 }
 
-bool policy_check_recv(Policy *p,
-                       uid_t uid,
-                       gid_t gid,
-                       int message_type,
-                       const char *name,
-                       const char *path,
-                       const char *interface,
-                       const char *member) {
+bool policy_check_one_recv(Policy *p,
+                           uid_t uid,
+                           gid_t gid,
+                           int message_type,
+                           const char *name,
+                           const char *path,
+                           const char *interface,
+                           const char *member) {
 
         struct policy_check_filter filter = {
                 .class        = POLICY_ITEM_RECV,
@@ -850,27 +885,63 @@ bool policy_check_recv(Policy *p,
                 .member       = member,
         };
 
-        int verdict;
-
         assert(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)));
-
-        return verdict == ALLOW;
+        return policy_check(p, &filter) == ALLOW;
 }
 
-bool policy_check_send(Policy *p,
+bool policy_check_recv(Policy *p,
                        uid_t uid,
                        gid_t gid,
                        int message_type,
-                       const char *name,
+                       Set *names,
+                       char **namesv,
                        const char *path,
                        const char *interface,
-                       const char *member) {
+                       const char *member,
+                       bool dbus_to_kernel) {
+
+        char *n, **nv, *last = NULL;
+        bool allow = false;
+        Iterator i;
+
+        assert(p);
+
+        if (set_isempty(names) && strv_isempty(namesv)) {
+                allow = policy_check_one_recv(p, uid, gid, message_type, NULL, path, interface, member);
+        } else {
+                SET_FOREACH(n, names, i) {
+                        last = n;
+                        allow = policy_check_one_recv(p, uid, gid, message_type, n, path, interface, member);
+                        if (allow)
+                                break;
+                }
+                if (!allow) {
+                        STRV_FOREACH(nv, namesv) {
+                                last = *nv;
+                                allow = policy_check_one_recv(p, uid, gid, message_type, *nv, path, interface, member);
+                                if (allow)
+                                        break;
+                        }
+                }
+        }
+
+        log_full(LOG_AUTH | (!allow ? LOG_WARNING : LOG_DEBUG),
+                 "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(last),
+                 strna(path), strna(interface), strna(member), allow ? "ALLOW" : "DENY");
+
+        return allow;
+}
+
+bool policy_check_one_send(Policy *p,
+                           uid_t uid,
+                           gid_t gid,
+                           int message_type,
+                           const char *name,
+                           const char *path,
+                           const char *interface,
+                           const char *member) {
 
         struct policy_check_filter filter = {
                 .class        = POLICY_ITEM_SEND,
@@ -883,17 +954,57 @@ bool policy_check_send(Policy *p,
                 .member       = member,
         };
 
-        int verdict;
+        assert(p);
+
+        return policy_check(p, &filter) == ALLOW;
+}
+
+bool policy_check_send(Policy *p,
+                       uid_t uid,
+                       gid_t gid,
+                       int message_type,
+                       Set *names,
+                       char **namesv,
+                       const char *path,
+                       const char *interface,
+                       const char *member,
+                       bool dbus_to_kernel,
+                       char **out_used_name) {
+
+        char *n, **nv, *last = NULL;
+        bool allow = false;
+        Iterator i;
 
         assert(p);
 
-        verdict = policy_check(p, &filter);
+        if (set_isempty(names) && strv_isempty(namesv)) {
+                allow = policy_check_one_send(p, uid, gid, message_type, NULL, path, interface, member);
+        } else {
+                SET_FOREACH(n, names, i) {
+                        last = n;
+                        allow = policy_check_one_send(p, uid, gid, message_type, n, path, interface, member);
+                        if (allow)
+                                break;
+                }
+                if (!allow) {
+                        STRV_FOREACH(nv, namesv) {
+                                last = *nv;
+                                allow = policy_check_one_send(p, uid, gid, message_type, *nv, path, interface, member);
+                                if (allow)
+                                        break;
+                        }
+                }
+        }
 
-        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)));
+        if (out_used_name)
+                *out_used_name = last;
 
-        return verdict == ALLOW;
+        log_full(LOG_AUTH | (!allow ? LOG_WARNING : LOG_DEBUG),
+                 "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(last),
+                 strna(path), strna(interface), strna(member), allow ? "ALLOW" : "DENY");
+
+        return allow;
 }
 
 int policy_load(Policy *p, char **files) {
@@ -939,6 +1050,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)) {
@@ -1007,7 +1128,7 @@ static void dump_items(PolicyItem *items, const char *prefix) {
 
                         user = uid_to_name(i->uid);
 
-                        printf("%sUser: %s (%d)\n",
+                        printf("%sUser: %s ("UID_FMT")\n",
                                prefix, strna(user), i->uid);
                 }
 
@@ -1016,7 +1137,7 @@ static void dump_items(PolicyItem *items, const char *prefix) {
 
                         group = gid_to_name(i->gid);
 
-                        printf("%sGroup: %s (%d)\n",
+                        printf("%sGroup: %s ("GID_FMT")\n",
                                prefix, strna(group), i->gid);
                 }
                 printf("%s-\n", prefix);
@@ -1045,12 +1166,144 @@ 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);
+        strv_free(sp->configuration);
+        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");
+
+        log_debug("Reloading configuration");
+        /* 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) {
+        int r;
+
+        assert(sp);
+
+        pthread_mutex_lock(&sp->lock);
+        r = shared_policy_reload_unlocked(sp, sp->configuration);
+        pthread_mutex_unlock(&sp->lock);
+
+        return r;
+}
+
+int shared_policy_preload(SharedPolicy *sp, char **configuration) {
+        _cleanup_strv_free_ char **conf = NULL;
+        int r = 0;
+
+        assert(sp);
+
+        conf = strv_copy(configuration);
+        if (!conf)
+                return log_oom();
+
+        pthread_mutex_lock(&sp->lock);
+        if (!sp->policy) {
+                r = shared_policy_reload_unlocked(sp, conf);
+                if (r >= 0) {
+                        sp->configuration = conf;
+                        conf = NULL;
+                }
+        }
+        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",