]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-id128/sd-id128.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[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 "id128-util.h"
13 #include "io-util.h"
14 #include "khash.h"
15 #include "macro.h"
16 #include "missing_syscall.h"
17 #include "random-util.h"
18 #include "user-util.h"
19 #include "util.h"
20
21 _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
22 unsigned n;
23
24 assert_return(s, NULL);
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
36 _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
37 unsigned n, i;
38 sd_id128_t t;
39 bool is_guid = false;
40
41 assert_return(s, -EINVAL);
42
43 for (n = 0, i = 0; n < 16;) {
44 int a, b;
45
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;
52 else if (IN_SET(i, 13, 18, 23)) {
53 if (!is_guid)
54 return -EINVAL;
55 } else
56 return -EINVAL;
57
58 i++;
59 continue;
60 }
61
62 a = unhexchar(s[i++]);
63 if (a < 0)
64 return -EINVAL;
65
66 b = unhexchar(s[i++]);
67 if (b < 0)
68 return -EINVAL;
69
70 t.bytes[n++] = (a << 4) | b;
71 }
72
73 if (i != (is_guid ? 36 : 32))
74 return -EINVAL;
75
76 if (s[i] != 0)
77 return -EINVAL;
78
79 if (ret)
80 *ret = t;
81 return 0;
82 }
83
84 _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 assert_return(ret, -EINVAL);
89
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;
94
95 if (sd_id128_is_null(saved_machine_id))
96 return -ENOMEDIUM;
97 }
98
99 *ret = saved_machine_id;
100 return 0;
101 }
102
103 _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 assert_return(ret, -EINVAL);
108
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;
113 }
114
115 *ret = saved_boot_id;
116 return 0;
117 }
118
119 static int get_invocation_from_keyring(sd_id128_t *ret) {
120 _cleanup_free_ char *description = NULL;
121 char *d, *p, *g, *u, *e;
122 unsigned long perms;
123 key_serial_t key;
124 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 assert(ret);
133
134 key = request_key("user", "invocation_id", NULL, 0);
135 if (key == -1) {
136 /* Keyring support not available? No invocation key stored? */
137 if (IN_SET(errno, ENOSYS, ENOKEY))
138 return -ENXIO;
139
140 return -errno;
141 }
142
143 for (;;) {
144 description = new(char, sz);
145 if (!description)
146 return -ENOMEM;
147
148 c = keyctl(KEYCTL_DESCRIBE, key, (unsigned long) description, sz, 0);
149 if (c < 0)
150 return -errno;
151
152 if ((size_t) c <= sz)
153 break;
154
155 sz = c;
156 free(description);
157 }
158
159 /* The kernel returns a final NUL in the string, verify that. */
160 assert(description[c-1] == 0);
161
162 /* Chop off the final description string */
163 d = strrchr(description, ';');
164 if (!d)
165 return -EIO;
166 *d = 0;
167
168 /* Look for the permissions */
169 p = strrchr(description, ';');
170 if (!p)
171 return -EIO;
172
173 errno = 0;
174 perms = strtoul(p + 1, &e, 16);
175 if (errno > 0)
176 return -errno;
177 if (e == p + 1) /* Read at least one character */
178 return -EIO;
179 if (e != d) /* Must reached the end */
180 return -EIO;
181
182 if ((perms & ~MAX_PERMS) != 0)
183 return -EPERM;
184
185 *p = 0;
186
187 /* Look for the group ID */
188 g = strrchr(description, ';');
189 if (!g)
190 return -EIO;
191 r = parse_gid(g + 1, &gid);
192 if (r < 0)
193 return r;
194 if (gid != 0)
195 return -EPERM;
196 *g = 0;
197
198 /* Look for the user ID */
199 u = strrchr(description, ';');
200 if (!u)
201 return -EIO;
202 r = parse_uid(u + 1, &uid);
203 if (r < 0)
204 return r;
205 if (uid != 0)
206 return -EPERM;
207
208 c = keyctl(KEYCTL_READ, key, (unsigned long) ret, sizeof(sd_id128_t), 0);
209 if (c < 0)
210 return -errno;
211 if (c != sizeof(sd_id128_t))
212 return -EIO;
213
214 return 0;
215 }
216
217 static int get_invocation_from_environment(sd_id128_t *ret) {
218 const char *e;
219
220 assert(ret);
221
222 e = secure_getenv("INVOCATION_ID");
223 if (!e)
224 return -ENXIO;
225
226 return sd_id128_from_string(e, ret);
227 }
228
229 _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 assert_return(ret, -EINVAL);
234
235 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 r = get_invocation_from_environment(&saved_invocation_id);
239 if (r < 0 && r != -ENXIO)
240 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 r = get_invocation_from_keyring(&saved_invocation_id);
245 if (r < 0)
246 return r;
247 }
248
249 *ret = saved_invocation_id;
250 return 0;
251 }
252
253 _public_ int sd_id128_randomize(sd_id128_t *ret) {
254 sd_id128_t t;
255 int r;
256
257 assert_return(ret, -EINVAL);
258
259 /* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be
260 * fine for UUIDS. */
261 r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND);
262 if (r < 0)
263 return r;
264
265 /* Turn this into a valid v4 UUID, to be nice. Note that we
266 * only guarantee this for newly generated UUIDs, not for
267 * pre-existing ones. */
268
269 *ret = id128_make_v4_uuid(t);
270 return 0;
271 }
272
273 static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
274 _cleanup_(khash_unrefp) khash *h = NULL;
275 sd_id128_t result;
276 const void *p;
277 int r;
278
279 assert(ret);
280
281 r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base));
282 if (r < 0)
283 return r;
284
285 r = khash_put(h, &app_id, sizeof(app_id));
286 if (r < 0)
287 return r;
288
289 r = khash_digest_data(h, &p);
290 if (r < 0)
291 return r;
292
293 /* We chop off the trailing 16 bytes */
294 memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
295
296 *ret = id128_make_v4_uuid(result);
297 return 0;
298 }
299
300 _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
301 sd_id128_t id;
302 int r;
303
304 assert_return(ret, -EINVAL);
305
306 r = sd_id128_get_machine(&id);
307 if (r < 0)
308 return r;
309
310 return get_app_specific(id, app_id, ret);
311 }
312
313 _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
314 sd_id128_t id;
315 int r;
316
317 assert_return(ret, -EINVAL);
318
319 r = sd_id128_get_boot(&id);
320 if (r < 0)
321 return r;
322
323 return get_app_specific(id, app_id, ret);
324 }