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