]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/specifier.c
cryptenroll: allow to use a public key on a token
[thirdparty/systemd.git] / src / shared / specifier.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
9e2f7c11 2
a8fbdf54
TA
3#include <errno.h>
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdlib.h>
6aaa8c2f 7#include <sys/utsname.h>
9e2f7c11 8
a8fbdf54
TA
9#include "sd-id128.h"
10
b5efdb8a 11#include "alloc-util.h"
268f5a54 12#include "architecture.h"
f461a28d 13#include "chase.h"
de61a04b 14#include "fd-util.h"
ca78ad1d 15#include "format-util.h"
b294e594 16#include "fs-util.h"
07630cea 17#include "hostname-util.h"
de61a04b 18#include "id128-util.h"
9e2f7c11 19#include "macro.h"
268f5a54 20#include "os-util.h"
b380b643 21#include "path-lookup.h"
607f0328 22#include "path-util.h"
b5efdb8a 23#include "specifier.h"
07630cea 24#include "string-util.h"
e82f30d1 25#include "strv.h"
36444d22 26#include "user-util.h"
9e2f7c11
LP
27
28/*
29 * Generic infrastructure for replacing %x style specifiers in
30 * strings. Will call a callback for each replacement.
9e2f7c11
LP
31 */
32
751223fe
ZJS
33/* Any ASCII character or digit: our pool of potential specifiers,
34 * and "%" used for escaping. */
35#define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
36
de61a04b 37int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret) {
695c5fee 38 _cleanup_free_ char *result = NULL;
9e2f7c11 39 bool percent = false;
319a4f4b
LP
40 size_t l;
41 char *t;
19f6d710 42 int r;
9e2f7c11 43
b6e78cdd 44 assert(ret);
9e2f7c11
LP
45 assert(text);
46 assert(table);
47
48 l = strlen(text);
319a4f4b 49 if (!GREEDY_REALLOC(result, l + 1))
19f6d710 50 return -ENOMEM;
695c5fee 51 t = result;
9e2f7c11 52
01c69460 53 for (const char *f = text; *f != '\0'; f++, l--) {
9e2f7c11 54 if (percent) {
01c69460
ZJS
55 percent = false;
56
9e2f7c11
LP
57 if (*f == '%')
58 *(t++) = '%';
59 else {
60 const Specifier *i;
61
62 for (i = table; i->specifier; i++)
63 if (i->specifier == *f)
64 break;
65
66 if (i->lookup) {
19f6d710 67 _cleanup_free_ char *w = NULL;
9e2f7c11
LP
68 size_t k, j;
69
de61a04b 70 r = i->lookup(i->specifier, i->data, root, userdata, &w);
8208c8f2 71 if (r < 0)
19f6d710 72 return r;
01c69460
ZJS
73 if (isempty(w))
74 continue;
9e2f7c11 75
695c5fee 76 j = t - result;
9e2f7c11
LP
77 k = strlen(w);
78
319a4f4b 79 if (!GREEDY_REALLOC(result, j + k + l + 1))
19f6d710 80 return -ENOMEM;
d3936664 81 t = mempcpy(result + j, w, k);
8208c8f2 82 } else if (strchr(POSSIBLE_SPECIFIERS, *f))
751223fe
ZJS
83 /* Oops, an unknown specifier. */
84 return -EBADSLT;
8208c8f2 85 else {
9e2f7c11
LP
86 *(t++) = '%';
87 *(t++) = *f;
88 }
89 }
9e2f7c11
LP
90 } else if (*f == '%')
91 percent = true;
92 else
93 *(t++) = *f;
9e2f7c11 94
06536492
YW
95 if ((size_t) (t - result) > max_length)
96 return -ENAMETOOLONG;
97 }
98
bec8a68c 99 /* If string ended with a stray %, also end with % */
06536492 100 if (percent) {
038492ae 101 *(t++) = '%';
06536492
YW
102 if ((size_t) (t - result) > max_length)
103 return -ENAMETOOLONG;
104 }
bec8a68c
ZJS
105 *(t++) = 0;
106
695c5fee 107 *ret = TAKE_PTR(result);
19f6d710 108 return 0;
9e2f7c11
LP
109}
110
111/* Generic handler for simple string replacements */
112
de61a04b 113int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
01c69460 114 char *n = NULL;
19f6d710 115
b6e78cdd
FS
116 assert(ret);
117
01c69460
ZJS
118 if (!isempty(data)) {
119 n = strdup(data);
120 if (!n)
121 return -ENOMEM;
122 }
19f6d710
LP
123
124 *ret = n;
125 return 0;
9e2f7c11 126}
d848b9cb 127
607f0328
ZJS
128int specifier_real_path(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
129 const char *path = data;
130
b6e78cdd
FS
131 assert(ret);
132
607f0328
ZJS
133 if (!path)
134 return -ENOENT;
135
f461a28d 136 return chase(path, root, 0, ret, NULL);
607f0328
ZJS
137}
138
139int specifier_real_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
140 _cleanup_free_ char *path = NULL;
141 int r;
142
b6e78cdd
FS
143 assert(ret);
144
607f0328
ZJS
145 r = specifier_real_path(specifier, data, root, userdata, &path);
146 if (r < 0)
147 return r;
148
149 assert(path);
150 return path_extract_directory(path, ret);
151}
152
74235f6d
DDM
153int specifier_id128(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
154 const sd_id128_t *id = ASSERT_PTR(data);
155 char *n;
156
157 n = new(char, SD_ID128_STRING_MAX);
158 if (!n)
159 return -ENOMEM;
160
161 *ret = sd_id128_to_string(*id, n);
162 return 0;
163}
164
165int specifier_uuid(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
166 const sd_id128_t *id = ASSERT_PTR(data);
167 char *n;
168
169 n = new(char, SD_ID128_UUID_STRING_MAX);
170 if (!n)
171 return -ENOMEM;
172
173 *ret = sd_id128_to_uuid_string(*id, n);
174 return 0;
175}
176
7b52dc7f
DDM
177int specifier_uint64(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
178 const uint64_t *n = ASSERT_PTR(data);
179
180 return asprintf(ret, "%" PRIu64, *n) < 0 ? -ENOMEM : 0;
181}
182
de61a04b 183int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
d848b9cb 184 sd_id128_t id;
d848b9cb
ZJS
185 int r;
186
b6e78cdd
FS
187 assert(ret);
188
edd595f7
YW
189 r = id128_get_machine(root, &id);
190 if (r < 0) /* Translate error for missing /etc/machine-id file to EUNATCH. */
191 return r == -ENOENT ? -EUNATCH : r;
d848b9cb 192
74235f6d 193 return specifier_id128(specifier, &id, root, userdata, ret);
d848b9cb
ZJS
194}
195
de61a04b 196int specifier_boot_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
d848b9cb 197 sd_id128_t id;
d848b9cb
ZJS
198 int r;
199
b6e78cdd
FS
200 assert(ret);
201
d848b9cb
ZJS
202 r = sd_id128_get_boot(&id);
203 if (r < 0)
19f6d710 204 return r;
d848b9cb 205
74235f6d 206 return specifier_id128(specifier, &id, root, userdata, ret);
d848b9cb
ZJS
207}
208
9a5893e9 209int specifier_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
19f6d710
LP
210 char *n;
211
b6e78cdd
FS
212 assert(ret);
213
19f6d710
LP
214 n = gethostname_malloc();
215 if (!n)
216 return -ENOMEM;
217
218 *ret = n;
219 return 0;
d848b9cb 220}
6aaa8c2f 221
9a5893e9 222int specifier_short_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
e97708fa
ZJS
223 char *n;
224
b6e78cdd
FS
225 assert(ret);
226
e97708fa
ZJS
227 n = gethostname_short_malloc();
228 if (!n)
229 return -ENOMEM;
230
231 *ret = n;
232 return 0;
233}
234
9a5893e9 235int specifier_pretty_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
6ceb0a40
FS
236 char *n = NULL;
237
238 assert(ret);
239
240 if (get_pretty_hostname(&n) < 0) {
241 n = gethostname_short_malloc();
242 if (!n)
243 return -ENOMEM;
244 }
245
246 *ret = n;
247 return 0;
248}
249
de61a04b 250int specifier_kernel_release(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
6aaa8c2f 251 struct utsname uts;
19f6d710 252 char *n;
6aaa8c2f 253
b6e78cdd
FS
254 assert(ret);
255
01c69460 256 if (uname(&uts) < 0)
19f6d710
LP
257 return -errno;
258
259 n = strdup(uts.release);
260 if (!n)
261 return -ENOMEM;
6aaa8c2f 262
19f6d710
LP
263 *ret = n;
264 return 0;
6aaa8c2f 265}
e82f30d1 266
de61a04b 267int specifier_architecture(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
268f5a54
LP
268 char *t;
269
b6e78cdd
FS
270 assert(ret);
271
268f5a54
LP
272 t = strdup(architecture_to_string(uname_architecture()));
273 if (!t)
274 return -ENOMEM;
275
276 *ret = t;
277 return 0;
278}
279
01c69460 280/* Note: fields in /etc/os-release might quite possibly be missing, even if everything is entirely valid
7962116f 281 * otherwise. We'll return an empty value or NULL in that case from the functions below. But if the
6ec4c852 282 * os-release file is missing, we'll return -EUNATCH. This means that something is seriously wrong with the
7962116f 283 * installation. */
268f5a54 284
6ec4c852 285static int parse_os_release_specifier(const char *root, const char *id, char **ret) {
6c13a39a 286 _cleanup_free_ char *v = NULL;
6ec4c852
ZJS
287 int r;
288
b6e78cdd 289 assert(ret);
6ec4c852 290
1c7ec2d2
ZJS
291 r = parse_os_release(root, id, &v);
292 if (r >= 0)
293 /* parse_os_release() calls parse_env_file() which only sets the return value for
294 * entries found. Let's make sure we set the return value in all cases. */
6c13a39a 295 *ret = TAKE_PTR(v);
1c7ec2d2 296
6ec4c852 297 /* Translate error for missing os-release file to EUNATCH. */
6ec4c852
ZJS
298 return r == -ENOENT ? -EUNATCH : r;
299}
300
301int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
302 return parse_os_release_specifier(root, "ID", ret);
268f5a54
LP
303}
304
de61a04b 305int specifier_os_version_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
6ec4c852 306 return parse_os_release_specifier(root, "VERSION_ID", ret);
268f5a54
LP
307}
308
de61a04b 309int specifier_os_build_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
6ec4c852 310 return parse_os_release_specifier(root, "BUILD_ID", ret);
268f5a54
LP
311}
312
de61a04b 313int specifier_os_variant_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
6ec4c852 314 return parse_os_release_specifier(root, "VARIANT_ID", ret);
268f5a54
LP
315}
316
de61a04b 317int specifier_os_image_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
6ec4c852 318 return parse_os_release_specifier(root, "IMAGE_ID", ret);
9a515f0a
LP
319}
320
de61a04b 321int specifier_os_image_version(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
6ec4c852 322 return parse_os_release_specifier(root, "IMAGE_VERSION", ret);
9a515f0a
LP
323}
324
de61a04b 325int specifier_group_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
4870133b 326 RuntimeScope scope = PTR_TO_INT(data);
b75f0c69
DC
327 char *t;
328
b6e78cdd
FS
329 assert(ret);
330
4870133b 331 if (scope == RUNTIME_SCOPE_GLOBAL)
172e9cc3
ZJS
332 return -EINVAL;
333
4870133b 334 t = gid_to_name(scope == RUNTIME_SCOPE_USER ? getgid() : 0);
b75f0c69
DC
335 if (!t)
336 return -ENOMEM;
337
338 *ret = t;
339 return 0;
340}
341
de61a04b 342int specifier_group_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
4870133b 343 RuntimeScope scope = PTR_TO_INT(data);
172e9cc3
ZJS
344 gid_t gid;
345
b6e78cdd
FS
346 assert(ret);
347
4870133b 348 if (scope == RUNTIME_SCOPE_GLOBAL)
172e9cc3
ZJS
349 return -EINVAL;
350
4870133b 351 gid = scope == RUNTIME_SCOPE_USER ? getgid() : 0;
172e9cc3
ZJS
352
353 if (asprintf(ret, UID_FMT, gid) < 0)
b75f0c69
DC
354 return -ENOMEM;
355
356 return 0;
357}
358
de61a04b 359int specifier_user_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
4870133b 360 RuntimeScope scope = PTR_TO_INT(data);
172e9cc3 361 uid_t uid;
36444d22
LP
362 char *t;
363
b6e78cdd
FS
364 assert(ret);
365
4870133b 366 if (scope == RUNTIME_SCOPE_GLOBAL)
172e9cc3
ZJS
367 return -EINVAL;
368
4870133b 369 uid = scope == RUNTIME_SCOPE_USER ? getuid() : 0;
36444d22 370
172e9cc3
ZJS
371 /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want
372 * to be able to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed.
373
374 * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain
375 * consistent with specifer_user_id() below.
36444d22
LP
376 */
377
172e9cc3 378 t = uid_to_name(uid);
36444d22
LP
379 if (!t)
380 return -ENOMEM;
381
382 *ret = t;
383 return 0;
384}
385
de61a04b 386int specifier_user_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
4870133b 387 RuntimeScope scope = PTR_TO_INT(data);
172e9cc3
ZJS
388 uid_t uid;
389
b6e78cdd
FS
390 assert(ret);
391
4870133b 392 if (scope == RUNTIME_SCOPE_GLOBAL)
172e9cc3
ZJS
393 return -EINVAL;
394
4870133b 395 uid = scope == RUNTIME_SCOPE_USER ? getuid() : 0;
172e9cc3
ZJS
396
397 if (asprintf(ret, UID_FMT, uid) < 0)
36444d22
LP
398 return -ENOMEM;
399
400 return 0;
401}
402
de61a04b 403int specifier_user_home(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
b6e78cdd 404 assert(ret);
36444d22
LP
405
406 /* On PID 1 (which runs as root) this will not result in NSS,
407 * which is good. See above */
408
409 return get_home_dir(ret);
410}
411
de61a04b 412int specifier_user_shell(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
b6e78cdd 413 assert(ret);
36444d22
LP
414
415 /* On PID 1 (which runs as root) this will not result in NSS,
416 * which is good. See above */
417
418 return get_shell(ret);
419}
420
de61a04b 421int specifier_tmp_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
b294e594
LP
422 const char *p;
423 char *copy;
424 int r;
425
b6e78cdd
FS
426 assert(ret);
427
de61a04b
LP
428 if (root) /* If root dir is set, don't honour $TMP or similar */
429 p = "/tmp";
430 else {
431 r = tmp_dir(&p);
432 if (r < 0)
433 return r;
434 }
b294e594
LP
435 copy = strdup(p);
436 if (!copy)
437 return -ENOMEM;
438
439 *ret = copy;
440 return 0;
441}
442
de61a04b 443int specifier_var_tmp_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
b294e594
LP
444 const char *p;
445 char *copy;
446 int r;
447
b6e78cdd
FS
448 assert(ret);
449
de61a04b
LP
450 if (root)
451 p = "/var/tmp";
452 else {
453 r = var_tmp_dir(&p);
454 if (r < 0)
455 return r;
456 }
b294e594
LP
457 copy = strdup(p);
458 if (!copy)
459 return -ENOMEM;
460
461 *ret = copy;
462 return 0;
463}
464
e82f30d1 465int specifier_escape_strv(char **l, char ***ret) {
1cc3a130
DT
466 _cleanup_strv_free_ char **z = NULL;
467 char **p, **q;
e82f30d1
LP
468
469 assert(ret);
470
471 if (strv_isempty(l)) {
472 *ret = NULL;
473 return 0;
474 }
475
476 z = new(char*, strv_length(l)+1);
477 if (!z)
478 return -ENOMEM;
479
480 for (p = l, q = z; *p; p++, q++) {
481
482 *q = specifier_escape(*p);
1cc3a130 483 if (!*q)
e82f30d1 484 return -ENOMEM;
e82f30d1
LP
485 }
486
487 *q = NULL;
1cc3a130 488 *ret = TAKE_PTR(z);
e82f30d1
LP
489
490 return 0;
491}
2caed041
LP
492
493const Specifier system_and_tmp_specifier_table[] = {
494 COMMON_SYSTEM_SPECIFIERS,
495 COMMON_TMP_SPECIFIERS,
496 {}
497};