]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/creds/creds.c
strv: make iterator in STRV_FOREACH() declaread in the loop
[thirdparty/systemd.git] / src / creds / creds.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4 #include <unistd.h>
5
6 #include "creds-util.h"
7 #include "dirent-util.h"
8 #include "escape.h"
9 #include "fileio.h"
10 #include "format-table.h"
11 #include "hexdecoct.h"
12 #include "io-util.h"
13 #include "json.h"
14 #include "main-func.h"
15 #include "memory-util.h"
16 #include "missing_magic.h"
17 #include "pager.h"
18 #include "parse-argument.h"
19 #include "pretty-print.h"
20 #include "process-util.h"
21 #include "stat-util.h"
22 #include "string-table.h"
23 #include "terminal-util.h"
24 #include "tpm2-util.h"
25 #include "verbs.h"
26
27 typedef enum TranscodeMode {
28 TRANSCODE_OFF,
29 TRANSCODE_BASE64,
30 TRANSCODE_UNBASE64,
31 TRANSCODE_HEX,
32 TRANSCODE_UNHEX,
33 _TRANSCODE_MAX,
34 _TRANSCODE_INVALID = -EINVAL,
35 } TranscodeMode;
36
37 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
38 static PagerFlags arg_pager_flags = 0;
39 static bool arg_legend = true;
40 static bool arg_system = false;
41 static TranscodeMode arg_transcode = TRANSCODE_OFF;
42 static int arg_newline = -1;
43 static sd_id128_t arg_with_key = SD_ID128_NULL;
44 static const char *arg_tpm2_device = NULL;
45 static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
46 static const char *arg_name = NULL;
47 static bool arg_name_any = false;
48 static usec_t arg_timestamp = USEC_INFINITY;
49 static usec_t arg_not_after = USEC_INFINITY;
50 static bool arg_pretty = false;
51
52 static const char* transcode_mode_table[_TRANSCODE_MAX] = {
53 [TRANSCODE_OFF] = "off",
54 [TRANSCODE_BASE64] = "base64",
55 [TRANSCODE_UNBASE64] = "unbase64",
56 [TRANSCODE_HEX] = "hex",
57 [TRANSCODE_UNHEX] = "unhex",
58 };
59
60 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(transcode_mode, TranscodeMode);
61
62 static int open_credential_directory(DIR **ret) {
63 _cleanup_free_ char *j = NULL;
64 const char *p;
65 DIR *d;
66 int r;
67
68 if (arg_system) {
69 _cleanup_free_ char *cd = NULL;
70
71 r = getenv_for_pid(1, "CREDENTIALS_DIRECTORY", &cd);
72 if (r < 0)
73 return r;
74 if (!cd)
75 return -ENXIO;
76
77 if (!path_is_absolute(cd) || !path_is_normalized(cd))
78 return -EINVAL;
79
80 j = path_join("/proc/1/root", cd);
81 if (!j)
82 return -ENOMEM;
83
84 p = j;
85 } else {
86 r = get_credentials_dir(&p);
87 if (r < 0)
88 return r;
89 }
90
91 d = opendir(p);
92 if (!d)
93 return -errno;
94
95 *ret = d;
96 return 0;
97 }
98
99 static int verb_list(int argc, char **argv, void *userdata) {
100 _cleanup_(table_unrefp) Table *t = NULL;
101 _cleanup_(closedirp) DIR *d = NULL;
102 int r;
103
104 r = open_credential_directory(&d);
105 if (r == -ENXIO)
106 return log_error_errno(r, "No credentials received. (i.e. $CREDENTIALS_PATH not set or pointing to empty directory.)");
107 if (r < 0)
108 return log_error_errno(r, "Failed to open credentials directory: %m");
109
110 t = table_new("name", "secure", "size");
111 if (!t)
112 return log_oom();
113
114 (void) table_set_align_percent(t, table_get_cell(t, 0, 2), 100);
115
116 for (;;) {
117 _cleanup_close_ int fd = -1;
118 const char *secure, *secure_color = NULL;
119 struct dirent *de;
120 struct stat st;
121
122 errno = 0;
123 de = readdir_no_dot(d);
124 if (!de) {
125 if (errno == 0)
126 break;
127
128 return log_error_errno(errno, "Failed to read credentials directory: %m");
129 }
130
131 if (!IN_SET(de->d_type, DT_REG, DT_UNKNOWN))
132 continue;
133
134 if (!credential_name_valid(de->d_name))
135 continue;
136
137 fd = openat(dirfd(d), de->d_name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
138 if (fd < 0) {
139 if (errno == ENOENT) /* Vanished by now? */
140 continue;
141
142 return log_error_errno(errno, "Failed to open credential '%s': %m", de->d_name);
143 }
144
145 if (fstat(fd, &st) < 0)
146 return log_error_errno(errno, "Failed to stat credential '%s': %m", de->d_name);
147
148 if (!S_ISREG(st.st_mode))
149 continue;
150
151 if ((st.st_mode & 0377) != 0) {
152 secure = "insecure"; /* Anything that is accessible more than read-only to its owner is insecure */
153 secure_color = ansi_highlight_red();
154 } else {
155 r = fd_is_fs_type(fd, RAMFS_MAGIC);
156 if (r < 0)
157 return log_error_errno(r, "Failed to determine backing file system of '%s': %m", de->d_name);
158
159 secure = r ? "secure" : "weak"; /* ramfs is not swappable, hence "secure", everything else is "weak" */
160 secure_color = r ? ansi_highlight_green() : ansi_highlight_yellow4();
161 }
162
163 r = table_add_many(
164 t,
165 TABLE_STRING, de->d_name,
166 TABLE_STRING, secure,
167 TABLE_SET_COLOR, secure_color,
168 TABLE_SIZE, (uint64_t) st.st_size);
169 if (r < 0)
170 return table_log_add_error(r);
171 }
172
173 if ((arg_json_format_flags & JSON_FORMAT_OFF) && table_get_rows(t) <= 1) {
174 log_info("No credentials");
175 return 0;
176 }
177
178 return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
179 }
180
181 static int transcode(
182 const void *input,
183 size_t input_size,
184 void **ret_output,
185 size_t *ret_output_size) {
186
187 int r;
188
189 assert(input);
190 assert(input_size);
191 assert(ret_output);
192 assert(ret_output_size);
193
194 switch (arg_transcode) {
195
196 case TRANSCODE_BASE64: {
197 char *buf;
198 ssize_t l;
199
200 l = base64mem_full(input, input_size, 79, &buf);
201 if (l < 0)
202 return l;
203
204 *ret_output = buf;
205 *ret_output_size = l;
206 return 0;
207 }
208
209 case TRANSCODE_UNBASE64:
210 r = unbase64mem_full(input, input_size, true, ret_output, ret_output_size);
211 if (r == -EPIPE) /* Uneven number of chars */
212 return -EINVAL;
213
214 return r;
215
216 case TRANSCODE_HEX: {
217 char *buf;
218
219 buf = hexmem(input, input_size);
220 if (!buf)
221 return -ENOMEM;
222
223 *ret_output = buf;
224 *ret_output_size = input_size * 2;
225 return 0;
226 }
227
228 case TRANSCODE_UNHEX:
229 r = unhexmem_full(input, input_size, true, ret_output, ret_output_size);
230 if (r == -EPIPE) /* Uneven number of chars */
231 return -EINVAL;
232
233 return r;
234
235 default:
236 assert_not_reached();
237 }
238 }
239
240 static int print_newline(FILE *f, const char *data, size_t l) {
241 int fd;
242
243 assert(f);
244 assert(data || l == 0);
245
246 /* If turned off explicitly, don't print newline */
247 if (arg_newline == 0)
248 return 0;
249
250 /* If data already has newline, don't print either */
251 if (l > 0 && data[l-1] == '\n')
252 return 0;
253
254 /* Don't bother unless this is a tty */
255 fd = fileno(f);
256 if (fd >= 0 && isatty(fd) <= 0)
257 return 0;
258
259 if (fputc('\n', f) != '\n')
260 return log_error_errno(errno, "Failed to write trailing newline: %m");
261
262 return 1;
263 }
264
265 static int write_blob(FILE *f, const void *data, size_t size) {
266 _cleanup_(erase_and_freep) void *transcoded = NULL;
267 int r;
268
269 if (arg_transcode == TRANSCODE_OFF &&
270 arg_json_format_flags != JSON_FORMAT_OFF) {
271
272 _cleanup_(erase_and_freep) char *suffixed = NULL;
273 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
274
275 if (memchr(data, 0, size))
276 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Credential data contains embedded NUL, can't parse as JSON.");
277
278 suffixed = memdup_suffix0(data, size);
279 if (!suffixed)
280 return log_oom();
281
282 r = json_parse(suffixed, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
283 if (r < 0)
284 return log_error_errno(r, "Failed to parse JSON: %m");
285
286 json_variant_dump(v, arg_json_format_flags, f, NULL);
287 return 0;
288 }
289
290 if (arg_transcode != TRANSCODE_OFF) {
291 r = transcode(data, size, &transcoded, &size);
292 if (r < 0)
293 return log_error_errno(r, "Failed to transcode data: %m");
294
295 data = transcoded;
296 }
297
298 if (fwrite(data, 1, size, f) != size)
299 return log_error_errno(errno, "Failed to write credential data: %m");
300
301 r = print_newline(f, data, size);
302 if (r < 0)
303 return r;
304
305 if (fflush(f) != 0)
306 return log_error_errno(errno, "Failed to flush output: %m");
307
308 return 0;
309 }
310
311 static int verb_cat(int argc, char **argv, void *userdata) {
312 _cleanup_(closedirp) DIR *d = NULL;
313 int r, ret = 0;
314
315 r = open_credential_directory(&d);
316 if (r == -ENXIO)
317 return log_error_errno(r, "No credentials passed.");
318 if (r < 0)
319 return log_error_errno(r, "Failed to open credentials directory: %m");
320
321 STRV_FOREACH(cn, strv_skip(argv, 1)) {
322 _cleanup_(erase_and_freep) void *data = NULL;
323 size_t size = 0;
324
325 if (!credential_name_valid(*cn)) {
326 log_error("Credential name '%s' is not valid.", *cn);
327 if (ret >= 0)
328 ret = -EINVAL;
329 continue;
330 }
331
332 r = read_full_file_full(
333 dirfd(d), *cn,
334 UINT64_MAX, SIZE_MAX,
335 READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE,
336 NULL,
337 (char**) &data, &size);
338 if (r < 0) {
339 log_error_errno(r, "Failed to read credential '%s': %m", *cn);
340 if (ret >= 0)
341 ret = r;
342 continue;
343 }
344
345 r = write_blob(stdout, data, size);
346 if (r < 0)
347 return r;
348 }
349
350 return ret;
351 }
352
353 static int verb_encrypt(int argc, char **argv, void *userdata) {
354 _cleanup_free_ char *base64_buf = NULL, *fname = NULL;
355 _cleanup_(erase_and_freep) char *plaintext = NULL;
356 const char *input_path, *output_path, *name;
357 _cleanup_free_ void *output = NULL;
358 size_t plaintext_size, output_size;
359 ssize_t base64_size;
360 usec_t timestamp;
361 int r;
362
363 assert(argc == 3);
364
365 input_path = empty_or_dash(argv[1]) ? NULL : argv[1];
366
367 if (input_path)
368 r = read_full_file_full(AT_FDCWD, input_path, UINT64_MAX, CREDENTIAL_SIZE_MAX, READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, &plaintext, &plaintext_size);
369 else
370 r = read_full_stream_full(stdin, NULL, UINT64_MAX, CREDENTIAL_SIZE_MAX, READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER, &plaintext, &plaintext_size);
371 if (r == -E2BIG)
372 return log_error_errno(r, "Plaintext too long for credential (allowed size: %zu).", (size_t) CREDENTIAL_SIZE_MAX);
373 if (r < 0)
374 return log_error_errno(r, "Failed to read plaintext: %m");
375
376 output_path = empty_or_dash(argv[2]) ? NULL : argv[2];
377
378 if (arg_name_any)
379 name = NULL;
380 else if (arg_name)
381 name = arg_name;
382 else if (output_path) {
383 r = path_extract_filename(output_path, &fname);
384 if (r < 0)
385 return log_error_errno(r, "Failed to extract filename from '%s': %m", output_path);
386 if (r == O_DIRECTORY)
387 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Path '%s' refers to directory, refusing.", output_path);
388
389 name = fname;
390 } else {
391 log_warning("No credential name specified, not embedding credential name in encrypted data. (Disable this warning with --name=)");
392 name = NULL;
393 }
394
395 timestamp = arg_timestamp != USEC_INFINITY ? arg_timestamp : now(CLOCK_REALTIME);
396
397 if (arg_not_after != USEC_INFINITY && arg_not_after < timestamp)
398 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential is invalidated before it is valid.");
399
400 r = encrypt_credential_and_warn(
401 arg_with_key,
402 name,
403 timestamp,
404 arg_not_after,
405 arg_tpm2_device,
406 arg_tpm2_pcr_mask,
407 plaintext, plaintext_size,
408 &output, &output_size);
409 if (r < 0)
410 return r;
411
412 base64_size = base64mem_full(output, output_size, arg_pretty ? 69 : 79, &base64_buf);
413 if (base64_size < 0)
414 return base64_size;
415
416 if (arg_pretty) {
417 _cleanup_free_ char *escaped = NULL, *indented = NULL, *j = NULL;
418
419 if (name) {
420 escaped = cescape(name);
421 if (!escaped)
422 return log_oom();
423 }
424
425 indented = strreplace(base64_buf, "\n", " \\\n ");
426 if (!indented)
427 return log_oom();
428
429 j = strjoin("SetCredentialEncrypted=", name, ": \\\n ", indented, "\n");
430 if (!j)
431 return log_oom();
432
433 free_and_replace(base64_buf, j);
434 }
435
436 if (output_path)
437 r = write_string_file(output_path, base64_buf, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE);
438 else
439 r = write_string_stream(stdout, base64_buf, 0);
440 if (r < 0)
441 return log_error_errno(r, "Failed to write result: %m");
442
443 return EXIT_SUCCESS;
444 }
445
446 static int verb_decrypt(int argc, char **argv, void *userdata) {
447 _cleanup_(erase_and_freep) void *plaintext = NULL;
448 _cleanup_free_ char *input = NULL, *fname = NULL;
449 _cleanup_fclose_ FILE *output_file = NULL;
450 const char *input_path, *output_path, *name;
451 size_t input_size, plaintext_size;
452 usec_t timestamp;
453 FILE *f;
454 int r;
455
456 assert(IN_SET(argc, 2, 3));
457
458 input_path = empty_or_dash(argv[1]) ? NULL : argv[1];
459
460 if (input_path)
461 r = read_full_file_full(AT_FDCWD, argv[1], UINT64_MAX, CREDENTIAL_ENCRYPTED_SIZE_MAX, READ_FULL_FILE_UNBASE64|READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, &input, &input_size);
462 else
463 r = read_full_stream_full(stdin, NULL, UINT64_MAX, CREDENTIAL_ENCRYPTED_SIZE_MAX, READ_FULL_FILE_UNBASE64|READ_FULL_FILE_FAIL_WHEN_LARGER, &input, &input_size);
464 if (r == -E2BIG)
465 return log_error_errno(r, "Data too long for encrypted credential (allowed size: %zu).", (size_t) CREDENTIAL_ENCRYPTED_SIZE_MAX);
466 if (r < 0)
467 return log_error_errno(r, "Failed to read encrypted credential data: %m");
468
469 output_path = (argc < 3 || isempty(argv[2]) || streq(argv[2], "-")) ? NULL : argv[2];
470
471 if (arg_name_any)
472 name = NULL;
473 else if (arg_name)
474 name = arg_name;
475 else if (input_path) {
476 r = path_extract_filename(input_path, &fname);
477 if (r < 0)
478 return log_error_errno(r, "Failed to extract filename from '%s': %m", input_path);
479 if (r == O_DIRECTORY)
480 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Path '%s' refers to directory, refusing.", input_path);
481
482 name = fname;
483 } else {
484 log_warning("No credential name specified, not validating credential name embedded in encrypted data. (Disable this warning with --name=.)");
485 name = NULL;
486 }
487
488 timestamp = arg_timestamp != USEC_INFINITY ? arg_timestamp : now(CLOCK_REALTIME);
489
490 r = decrypt_credential_and_warn(
491 name,
492 timestamp,
493 arg_tpm2_device,
494 input, input_size,
495 &plaintext, &plaintext_size);
496 if (r < 0)
497 return r;
498
499 if (output_path) {
500 output_file = fopen(output_path, "we");
501 if (!output_file)
502 return log_error_errno(errno, "Failed to create output file '%s': %m", output_path);
503
504 f = output_file;
505 } else
506 f = stdout;
507
508 r = write_blob(f, plaintext, plaintext_size);
509 if (r < 0)
510 return r;
511
512 return EXIT_SUCCESS;
513 }
514
515 static int verb_setup(int argc, char **argv, void *userdata) {
516 size_t size;
517 int r;
518
519 r = get_credential_host_secret(CREDENTIAL_SECRET_GENERATE|CREDENTIAL_SECRET_WARN_NOT_ENCRYPTED, NULL, &size);
520 if (r < 0)
521 return log_error_errno(r, "Failed to setup credentials host key: %m");
522
523 log_info("%zu byte credentials host key set up.", size);
524
525 return EXIT_SUCCESS;
526 }
527
528 static int verb_help(int argc, char **argv, void *userdata) {
529 _cleanup_free_ char *link = NULL;
530 int r;
531
532 r = terminal_urlify_man("systemd-creds", "1", &link);
533 if (r < 0)
534 return log_oom();
535
536 printf("%1$s [OPTIONS...] COMMAND ...\n"
537 "\n%5$sDisplay and Process Credentials.%6$s\n"
538 "\n%3$sCommands:%4$s\n"
539 " list Show installed and available versions\n"
540 " cat CREDENTIAL... Show specified credentials\n"
541 " setup Generate credentials host key, if not existing yet\n"
542 " encrypt INPUT OUTPUT Encrypt plaintext credential file and write to\n"
543 " ciphertext credential file\n"
544 " decrypt INPUT [OUTPUT] Decrypt ciphertext credential file and write to\n"
545 " plaintext credential file\n"
546 " -h --help Show this help\n"
547 " --version Show package version\n"
548 "\n%3$sOptions:%4$s\n"
549 " --no-pager Do not pipe output into a pager\n"
550 " --no-legend Do not show the headers and footers\n"
551 " --json=pretty|short|off\n"
552 " Generate JSON output\n"
553 " --system Show credentials passed to system\n"
554 " --transcode=base64|unbase64|hex|unhex\n"
555 " Transcode credential data\n"
556 " --newline=auto|yes|no\n"
557 " Suffix output with newline\n"
558 " -p --pretty Output as SetCredentialEncrypted= line\n"
559 " --name=NAME Override filename included in encrypted credential\n"
560 " --timestamp=TIME Include specified timestamp in encrypted credential\n"
561 " --not-after=TIME Include specified invalidation time in encrypted\n"
562 " credential\n"
563 " --with-key=host|tpm2|host+tpm2|auto\n"
564 " Which keys to encrypt with\n"
565 " -H Shortcut for --with-key=host\n"
566 " -T Shortcut for --with-key=tpm2\n"
567 " --tpm2-device=PATH\n"
568 " Pick TPM2 device\n"
569 " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
570 " Specify TPM2 PCRs to seal against\n"
571 "\nSee the %2$s for details.\n"
572 , program_invocation_short_name
573 , link
574 , ansi_underline(), ansi_normal()
575 , ansi_highlight(), ansi_normal()
576 );
577
578 return 0;
579 }
580
581 static int parse_argv(int argc, char *argv[]) {
582
583 enum {
584 ARG_VERSION = 0x100,
585 ARG_NO_PAGER,
586 ARG_NO_LEGEND,
587 ARG_JSON,
588 ARG_SYSTEM,
589 ARG_TRANSCODE,
590 ARG_NEWLINE,
591 ARG_WITH_KEY,
592 ARG_TPM2_DEVICE,
593 ARG_TPM2_PCRS,
594 ARG_NAME,
595 ARG_TIMESTAMP,
596 ARG_NOT_AFTER,
597 };
598
599 static const struct option options[] = {
600 { "help", no_argument, NULL, 'h' },
601 { "version", no_argument, NULL, ARG_VERSION },
602 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
603 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
604 { "json", required_argument, NULL, ARG_JSON },
605 { "system", no_argument, NULL, ARG_SYSTEM },
606 { "transcode", required_argument, NULL, ARG_TRANSCODE },
607 { "newline", required_argument, NULL, ARG_NEWLINE },
608 { "pretty", no_argument, NULL, 'p' },
609 { "with-key", required_argument, NULL, ARG_WITH_KEY },
610 { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
611 { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
612 { "name", required_argument, NULL, ARG_NAME },
613 { "timestamp", required_argument, NULL, ARG_TIMESTAMP },
614 { "not-after", required_argument, NULL, ARG_NOT_AFTER },
615 {}
616 };
617
618 int c, r;
619
620 assert(argc >= 0);
621 assert(argv);
622
623 while ((c = getopt_long(argc, argv, "hHTp", options, NULL)) >= 0) {
624
625 switch (c) {
626
627 case 'h':
628 return verb_help(0, NULL, NULL);
629
630 case ARG_VERSION:
631 return version();
632
633 case ARG_NO_PAGER:
634 arg_pager_flags |= PAGER_DISABLE;
635 break;
636
637 case ARG_NO_LEGEND:
638 arg_legend = false;
639 break;
640
641 case ARG_JSON:
642 r = parse_json_argument(optarg, &arg_json_format_flags);
643 if (r <= 0)
644 return r;
645
646 break;
647
648 case ARG_SYSTEM:
649 arg_system = true;
650 break;
651
652 case ARG_TRANSCODE:
653 if (parse_boolean(optarg) == 0) /* If specified as "false", turn transcoding off */
654 arg_transcode = TRANSCODE_OFF;
655 else {
656 TranscodeMode m;
657
658 m = transcode_mode_from_string(optarg);
659 if (m < 0)
660 return log_error_errno(m, "Failed to parse transcode mode: %m");
661
662 arg_transcode = m;
663 }
664
665 break;
666
667 case ARG_NEWLINE:
668 if (isempty(optarg) || streq(optarg, "auto"))
669 arg_newline = -1;
670 else {
671 bool b;
672
673 r = parse_boolean_argument("--newline=", optarg, &b);
674 if (r < 0)
675 return r;
676
677 arg_newline = b;
678 }
679 break;
680
681 case 'p':
682 arg_pretty = true;
683 break;
684
685 case ARG_WITH_KEY:
686 if (isempty(optarg) || streq(optarg, "auto"))
687 arg_with_key = SD_ID128_NULL;
688 else if (streq(optarg, "host"))
689 arg_with_key = CRED_AES256_GCM_BY_HOST;
690 else if (streq(optarg, "tpm2"))
691 arg_with_key = CRED_AES256_GCM_BY_TPM2_HMAC;
692 else if (STR_IN_SET(optarg, "host+tpm2", "tpm2+host"))
693 arg_with_key = CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC;
694 else
695 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown key type: %s", optarg);
696
697 break;
698
699 case 'H':
700 arg_with_key = CRED_AES256_GCM_BY_HOST;
701 break;
702
703 case 'T':
704 arg_with_key = CRED_AES256_GCM_BY_TPM2_HMAC;
705 break;
706
707 case ARG_TPM2_DEVICE:
708 if (streq(optarg, "list"))
709 return tpm2_list_devices();
710
711 arg_tpm2_device = streq(optarg, "auto") ? NULL : optarg;
712 break;
713
714 case ARG_TPM2_PCRS:
715 if (isempty(optarg)) {
716 arg_tpm2_pcr_mask = 0;
717 break;
718 }
719
720 uint32_t mask;
721 r = tpm2_parse_pcrs(optarg, &mask);
722 if (r < 0)
723 return r;
724
725 if (arg_tpm2_pcr_mask == UINT32_MAX)
726 arg_tpm2_pcr_mask = mask;
727 else
728 arg_tpm2_pcr_mask |= mask;
729
730 break;
731
732 case ARG_NAME:
733 if (isempty(optarg)) {
734 arg_name = NULL;
735 arg_name_any = true;
736 break;
737 }
738
739 if (!credential_name_valid(optarg))
740 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid credential name: %s", optarg);
741
742 arg_name = optarg;
743 arg_name_any = false;
744 break;
745
746 case ARG_TIMESTAMP:
747 r = parse_timestamp(optarg, &arg_timestamp);
748 if (r < 0)
749 return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
750
751 break;
752
753 case ARG_NOT_AFTER:
754 r = parse_timestamp(optarg, &arg_not_after);
755 if (r < 0)
756 return log_error_errno(r, "Failed to parse --not-after= timestamp: %s", optarg);
757
758 break;
759
760 case '?':
761 return -EINVAL;
762
763 default:
764 assert_not_reached();
765 }
766 }
767
768 if (arg_tpm2_pcr_mask == UINT32_MAX)
769 arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
770
771 return 1;
772 }
773
774 static int creds_main(int argc, char *argv[]) {
775
776 static const Verb verbs[] = {
777 { "list", VERB_ANY, 1, VERB_DEFAULT, verb_list },
778 { "cat", 2, VERB_ANY, 0, verb_cat },
779 { "encrypt", 3, 3, 0, verb_encrypt },
780 { "decrypt", 2, 3, 0, verb_decrypt },
781 { "setup", VERB_ANY, 1, 0, verb_setup },
782 { "help", VERB_ANY, 1, 0, verb_help },
783 {}
784 };
785
786 return dispatch_verb(argc, argv, verbs, NULL);
787 }
788
789 static int run(int argc, char *argv[]) {
790 int r;
791
792 log_setup();
793
794 r = parse_argv(argc, argv);
795 if (r <= 0)
796 return r;
797
798 return creds_main(argc, argv);
799 }
800
801 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);