#include "strv.h"
#include "conf-files.h"
#include "bus-internal.h"
+#include "bus-message.h"
#include "bus-policy.h"
static void policy_item_free(PolicyItem *i) {
if (r == -EISDIR)
return r;
- log_error("Failed to load %s: %s", path, strerror(-r));
+ log_error_errno(r, "Failed to load %s: %m", path);
return r;
}
t = xml_tokenize(&q, &name, &xml_state, &line);
if (t < 0) {
- log_error("XML parse failure in %s: %s", path, strerror(-t));
+ log_error_errno(t, "XML parse failure in %s: %m", path);
return t;
}
r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
if (r < 0) {
- log_error("Failed to resolve user %s, ignoring policy: %s", u, strerror(-r));
+ log_error_errno(r, "Failed to resolve user %s, ignoring policy: %m", u);
free(i);
} else {
PolicyItem *first;
r = get_group_creds(&g, &i->gid);
if (r < 0) {
- log_error("Failed to resolve group %s, ignoring policy: %s", g, strerror(-r));
+ log_error_errno(r, "Failed to resolve group %s, ignoring policy: %m", g);
free(i);
} else {
PolicyItem *first;
r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
if (r < 0)
- log_error("Failed to resolve user %s: %s", name, strerror(-r));
+ log_error_errno(r, "Failed to resolve user %s: %m", name);
else
i->uid_valid = true;
}
r = get_group_creds(&g, &i->gid);
if (r < 0)
- log_error("Failed to resolve group %s: %s", name, strerror(-r));
+ log_error_errno(r, "Failed to resolve group %s: %m", name);
else
i->gid_valid = true;
}
}
}
+enum {
+ DENY,
+ ALLOW,
+ DUNNO,
+};
+
+static const char *verdict_to_string(int v) {
+ switch (v) {
+
+ case DENY:
+ return "DENY";
+ case ALLOW:
+ return "ALLOW";
+ case DUNNO:
+ return "DUNNO";
+ }
+
+ return NULL;
+}
+
+struct policy_check_filter {
+ PolicyItemClass class;
+ uid_t uid;
+ gid_t gid;
+ int message_type;
+ const char *name;
+ const char *interface;
+ const char *path;
+ const char *member;
+};
+
+static int is_permissive(PolicyItem *i) {
+
+ assert(i);
+
+ return (i->type == POLICY_ITEM_ALLOW) ? ALLOW : DENY;
+}
+
+static int check_policy_item(PolicyItem *i, const struct policy_check_filter *filter) {
+
+ assert(i);
+ assert(filter);
+
+ switch (i->class) {
+ case POLICY_ITEM_SEND:
+ case POLICY_ITEM_RECV:
+
+ if (i->name && !streq_ptr(i->name, filter->name))
+ break;
+
+ if ((i->message_type != 0) && (i->message_type != filter->message_type))
+ break;
+
+ if (i->path && !streq_ptr(i->path, filter->path))
+ break;
+
+ if (i->member && !streq_ptr(i->member, filter->member))
+ break;
+
+ if (i->interface && !streq_ptr(i->interface, filter->interface))
+ break;
+
+ return is_permissive(i);
+
+ case POLICY_ITEM_OWN:
+ assert(filter->name);
+
+ if (streq(i->name, "*") || streq(i->name, filter->name))
+ return is_permissive(i);
+ break;
+
+ case POLICY_ITEM_OWN_PREFIX:
+ assert(filter->name);
+
+ if (streq(i->name, "*") || service_name_startswith(filter->name, i->name))
+ return is_permissive(i);
+ break;
+
+ case POLICY_ITEM_USER:
+ if (filter->uid != (uid_t) -1)
+ if ((streq_ptr(i->name, "*") || (i->uid_valid && i->uid == filter->uid)))
+ return is_permissive(i);
+ break;
+
+ case POLICY_ITEM_GROUP:
+ if (filter->gid != (gid_t) -1)
+ if ((streq_ptr(i->name, "*") || (i->gid_valid && i->gid == filter->gid)))
+ return is_permissive(i);
+ break;
+
+ case POLICY_ITEM_IGNORE:
+ default:
+ break;
+ }
+
+ return DUNNO;
+}
+
+static int check_policy_items(PolicyItem *items, const struct policy_check_filter *filter) {
+
+ PolicyItem *i;
+ int verdict = DUNNO;
+
+ assert(filter);
+
+ /* Check all policies in a set - a broader one might be followed by a more specific one,
+ * and the order of rules in policy definitions matters */
+ LIST_FOREACH(items, i, items) {
+ int v;
+
+ if (i->class != filter->class &&
+ !(i->class == POLICY_ITEM_OWN_PREFIX && filter->class == POLICY_ITEM_OWN))
+ continue;
+
+ v = check_policy_item(i, filter);
+ if (v != DUNNO)
+ verdict = v;
+ }
+
+ return verdict;
+}
+
+static int policy_check(Policy *p, const struct policy_check_filter *filter) {
+
+ PolicyItem *items;
+ int verdict, v;
+
+ assert(p);
+ assert(filter);
+
+ assert(IN_SET(filter->class, POLICY_ITEM_SEND, POLICY_ITEM_RECV, POLICY_ITEM_OWN, POLICY_ITEM_USER, POLICY_ITEM_GROUP));
+
+ /*
+ * The policy check is implemented by the following logic:
+ *
+ * 1. Check default items
+ * 2. Check group items
+ * 3. Check user items
+ * 4. Check mandatory items
+ *
+ * Later rules override earlier rules.
+ */
+
+ verdict = check_policy_items(p->default_items, filter);
+
+ if (filter->gid != (gid_t) -1) {
+ items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->gid));
+ if (items) {
+ v = check_policy_items(items, filter);
+ if (v != DUNNO)
+ verdict = v;
+ }
+ }
+
+ if (filter->uid != (uid_t) -1) {
+ items = hashmap_get(p->user_items, UINT32_TO_PTR(filter->uid));
+ if (items) {
+ v = check_policy_items(items, filter);
+ if (v != DUNNO)
+ verdict = v;
+ }
+ }
+
+ v = check_policy_items(p->mandatory_items, filter);
+ if (v != DUNNO)
+ verdict = v;
+
+ return verdict;
+}
+
+bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name) {
+
+ struct policy_check_filter filter = {
+ .class = POLICY_ITEM_OWN,
+ .uid = uid,
+ .gid = gid,
+ .name = name,
+ };
+
+ int verdict;
+
+ assert(p);
+ assert(name);
+
+ verdict = policy_check(p, &filter);
+
+ log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
+ "Ownership permission check for uid=" UID_FMT " gid=" GID_FMT" name=%s: %s",
+ uid, gid, strna(name), strna(verdict_to_string(verdict)));
+
+ return verdict == ALLOW;
+}
+
+bool policy_check_hello(Policy *p, uid_t uid, gid_t gid) {
+
+ struct policy_check_filter filter = {
+ .uid = uid,
+ .gid = gid,
+ };
+ int verdict;
+
+ assert(p);
+
+ filter.class = POLICY_ITEM_USER;
+ verdict = policy_check(p, &filter);
+
+ if (verdict != DENY) {
+ int v;
+
+ filter.class = POLICY_ITEM_GROUP;
+ v = policy_check(p, &filter);
+ if (v != DUNNO)
+ verdict = v;
+ }
+
+ log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
+ "Hello permission check for uid=" UID_FMT " gid=" GID_FMT": %s",
+ uid, gid, strna(verdict_to_string(verdict)));
+
+ 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) {
+
+ struct policy_check_filter filter = {
+ .class = POLICY_ITEM_RECV,
+ .uid = uid,
+ .gid = gid,
+ .message_type = message_type,
+ .name = name,
+ .interface = interface,
+ .path = path,
+ .member = member,
+ };
+
+ int verdict;
+
+ assert(p);
+
+ verdict = policy_check(p, &filter);
+
+ log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG),
+ "Recieve 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;
+}
+
+bool policy_check_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,
+ .uid = uid,
+ .gid = gid,
+ .message_type = message_type,
+ .name = name,
+ .interface = interface,
+ .path = path,
+ .member = member,
+ };
+
+ int verdict;
+
+ assert(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)));
+
+ return verdict == ALLOW;
+}
+
int policy_load(Policy *p, char **files) {
char **i;
int r;
r = conf_files_list(&l, ".conf", NULL, *i, NULL);
if (r < 0) {
- log_error("Failed to get configuration file list: %s", strerror(-r));
+ log_error_errno(r, "Failed to get configuration file list: %m");
return r;
}
p->user_items = p->group_items = NULL;
}
-static void dump_items(PolicyItem *i, const char *prefix) {
+static void dump_items(PolicyItem *items, const char *prefix) {
+
+ PolicyItem *i;
- if (!i)
+ if (!items)
return;
if (!prefix)
prefix = "";
- printf("%sType: %s\n"
- "%sClass: %s\n",
- prefix, policy_item_type_to_string(i->type),
- prefix, policy_item_class_to_string(i->class));
+ LIST_FOREACH(items, i, items) {
- if (i->interface)
- printf("%sInterface: %s\n",
- prefix, i->interface);
+ printf("%sType: %s\n"
+ "%sClass: %s\n",
+ prefix, policy_item_type_to_string(i->type),
+ prefix, policy_item_class_to_string(i->class));
- if (i->member)
- printf("%sMember: %s\n",
- prefix, i->member);
+ if (i->interface)
+ printf("%sInterface: %s\n",
+ prefix, i->interface);
- if (i->error)
- printf("%sError: %s\n",
- prefix, i->error);
+ if (i->member)
+ printf("%sMember: %s\n",
+ prefix, i->member);
- if (i->path)
- printf("%sPath: %s\n",
- prefix, i->path);
+ if (i->error)
+ printf("%sError: %s\n",
+ prefix, i->error);
- if (i->name)
- printf("%sName: %s\n",
- prefix, i->name);
+ if (i->path)
+ printf("%sPath: %s\n",
+ prefix, i->path);
- if (i->message_type != 0)
- printf("%sMessage Type: %s\n",
- prefix, bus_message_type_to_string(i->message_type));
+ if (i->name)
+ printf("%sName: %s\n",
+ prefix, i->name);
- if (i->uid_valid) {
- _cleanup_free_ char *user;
+ if (i->message_type != 0)
+ printf("%sMessage Type: %s\n",
+ prefix, bus_message_type_to_string(i->message_type));
- user = uid_to_name(i->uid);
+ if (i->uid_valid) {
+ _cleanup_free_ char *user;
- printf("%sUser: %s\n",
- prefix, strna(user));
- }
+ user = uid_to_name(i->uid);
- if (i->gid_valid) {
- _cleanup_free_ char *group;
+ printf("%sUser: %s (%d)\n",
+ prefix, strna(user), i->uid);
+ }
- group = gid_to_name(i->gid);
+ if (i->gid_valid) {
+ _cleanup_free_ char *group;
- printf("%sGroup: %s\n",
- prefix, strna(group));
- }
+ group = gid_to_name(i->gid);
- if (i->items_next) {
- printf("%s%s\n", prefix, draw_special_char(DRAW_DASH));
- dump_items(i->items_next, prefix);
+ printf("%sGroup: %s (%d)\n",
+ prefix, strna(group), i->gid);
+ }
+ printf("%s-\n", prefix);
}
}
}
}
-noreturn void policy_dump(Policy *p) {
+void policy_dump(Policy *p) {
printf("%s Default Items:\n", draw_special_char(DRAW_ARROW));
dump_items(p->default_items, "\t");
printf("%s Mandatory Items:\n", draw_special_char(DRAW_ARROW));
dump_items(p->mandatory_items, "\t");
-
- exit(0);
}
static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = {