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