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