]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/creds/creds.c
variuos: add missing includes
[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 char **cn;
315
316 r = open_credential_directory(&d);
317 if (r == -ENXIO)
318 return log_error_errno(r, "No credentials passed.");
319 if (r < 0)
320 return log_error_errno(r, "Failed to open credentials directory: %m");
321
322 STRV_FOREACH(cn, strv_skip(argv, 1)) {
323 _cleanup_(erase_and_freep) void *data = NULL;
324 size_t size = 0;
325
326 if (!credential_name_valid(*cn)) {
327 log_error("Credential name '%s' is not valid.", *cn);
328 if (ret >= 0)
329 ret = -EINVAL;
330 continue;
331 }
332
333 r = read_full_file_full(
334 dirfd(d), *cn,
335 UINT64_MAX, SIZE_MAX,
336 READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE,
337 NULL,
338 (char**) &data, &size);
339 if (r < 0) {
340 log_error_errno(r, "Failed to read credential '%s': %m", *cn);
341 if (ret >= 0)
342 ret = r;
343 continue;
344 }
345
346 r = write_blob(stdout, data, size);
347 if (r < 0)
348 return r;
349 }
350
351 return ret;
352 }
353
354 static int verb_encrypt(int argc, char **argv, void *userdata) {
355 _cleanup_free_ char *base64_buf = NULL, *fname = NULL;
356 _cleanup_(erase_and_freep) char *plaintext = NULL;
357 const char *input_path, *output_path, *name;
358 _cleanup_free_ void *output = NULL;
359 size_t plaintext_size, output_size;
360 ssize_t base64_size;
361 usec_t timestamp;
362 int r;
363
364 assert(argc == 3);
365
366 input_path = empty_or_dash(argv[1]) ? NULL : argv[1];
367
368 if (input_path)
369 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);
370 else
371 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);
372 if (r == -E2BIG)
373 return log_error_errno(r, "Plaintext too long for credential (allowed size: %zu).", (size_t) CREDENTIAL_SIZE_MAX);
374 if (r < 0)
375 return log_error_errno(r, "Failed to read plaintext: %m");
376
377 output_path = empty_or_dash(argv[2]) ? NULL : argv[2];
378
379 if (arg_name_any)
380 name = NULL;
381 else if (arg_name)
382 name = arg_name;
383 else if (output_path) {
384 r = path_extract_filename(output_path, &fname);
385 if (r < 0)
386 return log_error_errno(r, "Failed to extract filename from '%s': %m", output_path);
387 if (r == O_DIRECTORY)
388 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Path '%s' refers to directory, refusing.", output_path);
389
390 name = fname;
391 } else {
392 log_warning("No credential name specified, not embedding credential name in encrypted data. (Disable this warning with --name=)");
393 name = NULL;
394 }
395
396 timestamp = arg_timestamp != USEC_INFINITY ? arg_timestamp : now(CLOCK_REALTIME);
397
398 if (arg_not_after != USEC_INFINITY && arg_not_after < timestamp)
399 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential is invalidated before it is valid.");
400
401 r = encrypt_credential_and_warn(
402 arg_with_key,
403 name,
404 timestamp,
405 arg_not_after,
406 arg_tpm2_device,
407 arg_tpm2_pcr_mask,
408 plaintext, plaintext_size,
409 &output, &output_size);
410 if (r < 0)
411 return r;
412
413 base64_size = base64mem_full(output, output_size, arg_pretty ? 69 : 79, &base64_buf);
414 if (base64_size < 0)
415 return base64_size;
416
417 if (arg_pretty) {
418 _cleanup_free_ char *escaped = NULL, *indented = NULL, *j = NULL;
419
420 if (name) {
421 escaped = cescape(name);
422 if (!escaped)
423 return log_oom();
424 }
425
426 indented = strreplace(base64_buf, "\n", " \\\n ");
427 if (!indented)
428 return log_oom();
429
430 j = strjoin("SetCredentialEncrypted=", name, ": \\\n ", indented, "\n");
431 if (!j)
432 return log_oom();
433
434 free_and_replace(base64_buf, j);
435 }
436
437 if (output_path)
438 r = write_string_file(output_path, base64_buf, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE);
439 else
440 r = write_string_stream(stdout, base64_buf, 0);
441 if (r < 0)
442 return log_error_errno(r, "Failed to write result: %m");
443
444 return EXIT_SUCCESS;
445 }
446
447 static int verb_decrypt(int argc, char **argv, void *userdata) {
448 _cleanup_(erase_and_freep) void *plaintext = NULL;
449 _cleanup_free_ char *input = NULL, *fname = NULL;
450 _cleanup_fclose_ FILE *output_file = NULL;
451 const char *input_path, *output_path, *name;
452 size_t input_size, plaintext_size;
453 usec_t timestamp;
454 FILE *f;
455 int r;
456
457 assert(IN_SET(argc, 2, 3));
458
459 input_path = empty_or_dash(argv[1]) ? NULL : argv[1];
460
461 if (input_path)
462 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);
463 else
464 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);
465 if (r == -E2BIG)
466 return log_error_errno(r, "Data too long for encrypted credential (allowed size: %zu).", (size_t) CREDENTIAL_ENCRYPTED_SIZE_MAX);
467 if (r < 0)
468 return log_error_errno(r, "Failed to read encrypted credential data: %m");
469
470 output_path = (argc < 3 || isempty(argv[2]) || streq(argv[2], "-")) ? NULL : argv[2];
471
472 if (arg_name_any)
473 name = NULL;
474 else if (arg_name)
475 name = arg_name;
476 else if (input_path) {
477 r = path_extract_filename(input_path, &fname);
478 if (r < 0)
479 return log_error_errno(r, "Failed to extract filename from '%s': %m", input_path);
480 if (r == O_DIRECTORY)
481 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Path '%s' refers to directory, refusing.", input_path);
482
483 name = fname;
484 } else {
485 log_warning("No credential name specified, not validating credential name embedded in encrypted data. (Disable this warning with --name=.)");
486 name = NULL;
487 }
488
489 timestamp = arg_timestamp != USEC_INFINITY ? arg_timestamp : now(CLOCK_REALTIME);
490
491 r = decrypt_credential_and_warn(
492 name,
493 timestamp,
494 arg_tpm2_device,
495 input, input_size,
496 &plaintext, &plaintext_size);
497 if (r < 0)
498 return r;
499
500 if (output_path) {
501 output_file = fopen(output_path, "we");
502 if (!output_file)
503 return log_error_errno(errno, "Failed to create output file '%s': %m", output_path);
504
505 f = output_file;
506 } else
507 f = stdout;
508
509 r = write_blob(f, plaintext, plaintext_size);
510 if (r < 0)
511 return r;
512
513 return EXIT_SUCCESS;
514 }
515
516 static int verb_setup(int argc, char **argv, void *userdata) {
517 size_t size;
518 int r;
519
520 r = get_credential_host_secret(CREDENTIAL_SECRET_GENERATE|CREDENTIAL_SECRET_WARN_NOT_ENCRYPTED, NULL, &size);
521 if (r < 0)
522 return log_error_errno(r, "Failed to setup credentials host key: %m");
523
524 log_info("%zu byte credentials host key set up.", size);
525
526 return EXIT_SUCCESS;
527 }
528
529 static int verb_help(int argc, char **argv, void *userdata) {
530 _cleanup_free_ char *link = NULL;
531 int r;
532
533 r = terminal_urlify_man("systemd-creds", "1", &link);
534 if (r < 0)
535 return log_oom();
536
537 printf("%1$s [OPTIONS...] COMMAND ...\n"
538 "\n%5$sDisplay and Process Credentials.%6$s\n"
539 "\n%3$sCommands:%4$s\n"
540 " list Show installed and available versions\n"
541 " cat CREDENTIAL... Show specified credentials\n"
542 " setup Generate credentials host key, if not existing yet\n"
543 " encrypt INPUT OUTPUT Encrypt plaintext credential file and write to\n"
544 " ciphertext credential file\n"
545 " decrypt INPUT [OUTPUT] Decrypt ciphertext credential file and write to\n"
546 " plaintext credential file\n"
547 " -h --help Show this help\n"
548 " --version Show package version\n"
549 "\n%3$sOptions:%4$s\n"
550 " --no-pager Do not pipe output into a pager\n"
551 " --no-legend Do not show the headers and footers\n"
552 " --json=pretty|short|off\n"
553 " Generate JSON output\n"
554 " --system Show credentials passed to system\n"
555 " --transcode=base64|unbase64|hex|unhex\n"
556 " Transcode credential data\n"
557 " --newline=auto|yes|no\n"
558 " Suffix output with newline\n"
559 " -p --pretty Output as SetCredentialEncrypted= line\n"
560 " --name=NAME Override filename included in encrypted credential\n"
561 " --timestamp=TIME Include specified timestamp in encrypted credential\n"
562 " --not-after=TIME Include specified invalidation time in encrypted\n"
563 " credential\n"
564 " --with-key=host|tpm2|host+tpm2|auto\n"
565 " Which keys to encrypt with\n"
566 " -H Shortcut for --with-key=host\n"
567 " -T Shortcut for --with-key=tpm2\n"
568 " --tpm2-device=PATH\n"
569 " Pick TPM2 device\n"
570 " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
571 " Specify TPM2 PCRs to seal against\n"
572 "\nSee the %2$s for details.\n"
573 , program_invocation_short_name
574 , link
575 , ansi_underline(), ansi_normal()
576 , ansi_highlight(), ansi_normal()
577 );
578
579 return 0;
580 }
581
582 static int parse_argv(int argc, char *argv[]) {
583
584 enum {
585 ARG_VERSION = 0x100,
586 ARG_NO_PAGER,
587 ARG_NO_LEGEND,
588 ARG_JSON,
589 ARG_SYSTEM,
590 ARG_TRANSCODE,
591 ARG_NEWLINE,
592 ARG_WITH_KEY,
593 ARG_TPM2_DEVICE,
594 ARG_TPM2_PCRS,
595 ARG_NAME,
596 ARG_TIMESTAMP,
597 ARG_NOT_AFTER,
598 };
599
600 static const struct option options[] = {
601 { "help", no_argument, NULL, 'h' },
602 { "version", no_argument, NULL, ARG_VERSION },
603 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
604 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
605 { "json", required_argument, NULL, ARG_JSON },
606 { "system", no_argument, NULL, ARG_SYSTEM },
607 { "transcode", required_argument, NULL, ARG_TRANSCODE },
608 { "newline", required_argument, NULL, ARG_NEWLINE },
609 { "pretty", no_argument, NULL, 'p' },
610 { "with-key", required_argument, NULL, ARG_WITH_KEY },
611 { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
612 { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
613 { "name", required_argument, NULL, ARG_NAME },
614 { "timestamp", required_argument, NULL, ARG_TIMESTAMP },
615 { "not-after", required_argument, NULL, ARG_NOT_AFTER },
616 {}
617 };
618
619 int c, r;
620
621 assert(argc >= 0);
622 assert(argv);
623
624 while ((c = getopt_long(argc, argv, "hHTp", options, NULL)) >= 0) {
625
626 switch (c) {
627
628 case 'h':
629 return verb_help(0, NULL, NULL);
630
631 case ARG_VERSION:
632 return version();
633
634 case ARG_NO_PAGER:
635 arg_pager_flags |= PAGER_DISABLE;
636 break;
637
638 case ARG_NO_LEGEND:
639 arg_legend = false;
640 break;
641
642 case ARG_JSON:
643 r = parse_json_argument(optarg, &arg_json_format_flags);
644 if (r <= 0)
645 return r;
646
647 break;
648
649 case ARG_SYSTEM:
650 arg_system = true;
651 break;
652
653 case ARG_TRANSCODE:
654 if (parse_boolean(optarg) == 0) /* If specified as "false", turn transcoding off */
655 arg_transcode = TRANSCODE_OFF;
656 else {
657 TranscodeMode m;
658
659 m = transcode_mode_from_string(optarg);
660 if (m < 0)
661 return log_error_errno(m, "Failed to parse transcode mode: %m");
662
663 arg_transcode = m;
664 }
665
666 break;
667
668 case ARG_NEWLINE:
669 if (isempty(optarg) || streq(optarg, "auto"))
670 arg_newline = -1;
671 else {
672 bool b;
673
674 r = parse_boolean_argument("--newline=", optarg, &b);
675 if (r < 0)
676 return r;
677
678 arg_newline = b;
679 }
680 break;
681
682 case 'p':
683 arg_pretty = true;
684 break;
685
686 case ARG_WITH_KEY:
687 if (isempty(optarg) || streq(optarg, "auto"))
688 arg_with_key = SD_ID128_NULL;
689 else if (streq(optarg, "host"))
690 arg_with_key = CRED_AES256_GCM_BY_HOST;
691 else if (streq(optarg, "tpm2"))
692 arg_with_key = CRED_AES256_GCM_BY_TPM2_HMAC;
693 else if (STR_IN_SET(optarg, "host+tpm2", "tpm2+host"))
694 arg_with_key = CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC;
695 else
696 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown key type: %s", optarg);
697
698 break;
699
700 case 'H':
701 arg_with_key = CRED_AES256_GCM_BY_HOST;
702 break;
703
704 case 'T':
705 arg_with_key = CRED_AES256_GCM_BY_TPM2_HMAC;
706 break;
707
708 case ARG_TPM2_DEVICE:
709 if (streq(optarg, "list"))
710 return tpm2_list_devices();
711
712 arg_tpm2_device = streq(optarg, "auto") ? NULL : optarg;
713 break;
714
715 case ARG_TPM2_PCRS:
716 if (isempty(optarg)) {
717 arg_tpm2_pcr_mask = 0;
718 break;
719 }
720
721 uint32_t mask;
722 r = tpm2_parse_pcrs(optarg, &mask);
723 if (r < 0)
724 return r;
725
726 if (arg_tpm2_pcr_mask == UINT32_MAX)
727 arg_tpm2_pcr_mask = mask;
728 else
729 arg_tpm2_pcr_mask |= mask;
730
731 break;
732
733 case ARG_NAME:
734 if (isempty(optarg)) {
735 arg_name = NULL;
736 arg_name_any = true;
737 break;
738 }
739
740 if (!credential_name_valid(optarg))
741 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid credential name: %s", optarg);
742
743 arg_name = optarg;
744 arg_name_any = false;
745 break;
746
747 case ARG_TIMESTAMP:
748 r = parse_timestamp(optarg, &arg_timestamp);
749 if (r < 0)
750 return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
751
752 break;
753
754 case ARG_NOT_AFTER:
755 r = parse_timestamp(optarg, &arg_not_after);
756 if (r < 0)
757 return log_error_errno(r, "Failed to parse --not-after= timestamp: %s", optarg);
758
759 break;
760
761 case '?':
762 return -EINVAL;
763
764 default:
765 assert_not_reached();
766 }
767 }
768
769 if (arg_tpm2_pcr_mask == UINT32_MAX)
770 arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
771
772 return 1;
773 }
774
775 static int creds_main(int argc, char *argv[]) {
776
777 static const Verb verbs[] = {
778 { "list", VERB_ANY, 1, VERB_DEFAULT, verb_list },
779 { "cat", 2, VERB_ANY, 0, verb_cat },
780 { "encrypt", 3, 3, 0, verb_encrypt },
781 { "decrypt", 2, 3, 0, verb_decrypt },
782 { "setup", VERB_ANY, 1, 0, verb_setup },
783 { "help", VERB_ANY, 1, 0, verb_help },
784 {}
785 };
786
787 return dispatch_verb(argc, argv, verbs, NULL);
788 }
789
790 static int run(int argc, char *argv[]) {
791 int r;
792
793 log_setup();
794
795 r = parse_argv(argc, argv);
796 if (r <= 0)
797 return r;
798
799 return creds_main(argc, argv);
800 }
801
802 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);