bus-xml-policy.c revision 8752c5752f3b9023f9ce96a55d70c6e5fc31118f
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "sd-login.h"
#include "bus-internal.h"
#include "bus-xml-policy.h"
#include "conf-files.h"
#include "fileio.h"
#include "formats-util.h"
#include "locale-util.h"
#include "set.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
#include "xml.h"
static void policy_item_free(PolicyItem *i) {
assert(i);
free(i);
}
}
unsigned n_other = 0;
const char *q;
int r;
enum {
} state = STATE_OUTSIDE;
enum {
unsigned line = 0;
assert(p);
if (r < 0) {
if (r == -ENOENT)
return 0;
if (r == -EISDIR)
return r;
}
q = c;
for (;;) {
int t;
if (t < 0)
switch (state) {
case STATE_OUTSIDE:
if (t == XML_TAG_OPEN) {
else {
return -EINVAL;
}
} else if (t == XML_END)
return 0;
return -EINVAL;
}
break;
case STATE_BUSCONFIG:
if (t == XML_TAG_OPEN) {
} else {
state = STATE_OTHER;
n_other = 0;
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
return -EINVAL;
}
break;
case STATE_POLICY:
if (t == XML_ATTRIBUTE_NAME) {
else {
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
else if (t == XML_TAG_OPEN) {
else {
return -EINVAL;
}
assert(!i);
if (!i)
return log_oom();
return -EINVAL;
}
break;
case STATE_POLICY_CONTEXT:
if (t == XML_ATTRIBUTE_VALUE) {
} else {
return -EINVAL;
}
} else {
return -EINVAL;
}
break;
case STATE_POLICY_CONSOLE:
if (t == XML_ATTRIBUTE_VALUE) {
} else {
return -EINVAL;
}
} else {
return -EINVAL;
}
break;
case STATE_POLICY_USER:
if (t == XML_ATTRIBUTE_VALUE) {
policy_user = name;
} else {
return -EINVAL;
}
break;
case STATE_POLICY_GROUP:
if (t == XML_ATTRIBUTE_VALUE) {
policy_group = name;
} else {
return -EINVAL;
}
break;
if (t == XML_ATTRIBUTE_VALUE)
else {
return -EINVAL;
}
break;
case STATE_ALLOW_DENY:
assert(i);
if (t == XML_ATTRIBUTE_NAME) {
break;
} else {
break;
}
return -EINVAL;
}
const char *u;
assert(u);
u++;
if (streq(u, "interface"))
else if (streq(u, "member"))
else if (streq(u, "error"))
else if (streq(u, "path"))
else if (streq(u, "type"))
else {
if (streq(u, "requested_reply"))
else
break;
}
} else
} else if (t == XML_TAG_CLOSE_EMPTY ||
/* If the tag is fully empty so far, we consider it a recv */
if (i->class == _POLICY_ITEM_CLASS_UNSET)
i->class = POLICY_ITEM_RECV;
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;
if (r < 0)
return log_oom();
if (!u) {
log_error("User policy without name");
return -EINVAL;
}
if (r < 0) {
log_error_errno(r, "Failed to resolve user %s, ignoring policy: %m", u);
free(i);
} else {
item_append(i, &first);
i->uid_valid = true;
if (r < 0) {
return log_oom();
}
}
} else if (policy_category == POLICY_CATEGORY_GROUP) {
const char *g = policy_group;
if (r < 0)
return log_oom();
if (!g) {
log_error("Group policy without name");
return -EINVAL;
}
r = get_group_creds(&g, &i->gid);
if (r < 0) {
log_error_errno(r, "Failed to resolve group %s, ignoring policy: %m", g);
free(i);
} else {
item_append(i, &first);
i->gid_valid = true;
if (r < 0) {
return log_oom();
}
}
}
i = NULL;
return -EINVAL;
}
break;
if (t == XML_ATTRIBUTE_VALUE) {
assert(i);
if (i->interface) {
return -EINVAL;
}
}
} else {
return -EINVAL;
}
break;
case STATE_ALLOW_DENY_MEMBER:
if (t == XML_ATTRIBUTE_VALUE) {
assert(i);
if (i->member) {
return -EINVAL;
}
}
} else {
return -EINVAL;
}
break;
case STATE_ALLOW_DENY_ERROR:
if (t == XML_ATTRIBUTE_VALUE) {
assert(i);
if (i->error) {
return -EINVAL;
}
}
} else {
return -EINVAL;
}
break;
case STATE_ALLOW_DENY_PATH:
if (t == XML_ATTRIBUTE_VALUE) {
assert(i);
if (i->path) {
return -EINVAL;
}
}
} else {
return -EINVAL;
}
break;
if (t == XML_ATTRIBUTE_VALUE) {
assert(i);
if (i->message_type != 0) {
return -EINVAL;
}
if (r < 0) {
return -EINVAL;
}
}
} else {
return -EINVAL;
}
break;
case STATE_ALLOW_DENY_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
assert(i);
if (i->name) {
return -EINVAL;
}
switch (i->class) {
case POLICY_ITEM_USER:
const char *u = name;
if (r < 0)
else
i->uid_valid = true;
}
break;
case POLICY_ITEM_GROUP:
const char *g = name;
r = get_group_creds(&g, &i->gid);
if (r < 0)
else
i->gid_valid = true;
}
break;
case POLICY_ITEM_SEND:
case POLICY_ITEM_RECV:
break;
default:
break;
}
} else {
return -EINVAL;
}
break;
if (t == XML_ATTRIBUTE_VALUE)
else {
return -EINVAL;
}
break;
case STATE_OTHER:
if (t == XML_TAG_OPEN)
n_other++;
else if (t == XML_TAG_CLOSE || t == XML_TAG_CLOSE_EMPTY) {
if (n_other == 0)
else
n_other--;
}
break;
}
}
}
enum {
DENY,
};
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 {
int message_type;
const char *name;
const char *interface;
const char *path;
const char *member;
};
static int is_permissive(PolicyItem *i) {
assert(i);
}
assert(i);
switch (i->class) {
case POLICY_ITEM_SEND:
case POLICY_ITEM_RECV:
break;
break;
break;
break;
break;
return is_permissive(i);
case POLICY_ITEM_OWN:
return is_permissive(i);
break;
case POLICY_ITEM_OWN_PREFIX:
return is_permissive(i);
break;
case POLICY_ITEM_USER:
return is_permissive(i);
break;
case POLICY_ITEM_GROUP:
return is_permissive(i);
break;
case POLICY_ITEM_IGNORE:
default:
break;
}
return DUNNO;
}
PolicyItem *i;
/* 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 */
int v;
continue;
v = check_policy_item(i, filter);
if (v != DUNNO)
verdict = v;
}
return verdict;
}
int verdict, v;
assert(p);
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 on/no_console items
* 5. Check mandatory items
*
* Later rules override earlier rules.
*/
if (items) {
if (v != DUNNO)
verdict = v;
}
}
if (items) {
if (v != DUNNO)
verdict = v;
}
}
else
if (v != DUNNO)
verdict = v;
if (v != DUNNO)
verdict = v;
return verdict;
}
struct policy_check_filter filter = {
.class = POLICY_ITEM_OWN,
};
int verdict;
assert(p);
}
struct policy_check_filter filter = {
};
int verdict;
assert(p);
int v;
v = policy_check(p, &filter);
if (v != DUNNO)
verdict = v;
}
}
bool policy_check_one_recv(Policy *p,
int message_type,
const char *name,
const char *path,
const char *interface,
const char *member) {
struct policy_check_filter filter = {
};
assert(p);
}
bool policy_check_recv(Policy *p,
int message_type,
char **namesv,
const char *path,
const char *interface,
const char *member,
bool dbus_to_kernel) {
bool allow = false;
Iterator i;
assert(p);
} else {
SET_FOREACH(n, names, i) {
last = n;
if (allow)
break;
}
if (!allow) {
if (allow)
break;
}
}
}
"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),
return allow;
}
bool policy_check_one_send(Policy *p,
int message_type,
const char *name,
const char *path,
const char *interface,
const char *member) {
struct policy_check_filter filter = {
};
assert(p);
}
bool policy_check_send(Policy *p,
int message_type,
char **namesv,
const char *path,
const char *interface,
const char *member,
bool dbus_to_kernel,
char **out_used_name) {
bool allow = false;
Iterator i;
assert(p);
} else {
SET_FOREACH(n, names, i) {
last = n;
if (allow)
break;
}
if (!allow) {
if (allow)
break;
}
}
}
if (out_used_name)
*out_used_name = last;
"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),
return allow;
}
char **i;
int r;
assert(p);
STRV_FOREACH(i, files) {
r = file_load(p, *i);
if (r == -EISDIR) {
_cleanup_strv_free_ char **l = NULL;
char **j;
if (r < 0)
return log_error_errno(r, "Failed to get configuration file list: %m");
STRV_FOREACH(j, l)
file_load(p, *j);
}
/* We ignore all errors but EISDIR, and just proceed. */
}
return 0;
}
void policy_free(Policy *p) {
PolicyItem *i, *first;
if (!p)
return;
while ((i = p->default_items)) {
policy_item_free(i);
}
while ((i = p->mandatory_items)) {
policy_item_free(i);
}
while ((i = p->on_console_items)) {
policy_item_free(i);
}
while ((i = p->no_console_items)) {
policy_item_free(i);
}
while ((i = first)) {
policy_item_free(i);
}
}
while ((i = first)) {
policy_item_free(i);
}
}
hashmap_free(p->user_items);
hashmap_free(p->group_items);
}
PolicyItem *i;
if (!items)
return;
if (!prefix)
prefix = "";
printf("%sType: %s\n"
"%sClass: %s\n",
if (i->interface)
printf("%sInterface: %s\n",
if (i->member)
printf("%sMember: %s\n",
if (i->error)
printf("%sError: %s\n",
if (i->path)
printf("%sPath: %s\n",
if (i->name)
printf("%sName: %s\n",
if (i->message_type != 0)
printf("%sMessage Type: %s\n",
if (i->uid_valid) {
_cleanup_free_ char *user;
}
if (i->gid_valid) {
_cleanup_free_ char *group;
}
}
}
static void dump_hashmap_items(Hashmap *h) {
PolicyItem *i;
Iterator j;
void *k;
HASHMAP_FOREACH_KEY(i, k, h, j) {
dump_items(i, "\t\t");
}
}
void policy_dump(Policy *p) {
}
int r;
if (!sp)
return log_oom();
if (r != 0) {
r = log_error_errno(r, "Cannot initialize shared policy mutex: %m");
goto exit_free;
}
if (r != 0) {
r = log_error_errno(r, "Cannot initialize shared policy rwlock: %m");
goto exit_mutex;
}
return 0;
/* pthread lock destruction is not fail-safe... meh! */
return r;
}
if (!sp)
return NULL;
return NULL;
}
bool free_old;
int r;
if (r < 0)
return log_error_errno(r, "Failed to load policy: %m");
log_debug("Reloading configuration");
/* policy_dump(&buffer); */
if (free_old)
policy_free(&old);
return 0;
}
int r;
return r;
}
int r = 0;
if (!conf)
return log_oom();
if (r >= 0) {
}
}
return r;
}
return NULL;
}
if (p)
}
static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = {
[_POLICY_ITEM_TYPE_UNSET] = "unset",
[POLICY_ITEM_ALLOW] = "allow",
[POLICY_ITEM_DENY] = "deny",
};
static const char* const policy_item_class_table[_POLICY_ITEM_CLASS_MAX] = {
[_POLICY_ITEM_CLASS_UNSET] = "unset",
[POLICY_ITEM_SEND] = "send",
[POLICY_ITEM_RECV] = "recv",
[POLICY_ITEM_OWN] = "own",
[POLICY_ITEM_OWN_PREFIX] = "own-prefix",
[POLICY_ITEM_USER] = "user",
[POLICY_ITEM_GROUP] = "group",
[POLICY_ITEM_IGNORE] = "ignore",
};