proxy.c revision de865432f887e68ac7add166cf618c88431d6538
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering This file is part of systemd.
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering Copyright 2010 Lennart Poettering
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering Copyright 2013 Daniel Mack
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering Copyright 2014 Kay Sievers
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering Copyright 2014 David Herrmann
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering systemd is free software; you can redistribute it and/or modify it
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering under the terms of the GNU Lesser General Public License as published by
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering (at your option) any later version.
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering systemd is distributed in the hope that it will be useful, but
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering Lesser General Public License for more details.
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering You should have received a copy of the GNU Lesser General Public License
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poetteringstatic int proxy_create_destination(Proxy *p, const char *destination, const char *local_sec, bool negotiate_fds) {
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering _cleanup_bus_flush_close_unref_ sd_bus *b = NULL;
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to allocate bus: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_set_description(b, "sd-proxy");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to set bus name: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to set address to connect to: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_negotiate_fds(b, negotiate_fds);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to set FD negotiation: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_negotiate_creds(b, true, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SELINUX_CONTEXT);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to set credential negotiation: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to start bus client: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poetteringstatic int proxy_create_local(Proxy *p, int in_fd, int out_fd, bool negotiate_fds) {
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering _cleanup_bus_flush_close_unref_ sd_bus *b = NULL;
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to allocate bus: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to set fds: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_get_bus_id(p->destination_bus, &server_id);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to get server ID: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to set server mode: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_negotiate_fds(b, negotiate_fds);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to set FD negotiation: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_negotiate_creds(b, true, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SELINUX_CONTEXT);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to set credential negotiation: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to set anonymous authentication: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to start bus client: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poetteringstatic int proxy_prepare_matches(Proxy *p) {
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_get_unique_name(p->destination_bus, &unique);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to get unique name: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering "sender='org.freedesktop.DBus',"
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering "interface='org.freedesktop.DBus',"
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering "member='NameOwnerChanged',"
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_add_match(p->destination_bus, NULL, match, NULL, NULL);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to add match for NameLost: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering "sender='org.freedesktop.DBus',"
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering "interface='org.freedesktop.DBus',"
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering "member='NameOwnerChanged',"
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_add_match(p->destination_bus, NULL, match, NULL, NULL);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to add match for NameAcquired: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering "destination='",
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_add_match(p->destination_bus, NULL, match, NULL, NULL);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering log_error_errno(r, "Failed to add match for NameAcquired: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poetteringint proxy_new(Proxy **out, int in_fd, int out_fd, const char *destination) {
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering p->owned_names = set_new(&string_hash_ops);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering is_unix = sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering (void) getpeercred(in_fd, &p->local_creds);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = proxy_create_destination(p, destination, local_sec, is_unix);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = proxy_create_local(p, in_fd, out_fd, is_unix);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering sd_bus_flush_close_unref(p->destination_bus);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poetteringint proxy_set_policy(Proxy *p, SharedPolicy *sp, char **configuration) {
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering /* no need to load legacy policy if destination is not kdbus */
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering /* policy already pre-loaded */
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_get_scope(p->destination_bus, &scope);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Couldn't determine bus scope: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering strv = strv_new("/usr/share/dbus-1/system.conf",
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering strv = strv_new("/usr/share/dbus-1/session.conf",
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error("Unknown scope %s, don't know which policy to load. Refusing.", scope);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return shared_policy_preload(sp, configuration);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poetteringint proxy_hello_policy(Proxy *p, uid_t original_uid) {
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering policy = shared_policy_acquire(p->policy);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering log_debug("Permitting access, since bus owner matches bus client.");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering else if (policy_check_hello(policy, p->local_creds.uid, p->local_creds.gid))
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering log_debug("Permitting access due to XML policy.");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = log_error_errno(EPERM, "Policy denied connection.");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering shared_policy_release(p->policy, policy);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering uint64_t timeout_destination, timeout_local, t;
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering int events_destination, events_local, fd;
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(fd, "Failed to get fd: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering events_destination = sd_bus_get_events(p->destination_bus);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(events_destination, "Failed to get events mask: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_get_timeout(p->destination_bus, &timeout_destination);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to get timeout: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering events_local = sd_bus_get_events(p->local_bus);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(events_local, "Failed to get events mask: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = sd_bus_get_timeout(p->local_bus, &timeout_local);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(r, "Failed to get timeout: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering if (t == (uint64_t) -1 || (timeout_local != (uint64_t) -1 && timeout_local < timeout_destination))
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering { .fd = fd, .events = events_destination, },
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering { .fd = p->local_in, .events = events_local & POLLIN, },
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering { .fd = p->local_out, .events = events_local & POLLOUT, },
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return log_error_errno(errno, "ppoll() failed: %m");
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poetteringstatic int handle_policy_error(sd_bus_message *m, int r) {
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering return synthetic_reply_method_errorf(m, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Name %s is currently not owned by anyone.", m->destination);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poetteringstatic int process_policy_unlocked(sd_bus *from, sd_bus *to, sd_bus_message *m, Policy *policy, const struct ucred *our_ucred, Set *owned_names) {
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * dbus-1 distinguishes expected and non-expected replies by tracking
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * method-calls and timeouts. By default, DENY rules are *NEVER* applied
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * on expected replies, unless explicitly specified. But we dont track
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * method-calls, thus, we cannot know whether a reply is expected.
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * Fortunately, the kdbus forbids non-expected replies, so we can safely
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * ignore any policy on those and let the kernel deal with it.
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * TODO: To be correct, we should only ignore policy-tags that are
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * applied on non-expected replies. However, so far we don't parse those
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * tags so we let everything pass. I haven't seen a DENY policy tag on
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * expected-replies, ever, so don't bother..
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering /* Driver messages are always OK */
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering if (streq_ptr(m->sender, "org.freedesktop.DBus"))
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering /* The message came from the kernel, and is sent to our legacy client. */
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering (void) sd_bus_creds_get_well_known_names(&m->creds, &sender_names);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering (void) sd_bus_creds_get_euid(&m->creds, &sender_uid);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering (void) sd_bus_creds_get_egid(&m->creds, &sender_gid);
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering if (sender_uid == UID_INVALID || sender_gid == GID_INVALID) {
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering _cleanup_bus_creds_unref_ sd_bus_creds *sender_creds = NULL;
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering /* If the message came from another legacy
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * client, then the message creds will be
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * missing, simply because on legacy clients
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * per-message creds were unknown. In this
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering * case, query the creds of the peer
a45b9fca6b91a767dcd9060cfcb30617dad234c7Lennart Poettering r = bus_get_name_creds_kdbus(from, m->sender, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID, true, &sender_creds);
if (policy_check_send(policy, sender_uid, sender_gid, m->header->type, owned_names, NULL, m->path, m->interface, m->member, false, NULL) &&
policy_check_recv(policy, our_ucred->uid, our_ucred->gid, m->header->type, NULL, sender_names, m->path, m->interface, m->member, false))
return synthetic_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Access prohibited by XML receiver policy.");
if (m->destination) {
true, &destination_creds);
return handle_policy_error(m, r);
return handle_policy_error(m, r);
} else if (policy_check_send(policy, our_ucred->uid, our_ucred->gid, m->header->type, NULL, destination_names, m->path, m->interface, m->member, true, &n)) {
if (policy_check_recv(policy, destination_uid, destination_gid, m->header->type, owned_names, NULL, m->path, m->interface, m->member, true))
return synthetic_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Access prohibited by XML sender policy.");
static int process_policy(sd_bus *from, sd_bus *to, sd_bus_message *m, SharedPolicy *sp, const struct ucred *our_ucred, Set *owned_names) {
bool is_hello;
assert(p);
assert(m);
is_hello =
if (!is_hello) {
if (p->got_hello)
return log_error_errno(EIO, "First packet isn't hello (it's %s.%s), aborting.", m->interface, m->member);
if (p->got_hello)
p->got_hello = true;
r = sd_bus_message_new_method_return(m, &n);
n = sd_bus_message_unref(n);
p->local_bus,
"/org/freedesktop/DBus",
sd_bus_creds *c;
assert(a);
assert(m);
if (!a->is_kernel)
c = sd_bus_message_get_creds(m);
assert(p);
if (r == -ECONNRESET || r == -ENOTCONN) /* Treat 'connection reset by peer' as clean exit condition */
return -ECONNRESET;
if (p->policy) {
r = process_policy(p->destination_bus, p->local_bus, m, p->policy, &p->local_creds, p->owned_names);
if (r == -ENOBUFS) {
if (!p->queue_overflow)
log_error("Dropped messages due to queue overflow of local peer (pid: "PID_FMT" uid: "UID_FMT")", p->local_creds.pid, p->local_creds.uid);
p->queue_overflow = true;
"Failed to forward message we got from destination: uid=" UID_FMT " gid=" GID_FMT" message=%s destination=%s path=%s interface=%s member=%s: %m",
p->queue_overflow = false;
assert(p);
if (r == -ECONNRESET || r == -ENOTCONN) /* Treat 'connection reset by peer' as clean exit condition */
return -ECONNRESET;
r = process_hello(p, m);
r = bus_proxy_process_driver(p->destination_bus, p->local_bus, m, p->policy, &p->local_creds, p->owned_names);
if (p->policy) {
r = process_policy(p->local_bus, p->destination_bus, m, p->policy, &p->local_creds, p->owned_names);
if (r == -EREMCHG)
"Failed to forward message we got from local: uid=" UID_FMT " gid=" GID_FMT" message=%s destination=%s path=%s interface=%s member=%s: %m",
assert(p);
bool busy = false;
if (p->got_hello) {
busy = true;
busy = true;
if (!busy) {
r = proxy_wait(p);