In order to check for matching policy entries at message transfers, we
have to consider the following:
* check the currently owned names of both the sending and the receiving
peer. If the sending peer is connected via kdbus, the currently owned
names are already attached to the message. If it was originated by the
connection we're proxying for, we store the owned names in our own strv
so we can check against them.
* Walk the list of names to check which name would allow the message to
pass, and explicitly use that name as destination of the message. If the
destination is on kdbus, store both the connection's unique name and the
chosen well-known-name in the message. That way, the kernel will make sure
the supplied name is owned by the supplied unique name, at the time of
sending, and return -EREMCHG otherwise.
* Make the policy checks optional by retrieving the bus owner creds, and
when the uid matches the current user's uid and is non-null, don't check
the bus policy.
static int process_policy(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *policy, const struct ucred *ucred) {
int r;
static int process_policy(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *policy, const struct ucred *ucred) {
int r;
+ bool granted = false;
+ Iterator i;
assert(a);
assert(b);
assert(m);
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. */
if (b->is_kernel) {
/* The message came from the kernel, and is sent to our legacy client. */
-/*
- if (!policy_check_recv(policy, ucred, names_hash, m->header->type, m->path, m->interface, m->member))
- return -EPERM;
+ 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;
- if (!policy_check_send(policy, ucred, names_strv, m->header->type, m->path, m->interface, m->member))
+ granted = true;
+ break;
+ }
+ }
+
+ if (r < 0)
+ return r;
+
+ if (!granted)
+
+ 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;
+ 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 (!granted)
+ return -EPERM;
+ 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;
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
- if (!policy_check_own(policy, ucred, name))
+ if (policy && !policy_check_own(policy, ucred, name))
return synthetic_reply_method_errno(m, -EPERM, NULL);
if (!service_name_is_valid(name))
return synthetic_reply_method_errno(m, -EPERM, NULL);
if (!service_name_is_valid(name))
- if (!policy_check_hello(policy, ucred)) {
+ if (policy && !policy_check_hello(policy, ucred)) {
log_error("Policy denied HELLO");
return -EPERM;
}
log_error("Policy denied HELLO");
return -EPERM;
}
int main(int argc, char *argv[]) {
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;
_cleanup_bus_close_unref_ sd_bus *a = NULL, *b = NULL;
sd_id128_t server_id;
int r, in_fd, out_fd;
+ 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_new(&b);
if (r < 0) {
log_error("Failed to allocate bus: %s", strerror(-r));
r = sd_bus_new(&b);
if (r < 0) {
log_error("Failed to allocate bus: %s", strerror(-r));
+ 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;
+
/* We officially got EOF, let's quit */
if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
r = 0;
goto finish;
}
/* We officially got EOF, let's quit */
if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
r = 0;
goto finish;
}
- k = process_hello(a, b, m, &policy, &ucred, &got_hello);
+ k = process_hello(a, b, m, p, &ucred, &got_hello);
if (k < 0) {
r = k;
log_error("Failed to process HELLO: %s", strerror(-r));
if (k < 0) {
r = k;
log_error("Failed to process HELLO: %s", strerror(-r));
- k = process_driver(a, b, m, &policy, &ucred);
+ k = process_driver(a, b, m, p, &ucred);
if (k < 0) {
r = k;
log_error("Failed to process driver calls: %s", strerror(-r));
if (k < 0) {
r = k;
log_error("Failed to process driver calls: %s", strerror(-r));
- k = process_policy(a, b, m, &policy, &ucred);
- if (k < 0) {
- r = k;
- log_error("Failed to process policy: %s", strerror(-r));
- goto finish;
- }
+ bool retry;
+
+ do {
+ retry = false;
- k = sd_bus_send(a, m, NULL);
- if (k < 0) {
- if (k == -ECONNRESET)
- r = 0;
- else {
+ k = process_policy(a, b, m, p, &ucred);
+ if (k < 0) {
- log_error("Failed to send message: %s", strerror(-r));
+ log_error("Failed to process policy: %s", strerror(-r));
+ goto finish;
+ k = sd_bus_send(a, m, NULL);
+ if (k < 0) {
+ if (k == -ECONNRESET)
+ r = 0;
+ else if (k == -EREMCHG)
+ retry = true;
+ else {
+ r = k;
+ log_error("Failed to send message: %s", strerror(-r));
+ }
+
+ if (!retry)
+ goto finish;
+ }
+ } while (retry);