c2dfb7
From ccdb8883c6ff0e72d5e221768af0718e25cbf177 Mon Sep 17 00:00:00 2001
c2dfb7
From: David Rheinsberg <david.rheinsberg@gmail.com>
c2dfb7
Date: Thu, 14 Mar 2019 13:34:13 +0100
c2dfb7
Subject: [PATCH] sd-bus: skip sending formatted UIDs via SASL
c2dfb7
c2dfb7
The dbus external authentication takes as optional argument the UID the
c2dfb7
sender wants to authenticate as. This uid is purely optional. The
c2dfb7
AF_UNIX socket already conveys the same information through the
c2dfb7
auxiliary socket data, so we really don't have to provide that
c2dfb7
information.
c2dfb7
c2dfb7
Unfortunately, there is no way to send empty arguments, since they are
c2dfb7
interpreted as "missing argument", which has a different meaning. The
c2dfb7
SASL negotiation thus changes from:
c2dfb7
c2dfb7
    AUTH EXTERNAL <uid>
c2dfb7
    NEGOTIATE_UNIX_FD                   (optional)
c2dfb7
    BEGIN
c2dfb7
c2dfb7
to:
c2dfb7
c2dfb7
    AUTH EXTERNAL
c2dfb7
    DATA
c2dfb7
    NEGOTIATE_UNIX_FD                   (optional)
c2dfb7
    BEGIN
c2dfb7
c2dfb7
And thus the replies we expect as a client change from:
c2dfb7
c2dfb7
    OK <server-id>
c2dfb7
    AGREE_UNIX_FD                       (optional)
c2dfb7
c2dfb7
to:
c2dfb7
c2dfb7
    DATA
c2dfb7
    OK <server-id>
c2dfb7
    AGREE_UNIX_FD                       (optional)
c2dfb7
c2dfb7
Since the old sd-bus server implementation used the wrong reply for
c2dfb7
"AUTH" requests that do not carry the arguments inlined, we decided to
c2dfb7
make sd-bus clients accept this as well. Hence, sd-bus now allows
c2dfb7
"OK <server-id>\r\n" replies instead of "DATA\r\n" replies.
c2dfb7
c2dfb7
Signed-off-by: David Rheinsberg <david.rheinsberg@gmail.com>
c2dfb7
(cherry picked from commit 1ed4723d38cd0d1423c8fe650f90fa86007ddf55)
c2dfb7
c2dfb7
Resolves: #1838081
c2dfb7
---
c2dfb7
 src/libsystemd/sd-bus/bus-socket.c | 106 +++++++++++++++++------------
c2dfb7
 1 file changed, 64 insertions(+), 42 deletions(-)
c2dfb7
c2dfb7
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
c2dfb7
index e505d43c6b..8813dd5efd 100644
c2dfb7
--- a/src/libsystemd/sd-bus/bus-socket.c
c2dfb7
+++ b/src/libsystemd/sd-bus/bus-socket.c
c2dfb7
@@ -162,17 +162,25 @@ static int bus_socket_write_auth(sd_bus *b) {
c2dfb7
 }
c2dfb7
 
c2dfb7
 static int bus_socket_auth_verify_client(sd_bus *b) {
c2dfb7
-        char *e, *f, *start;
c2dfb7
+        char *d, *e, *f, *start;
c2dfb7
         sd_id128_t peer;
c2dfb7
         unsigned i;
c2dfb7
         int r;
c2dfb7
 
c2dfb7
         assert(b);
c2dfb7
 
c2dfb7
-        /* We expect two response lines: "OK" and possibly
c2dfb7
-         * "AGREE_UNIX_FD" */
c2dfb7
+        /*
c2dfb7
+         * We expect three response lines:
c2dfb7
+         *   "DATA\r\n"
c2dfb7
+         *   "OK <server-id>\r\n"
c2dfb7
+         *   "AGREE_UNIX_FD\r\n"        (optional)
c2dfb7
+         */
c2dfb7
 
c2dfb7
-        e = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2);
c2dfb7
+        d = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2);
c2dfb7
+        if (!d)
c2dfb7
+                return 0;
c2dfb7
+
c2dfb7
+        e = memmem(d + 2, b->rbuffer_size - (d - (char*) b->rbuffer) - 2, "\r\n", 2);
c2dfb7
         if (!e)
c2dfb7
                 return 0;
c2dfb7
 
c2dfb7
@@ -187,13 +195,30 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
c2dfb7
                 start = e + 2;
c2dfb7
         }
c2dfb7
 
c2dfb7
-        /* Nice! We got all the lines we need. First check the OK
c2dfb7
-         * line */
c2dfb7
+        /* Nice! We got all the lines we need. First check the DATA line. */
c2dfb7
+
c2dfb7
+        if (d - (char*) b->rbuffer == 4) {
c2dfb7
+                if (memcmp(b->rbuffer, "DATA", 4))
c2dfb7
+                        return -EPERM;
c2dfb7
+        } else if (d - (char*) b->rbuffer == 3 + 32) {
c2dfb7
+                /*
c2dfb7
+                 * Old versions of the server-side implementation of `sd-bus` replied with "OK <id>" to
c2dfb7
+                 * "AUTH" requests from a client, even if the "AUTH" line did not contain inlined
c2dfb7
+                 * arguments. Therefore, we also accept "OK <id>" here, even though it is technically the
c2dfb7
+                 * wrong reply. We ignore the "<id>" parameter, though, since it has no real value.
c2dfb7
+                 */
c2dfb7
+                if (memcmp(b->rbuffer, "OK ", 3))
c2dfb7
+                        return -EPERM;
c2dfb7
+        } else {
c2dfb7
+                return -EPERM;
c2dfb7
+        }
c2dfb7
+
c2dfb7
+        /* Now check the OK line. */
c2dfb7
 
