]> 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 0d673ba655e18905b4ea1742b98ef9b81ba25d0e..561bcdf4f1072e8821e951289fe3aa6f548ad9d3 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 
 #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]) {
@@ -63,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
@@ -130,6 +134,105 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
         return 0;
 }
 
+static int get_invocation_from_keyring(sd_id128_t *ret) {
+
+        _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;
+
+#define MAX_PERMS ((unsigned long) (KEY_POS_VIEW|KEY_POS_READ|KEY_POS_SEARCH| \
+                                    KEY_USR_VIEW|KEY_USR_READ|KEY_USR_SEARCH))
+
+        assert(ret);
+
+        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;
+        }
+
+        for (;;) {
+                description = new(char, sz);
+                if (!description)
+                        return -ENOMEM;
+
+                c = keyctl(KEYCTL_DESCRIBE, key, (unsigned long) description, sz, 0);
+                if (c < 0)
+                        return -errno;
+
+                if ((size_t) c <= sz)
+                        break;
+
+                sz = c;
+                free(description);
+        }
+
+        /* 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;
+
+        /* 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;
+
+        /* 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;
+
+        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;
+
+        return 1;
+}
+
 _public_ int sd_id128_get_invocation(sd_id128_t *ret) {
         static thread_local sd_id128_t saved_invocation_id = {};
         int r;
@@ -137,15 +240,31 @@ _public_ int sd_id128_get_invocation(sd_id128_t *ret) {
         assert_return(ret, -EINVAL);
 
         if (sd_id128_is_null(saved_invocation_id)) {
-                const char *e;
 
-                e = secure_getenv("INVOCATION_ID");
-                if (!e)
-                        return -ENXIO;
+                /* 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. */
 
-                r = sd_id128_from_string(e, &saved_invocation_id);
+                r = get_invocation_from_keyring(&saved_invocation_id);
                 if (r < 0)
                         return r;
+
+                if (r == 0) {
+                        const char *e;
+
+                        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;
@@ -171,7 +290,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
 
         assert_return(ret, -EINVAL);
 
-        r = dev_urandom(&t, sizeof(t));
+        r = acquire_random_bytes(&t, sizeof t, true);
         if (r < 0)
                 return r;