]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd/sd-id128/sd-id128.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd / sd-id128 / sd-id128.c
index eb539ad318fdee7c8fa37123d7809c91d62225b0..561bcdf4f1072e8821e951289fe3aa6f548ad9d3 100644 (file)
@@ -1,5 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 #include <fcntl.h>
 #include <unistd.h>
 
-#include "util.h"
-#include "macro.h"
 #include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "id128-util.h"
+#include "io-util.h"
+#include "khash.h"
+#include "macro.h"
+#include "missing.h"
 #include "random-util.h"
+#include "user-util.h"
+#include "util.h"
 
 _public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
         unsigned n;
@@ -49,7 +57,6 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
         bool is_guid = false;
 
         assert_return(s, -EINVAL);
-        assert_return(ret, -EINVAL);
 
         for (n = 0, i = 0; n < 16;) {
                 int a, b;
@@ -60,7 +67,7 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
 
                         if (i == 8)
                                 is_guid = true;
-                        else if (i == 13 || i == 18 || i == 23) {
+                        else if (IN_SET(i, 13, 18, 23)) {
                                 if (!is_guid)
                                         return -EINVAL;
                         } else
@@ -87,130 +94,203 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
         if (s[i] != 0)
                 return -EINVAL;
 
-        *ret = t;
+        if (ret)
+                *ret = t;
         return 0;
 }
 
-static sd_id128_t make_v4_uuid(sd_id128_t id) {
-        /* Stolen from generate_random_uuid() of drivers/char/random.c
-         * in the kernel sources */
+_public_ int sd_id128_get_machine(sd_id128_t *ret) {
+        static thread_local sd_id128_t saved_machine_id = {};
+        int r;
 
-        /* Set UUID version to 4 --- truly random generation */
-        id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
+        assert_return(ret, -EINVAL);
 
-        /* Set the UUID variant to DCE */
-        id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
+        if (sd_id128_is_null(saved_machine_id)) {
+                r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id);
+                if (r < 0)
+                        return r;
 
-        return id;
+                if (sd_id128_is_null(saved_machine_id))
+                        return -EINVAL;
+        }
+
+        *ret = saved_machine_id;
+        return 0;
 }
 
-_public_ int sd_id128_get_machine(sd_id128_t *ret) {
-        static thread_local sd_id128_t saved_machine_id;
-        static thread_local bool saved_machine_id_valid = false;
-        _cleanup_close_ int fd = -1;
-        char buf[33];
-        unsigned j;
-        sd_id128_t t;
+_public_ int sd_id128_get_boot(sd_id128_t *ret) {
+        static thread_local sd_id128_t saved_boot_id = {};
         int r;
 
         assert_return(ret, -EINVAL);
 
-        if (saved_machine_id_valid) {
-                *ret = saved_machine_id;
-                return 0;
+        if (sd_id128_is_null(saved_boot_id)) {
+                r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id);
+                if (r < 0)
+                        return r;
         }
 
-        fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return -errno;
+        *ret = saved_boot_id;
+        return 0;
+}
 
-        r = loop_read_exact(fd, buf, 33, false);
-        if (r < 0)
-                return r;
-        if (buf[32] !='\n')
-                return -EIO;
+static int get_invocation_from_keyring(sd_id128_t *ret) {
 
-        for (j = 0; j < 16; j++) {
-                int a, b;
+        _cleanup_free_ char *description = NULL;
+        char *d, *p, *g, *u, *e;
+        unsigned long perms;
+        key_serial_t key;
+        size_t sz = 256;
+        uid_t uid;
+        gid_t gid;
+        int r, c;
 
-                a = unhexchar(buf[j*2]);
-                b = unhexchar(buf[j*2+1]);
+#define MAX_PERMS ((unsigned long) (KEY_POS_VIEW|KEY_POS_READ|KEY_POS_SEARCH| \
+                                    KEY_USR_VIEW|KEY_USR_READ|KEY_USR_SEARCH))
 
-                if (a < 0 || b < 0)
-                        return -EIO;
+        assert(ret);
 
-                t.bytes[j] = a << 4 | b;
+        key = request_key("user", "invocation_id", NULL, 0);
+        if (key == -1) {
+                /* Keyring support not available? No invocation key stored? */
+                if (IN_SET(errno, ENOSYS, ENOKEY))
+                        return 0;
+
+                return -errno;
         }
 
-        saved_machine_id = t;
-        saved_machine_id_valid = true;
+        for (;;) {
+                description = new(char, sz);
+                if (!description)
+                        return -ENOMEM;
 
-        *ret = t;
-        return 0;
-}
+                c = keyctl(KEYCTL_DESCRIBE, key, (unsigned long) description, sz, 0);
+                if (c < 0)
+                        return -errno;
 
-_public_ int sd_id128_get_boot(sd_id128_t *ret) {
-        static thread_local sd_id128_t saved_boot_id;
-        static thread_local bool saved_boot_id_valid = false;
-        _cleanup_close_ int fd = -1;
-        char buf[36];
-        unsigned j;
-        sd_id128_t t;
-        char *p;
-        int r;
-
-        assert_return(ret, -EINVAL);
+                if ((size_t) c <= sz)
+                        break;
 
-        if (saved_boot_id_valid) {
-                *ret = saved_boot_id;
-                return 0;
+                sz = c;
+                free(description);
         }
 
-        fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
+        /* The kernel returns a final NUL in the string, verify that. */
+        assert(description[c-1] == 0);
+
+        /* Chop off the final description string */
+        d = strrchr(description, ';');
+        if (!d)
+                return -EIO;
+        *d = 0;
+
+        /* Look for the permissions */
+        p = strrchr(description, ';');
+        if (!p)
+                return -EIO;
+
+        errno = 0;
+        perms = strtoul(p + 1, &e, 16);
+        if (errno > 0)
                 return -errno;
+        if (e == p + 1) /* Read at least one character */
+                return -EIO;
+        if (e != d) /* Must reached the end */
+                return -EIO;
+
+        if ((perms & ~MAX_PERMS) != 0)
+                return -EPERM;
+
+        *p = 0;
 
-        r = loop_read_exact(fd, buf, 36, false);
+        /* Look for the group ID */
+        g = strrchr(description, ';');
+        if (!g)
+                return -EIO;
+        r = parse_gid(g + 1, &gid);
         if (r < 0)
                 return r;
+        if (gid != 0)
+                return -EPERM;
+        *g = 0;
 
-        for (j = 0, p = buf; j < 16; j++) {
-                int a, b;
+        /* Look for the user ID */
+        u = strrchr(description, ';');
+        if (!u)
+                return -EIO;
+        r = parse_uid(u + 1, &uid);
+        if (r < 0)
+                return r;
+        if (uid != 0)
+                return -EPERM;
 
-                if (p >= buf + 35)
-                        return -EIO;
+        c = keyctl(KEYCTL_READ, key, (unsigned long) ret, sizeof(sd_id128_t), 0);
+        if (c < 0)
+                return -errno;
+        if (c != sizeof(sd_id128_t))
+                return -EIO;
 
-                if (*p == '-') {
-                        p++;
-                        if (p >= buf + 35)
-                                return -EIO;
-                }
+        return 1;
+}
+
+_public_ int sd_id128_get_invocation(sd_id128_t *ret) {
+        static thread_local sd_id128_t saved_invocation_id = {};
+        int r;
 
-                a = unhexchar(p[0]);
-                b = unhexchar(p[1]);
+        assert_return(ret, -EINVAL);
 
-                if (a < 0 || b < 0)
-                        return -EIO;
+        if (sd_id128_is_null(saved_invocation_id)) {
 
-                t.bytes[j] = a << 4 | b;
+                /* We first try to read the invocation ID from the kernel keyring. This has the benefit that it is not
+                 * fakeable by unprivileged code. If the information is not available in the keyring, we use
+                 * $INVOCATION_ID but ignore the data if our process was called by less privileged code
+                 * (i.e. secure_getenv() instead of getenv()).
+                 *
+                 * The kernel keyring is only relevant for system services (as for user services we don't store the
+                 * invocation ID in the keyring, as there'd be no trust benefit in that). The environment variable is
+                 * primarily relevant for user services, and sufficiently safe as no privilege boundary is involved. */
 
-                p += 2;
-        }
+                r = get_invocation_from_keyring(&saved_invocation_id);
+                if (r < 0)
+                        return r;
 
-        saved_boot_id = t;
-        saved_boot_id_valid = true;
+                if (r == 0) {
+                        const char *e;
 
-        *ret = t;
+                        e = secure_getenv("INVOCATION_ID");
+                        if (!e)
+                                return -ENXIO;
+
+                        r = sd_id128_from_string(e, &saved_invocation_id);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        *ret = saved_invocation_id;
         return 0;
 }
 
+static sd_id128_t make_v4_uuid(sd_id128_t id) {
+        /* Stolen from generate_random_uuid() of drivers/char/random.c
+         * in the kernel sources */
+
+        /* Set UUID version to 4 --- truly random generation */
+        id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
+
+        /* Set the UUID variant to DCE */
+        id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
+
+        return id;
+}
+
 _public_ int sd_id128_randomize(sd_id128_t *ret) {
         sd_id128_t t;
         int r;
 
         assert_return(ret, -EINVAL);
 
-        r = dev_urandom(&t, sizeof(t));
+        r = acquire_random_bytes(&t, sizeof t, true);
         if (r < 0)
                 return r;
 
@@ -221,3 +301,34 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
         *ret = make_v4_uuid(t);
         return 0;
 }
+
+_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+        _cleanup_(khash_unrefp) khash *h = NULL;
+        sd_id128_t m, result;
+        const void *p;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        r = sd_id128_get_machine(&m);
+        if (r < 0)
+                return r;
+
+        r = khash_new_with_key(&h, "hmac(sha256)", &m, sizeof(m));
+        if (r < 0)
+                return r;
+
+        r = khash_put(h, &app_id, sizeof(app_id));
+        if (r < 0)
+                return r;
+
+        r = khash_digest_data(h, &p);
+        if (r < 0)
+                return r;
+
+        /* We chop off the trailing 16 bytes */
+        memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
+
+        *ret = make_v4_uuid(result);
+        return 0;
+}