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