c2dfb7
-        if (e - (char*) b->rbuffer != 3 + 32)
c2dfb7
+        if (e - d != 2 + 3 + 32)
c2dfb7
                 return -EPERM;
c2dfb7
 
c2dfb7
-        if (memcmp(b->rbuffer, "OK ", 3))
c2dfb7
+        if (memcmp(d + 2, "OK ", 3))
c2dfb7
                 return -EPERM;
c2dfb7
 
c2dfb7
         b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL;
c2dfb7
@@ -201,8 +226,8 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
c2dfb7
         for (i = 0; i < 32; i += 2) {
c2dfb7
                 int x, y;
c2dfb7
 
c2dfb7
-                x = unhexchar(((char*) b->rbuffer)[3 + i]);
c2dfb7
-                y = unhexchar(((char*) b->rbuffer)[3 + i + 1]);
c2dfb7
+                x = unhexchar(d[2 + 3 + i]);
c2dfb7
+                y = unhexchar(d[2 + 3 + i + 1]);
c2dfb7
 
c2dfb7
                 if (x < 0 || y < 0)
c2dfb7
                         return -EINVAL;
c2dfb7
@@ -216,7 +241,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
c2dfb7
 
c2dfb7
         b->server_id = peer;
c2dfb7
 
c2dfb7
-        /* And possibly check the second line, too */
c2dfb7
+        /* And possibly check the third line, too */
c2dfb7
 
c2dfb7
         if (f)
c2dfb7
                 b->can_fds =
c2dfb7
@@ -616,42 +641,39 @@ static void bus_get_peercred(sd_bus *b) {
c2dfb7
 }
c2dfb7
 
c2dfb7
 static int bus_socket_start_auth_client(sd_bus *b) {
c2dfb7
-        size_t l;
c2dfb7
-        const char *auth_suffix, *auth_prefix;
c2dfb7
+        static const char sasl_auth_anonymous[] = {
c2dfb7
+                /*
c2dfb7
+                 * We use an arbitrary trace-string for the ANONYMOUS authentication. It can be used by the
c2dfb7
+                 * message broker to aid debugging of clients. We fully anonymize the connection and use a
c2dfb7
+                 * static default.
c2dfb7
+                 */
c2dfb7
+                "\0AUTH ANONYMOUS\r\n"
c2dfb7
+                /* HEX a n o n y m o u s */
c2dfb7
+                "DATA 616e6f6e796d6f7573\r\n"
c2dfb7
+        };
c2dfb7
+        static const char sasl_auth_external[] = {
c2dfb7
+                "\0AUTH EXTERNAL\r\n"
c2dfb7
+                "DATA\r\n"
c2dfb7
+        };
c2dfb7
+        static const char sasl_negotiate_unix_fd[] = {
c2dfb7
+                "NEGOTIATE_UNIX_FD\r\n"
c2dfb7
+        };
c2dfb7
+        static const char sasl_begin[] = {
c2dfb7
+                "BEGIN\r\n"
c2dfb7
+        };
c2dfb7
+        size_t i = 0;
c2dfb7
 
c2dfb7
         assert(b);
c2dfb7
 
c2dfb7
-        if (b->anonymous_auth) {
c2dfb7
-                auth_prefix = "\0AUTH ANONYMOUS ";
c2dfb7
-
c2dfb7
-                /* For ANONYMOUS auth we send some arbitrary "trace" string */
c2dfb7
-                l = 9;
c2dfb7
-                b->auth_buffer = hexmem("anonymous", l);
c2dfb7
-        } else {
c2dfb7
-                char text[DECIMAL_STR_MAX(uid_t) + 1];
c2dfb7
-
c2dfb7
-                auth_prefix = "\0AUTH EXTERNAL ";
c2dfb7
-
c2dfb7
-                xsprintf(text, UID_FMT, geteuid());
c2dfb7
-
c2dfb7
-                l = strlen(text);
c2dfb7
-                b->auth_buffer = hexmem(text, l);
c2dfb7
-        }
c2dfb7
-
c2dfb7
-        if (!b->auth_buffer)
c2dfb7
-                return -ENOMEM;
c2dfb7
+        if (b->anonymous_auth)
c2dfb7
+                b->auth_iovec[i++] = IOVEC_MAKE((char*) sasl_auth_anonymous, sizeof(sasl_auth_anonymous) - 1);
c2dfb7
+        else
c2dfb7
+                b->auth_iovec[i++] = IOVEC_MAKE((char*) sasl_auth_external, sizeof(sasl_auth_external) - 1);
c2dfb7
 
c2dfb7
         if (b->accept_fd)
c2dfb7
-                auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
c2dfb7
-        else
c2dfb7
-                auth_suffix = "\r\nBEGIN\r\n";
c2dfb7
-
c2dfb7
-        b->auth_iovec[0].iov_base = (void*) auth_prefix;
c2dfb7
-        b->auth_iovec[0].iov_len = 1 + strlen(auth_prefix + 1);
c2dfb7
-        b->auth_iovec[1].iov_base = (void*) b->auth_buffer;
c2dfb7
-        b->auth_iovec[1].iov_len = l * 2;
c2dfb7
-        b->auth_iovec[2].iov_base = (void*) auth_suffix;
c2dfb7
-        b->auth_iovec[2].iov_len = strlen(auth_suffix);
c2dfb7
+                b->auth_iovec[i++] = IOVEC_MAKE_STRING(sasl_negotiate_unix_fd);
c2dfb7
+
c2dfb7
+        b->auth_iovec[i++] = IOVEC_MAKE_STRING(sasl_begin);
c2dfb7
 
c2dfb7
         return bus_socket_write_auth(b);
c2dfb7
 }