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