]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/creds/creds.c
creds: add a new tool for listing/showing/encrypting/decrypting credentials
[thirdparty/systemd.git] / src / creds / creds.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4
5 #include "creds-util.h"
6 #include "dirent-util.h"
7 #include "escape.h"
8 #include "fileio.h"
9 #include "format-table.h"
10 #include "hexdecoct.h"
11 #include "io-util.h"
12 #include "json.h"
13 #include "main-func.h"
14 #include "memory-util.h"
15 #include "missing_magic.h"
16 #include "pager.h"
17 #include "parse-argument.h"
18 #include "pretty-print.h"
19 #include "process-util.h"
20 #include "stat-util.h"
21 #include "string-table.h"
22 #include "terminal-util.h"
23 #include "tpm2-util.h"
24 #include "verbs.h"
25
26 typedef enum TranscodeMode {
27 TRANSCODE_OFF,
28 TRANSCODE_BASE64,
29 TRANSCODE_UNBASE64,
30 TRANSCODE_HEX,
31 TRANSCODE_UNHEX,
32 _TRANSCODE_MAX,
33 _TRANSCODE_INVALID = -EINVAL,
34 } TranscodeMode;
35
36 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
37 static PagerFlags arg_pager_flags = 0;
38 static bool arg_legend = true;
39 static bool arg_system = false;
40 static TranscodeMode arg_transcode = TRANSCODE_OFF;
41 static int arg_newline = -1;
42 static sd_id128_t arg_with_key = SD_ID128_NULL;
43 static const char *arg_tpm2_device = NULL;
44 static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
45 static const char *arg_name = NULL;
46 static bool arg_name_any = false;
47 static usec_t arg_timestamp = USEC_INFINITY;
48 static usec_t arg_not_after = USEC_INFINITY;
49 static bool arg_pretty = false;
50
51 static const char* transcode_mode_table[_TRANSCODE_MAX] = {
52 [TRANSCODE_OFF] = "off",
53 [TRANSCODE_BASE64] = "base64",
54 [TRANSCODE_UNBASE64] = "unbase64",
55 [TRANSCODE_HEX] = "hex",
56 [TRANSCODE_UNHEX] = "unhex",
57 };
58
59 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(transcode_mode, TranscodeMode);
60
61 static int open_credential_directory(DIR **ret) {
62 _cleanup_free_ char *j = NULL;
63 const char *p;
64 DIR *d;
65 int r;
66
67 if (arg_system) {
68 _cleanup_free_ char *cd = NULL;
69
70 r = getenv_for_pid(1, "CREDENTIALS_DIRECTORY", &cd);
71 if (r < 0)
72 return r;
73 if (!cd)
74 return -ENXIO;
75
76 if (!path_is_absolute(cd) || !path_is_normalized(cd))
77 return -EINVAL;
78
79 j = path_join("/proc/1/root", cd);
80 if (!j)
81 return -ENOMEM;
82
83 p = j;
84 } else {
85 r = get_credentials_dir(&p);
86 if (r < 0)
87 return r;
88 }
89
90 d = opendir(p);
91 if (!d)
92 return -errno;
93
94 *ret = d;
95 return 0;
96 }
97
98 static int verb_list(int argc, char **argv, void *userdata) {
99 _cleanup_(table_unrefp) Table *t = NULL;
100 _cleanup_(closedirp) DIR *d = NULL;
101 int r;
102
103 r = open_credential_directory(&d);
104 if (r == -ENXIO)
105 return log_error_errno(r, "No credentials received. (i.e. $CREDENTIALS_PATH not set or pointing to empty directory.)");
106 if (r < 0)
107 return log_error_errno(r, "Failed to open credentials directory: %m");
108
109 t = table_new("name", "secure", "size");
110 if (!t)
111 return log_oom();
112
113 (void) table_set_align_percent(t, table_get_cell(t, 0, 2), 100);
114
115 for (;;) {
116 _cleanup_close_ int fd = -1;
117 const char *secure, *secure_color = NULL;
118 struct dirent *de;
119 struct stat st;
120
121 errno = 0;
122 de = readdir_no_dot(d);
123 if (!de) {
124 if (errno == 0)
125 break;
126
127 return log_error_errno(errno, "Failed to read credentials directory: %m");
128 }
129
130 if (!IN_SET(de->d_type, DT_REG, DT_UNKNOWN))
131 continue;
132
133 if (!credential_name_valid(de->d_name))
134 continue;
135
136 fd = openat(dirfd(d), de->d_name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
137 if (fd < 0) {
138 if (errno == ENOENT) /* Vanished by now? */
139 continue;
140
141 return log_error_errno(errno, "Failed to open credential '%s': %m", de->d_name);
142 }
143
144 if (fstat(fd, &st) < 0)
145 return log_error_errno(errno, "Failed to stat credential '%s': %m", de->d_name);
146
147 if (!S_ISREG(st.st_mode))
148 continue;
149
150 if ((st.st_mode & 0377) != 0) {
151 secure = "insecure"; /* Anything that is accessible more than read-only to its owner is insecure */
152 secure_color = ansi_highlight_red();
153 } else {
154 r = fd_is_fs_type(fd, RAMFS_MAGIC);
155 if (r < 0)
156 return log_error_errno(r, "Failed to determine backing file system of '%s': %m", de->d_name);
157
158 secure = r ? "secure" : "weak"; /* ramfs is not swappable, hence "secure", everything else is "weak" */
159 secure_color = r ? ansi_highlight_green() : ansi_highlight_yellow4();
160 }
161
162 r = table_add_many(
163 t,
164 TABLE_STRING, de->d_name,
165 TABLE_STRING, secure,
166 TABLE_SET_COLOR, secure_color,
167 TABLE_SIZE, (uint64_t) st.st_size);
168 if (r < 0)
169 return table_log_add_error(r);
170 }
171
172 if ((arg_json_format_flags & JSON_FORMAT_OFF) && table_get_rows(t) <= 1) {
173 log_info("No credentials");
174 return 0;
175 }
176
177 return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
178 }
179
180 static int transcode(
181 const void *input,
182 size_t input_size,
183 void **ret_output,
184 size_t *ret_output_size) {
185
186 int r;
187
188 assert(input);
189 assert(input_size);
190 assert(ret_output);
191 assert(ret_output_size);
192
193 switch (arg_transcode) {
194
195 case TRANSCODE_BASE64: {
196 char *buf = NULL;
197 ssize_t l;
198
199 l = base64mem_full(input, input_size, 79, &buf);
200 if (l < 0)
201 return l;
202
203 *ret_output = buf;
204 *ret_output_size = l;
205 return 0;
206 }
207
208 case TRANSCODE_UNBASE64:
209 r = unbase64mem_full(input, input_size, true, ret_output, ret_output_size);
210 if (r == -EPIPE) /* Uneven number of chars */
211 return -EINVAL;
212
213 return r;
214
215 case TRANSCODE_HEX: {
216 char *buf;
217
218 buf = hexmem(input, input_size);
219 if (!buf)
220 return -ENOMEM;
221
222 *ret_output = buf;
223 *ret_output_size = input_size * 2;
224 return 0;
225 }
226
227 case TRANSCODE_UNHEX:
228 r = unhexmem_full(input, input_size, true, ret_output, ret_output_size);
229 if (r == -EPIPE) /* Uneven number of chars */
230 return -EINVAL;
231
232 return r;
233
234 default:
235 assert_not_reached("Unexpected transcoding mode");
236 }
237 }
238
239 static int print_newline(FILE *f, const char *data, size_t l) {
240 int fd;
241
242 assert(f);
243 assert(data || l == 0);
244
245 /* If turned off explicitly, don't print newline */
246 if (arg_newline == 0)
247 return 0;
248
249 /* If data already has newline, don't print either */
250 if (l > 0 && data[l-1] == '\n')
251 return 0;
252
253 /* Don't bother unless this is a tty */
254 fd = fileno(f);
255 if (fd >= 0 && isatty(fd) <= 0)
256 return 0;
257
258 if (fputc('\n', f) != '\n')
259 return log_error_errno(errno, "Failed to write trailing newline: %m");
260
261 return 1;
262 }
263
264 static int write_blob(FILE *f, const void *data, size_t size) {
265 _cleanup_(erase_and_freep) void *transcoded = NULL;
266 int r;
267
268 if (arg_transcode == TRANSCODE_OFF &&
269 arg_json_format_flags != JSON_FORMAT_OFF) {
270
271 _cleanup_(erase_and_freep) char *suffixed = NULL;
272 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
273
274 if (memchr(data, 0, size))
275 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Credential data contains embedded NUL, can't parse as JSON.");
276
277 suffixed = memdup_suffix0(data, size);
278 if (!suffixed)
279 return log_oom();
280
281 r = json_parse(suffixed, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
282 if (r < 0)
283 return log_error_errno(r, "Failed to parse JSON: %m");
284
285 json_variant_dump(v, arg_json_format_flags, f, NULL);
286 return 0;
287 }
288
289 if (arg_transcode != TRANSCODE_OFF) {
290 r = transcode(data, size, &transcoded, &size);
291 if (r < 0)
292 return log_error_errno(r, "Failed to transcode data: %m");
293
294 data = transcoded;
295 }
296
297 if (fwrite(data, 1, size, f) != size)
298 return log_error_errno(errno, "Failed to write credential data: %m");
299
300 r = print_newline(f, data, size);
301 if (r < 0)
302 return r;
303
304 if (fflush(f) != 0)
305 return log_error_errno(errno, "Failed to flush output: %m");
306
307 return 0;
308 }
309
310 static int verb_cat(int argc, char **argv, void *userdata) {
311 _cleanup_(closedirp) DIR *d = NULL;
312 int r, ret = 0;
313 char **cn;
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 = (isempty(argv[1]) || streq(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 = (isempty(argv[2]) || streq(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 = (isempty(argv[1]) || streq(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 _cleanup_free_ char *device = NULL;
709
710 if (streq(optarg, "list"))
711 return tpm2_list_devices();
712
713 if (!streq(optarg, "auto")) {
714 device = strdup(optarg);
715 if (!device)
716 return log_oom();
717 }
718
719 arg_tpm2_device = TAKE_PTR(device);
720 break;
721 }
722
723 case ARG_TPM2_PCRS: {
724 uint32_t mask;
725
726 if (isempty(optarg)) {
727 arg_tpm2_pcr_mask = 0;
728 break;
729 }
730
731 r = tpm2_parse_pcrs(optarg, &mask);
732 if (r < 0)
733 return r;
734
735 if (arg_tpm2_pcr_mask == UINT32_MAX)
736 arg_tpm2_pcr_mask = mask;
737 else
738 arg_tpm2_pcr_mask |= mask;
739
740 break;
741 }
742
743 case ARG_NAME:
744 if (isempty(optarg)) {
745 arg_name = NULL;
746 arg_name_any = true;
747 break;
748 }
749
750 if (!credential_name_valid(optarg))
751 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid credential name: %s", optarg);
752
753 arg_name = optarg;
754 arg_name_any = false;
755 break;
756
757 case ARG_TIMESTAMP:
758 r = parse_timestamp(optarg, &arg_timestamp);
759 if (r < 0)
760 return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
761
762 break;
763
764 case ARG_NOT_AFTER:
765 r = parse_timestamp(optarg, &arg_not_after);
766 if (r < 0)
767 return log_error_errno(r, "Failed to parse --not-after= timestamp: %s", optarg);
768
769 break;
770
771 case '?':
772 return -EINVAL;
773
774 default:
775 assert_not_reached("Unhandled option");
776 }
777 }
778
779 if (arg_tpm2_pcr_mask == UINT32_MAX)
780 arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
781
782 return 1;
783 }
784
785 static int creds_main(int argc, char *argv[]) {
786
787 static const Verb verbs[] = {
788 { "list", VERB_ANY, 1, VERB_DEFAULT, verb_list },
789 { "cat", 2, VERB_ANY, 0, verb_cat },
790 { "encrypt", 3, 3, 0, verb_encrypt },
791 { "decrypt", 2, 3, 0, verb_decrypt },
792 { "setup", VERB_ANY, 1, 0, verb_setup },
793 { "help", VERB_ANY, 1, 0, verb_help },
794 {}
795 };
796
797 return dispatch_verb(argc, argv, verbs, NULL);
798 }
799
800 static int run(int argc, char *argv[]) {
801 int r;
802
803 log_setup();
804
805 r = parse_argv(argc, argv);
806 if (r <= 0)
807 return r;
808
809 return creds_main(argc, argv);
810 }
811
812 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);