X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibelogind%2Fsd-id128%2Fsd-id128.c;fp=src%2Flibelogind%2Fsd-id128%2Fsd-id128.c;h=14dfc31749d339525147abb5a431f72c5543e9e2;hp=43cc86f43f5a25664892eb55e53d71ed43661448;hb=b6c1f19fc321ff2c88eb7d4a39d79a4943446a2d;hpb=ea4774c2dd76bec7cb099f73ba75b9993e2132fe diff --git a/src/libelogind/sd-id128/sd-id128.c b/src/libelogind/sd-id128/sd-id128.c index 43cc86f43..14dfc3174 100644 --- a/src/libelogind/sd-id128/sd-id128.c +++ b/src/libelogind/sd-id128/sd-id128.c @@ -129,6 +129,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; @@ -136,15 +235,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;