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