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