chiark / gitweb /
util: unify SO_PEERCRED/SO_PEERSEC invocations
[elogind.git] / src / libsystemd-bus / bus-socket.c
index ce6af49b266a3c6fb9ef0d827e6a531d7e697010..0c4b6af447297fbe8f1e348f096df36d879f37d9 100644 (file)
 #include "missing.h"
 #include "strv.h"
 #include "utf8.h"
+#include "sd-daemon.h"
 
 #include "sd-bus.h"
 #include "bus-socket.h"
 #include "bus-internal.h"
 #include "bus-message.h"
 
+#define SNDBUF_SIZE (8*1024*1024)
+
 static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) {
 
         while (size > 0) {
@@ -57,6 +60,65 @@ static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) {
         }
 }
 
+static int append_iovec(sd_bus_message *m, const void *p, size_t sz) {
+        assert(m);
+        assert(p);
+        assert(sz > 0);
+
+        m->iovec[m->n_iovec].iov_base = (void*) p;
+        m->iovec[m->n_iovec].iov_len = sz;
+        m->n_iovec++;
+
+        return 0;
+}
+
+static int bus_message_setup_iovec(sd_bus_message *m) {
+        struct bus_body_part *part;
+        unsigned n, i;
+        int r;
+
+        assert(m);
+        assert(m->sealed);
+
+        if (m->n_iovec > 0)
+                return 0;
+
+        assert(!m->iovec);
+
+        n = 1 + m->n_body_parts;
+        if (n < ELEMENTSOF(m->iovec_fixed))
+                m->iovec = m->iovec_fixed;
+        else {
+                m->iovec = new(struct iovec, n);
+                if (!m->iovec) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        r = append_iovec(m, m->header, BUS_MESSAGE_BODY_BEGIN(m));
+        if (r < 0)
+                goto fail;
+
+        MESSAGE_FOREACH_PART(part, i, m)  {
+                r = bus_body_part_map(part);
+                if (r < 0)
+                        goto fail;
+
+                r = append_iovec(m, part->data, part->size);
+                if (r < 0)
+                        goto fail;
+        }
+
+        assert(n == m->n_iovec);
+
+        return 0;
+
+fail:
+        m->poisoned = true;
+        return r;
+}
+
 bool bus_socket_auth_needs_write(sd_bus *b) {
 
         unsigned i;
@@ -121,7 +183,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
         if (!e)
                 return 0;
 
-        if (b->negotiate_fds) {
+        if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) {
                 f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
                 if (!f)
                         return 0;
@@ -234,7 +296,7 @@ static int verify_external_token(sd_bus *b, const char *p, size_t l) {
          * the owner of this bus wanted authentication he should have
          * checked SO_PEERCRED before even creating the bus object. */
 
-        if (!b->ucred_valid)
+        if (!b->anonymous_auth && !b->ucred_valid)
                 return 0;
 
         if (l <= 0)
@@ -257,7 +319,9 @@ static int verify_external_token(sd_bus *b, const char *p, size_t l) {
         if (r < 0)
                 return 0;
 
-        if (u != b->ucred.uid)
+        /* We ignore the passed value if anonymous authentication is
+         * on anyway. */
+        if (!b->anonymous_auth && u != b->ucred.uid)
                 return 0;
 
         return 1;
@@ -310,13 +374,16 @@ static int bus_socket_auth_verify_server(sd_bus *b) {
 
         assert(b);
 
-        if (b->rbuffer_size < 3)
+        if (b->rbuffer_size < 1)
                 return 0;
 
         /* First char must be a NUL byte */
         if (*(char*) b->rbuffer != 0)
                 return -EIO;
 
+        if (b->rbuffer_size < 3)
+                return 0;
+
         /* Begin with the first line */
         if (b->auth_rbegin <= 0)
                 b->auth_rbegin = 1;
@@ -399,7 +466,7 @@ static int bus_socket_auth_verify_server(sd_bus *b) {
                                         r = bus_socket_auth_write_ok(b);
                         }
                 } else if (line_equals(line, l, "NEGOTIATE_UNIX_FD")) {
-                        if (b->auth == _BUS_AUTH_INVALID || !b->negotiate_fds)
+                        if (b->auth == _BUS_AUTH_INVALID || !(b->hello_flags & KDBUS_HELLO_ACCEPT_FD))
                                 r = bus_socket_auth_write(b, "ERROR\r\n");
                         else {
                                 b->can_fds = true;
@@ -449,7 +516,7 @@ static int bus_socket_read_auth(sd_bus *b) {
         if (r != 0)
                 return r;
 
-        n = MAX(256, b->rbuffer_size * 2);
+        n = MAX(256u, b->rbuffer_size * 2);
 
         if (n > BUS_AUTH_SIZE_MAX)
                 n = BUS_AUTH_SIZE_MAX;
@@ -507,16 +574,23 @@ static int bus_socket_read_auth(sd_bus *b) {
                                    cmsg->cmsg_type == SCM_CREDENTIALS &&
                                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
 
-                                memcpy(&b->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
-                                b->ucred_valid = true;
+                                /* Ignore bogus data, which we might
+                                 * get on socketpair() sockets */
+                                if (((struct ucred*) CMSG_DATA(cmsg))->pid != 0) {
+                                        memcpy(&b->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
+                                        b->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(&b->label, CMSG_DATA(cmsg), l);
-                                b->label[l] = 0;
+                                if (l > 0) {
+                                        memcpy(&b->label, CMSG_DATA(cmsg), l);
+                                        b->label[l] = 0;
+                                }
                         }
                 }
         }
@@ -528,7 +602,7 @@ static int bus_socket_read_auth(sd_bus *b) {
         return 1;
 }
 
-static int bus_socket_setup(sd_bus *b) {
+void bus_socket_setup(sd_bus *b) {
         int enable;
 
         assert(b);
@@ -537,13 +611,24 @@ static int bus_socket_setup(sd_bus *b) {
          * socket, just in case. */
         enable = !b->bus_client;
         setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable));
+
+        enable = !b->bus_client && (b->attach_flags & KDBUS_ATTACH_SECLABEL);
         setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable));
 
-        /* Increase the buffers to a MB */
-        fd_inc_rcvbuf(b->input_fd, 1024*1024);
-        fd_inc_sndbuf(b->output_fd, 1024*1024);
+        /* Increase the buffers to 8 MB */
+        fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE);
+        fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE);
 
-        return 0;
+        b->is_kernel = false;
+        b->message_version = 1;
+        b->message_endian = 0;
+}
+
+static void bus_get_peercred(sd_bus *b) {
+        assert(b);
+
+        /* Get the peer for socketpair() sockets */
+        b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0;
 }
 
 static int bus_socket_start_auth_client(sd_bus *b) {
@@ -573,7 +658,7 @@ static int bus_socket_start_auth_client(sd_bus *b) {
         if (!b->auth_buffer)
                 return -ENOMEM;
 
-        if (b->negotiate_fds)
+        if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD)
                 auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
         else
                 auth_suffix = "\r\nBEGIN\r\n";
@@ -588,26 +673,20 @@ static int bus_socket_start_auth_client(sd_bus *b) {
         return bus_socket_write_auth(b);
 }
 
-static int bus_socket_start_auth(sd_bus *b) {
-        int domain = 0, r;
-        socklen_t sl;
-
+int bus_socket_start_auth(sd_bus *b) {
         assert(b);
 
+        bus_get_peercred(b);
+
         b->state = BUS_AUTHENTICATING;
         b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_DEFAULT_TIMEOUT;
 
-        sl = sizeof(domain);
-        r = getsockopt(b->input_fd, SOL_SOCKET, SO_DOMAIN, &domain, &sl);
-        if (r < 0 || domain != AF_UNIX)
-                b->negotiate_fds = false;
-
-        if (b->output_fd != b->input_fd) {
-                r = getsockopt(b->output_fd, SOL_SOCKET, SO_DOMAIN, &domain, &sl);
-                if (r < 0 || domain != AF_UNIX)
-                        b->negotiate_fds = false;
-        }
+        if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0)
+                b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
 
+        if (b->output_fd != b->input_fd)
+                if (sd_is_socket(b->output_fd, AF_UNIX, 0, 0) <= 0)
+                        b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
 
         if (b->is_server)
                 return bus_socket_read_auth(b);
@@ -629,9 +708,7 @@ int bus_socket_connect(sd_bus *b) {
 
         b->output_fd = b->input_fd;
 
-        r = bus_socket_setup(b);
-        if (r < 0)
-                return r;
+        bus_socket_setup(b);
 
         r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
         if (r < 0) {
@@ -693,16 +770,15 @@ int bus_socket_exec(sd_bus *b) {
         close_nointr_nofail(s[1]);
         b->output_fd = b->input_fd = s[0];
 
+        bus_socket_setup(b);
+
         return bus_socket_start_auth(b);
 }
 
 int bus_socket_take_fd(sd_bus *b) {
-        int  r;
         assert(b);
 
-        r = bus_socket_setup(b);
-        if (r < 0)
-                return r;
+        bus_socket_setup(b);
 
         return bus_socket_start_auth(b);
 }
@@ -712,15 +788,20 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
         ssize_t k;
         size_t n;
         unsigned j;
+        int r;
 
         assert(bus);
         assert(m);
         assert(idx);
         assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
 
-        if (*idx >= m->size)
+        if (*idx >= BUS_MESSAGE_SIZE(m))
                 return 0;
 
+        r = bus_message_setup_iovec(m);
+        if (r < 0)
+                return r;
+
         n = m->n_iovec * sizeof(struct iovec);
         iov = alloca(n);
         memcpy(iov, m->iovec, n);
@@ -797,10 +878,10 @@ static int bus_socket_read_message_need(sd_bus *bus, size_t *need) {
         b = ((const uint32_t*) bus->rbuffer)[3];
 
         e = ((const uint8_t*) bus->rbuffer)[0];
-        if (e == SD_BUS_LITTLE_ENDIAN) {
+        if (e == BUS_LITTLE_ENDIAN) {
                 a = le32toh(a);
                 b = le32toh(b);
-        } else if (e == SD_BUS_BIG_ENDIAN) {
+        } else if (e == BUS_BIG_ENDIAN) {
                 a = be32toh(a);
                 b = be32toh(b);
         } else
@@ -814,16 +895,19 @@ static int bus_socket_read_message_need(sd_bus *bus, size_t *need) {
         return 0;
 }
 
-static int bus_socket_make_message(sd_bus *bus, size_t size, sd_bus_message **m) {
+static int bus_socket_make_message(sd_bus *bus, size_t size) {
         sd_bus_message *t;
         void *b;
         int r;
 
         assert(bus);
-        assert(m);
         assert(bus->rbuffer_size >= size);
         assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
 
+        r = bus_rqueue_make_room(bus);
+        if (r < 0)
+                return r;
+
         if (bus->rbuffer_size > size) {
                 b = memdup((const uint8_t*) bus->rbuffer + size,
                            bus->rbuffer_size - size);
@@ -832,10 +916,11 @@ static int bus_socket_make_message(sd_bus *bus, size_t size, sd_bus_message **m)
         } else
                 b = NULL;
 
-        r = bus_message_from_malloc(bus->rbuffer, size,
+        r = bus_message_from_malloc(bus,
+                                    bus->rbuffer, size,
                                     bus->fds, bus->n_fds,
-                                    bus->ucred_valid ? &bus->ucred : NULL,
-                                    bus->label[0] ? bus->label : NULL,
+                                    !bus->bus_client && bus->ucred_valid ? &bus->ucred : NULL,
+                                    !bus->bus_client && bus->label[0] ? bus->label : NULL,
                                     &t);
         if (r < 0) {
                 free(b);
@@ -848,11 +933,12 @@ static int bus_socket_make_message(sd_bus *bus, size_t size, sd_bus_message **m)
         bus->fds = NULL;
         bus->n_fds = 0;
 
-        *m = t;
+        bus->rqueue[bus->rqueue_size++] = t;
+
         return 1;
 }
 
-int bus_socket_read_message(sd_bus *bus, sd_bus_message **m) {
+int bus_socket_read_message(sd_bus *bus) {
         struct msghdr mh;
         struct iovec iov;
         ssize_t k;
@@ -866,10 +952,9 @@ int bus_socket_read_message(sd_bus *bus, sd_bus_message **m) {
                             CMSG_SPACE(NAME_MAX)]; /*selinux label */
         } control;
         struct cmsghdr *cmsg;
-        bool handle_cmsg;
+        bool handle_cmsg = false;
 
         assert(bus);
-        assert(m);
         assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
 
         r = bus_socket_read_message_need(bus, &need);
@@ -877,7 +962,7 @@ int bus_socket_read_message(sd_bus *bus, sd_bus_message **m) {
                 return r;
 
         if (bus->rbuffer_size >= need)
-                return bus_socket_make_message(bus, need, m);
+                return bus_socket_make_message(bus, need);
 
         b = realloc(bus->rbuffer, need);
         if (!b)
@@ -942,16 +1027,22 @@ int bus_socket_read_message(sd_bus *bus, sd_bus_message **m) {
                                    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;
+                                /* Ignore bogus data, which we might
+                                 * get on socketpair() sockets */
+                                if (((struct ucred*) CMSG_DATA(cmsg))->pid != 0) {
+                                        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;
+                                if (l > 0) {
+                                        memcpy(&bus->label, CMSG_DATA(cmsg), l);
+                                        bus->label[l] = 0;
+                                }
                         }
                 }
         }
@@ -961,7 +1052,7 @@ int bus_socket_read_message(sd_bus *bus, sd_bus_message **m) {
                 return r;
 
         if (bus->rbuffer_size >= need)
-                return bus_socket_make_message(bus, need, m);
+                return bus_socket_make_message(bus, need);
 
         return 1;
 }
@@ -969,16 +1060,14 @@ int bus_socket_read_message(sd_bus *bus, sd_bus_message **m) {
 int bus_socket_process_opening(sd_bus *b) {
         int error = 0;
         socklen_t slen = sizeof(error);
-        struct pollfd p;
+        struct pollfd p = {
+                .fd = b->output_fd,
+                .events = POLLOUT,
+        };
         int r;
 
-        assert(b);
         assert(b->state == BUS_OPENING);
 
-        zero(p);
-        p.fd = b->output_fd;
-        p.events = POLLOUT;
-
         r = poll(&p, 1, 0);
         if (r < 0)
                 return -errno;