chiark / gitweb /
bus: fake client side creds in the proxy to the caller's creds
authorLennart Poettering <lennart@poettering.net>
Tue, 24 Dec 2013 14:03:32 +0000 (15:03 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 24 Dec 2013 14:27:59 +0000 (15:27 +0100)
src/bus-proxyd/bus-proxyd.c
src/libsystemd-bus/bus-internal.h
src/libsystemd-bus/bus-kernel.c
src/libsystemd-bus/sd-bus.c

index a425fd2facf764f281dcc1549c5cf83304304cb5..b87dffe0e8b77202d4858feda63e8f3b6858b63e 100644 (file)
@@ -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));
index 7af8c1e22d61cd96974ef2dbfbae434f0acefe31..673f30eb912e379ece4a4d597350a59441a3d102 100644 (file)
@@ -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))
index e53bc51727481f9237af566f1bcd7738968b10b6..fb5266d44e5907da435286f37b69ff511b80a28c 100644 (file)
@@ -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);
 }
index 9dfb6e444215012dfe363660424d54d3179f6dbb..910a5f81361a5f3d65cec026f1086fa9610d22e1 100644 (file)
@@ -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);