]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-id128/sd-id128.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd / sd-id128 / sd-id128.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
87d2c1ff
LP
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
5430f7f2
LP
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
87d2c1ff
LP
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
5430f7f2 15 Lesser General Public License for more details.
87d2c1ff 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
87d2c1ff
LP
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
80514f9c 25#include "sd-id128.h"
07630cea 26
b3415f5d 27#include "alloc-util.h"
c004493c 28#include "fd-util.h"
cf0fbc49 29#include "hexdecoct.h"
910fd145 30#include "id128-util.h"
c004493c 31#include "io-util.h"
70fc4f57 32#include "khash.h"
07630cea 33#include "macro.h"
b3415f5d 34#include "missing.h"
3df3e884 35#include "random-util.h"
b3415f5d 36#include "user-util.h"
07630cea 37#include "util.h"
87d2c1ff 38
3ade55d3 39_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
87d2c1ff
LP
40 unsigned n;
41
1ae464e0 42 assert_return(s, NULL);
87d2c1ff
LP
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
aa96c6cb
LP
54_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
55 unsigned n, i;
87d2c1ff 56 sd_id128_t t;
aa96c6cb 57 bool is_guid = false;
87d2c1ff 58
1ae464e0 59 assert_return(s, -EINVAL);
87d2c1ff 60
aa96c6cb 61 for (n = 0, i = 0; n < 16;) {
87d2c1ff
LP
62 int a, b;
63
aa96c6cb
LP
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;
945c2931 70 else if (IN_SET(i, 13, 18, 23)) {
aa96c6cb
LP
71 if (!is_guid)
72 return -EINVAL;
73 } else
74 return -EINVAL;
75
76 i++;
77 continue;
78 }
79
80 a = unhexchar(s[i++]);
87d2c1ff
LP
81 if (a < 0)
82 return -EINVAL;
83
aa96c6cb 84 b = unhexchar(s[i++]);
87d2c1ff
LP
85 if (b < 0)
86 return -EINVAL;
87
aa96c6cb 88 t.bytes[n++] = (a << 4) | b;
87d2c1ff
LP
89 }
90
aa96c6cb
LP
91 if (i != (is_guid ? 36 : 32))
92 return -EINVAL;
93
94 if (s[i] != 0)
87d2c1ff
LP
95 return -EINVAL;
96
9ca8d434
LP
97 if (ret)
98 *ret = t;
87d2c1ff
LP
99 return 0;
100}
101
000a2c98 102_public_ int sd_id128_get_machine(sd_id128_t *ret) {
910fd145 103 static thread_local sd_id128_t saved_machine_id = {};
a6dcc7e5 104 int r;
87d2c1ff 105
1ae464e0 106 assert_return(ret, -EINVAL);
000a2c98 107
910fd145
LP
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;
87d2c1ff 112
910fd145
LP
113 if (sd_id128_is_null(saved_machine_id))
114 return -EINVAL;
87d2c1ff
LP
115 }
116
910fd145 117 *ret = saved_machine_id;
87d2c1ff
LP
118 return 0;
119}
120
000a2c98 121_public_ int sd_id128_get_boot(sd_id128_t *ret) {
910fd145 122 static thread_local sd_id128_t saved_boot_id = {};
a6dcc7e5 123 int r;
87d2c1ff 124
1ae464e0 125 assert_return(ret, -EINVAL);
000a2c98 126
910fd145
LP
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;
87d2c1ff
LP
131 }
132
910fd145
LP
133 *ret = saved_boot_id;
134 return 0;
135}
87d2c1ff 136
b3415f5d
LP
137static 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
4b58153d
LP
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)) {
4b58153d 243
b3415f5d
LP
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. */
4b58153d 252
b3415f5d 253 r = get_invocation_from_keyring(&saved_invocation_id);
4b58153d
LP
254 if (r < 0)
255 return r;
b3415f5d
LP
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 }
4b58153d
LP
268 }
269
270 *ret = saved_invocation_id;
271 return 0;
272}
273
910fd145
LP
274static 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 */
87d2c1ff 277
910fd145
LP
278 /* Set UUID version to 4 --- truly random generation */
279 id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
87d2c1ff 280
910fd145
LP
281 /* Set the UUID variant to DCE */
282 id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
87d2c1ff 283
910fd145 284 return id;
87d2c1ff
LP
285}
286
000a2c98 287_public_ int sd_id128_randomize(sd_id128_t *ret) {
87d2c1ff 288 sd_id128_t t;
0f0e240c 289 int r;
87d2c1ff 290
1ae464e0 291 assert_return(ret, -EINVAL);
87d2c1ff 292
f0d09059 293 r = acquire_random_bytes(&t, sizeof t, true);
0f0e240c
LP
294 if (r < 0)
295 return r;
87d2c1ff
LP
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
f7340ab2 299 * pre-existing ones. */
87d2c1ff 300
e4bac488 301 *ret = make_v4_uuid(t);
87d2c1ff
LP
302 return 0;
303}
70fc4f57
LP
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}