LCOV - code coverage report
Current view: top level - libsystemd/sd-id128 - sd-id128.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 95 168 56.5 %
Date: 2019-08-22 15:41:25 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <fcntl.h>
       5             : #include <unistd.h>
       6             : 
       7             : #include "sd-id128.h"
       8             : 
       9             : #include "alloc-util.h"
      10             : #include "fd-util.h"
      11             : #include "hexdecoct.h"
      12             : #include "id128-util.h"
      13             : #include "io-util.h"
      14             : #include "khash.h"
      15             : #include "macro.h"
      16             : #include "missing.h"
      17             : #include "random-util.h"
      18             : #include "user-util.h"
      19             : #include "util.h"
      20             : 
      21        2693 : _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
      22             :         unsigned n;
      23             : 
      24        2693 :         assert_return(s, NULL);
      25             : 
      26       45781 :         for (n = 0; n < 16; n++) {
      27       43088 :                 s[n*2] = hexchar(id.bytes[n] >> 4);
      28       43088 :                 s[n*2+1] = hexchar(id.bytes[n] & 0xF);
      29             :         }
      30             : 
      31        2693 :         s[32] = 0;
      32             : 
      33        2693 :         return s;
      34             : }
      35             : 
      36        2394 : _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
      37             :         unsigned n, i;
      38             :         sd_id128_t t;
      39        2394 :         bool is_guid = false;
      40             : 
      41        2394 :         assert_return(s, -EINVAL);
      42             : 
      43       40755 :         for (n = 0, i = 0; n < 16;) {
      44             :                 int a, b;
      45             : 
      46       38365 :                 if (s[i] == '-') {
      47             :                         /* Is this a GUID? Then be nice, and skip over
      48             :                          * the dashes */
      49             : 
      50         116 :                         if (i == 8)
      51          29 :                                 is_guid = true;
      52          87 :                         else if (IN_SET(i, 13, 18, 23)) {
      53          86 :                                 if (!is_guid)
      54           0 :                                         return -EINVAL;
      55             :                         } else
      56           1 :                                 return -EINVAL;
      57             : 
      58         115 :                         i++;
      59         115 :                         continue;
      60             :                 }
      61             : 
      62       38249 :                 a = unhexchar(s[i++]);
      63       38249 :                 if (a < 0)
      64           3 :                         return -EINVAL;
      65             : 
      66       38246 :                 b = unhexchar(s[i++]);
      67       38246 :                 if (b < 0)
      68           0 :                         return -EINVAL;
      69             : 
      70       38246 :                 t.bytes[n++] = (a << 4) | b;
      71             :         }
      72             : 
      73        2390 :         if (i != (is_guid ? 36 : 32))
      74           1 :                 return -EINVAL;
      75             : 
      76        2389 :         if (s[i] != 0)
      77           2 :                 return -EINVAL;
      78             : 
      79        2387 :         if (ret)
      80        2387 :                 *ret = t;
      81        2387 :         return 0;
      82             : }
      83             : 
      84        1375 : _public_ int sd_id128_get_machine(sd_id128_t *ret) {
      85             :         static thread_local sd_id128_t saved_machine_id = {};
      86             :         int r;
      87             : 
      88        1375 :         assert_return(ret, -EINVAL);
      89             : 
      90        1375 :         if (sd_id128_is_null(saved_machine_id)) {
      91          18 :                 r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id);
      92          18 :                 if (r < 0)
      93           0 :                         return r;
      94             : 
      95          18 :                 if (sd_id128_is_null(saved_machine_id))
      96           0 :                         return -ENOMEDIUM;
      97             :         }
      98             : 
      99        1375 :         *ret = saved_machine_id;
     100        1375 :         return 0;
     101             : }
     102             : 
     103          54 : _public_ int sd_id128_get_boot(sd_id128_t *ret) {
     104             :         static thread_local sd_id128_t saved_boot_id = {};
     105             :         int r;
     106             : 
     107          54 :         assert_return(ret, -EINVAL);
     108             : 
     109          54 :         if (sd_id128_is_null(saved_boot_id)) {
     110          22 :                 r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id);
     111          22 :                 if (r < 0)
     112           0 :                         return r;
     113             :         }
     114             : 
     115          54 :         *ret = saved_boot_id;
     116          54 :         return 0;
     117             : }
     118             : 
     119           1 : static int get_invocation_from_keyring(sd_id128_t *ret) {
     120           1 :         _cleanup_free_ char *description = NULL;
     121             :         char *d, *p, *g, *u, *e;
     122             :         unsigned long perms;
     123             :         key_serial_t key;
     124           1 :         size_t sz = 256;
     125             :         uid_t uid;
     126             :         gid_t gid;
     127             :         int r, c;
     128             : 
     129             : #define MAX_PERMS ((unsigned long) (KEY_POS_VIEW|KEY_POS_READ|KEY_POS_SEARCH| \
     130             :                                     KEY_USR_VIEW|KEY_USR_READ|KEY_USR_SEARCH))
     131             : 
     132           1 :         assert(ret);
     133             : 
     134           1 :         key = request_key("user", "invocation_id", NULL, 0);
     135           1 :         if (key == -1) {
     136             :                 /* Keyring support not available? No invocation key stored? */
     137           1 :                 if (IN_SET(errno, ENOSYS, ENOKEY))
     138           1 :                         return -ENXIO;
     139             : 
     140           0 :                 return -errno;
     141             :         }
     142             : 
     143             :         for (;;) {
     144           0 :                 description = new(char, sz);
     145           0 :                 if (!description)
     146           0 :                         return -ENOMEM;
     147             : 
     148           0 :                 c = keyctl(KEYCTL_DESCRIBE, key, (unsigned long) description, sz, 0);
     149           0 :                 if (c < 0)
     150           0 :                         return -errno;
     151             : 
     152           0 :                 if ((size_t) c <= sz)
     153           0 :                         break;
     154             : 
     155           0 :                 sz = c;
     156           0 :                 free(description);
     157             :         }
     158             : 
     159             :         /* The kernel returns a final NUL in the string, verify that. */
     160           0 :         assert(description[c-1] == 0);
     161             : 
     162             :         /* Chop off the final description string */
     163           0 :         d = strrchr(description, ';');
     164           0 :         if (!d)
     165           0 :                 return -EIO;
     166           0 :         *d = 0;
     167             : 
     168             :         /* Look for the permissions */
     169           0 :         p = strrchr(description, ';');
     170           0 :         if (!p)
     171           0 :                 return -EIO;
     172             : 
     173           0 :         errno = 0;
     174           0 :         perms = strtoul(p + 1, &e, 16);
     175           0 :         if (errno > 0)
     176           0 :                 return -errno;
     177           0 :         if (e == p + 1) /* Read at least one character */
     178           0 :                 return -EIO;
     179           0 :         if (e != d) /* Must reached the end */
     180           0 :                 return -EIO;
     181             : 
     182           0 :         if ((perms & ~MAX_PERMS) != 0)
     183           0 :                 return -EPERM;
     184             : 
     185           0 :         *p = 0;
     186             : 
     187             :         /* Look for the group ID */
     188           0 :         g = strrchr(description, ';');
     189           0 :         if (!g)
     190           0 :                 return -EIO;
     191           0 :         r = parse_gid(g + 1, &gid);
     192           0 :         if (r < 0)
     193           0 :                 return r;
     194           0 :         if (gid != 0)
     195           0 :                 return -EPERM;
     196           0 :         *g = 0;
     197             : 
     198             :         /* Look for the user ID */
     199           0 :         u = strrchr(description, ';');
     200           0 :         if (!u)
     201           0 :                 return -EIO;
     202           0 :         r = parse_uid(u + 1, &uid);
     203           0 :         if (r < 0)
     204           0 :                 return r;
     205           0 :         if (uid != 0)
     206           0 :                 return -EPERM;
     207             : 
     208           0 :         c = keyctl(KEYCTL_READ, key, (unsigned long) ret, sizeof(sd_id128_t), 0);
     209           0 :         if (c < 0)
     210           0 :                 return -errno;
     211           0 :         if (c != sizeof(sd_id128_t))
     212           0 :                 return -EIO;
     213             : 
     214           0 :         return 0;
     215             : }
     216             : 
     217           1 : static int get_invocation_from_environment(sd_id128_t *ret) {
     218             :         const char *e;
     219             : 
     220           1 :         assert(ret);
     221             : 
     222           1 :         e = secure_getenv("INVOCATION_ID");
     223           1 :         if (!e)
     224           1 :                 return -ENXIO;
     225             : 
     226           0 :         return sd_id128_from_string(e, ret);
     227             : }
     228             : 
     229           1 : _public_ int sd_id128_get_invocation(sd_id128_t *ret) {
     230             :         static thread_local sd_id128_t saved_invocation_id = {};
     231             :         int r;
     232             : 
     233           1 :         assert_return(ret, -EINVAL);
     234             : 
     235           1 :         if (sd_id128_is_null(saved_invocation_id)) {
     236             :                 /* We first check the environment. The environment variable is primarily relevant for user
     237             :                  * services, and sufficiently safe as long as no privilege boundary is involved. */
     238           1 :                 r = get_invocation_from_environment(&saved_invocation_id);
     239           1 :                 if (r < 0 && r != -ENXIO)
     240           0 :                         return r;
     241             : 
     242             :                 /* The kernel keyring is relevant for system services (as for user services we don't store
     243             :                  * the invocation ID in the keyring, as there'd be no trust benefit in that). */
     244           1 :                 r = get_invocation_from_keyring(&saved_invocation_id);
     245           1 :                 if (r < 0)
     246           1 :                         return r;
     247             :         }
     248             : 
     249           0 :         *ret = saved_invocation_id;
     250           0 :         return 0;
     251             : }
     252             : 
     253        1528 : static sd_id128_t make_v4_uuid(sd_id128_t id) {
     254             :         /* Stolen from generate_random_uuid() of drivers/char/random.c
     255             :          * in the kernel sources */
     256             : 
     257             :         /* Set UUID version to 4 --- truly random generation */
     258        1528 :         id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
     259             : 
     260             :         /* Set the UUID variant to DCE */
     261        1528 :         id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
     262             : 
     263        1528 :         return id;
     264             : }
     265             : 
     266        1525 : _public_ int sd_id128_randomize(sd_id128_t *ret) {
     267             :         sd_id128_t t;
     268             :         int r;
     269             : 
     270        1525 :         assert_return(ret, -EINVAL);
     271             : 
     272             :         /* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be
     273             :          * fine for UUIDS. */
     274        1525 :         r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND);
     275        1525 :         if (r < 0)
     276           0 :                 return r;
     277             : 
     278             :         /* Turn this into a valid v4 UUID, to be nice. Note that we
     279             :          * only guarantee this for newly generated UUIDs, not for
     280             :          * pre-existing ones. */
     281             : 
     282        1525 :         *ret = make_v4_uuid(t);
     283        1525 :         return 0;
     284             : }
     285             : 
     286           3 : static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
     287           3 :         _cleanup_(khash_unrefp) khash *h = NULL;
     288             :         sd_id128_t result;
     289             :         const void *p;
     290             :         int r;
     291             : 
     292           3 :         assert(ret);
     293             : 
     294           3 :         r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base));
     295           3 :         if (r < 0)
     296           0 :                 return r;
     297             : 
     298           3 :         r = khash_put(h, &app_id, sizeof(app_id));
     299           3 :         if (r < 0)
     300           0 :                 return r;
     301             : 
     302           3 :         r = khash_digest_data(h, &p);
     303           3 :         if (r < 0)
     304           0 :                 return r;
     305             : 
     306             :         /* We chop off the trailing 16 bytes */
     307           3 :         memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
     308             : 
     309           3 :         *ret = make_v4_uuid(result);
     310           3 :         return 0;
     311             : }
     312             : 
     313           3 : _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
     314             :         sd_id128_t id;
     315             :         int r;
     316             : 
     317           3 :         assert_return(ret, -EINVAL);
     318             : 
     319           3 :         r = sd_id128_get_machine(&id);
     320           3 :         if (r < 0)
     321           0 :                 return r;
     322             : 
     323           3 :         return get_app_specific(id, app_id, ret);
     324             : }
     325             : 
     326           0 : _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
     327             :         sd_id128_t id;
     328             :         int r;
     329             : 
     330           0 :         assert_return(ret, -EINVAL);
     331             : 
     332           0 :         r = sd_id128_get_boot(&id);
     333           0 :         if (r < 0)
     334           0 :                 return r;
     335             : 
     336           0 :         return get_app_specific(id, app_id, ret);
     337             : }

Generated by: LCOV version 1.14