chiark / gitweb /
bus: implicitly collect ucred/label information
[elogind.git] / src / libsystemd-bus / sd-bus.c
index 6a6d43fac0a03dd01eac4f38faf6527345601804..73774ba308c67430160892ab17bf4f7931426a87 100644 (file)
 
 #include "util.h"
 #include "macro.h"
+#include "missing.h"
 
 #include "sd-bus.h"
 #include "bus-internal.h"
 #include "bus-message.h"
 #include "bus-type.h"
 
-static int ensure_running(sd_bus *bus);
 static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
 
 static void bus_free(sd_bus *b) {
@@ -104,16 +104,19 @@ static int hello_callback(sd_bus *bus, int error, sd_bus_message *reply, void *u
 
         assert(reply);
 
-        bus->state = BUS_RUNNING;
-
         r = sd_bus_message_read(reply, "s", &s);
         if (r < 0)
                 return r;
 
+        if (!service_name_is_valid(s) || s[0] != ':')
+                return -EBADMSG;
+
         bus->unique_name = strdup(s);
         if (!bus->unique_name)
                 return -ENOMEM;
 
+        bus->state = BUS_RUNNING;
+
         return 1;
 }
 
@@ -528,6 +531,24 @@ static int bus_read_auth(sd_bus *b) {
         return 1;
 }
 
+static int bus_setup_fd(sd_bus *b) {
+        int one;
+
+        assert(b);
+
+        /* Enable SO_PASSCRED + SO_PASSEC. We try this on any socket,
+         * just in case. This is actually irrelavant for */
+        one = 1;
+        setsockopt(b->fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+        setsockopt(b->fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
+
+        /* Increase the buffers to a MB */
+        fd_inc_rcvbuf(b->fd, 1024*1024);
+        fd_inc_sndbuf(b->fd, 1024*1024);
+
+        return 0;
+}
+
 static int bus_start_auth(sd_bus *b) {
         static const char auth_prefix[] = "\0AUTH EXTERNAL ";
         static const char auth_suffix[] = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
@@ -576,8 +597,13 @@ static int bus_start_connect(sd_bus *b) {
                 b->fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
                 if (b->fd < 0) {
                         b->last_connect_error = errno;
-                        zero(b->sockaddr);
-                        continue;
+                        goto try_again;
+                }
+
+                r = bus_setup_fd(b);
+                if (r < 0) {
+                        b->last_connect_error = errno;
+                        goto try_again;
                 }
 
                 r = connect(b->fd, &b->sockaddr.sa, b->sockaddr_size);
@@ -586,13 +612,18 @@ static int bus_start_connect(sd_bus *b) {
                                 return 1;
 
                         b->last_connect_error = errno;
-                        close_nointr_nofail(b->fd);
-                        b->fd = -1;
-                        zero(b->sockaddr);
-                        continue;
+                        goto try_again;
                 }
 
                 return bus_start_auth(b);
+
+        try_again:
+                zero(b->sockaddr);
+
+                if (b->fd >= 0) {
+                        close_nointr_nofail(b->fd);
+                        b->fd = -1;
+                }
         }
 }
 
@@ -726,17 +757,29 @@ int sd_bus_open_fd(int fd, sd_bus **ret) {
                 return -ENOMEM;
 
         b->fd = fd;
-        fd_nonblock(b->fd, true);
+
+        r = fd_nonblock(b->fd, true);
+        if (r < 0)
+                goto fail;
+
         fd_cloexec(b->fd, true);
+        if (r < 0)
+                goto fail;
+
+        r = bus_setup_fd(b);
+        if (r < 0)
+                goto fail;
 
         r = bus_start_auth(b);
-        if (r < 0) {
-                bus_free(b);
-                return r;
-        }
+        if (r < 0)
+                goto fail;
 
         *ret = b;
         return 0;
+
+fail:
+                bus_free(b);
+        return r;
 }
 
 void sd_bus_close(sd_bus *bus) {
@@ -786,7 +829,7 @@ int sd_bus_can_send(sd_bus *bus, char type) {
                 return -EINVAL;
 
         if (type == SD_BUS_TYPE_UNIX_FD) {
-                r = ensure_running(bus);
+                r = bus_ensure_running(bus);
                 if (r < 0)
                         return r;
 
@@ -804,7 +847,7 @@ int sd_bus_get_peer(sd_bus *bus, sd_id128_t *peer) {
         if (!peer)
                 return -EINVAL;
 
-        r = ensure_running(bus);
+        r = bus_ensure_running(bus);
         if (r < 0)
                 return r;
 
@@ -928,7 +971,9 @@ static int message_make(sd_bus *bus, size_t size, sd_bus_message **m) {
                 }
         }
 
-        r = bus_message_from_malloc(bus->rbuffer, size, &t);
+        r = bus_message_from_malloc(bus->rbuffer, size,
+                                    bus->ucred_valid ? &bus->ucred : NULL,
+                                    bus->label[0] ? bus->label : NULL, &t);
         if (r < 0) {
                 free(b);
                 return r;
@@ -948,6 +993,12 @@ static int message_read(sd_bus *bus, sd_bus_message **m) {
         size_t need;
         int r;
         void *b;
+        union {
+                struct cmsghdr cmsghdr;
+                uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+                            CMSG_SPACE(NAME_MAX)]; /*selinux label */
+        } control;
+        struct cmsghdr *cmsg;
 
         assert(bus);
         assert(m);
@@ -973,12 +1024,34 @@ static int message_read(sd_bus *bus, sd_bus_message **m) {
         zero(mh);
         mh.msg_iov = &iov;
         mh.msg_iovlen = 1;
+        mh.msg_control = &control;
+        mh.msg_controllen = sizeof(control);
 
-        k = recvmsg(bus->fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
+        k = recvmsg(bus->fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
         if (k < 0)
                 return errno == EAGAIN ? 0 : -errno;
 
         bus->rbuffer_size += k;
+        bus->ucred_valid = false;
+        bus->label[0] = 0;
+
+        for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
+                if (cmsg->cmsg_level == SOL_SOCKET &&
+                    cmsg->cmsg_type == SCM_CREDENTIALS &&
+                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+
+                        memcpy(&bus->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
+                        bus->ucred_valid = true;
+
+                } else if (cmsg->cmsg_level == SOL_SOCKET &&
+                         cmsg->cmsg_type == SCM_SECURITY) {
+
+                        size_t l;
+                        l = cmsg->cmsg_len - CMSG_LEN(0);
+                        memcpy(&bus->label, CMSG_DATA(cmsg), l);
+                        bus->label[l] = 0;
+                }
+        }
 
         r = message_read_need(bus, &need);
         if (r < 0)
@@ -1032,7 +1105,7 @@ static int dispatch_wqueue(sd_bus *bus) {
 }
 
 static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
-        sd_bus_message *z;
+        sd_bus_message *z = NULL;
         int r, ret = 0;
 
         assert(bus);
@@ -1249,7 +1322,7 @@ int sd_bus_send_with_reply_cancel(sd_bus *bus, uint64_t serial) {
         return 1;
 }
 
-static int ensure_running(sd_bus *bus) {
+int bus_ensure_running(sd_bus *bus) {
         int r;
 
         assert(bus);
@@ -1297,7 +1370,7 @@ int sd_bus_send_with_reply_and_block(
         if (bus_error_is_dirty(error))
                 return -EINVAL;
 
-        r = ensure_running(bus);
+        r = bus_ensure_running(bus);
         if (r < 0)
                 return r;
 
@@ -1744,7 +1817,7 @@ int sd_bus_flush(sd_bus *bus) {
         if (bus->fd < 0)
                 return -ENOTCONN;
 
-        r = ensure_running(bus);
+        r = bus_ensure_running(bus);
         if (r < 0)
                 return r;