From 8a0e0ed9ddfa208e414355881acb886270039268 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Dec 2013 15:03:32 +0100 Subject: [PATCH] bus: fake client side creds in the proxy to the caller's creds --- src/bus-proxyd/bus-proxyd.c | 62 +++++++++++++++++++++++++++++++ src/libsystemd-bus/bus-internal.h | 5 +++ src/libsystemd-bus/bus-kernel.c | 55 ++++++++++++++++++++------- src/libsystemd-bus/sd-bus.c | 1 + 4 files changed, 109 insertions(+), 14 deletions(-) diff --git a/src/bus-proxyd/bus-proxyd.c b/src/bus-proxyd/bus-proxyd.c index a425fd2fa..b87dffe0e 100644 --- a/src/bus-proxyd/bus-proxyd.c +++ b/src/bus-proxyd/bus-proxyd.c @@ -358,6 +358,40 @@ static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hell return 1; } +static int getpeersec(int fd, char **ret) { + socklen_t n = 64; + char *s; + int r; + + assert(fd >= 0); + assert(ret); + + s = new0(char, n); + if (!s) + return -ENOMEM; + + r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); + if (r < 0) { + free(s); + + if (errno != ERANGE) + return r; + + s = new0(char, n); + if (!s) + return -ENOMEM; + + r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); + if (r < 0) { + free(s); + return r; + } + } + + *ret = s; + return 0; +} + int main(int argc, char *argv[]) { _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL; @@ -365,6 +399,8 @@ int main(int argc, char *argv[]) { int r, in_fd, out_fd; bool got_hello = false; bool is_unix; + struct ucred ucred = {}; + _cleanup_free_ char *peersec = NULL; log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_parse_environment(); @@ -390,6 +426,20 @@ int main(int argc, char *argv[]) { sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 && sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0; + if (is_unix) { + socklen_t l = sizeof(ucred); + + r = getsockopt(in_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l); + if (r < 0) { + r = -errno; + goto finish; + } + + assert(l == sizeof(ucred)); + + getpeersec(in_fd, &peersec); + } + r = sd_bus_new(&a); if (r < 0) { log_error("Failed to allocate bus: %s", strerror(-r)); @@ -408,6 +458,18 @@ int main(int argc, char *argv[]) { goto finish; } + if (ucred.pid > 0) { + a->fake_creds.pid = ucred.pid; + a->fake_creds.uid = ucred.uid; + a->fake_creds.gid = ucred.gid; + a->fake_creds_valid = true; + } + + if (peersec) { + a->fake_label = peersec; + peersec = NULL; + } + r = sd_bus_start(a); if (r < 0) { log_error("Failed to start bus client: %s", strerror(-r)); diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h index 7af8c1e22..673f30eb9 100644 --- a/src/libsystemd-bus/bus-internal.h +++ b/src/libsystemd-bus/bus-internal.h @@ -36,6 +36,7 @@ #include "bus-error.h" #include "bus-match.h" #include "bus-kernel.h" +#include "kdbus.h" struct reply_callback { sd_bus_message_handler_t callback; @@ -161,6 +162,7 @@ struct sd_bus { bool filter_callbacks_modified:1; bool nodes_modified:1; bool trusted:1; + bool fake_creds_valid:1; int use_memfd; @@ -259,6 +261,9 @@ struct sd_bus { sd_bus **default_bus_ptr; pid_t tid; + + struct kdbus_creds fake_creds; + char *fake_label; }; #define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) diff --git a/src/libsystemd-bus/bus-kernel.c b/src/libsystemd-bus/bus-kernel.c index e53bc5172..fb5266d44 100644 --- a/src/libsystemd-bus/bus-kernel.c +++ b/src/libsystemd-bus/bus-kernel.c @@ -317,7 +317,9 @@ fail: } int bus_kernel_take_fd(sd_bus *b) { - struct kdbus_cmd_hello hello; + struct kdbus_cmd_hello *hello; + struct kdbus_item *item; + size_t l, sz; int r; assert(b); @@ -327,13 +329,38 @@ int bus_kernel_take_fd(sd_bus *b) { b->use_memfd = 1; - zero(hello); - hello.size = sizeof(hello); - hello.conn_flags = b->hello_flags; - hello.attach_flags = b->attach_flags; - hello.pool_size = KDBUS_POOL_SIZE; + sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items)); + + if (b->fake_creds_valid) + sz += ALIGN8(offsetof(struct kdbus_item, creds)); + + if (b->fake_label) { + l = strlen(b->fake_label); + sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1); + } + + hello = alloca0(sz); + hello->size = sz; + hello->conn_flags = b->hello_flags; + hello->attach_flags = b->attach_flags; + hello->pool_size = KDBUS_POOL_SIZE; + + item = hello->items; - r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello); + if (b->fake_creds_valid) { + item->size = offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds); + item->type = KDBUS_ITEM_CREDS; + item->creds = b->fake_creds; + + item = KDBUS_ITEM_NEXT(item); + } + + if (b->fake_label) { + item->size = offsetof(struct kdbus_item, str) + l + 1; + memcpy(item->str, b->fake_label, l+1); + } + + r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello); if (r < 0) return -errno; @@ -347,26 +374,26 @@ int bus_kernel_take_fd(sd_bus *b) { /* The higher 32bit of both flags fields are considered * 'incompatible flags'. Refuse them all for now. */ - if (hello.bus_flags > 0xFFFFFFFFULL || - hello.conn_flags > 0xFFFFFFFFULL) + if (hello->bus_flags > 0xFFFFFFFFULL || + hello->conn_flags > 0xFFFFFFFFULL) return -ENOTSUP; - if (hello.bloom_size != BLOOM_SIZE) + if (hello->bloom_size != BLOOM_SIZE) return -ENOTSUP; - if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello.id) < 0) + if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0) return -ENOMEM; - b->unique_id = hello.id; + b->unique_id = hello->id; b->is_kernel = true; b->bus_client = true; - b->can_fds = !!(hello.conn_flags & KDBUS_HELLO_ACCEPT_FD); + b->can_fds = !!(hello->conn_flags & KDBUS_HELLO_ACCEPT_FD); b->message_version = 2; b->message_endian = BUS_NATIVE_ENDIAN; /* the kernel told us the UUID of the underlying bus */ - memcpy(b->server_id.bytes, hello.id128, sizeof(b->server_id.bytes)); + memcpy(b->server_id.bytes, hello->id128, sizeof(b->server_id.bytes)); return bus_start_running(b); } diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 9dfb6e444..910a5f813 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -141,6 +141,7 @@ static void bus_free(sd_bus *b) { free(b->address); free(b->kernel); free(b->machine); + free(b->fake_label); free(b->exec_path); strv_free(b->exec_argv); -- 2.30.2