bus-proxy: beef up policy enforcement

- actually return permission errors to clients

- use the right ucreds field

- fix error paths when we cannot keep track of locally acquired names
  due to OOM

- avoid unnecessary global variables

- log when the policy denies access

- enforce correct policy rule order

- always request all the metadata its we need to make decisions
This commit is contained in:
Lennart Poettering 2014-11-26 23:14:13 +01:00
parent 9398f65093
commit 78f9b196ab
4 changed files with 454 additions and 286 deletions

View file

@ -593,14 +593,29 @@ static int file_load(Policy *p, const char *path) {
}
enum {
DENY,
ALLOW,
DUNNO,
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 {
PolicyItemClass class;
const struct ucred *ucred;
uid_t uid;
gid_t gid;
int message_type;
const char *name;
const char *interface;
@ -656,17 +671,15 @@ static int check_policy_item(PolicyItem *i, const struct policy_check_filter *fi
break;
case POLICY_ITEM_USER:
assert(filter->ucred);
if ((streq_ptr(i->name, "*") || (i->uid_valid && i->uid == filter->ucred->uid)))
return is_permissive(i);
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:
assert(filter->ucred);
if ((streq_ptr(i->name, "*") || (i->gid_valid && i->gid == filter->ucred->gid)))
return is_permissive(i);
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:
@ -680,29 +693,31 @@ static int check_policy_item(PolicyItem *i, const struct policy_check_filter *fi
static int check_policy_items(PolicyItem *items, const struct policy_check_filter *filter) {
PolicyItem *i;
int r, ret = DUNNO;
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;
r = check_policy_item(i, filter);
if (r != DUNNO)
ret = r;
v = check_policy_item(i, filter);
if (v != DUNNO)
verdict = v;
}
return ret;
return verdict;
}
static int policy_check(Policy *p, const struct policy_check_filter *filter) {
PolicyItem *items;
int r;
int verdict, v;
assert(p);
assert(filter);
@ -712,68 +727,96 @@ static int policy_check(Policy *p, const struct policy_check_filter *filter) {
/*
* The policy check is implemented by the following logic:
*
* 1. Check mandatory items. If the message matches any of these, it is decisive.
* 2. See if the passed ucred match against the user/group hashmaps. A matching entry is also decisive.
* 3. Consult the defaults if non of the above matched with a more specific rule.
* 4. If the message isn't caught be the defaults either, reject it.
* 1. Check default items
* 2. Check group items
* 3. Check user items
* 4. Check mandatory items
*
* Later rules override earlier rules.
*/
r = check_policy_items(p->mandatory_items, filter);
if (r != DUNNO)
return r;
verdict = check_policy_items(p->default_items, filter);
if (filter->ucred) {
items = hashmap_get(p->user_items, UINT32_TO_PTR(filter->ucred->uid));
if (filter->gid != (gid_t) -1) {
items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->gid));
if (items) {
r = check_policy_items(items, filter);
if (r != DUNNO)
return r;
}
items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->ucred->gid));
if (items) {
r = check_policy_items(items, filter);
if (r != DUNNO)
return r;
v = check_policy_items(items, filter);
if (v != DUNNO)
verdict = v;
}
}
return check_policy_items(p->default_items, filter);
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, const struct ucred *ucred, const char *name) {
bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name) {
struct policy_check_filter filter = {
.class = POLICY_ITEM_OWN,
.ucred = ucred,
.uid = uid,
.gid = gid,
.name = name,
};
return policy_check(p, &filter) == ALLOW;
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, const struct ucred *ucred) {
bool policy_check_hello(Policy *p, uid_t uid, gid_t gid) {
struct policy_check_filter filter = {
.ucred = ucred,
.uid = uid,
.gid = gid,
};
int user, group;
int verdict;
assert(p);
filter.class = POLICY_ITEM_USER;
user = policy_check(p, &filter);
if (user == DENY)
return false;
verdict = policy_check(p, &filter);
filter.class = POLICY_ITEM_GROUP;
group = policy_check(p, &filter);
if (group == DENY)
return false;
if (verdict != DENY) {
int v;
return !(user == DUNNO && group == DUNNO);
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,
const struct ucred *ucred,
uid_t uid,
gid_t gid,
int message_type,
const char *name,
const char *path,
@ -782,7 +825,8 @@ bool policy_check_recv(Policy *p,
struct policy_check_filter filter = {
.class = POLICY_ITEM_RECV,
.ucred = ucred,
.uid = uid,
.gid = gid,
.message_type = message_type,
.name = name,
.interface = interface,
@ -790,11 +834,22 @@ bool policy_check_recv(Policy *p,
.member = member,
};
return policy_check(p, &filter) == ALLOW;
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,
const struct ucred *ucred,
uid_t uid,
gid_t gid,
int message_type,
const char *name,
const char *path,
@ -803,7 +858,8 @@ bool policy_check_send(Policy *p,
struct policy_check_filter filter = {
.class = POLICY_ITEM_SEND,
.ucred = ucred,
.uid = uid,
.gid = gid,
.message_type = message_type,
.name = name,
.interface = interface,
@ -811,7 +867,17 @@ bool policy_check_send(Policy *p,
.member = member,
};
return policy_check(p, &filter) == ALLOW;
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) {
@ -939,6 +1005,7 @@ static void dump_items(PolicyItem *items, const char *prefix) {
printf("%sGroup: %s (%d)\n",
prefix, strna(group), i->gid);
}
printf("%s-\n", prefix);
}
}

View file

@ -76,17 +76,19 @@ typedef struct Policy {
int policy_load(Policy *p, char **files);
void policy_free(Policy *p);
bool policy_check_own(Policy *p, const struct ucred *ucred, const char *name);
bool policy_check_hello(Policy *p, const struct ucred *ucred);
bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name);
bool policy_check_hello(Policy *p, uid_t uid, gid_t gid);
bool policy_check_recv(Policy *p,
const struct ucred *ucred,
uid_t uid,
gid_t gid,
int message_type,
const char *name,
const char *path,
const char *interface,
const char *member);
bool policy_check_send(Policy *p,
const struct ucred *ucred,
uid_t uid,
gid_t gid,
int message_type,
const char *name,
const char *path,

View file

@ -51,8 +51,6 @@ static char *arg_command_line_buffer = NULL;
static bool arg_drop_privileges = false;
static char **arg_configuration = NULL;
static Hashmap *names_hash = NULL;
static int help(void) {
printf("%s [OPTIONS...]\n\n"
@ -442,95 +440,7 @@ static int get_creds_by_message(sd_bus *bus, sd_bus_message *m, uint64_t mask, s
return get_creds_by_name(bus, name, mask, _creds, error);
}
static int process_policy(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *policy, const struct ucred *ucred) {
int r;
char **name;
char **names_strv;
bool granted = false;
Iterator i;
assert(a);
assert(b);
assert(m);
if (!policy)
return 0;
if (b->is_kernel) {
/* The message came from the kernel, and is sent to our legacy client. */
r = sd_bus_creds_get_well_known_names(&m->creds, &names_strv);
if (r < 0)
return r;
STRV_FOREACH(name, names_strv) {
if (policy_check_send(policy, ucred, m->header->type, *name, m->path, m->interface, m->member)) {
r = free_and_strdup(&m->destination_ptr, *name);
if (r < 0)
break;
granted = true;
break;
}
}
if (r < 0)
return r;
if (!granted)
return -EPERM;
granted = false;
HASHMAP_FOREACH(name, names_hash, i) {
if (policy_check_recv(policy, ucred, m->header->type, *name, m->path, m->interface, m->member))
return 0;
}
return -EPERM;
} else {
sd_bus_creds *bus_creds;
/* The message came from the legacy client, and is sent to kdbus. */
r = sd_bus_get_name_creds(a, m->destination, SD_BUS_CREDS_WELL_KNOWN_NAMES, &bus_creds);
if (r < 0)
return r;
STRV_FOREACH(name, names_strv) {
if (policy_check_send(policy, ucred, m->header->type, *name, m->path, m->interface, m->member)) {
r = free_and_strdup(&m->destination_ptr, *name);
if (r < 0)
break;
r = bus_kernel_parse_unique_name(m->sender, &m->verify_destination_id);
if (r < 0)
break;
granted = true;
break;
}
}
if (r < 0)
return r;
if (!granted)
return -EPERM;
granted = false;
HASHMAP_FOREACH(name, names_hash, i) {
if (policy_check_recv(policy, ucred, m->header->type, *name, m->path, m->interface, m->member))
return 0;
}
return -EPERM;
}
return 0;
}
static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *policy, const struct ucred *ucred) {
static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *policy, const struct ucred *ucred, Set *owned_names) {
int r;
assert(a);
@ -883,7 +793,7 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *polic
return synthetic_reply_method_errno(m, r, NULL);
}
hashmap_remove(names_hash, name);
set_remove(owned_names, (char*) name);
return synthetic_reply_method_return(m, "u", BUS_NAME_RELEASED);
@ -909,7 +819,7 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *polic
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (policy && !policy_check_own(policy, ucred, name))
if (policy && !policy_check_own(policy, ucred->uid, ucred->gid, name))
return synthetic_reply_method_errno(m, -EPERM, NULL);
if ((flags & ~(BUS_NAME_ALLOW_REPLACEMENT|BUS_NAME_REPLACE_EXISTING|BUS_NAME_DO_NOT_QUEUE)) != 0)
@ -923,21 +833,24 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *polic
if (!(flags & BUS_NAME_DO_NOT_QUEUE))
param |= SD_BUS_NAME_QUEUE;
r = set_put_strdup(owned_names, name);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_request_name(a, name, param);
if (r < 0) {
if (r == -EEXIST)
return synthetic_reply_method_return(m, "u", BUS_NAME_EXISTS);
if (r == -EALREADY)
return synthetic_reply_method_return(m, "u", BUS_NAME_ALREADY_OWNER);
set_remove(owned_names, (char*) name);
if (r == -EEXIST)
return synthetic_reply_method_return(m, "u", BUS_NAME_EXISTS);
return synthetic_reply_method_errno(m, r, NULL);
}
in_queue = (r == 0);
r = hashmap_put(names_hash, name, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (in_queue)
return synthetic_reply_method_return(m, "u", BUS_NAME_IN_QUEUE);
@ -1049,7 +962,166 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *polic
}
}
static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *policy, const struct ucred *ucred, bool *got_hello) {
static int process_policy(sd_bus *from, sd_bus *to, sd_bus_message *m, Policy *policy, const struct ucred *our_ucred, Set *owned_names) {
int r;
assert(from);
assert(to);
assert(m);
if (!policy)
return 0;
if (from->is_kernel) {
uid_t sender_uid = (uid_t) -1;
gid_t sender_gid = (gid_t) -1;
char **sender_names = NULL;
bool granted = false;
/* Driver messages are always OK */
if (streq_ptr(m->sender, "org.freedesktop.DBus"))
return 0;
/* The message came from the kernel, and is sent to our legacy client. */
r = sd_bus_creds_get_well_known_names(&m->creds, &sender_names);
if (r < 0)
return r;
(void) sd_bus_creds_get_uid(&m->creds, &sender_uid);
(void) sd_bus_creds_get_gid(&m->creds, &sender_gid);
/* First check whether the sender can send the message to our name */
if (set_isempty(owned_names)) {
if (policy_check_send(policy, sender_uid, sender_gid, m->header->type, NULL, m->path, m->interface, m->member))
granted = true;
} else {
Iterator i;
char *n;
SET_FOREACH(n, owned_names, i)
if (policy_check_send(policy, sender_uid, sender_gid, m->header->type, n, m->path, m->interface, m->member)) {
granted = true;
break;
}
}
if (granted) {
/* Then check whether us, the recipient can recieve from the sender's name */
if (strv_isempty(sender_names)) {
if (policy_check_recv(policy, our_ucred->uid, our_ucred->gid, m->header->type, NULL, m->path, m->interface, m->member))
return 0;
} else {
char **n;
STRV_FOREACH(n, sender_names) {
if (policy_check_recv(policy, our_ucred->uid, our_ucred->gid, m->header->type, *n, m->path, m->interface, m->member))
return 0;
}
}
}
/* Return an error back to the caller */
if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Access prohibited by XML receiver policy.");
/* Return 1, indicating that the message shall not be processed any further */
return 1;
}
if (to->is_kernel) {
_cleanup_bus_creds_unref_ sd_bus_creds *destination_creds = NULL;
uid_t destination_uid = (uid_t) -1;
gid_t destination_gid = (gid_t) -1;
const char *destination_unique = NULL;
char **destination_names = NULL;
bool granted = false;
/* Driver messages are always OK */
if (streq_ptr(m->destination, "org.freedesktop.DBus"))
return 0;
/* The message came from the legacy client, and is sent to kdbus. */
if (m->destination) {
r = sd_bus_get_name_creds(to, m->destination,
SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME|
SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID, &destination_creds);
if (r < 0)
return r;
r = sd_bus_creds_get_well_known_names(destination_creds, &destination_names);
if (r < 0)
return r;
r = sd_bus_creds_get_unique_name(destination_creds, &destination_unique);
if (r < 0)
return r;
(void) sd_bus_creds_get_uid(destination_creds, &destination_uid);
(void) sd_bus_creds_get_gid(destination_creds, &destination_gid);
}
/* First check if we, the sender can send to this name */
if (strv_isempty(destination_names)) {
if (policy_check_send(policy, our_ucred->uid, our_ucred->gid, m->header->type, NULL, m->path, m->interface, m->member))
granted = true;
} else {
char **n;
STRV_FOREACH(n, destination_names) {
if (policy_check_send(policy, our_ucred->uid, our_ucred->gid, m->header->type, *n, m->path, m->interface, m->member)) {
/* If we made a receiver decision,
then remember which name's policy
we used, and to which unique ID it
mapped when we made the
decision. Then, let's pass this to
the kernel when sending the
message, so that it refuses the
operation should the name and
unique ID not map to each other
anymore. */
r = free_and_strdup(&m->destination_ptr, *n);
if (r < 0)
return r;
r = bus_kernel_parse_unique_name(destination_unique, &m->verify_destination_id);
if (r < 0)
break;
granted = true;
break;
}
}
}
/* Then check if the recipient can receive from our name */
if (granted) {
if (set_isempty(owned_names)) {
if (policy_check_recv(policy, destination_uid, destination_gid, m->header->type, NULL, m->path, m->interface, m->member))
return 0;
} else {
Iterator i;
char *n;
SET_FOREACH(n, owned_names, i)
if (policy_check_recv(policy, destination_uid, destination_gid, m->header->type, n, m->path, m->interface, m->member))
return 0;
}
}
/* Return an error back to the caller */
if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Access prohibited by XML sender policy.");
/* Return 1, indicating that the message shall not be processed any further */
return 1;
}
return 0;
}
static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hello) {
_cleanup_bus_message_unref_ sd_bus_message *n = NULL;
bool is_hello;
int r;
@ -1081,11 +1153,6 @@ static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *policy
return -EIO;
}
if (policy && !policy_check_hello(policy, ucred)) {
log_error("Policy denied HELLO");
return -EPERM;
}
*got_hello = true;
if (!a->is_kernel)
@ -1192,7 +1259,6 @@ static int patch_sender(sd_bus *a, sd_bus_message *m) {
int main(int argc, char *argv[]) {
_cleanup_bus_creds_unref_ sd_bus_creds *bus_creds = NULL;
_cleanup_bus_close_unref_ sd_bus *a = NULL, *b = NULL;
sd_id128_t server_id;
int r, in_fd, out_fd;
@ -1200,7 +1266,8 @@ int main(int argc, char *argv[]) {
bool is_unix;
struct ucred ucred = {};
_cleanup_free_ char *peersec = NULL;
Policy policy = {};
Policy policy_buffer = {}, *policy = NULL;
_cleanup_set_free_free_ Set *owned_names = NULL;
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
log_parse_environment();
@ -1210,14 +1277,6 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
r = policy_load(&policy, arg_configuration);
if (r < 0) {
log_error("Failed to load policy: %s", strerror(-r));
goto finish;
}
/* policy_dump(&policy); */
r = sd_listen_fds(0);
if (r == 0) {
in_fd = STDIN_FILENO;
@ -1255,8 +1314,8 @@ int main(int argc, char *argv[]) {
goto finish;
}
names_hash = hashmap_new(&string_hash_ops);
if (!names_hash) {
owned_names = set_new(&string_hash_ops);
if (!owned_names) {
log_oom();
goto finish;
}
@ -1285,6 +1344,12 @@ int main(int argc, char *argv[]) {
goto finish;
}
r = sd_bus_negotiate_creds(a, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_GID|SD_BUS_CREDS_SELINUX_CONTEXT);
if (r < 0) {
log_error("Failed to set credential negotiation: %s", strerror(-r));
goto finish;
}
if (ucred.pid > 0) {
a->fake_pids.pid = ucred.pid;
a->fake_pids_valid = true;
@ -1319,10 +1384,41 @@ int main(int argc, char *argv[]) {
goto finish;
}
r = sd_bus_get_owner_creds(a, SD_BUS_CREDS_UID, &bus_creds);
if (r < 0) {
log_error("Failed to get bus creds: %s", strerror(-r));
goto finish;
if (a->is_kernel) {
_cleanup_bus_creds_unref_ sd_bus_creds *bus_creds = NULL;
uid_t bus_uid;
r = sd_bus_get_owner_creds(a, SD_BUS_CREDS_UID, &bus_creds);
if (r < 0) {
log_error("Failed to get bus creds: %s", strerror(-r));
goto finish;
}
r = sd_bus_creds_get_uid(bus_creds, &bus_uid);
if (r < 0) {
log_error("Failed to get bus owner UID: %s", strerror(-r));
goto finish;
}
if (bus_uid == 0) {
/* We only enforce the old XML policy on
* kernel busses owned by root users. */
r = policy_load(&policy_buffer, arg_configuration);
if (r < 0) {
log_error("Failed to load policy: %s", strerror(-r));
goto finish;
}
if (!policy_check_hello(&policy_buffer, ucred.uid, ucred.gid)) {
log_error("Policy denied connection");
r = -EPERM;
goto finish;
}
policy_dump(&policy_buffer);
policy = &policy_buffer;
}
}
r = sd_bus_new(&b);
@ -1349,6 +1445,12 @@ int main(int argc, char *argv[]) {
goto finish;
}
r = sd_bus_negotiate_creds(b, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_GID|SD_BUS_CREDS_SELINUX_CONTEXT);
if (r < 0) {
log_error("Failed to set credential negotiation: %s", strerror(-r));
goto finish;
}
r = sd_bus_set_anonymous(b, true);
if (r < 0) {
log_error("Failed to set anonymous authentication: %s", strerror(-r));
@ -1428,6 +1530,8 @@ int main(int argc, char *argv[]) {
int k;
if (got_hello) {
/* Read messages from bus, to pass them on to our client */
r = sd_bus_process(a, &m);
if (r < 0) {
/* treat 'connection reset by peer' as clean exit condition */
@ -1440,6 +1544,8 @@ int main(int argc, char *argv[]) {
}
if (m) {
bool processed = false;
/* We officially got EOF, let's quit */
if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
r = 0;
@ -1455,16 +1561,31 @@ int main(int argc, char *argv[]) {
patch_sender(a, m);
k = sd_bus_send(b, m, NULL);
if (k < 0) {
if (k == -ECONNRESET)
r = 0;
else {
if (policy) {
k = process_policy(a, b, m, policy, &ucred, owned_names);
if (k < 0) {
r = k;
log_error("Failed to send message: %s", strerror(-r));
log_error("Failed to process policy: %s", strerror(-r));
goto finish;
} else if (k > 0) {
r = 1;
processed = true;
}
}
goto finish;
if (!processed) {
k = sd_bus_send(b, m, NULL);
if (k < 0) {
if (k == -ECONNRESET)
r = 0;
else {
r = k;
log_error("Failed to send message to client: %s", strerror(-r));
}
goto finish;
} else
r = 1;
}
}
@ -1472,6 +1593,7 @@ int main(int argc, char *argv[]) {
continue;
}
/* Read messages from our client, to pass them on to the bus */
r = sd_bus_process(b, &m);
if (r < 0) {
/* treat 'connection reset by peer' as clean exit condition */
@ -1484,15 +1606,7 @@ int main(int argc, char *argv[]) {
}
if (m) {
Policy *p = NULL;
uid_t uid;
assert_se(sd_bus_creds_get_uid(bus_creds, &uid) == 0);
/*
if (uid == 0 || uid != ucred.uid)
p = &policy;
*/
bool processed = false;
/* We officially got EOF, let's quit */
if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
@ -1500,53 +1614,61 @@ int main(int argc, char *argv[]) {
goto finish;
}
k = process_hello(a, b, m, p, &ucred, &got_hello);
k = process_hello(a, b, m, &got_hello);
if (k < 0) {
r = k;
log_error("Failed to process HELLO: %s", strerror(-r));
goto finish;
} else if (k > 0) {
processed = true;
r = 1;
}
if (k > 0)
r = k;
else {
k = process_driver(a, b, m, p, &ucred);
if (!processed) {
k = process_driver(a, b, m, policy, &ucred, owned_names);
if (k < 0) {
r = k;
log_error("Failed to process driver calls: %s", strerror(-r));
goto finish;
} else if (k > 0) {
processed = true;
r = 1;
}
if (k > 0)
r = k;
else {
bool retry;
if (!processed) {
do {
retry = false;
k = process_policy(a, b, m, p, &ucred);
if (k < 0) {
r = k;
log_error("Failed to process policy: %s", strerror(-r));
goto finish;
for (;;) {
if (policy) {
k = process_policy(b, a, m, policy, &ucred, owned_names);
if (k < 0) {
r = k;
log_error("Failed to process policy: %s", strerror(-r));
goto finish;
} else if (k > 0) {
processed = true;
r = 1;
break;
}
}
k = sd_bus_send(a, m, NULL);
if (k < 0) {
if (k == -ECONNRESET)
if (k == -EREMCHG)
/* The name database changed since the policy check, hence let's check again */
continue;
else if (k == -ECONNRESET)
r = 0;
else if (k == -EREMCHG)
retry = true;
else {
r = k;
log_error("Failed to send message: %s", strerror(-r));
log_error("Failed to send message to bus: %s", strerror(-r));
}
if (!retry)
goto finish;
}
} while (retry);
goto finish;
} else
r = 1;
break;
}
}
}
}
@ -1620,9 +1742,8 @@ finish:
"STOPPING=1\n"
"STATUS=Shutting down.");
policy_free(&policy);
policy_free(&policy_buffer);
strv_free(arg_configuration);
hashmap_free(names_hash);
free(arg_address);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;

View file

@ -62,41 +62,29 @@ static int test_policy_load(Policy *p, const char *name)
int main(int argc, char *argv[]) {
Policy p = {};
struct ucred ucred = {};
/* Ownership tests */
assert_se(test_policy_load(&p, "ownerships.conf") == 0);
ucred.uid = 0;
assert_se(policy_check_own(&p, &ucred, "org.test.test1") == true);
ucred.uid = 1;
assert_se(policy_check_own(&p, &ucred, "org.test.test1") == true);
assert_se(policy_check_own(&p, 0, 0, "org.test.test1") == true);
assert_se(policy_check_own(&p, 1, 0, "org.test.test1") == true);
ucred.uid = 0;
assert_se(policy_check_own(&p, &ucred, "org.test.test2") == true);
ucred.uid = 1;
assert_se(policy_check_own(&p, &ucred, "org.test.test2") == false);
assert_se(policy_check_own(&p, 0, 0, "org.test.test2") == true);
assert_se(policy_check_own(&p, 1, 0, "org.test.test2") == false);
ucred.uid = 0;
assert_se(policy_check_own(&p, &ucred, "org.test.test3") == false);
ucred.uid = 1;
assert_se(policy_check_own(&p, &ucred, "org.test.test3") == false);
assert_se(policy_check_own(&p, 0, 0, "org.test.test3") == false);
assert_se(policy_check_own(&p, 1, 0, "org.test.test3") == false);
ucred.uid = 0;
assert_se(policy_check_own(&p, &ucred, "org.test.test4") == false);
ucred.uid = 1;
assert_se(policy_check_own(&p, &ucred, "org.test.test4") == true);
assert_se(policy_check_own(&p, 0, 0, "org.test.test4") == false);
assert_se(policy_check_own(&p, 1, 0, "org.test.test4") == true);
policy_free(&p);
/* Signaltest */
assert_se(test_policy_load(&p, "signals.conf") == 0);
ucred.uid = 0;
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == true);
ucred.uid = 1;
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == false);
assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == true);
assert_se(policy_check_send(&p, 1, 0, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == false);
policy_free(&p);
@ -104,14 +92,12 @@ int main(int argc, char *argv[]) {
assert_se(test_policy_load(&p, "methods.conf") == 0);
policy_dump(&p);
ucred.uid = 0;
assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false);
assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false);
assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int1", "Member") == true);
assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == true);
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false);
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false);
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int1", "Member") == true);
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == true);
assert_se(policy_check_recv(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test3", "/an/object/path", "org.test.int3", "Member111") == true);
assert_se(policy_check_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test3", "/an/object/path", "org.test.int3", "Member111") == true);
policy_free(&p);
@ -119,15 +105,9 @@ int main(int argc, char *argv[]) {
assert_se(test_policy_load(&p, "hello.conf") == 0);
policy_dump(&p);
ucred.uid = 0;
assert_se(policy_check_hello(&p, &ucred) == true);
ucred.uid = 1;
assert_se(policy_check_hello(&p, &ucred) == false);
ucred.uid = 0;
ucred.gid = 1;
assert_se(policy_check_hello(&p, &ucred) == false);
assert_se(policy_check_hello(&p, 0, 0) == true);
assert_se(policy_check_hello(&p, 1, 0) == false);
assert_se(policy_check_hello(&p, 0, 1) == false);
policy_free(&p);
@ -136,14 +116,14 @@ int main(int argc, char *argv[]) {
assert_se(test_policy_load(&p, "check-own-rules.conf") >= 0);
policy_dump(&p);
assert_se(policy_check_own(&p, &ucred, "org.freedesktop") == false);
assert_se(policy_check_own(&p, &ucred, "org.freedesktop.ManySystem") == false);
assert_se(policy_check_own(&p, &ucred, "org.freedesktop.ManySystems") == true);
assert_se(policy_check_own(&p, &ucred, "org.freedesktop.ManySystems.foo") == true);
assert_se(policy_check_own(&p, &ucred, "org.freedesktop.ManySystems.foo.bar") == true);
assert_se(policy_check_own(&p, &ucred, "org.freedesktop.ManySystems2") == false);
assert_se(policy_check_own(&p, &ucred, "org.freedesktop.ManySystems2.foo") == false);
assert_se(policy_check_own(&p, &ucred, "org.freedesktop.ManySystems2.foo.bar") == false);
assert_se(policy_check_own(&p, 0, 0, "org.freedesktop") == false);
assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystem") == false);
assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems") == true);
assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems.foo") == true);
assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems.foo.bar") == true);
assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems2") == false);
assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems2.foo") == false);
assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems2.foo.bar") == false);
policy_free(&p);
@ -158,23 +138,21 @@ int main(int argc, char *argv[]) {
assert_se(test_policy_load(&p, "test.conf") >= 0);
policy_dump(&p);
ucred.uid = 0;
assert_se(policy_check_own(&p, &ucred, "org.foo.FooService") == true);
assert_se(policy_check_own(&p, &ucred, "org.foo.FooService2") == false);
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == false);
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true);
assert_se(policy_check_recv(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true);
assert_se(policy_check_recv(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface2", "Member") == false);
assert_se(policy_check_recv(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService2", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false);
assert_se(policy_check_own(&p, 0, 0, "org.foo.FooService") == true);
assert_se(policy_check_own(&p, 0, 0, "org.foo.FooService2") == false);
assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == false);
assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true);
assert_se(policy_check_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true);
assert_se(policy_check_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface2", "Member") == false);
assert_se(policy_check_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService2", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false);
ucred.uid = 100;
assert_se(policy_check_own(&p, &ucred, "org.foo.FooService") == false);
assert_se(policy_check_own(&p, &ucred, "org.foo.FooService2") == false);
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == false);
assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false);
assert_se(policy_check_recv(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true);
assert_se(policy_check_recv(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface2", "Member") == false);
assert_se(policy_check_recv(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService2", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false);
assert_se(policy_check_own(&p, 100, 0, "org.foo.FooService") == false);
assert_se(policy_check_own(&p, 100, 0, "org.foo.FooService2") == false);
assert_se(policy_check_send(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == false);
assert_se(policy_check_send(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false);
assert_se(policy_check_recv(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true);
assert_se(policy_check_recv(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface2", "Member") == false);
assert_se(policy_check_recv(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService2", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false);
policy_free(&p);