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