X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fbus-proxyd%2Fbus-xml-policy.c;h=497bce7b5699805d1b87968cb9a8c88040d1fb89;hb=adfe5671ef794099068038dfccbf1eb5134433c8;hp=58241038ea5608bc1a140141d58927a74dec3424;hpb=a2be9e047e6e0b243fe946f972d5d4d04db140e5;p=elogind.git diff --git a/src/bus-proxyd/bus-xml-policy.c b/src/bus-proxyd/bus-xml-policy.c index 58241038e..497bce7b5 100644 --- a/src/bus-proxyd/bus-xml-policy.c +++ b/src/bus-proxyd/bus-xml-policy.c @@ -22,10 +22,11 @@ #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 tag unsupported at %s:%u, ignoring.", name, path, line); - else - log_warning("Attribute %s of tag unknown at %s:%u, ignoring.", name, path, line); + log_warning("Attribute %s of 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 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; @@ -421,8 +448,10 @@ static int file_load(Policy *p, const char *path) { return -EINVAL; } - i->interface = name; - name = NULL; + if (!streq(name, "*")) { + i->interface = name; + name = NULL; + } state = STATE_ALLOW_DENY; } else { log_error("Unexpected token (9) at %s:%u.", path, line); @@ -440,8 +469,10 @@ static int file_load(Policy *p, const char *path) { return -EINVAL; } - i->member = name; - name = NULL; + if (!streq(name, "*")) { + i->member = name; + name = NULL; + } state = STATE_ALLOW_DENY; } else { log_error("Unexpected token (10) in %s:%u.", path, line); @@ -459,8 +490,10 @@ static int file_load(Policy *p, const char *path) { return -EINVAL; } - i->error = name; - name = NULL; + if (!streq(name, "*")) { + i->error = name; + name = NULL; + } state = STATE_ALLOW_DENY; } else { log_error("Unexpected token (11) in %s:%u.", path, line); @@ -478,8 +511,10 @@ static int file_load(Policy *p, const char *path) { return -EINVAL; } - i->path = name; - name = NULL; + if (!streq(name, "*")) { + i->path = name; + name = NULL; + } state = STATE_ALLOW_DENY; } else { log_error("Unexpected token (12) in %s:%u.", path, line); @@ -498,10 +533,12 @@ static int file_load(Policy *p, const char *path) { return -EINVAL; } - r = bus_message_type_from_string(name, &i->message_type); - if (r < 0) { - log_error("Invalid message type in %s:%u.", path, line); - return -EINVAL; + if (!streq(name, "*")) { + r = bus_message_type_from_string(name, &i->message_type); + if (r < 0) { + log_error("Invalid message type in %s:%u.", path, line); + return -EINVAL; + } } state = STATE_ALLOW_DENY; @@ -544,6 +581,17 @@ static int file_load(Policy *p, const char *path) { i->gid_valid = true; } break; + + case POLICY_ITEM_SEND: + case POLICY_ITEM_RECV: + + if (streq(name, "*")) { + free(name); + name = NULL; + } + break; + + default: break; } @@ -725,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. */ @@ -750,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; @@ -809,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, @@ -829,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, @@ -862,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) { @@ -918,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)) { @@ -986,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); } @@ -995,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); @@ -1024,8 +1166,142 @@ 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] = {