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