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