1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
33 #include <sys/ioctl.h>
36 #include <systemd/sd-journal.h>
40 #include "path-util.h"
43 #include "logs-show.h"
45 #include "journal-internal.h"
46 #include "journal-def.h"
47 #include "journal-verify.h"
48 #include "journal-authenticate.h"
49 #include "journal-qrcode.h"
52 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
54 static OutputMode arg_output = OUTPUT_SHORT;
55 static bool arg_follow = false;
56 static bool arg_show_all = false;
57 static bool arg_no_pager = false;
58 static int arg_lines = -1;
59 static bool arg_no_tail = false;
60 static bool arg_quiet = false;
61 static bool arg_local = false;
62 static bool arg_this_boot = false;
63 static const char *arg_directory = NULL;
64 static int arg_priorities = 0xFF;
65 static const char *arg_verify_key = NULL;
67 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
76 } arg_action = ACTION_SHOW;
78 static int help(void) {
80 printf("%s [OPTIONS...] [MATCH]\n\n"
81 "Send control commands to or query the journal.\n\n"
82 " -h --help Show this help\n"
83 " --version Show package version\n"
84 " --no-pager Do not pipe output into a pager\n"
85 " -a --all Show all fields, including long and unprintable\n"
86 " -f --follow Follow journal\n"
87 " -n --lines=INTEGER Journal entries to show\n"
88 " --no-tail Show all lines, even in follow mode\n"
89 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
90 " verbose, export, json, cat)\n"
91 " -q --quiet Don't show privilege warning\n"
92 " -l --local Only local entries\n"
93 " -b --this-boot Show data only from current boot\n"
94 " -D --directory=PATH Show journal files from directory\n"
95 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
97 " --new-id128 Generate a new 128 Bit ID\n"
98 " --header Show journal header information\n"
100 " --setup-keys Generate new FSS key pair\n"
101 " --interval=TIME Time interval for changing the FSS sealing key\n"
102 " --verify Verify journal file consistency\n"
103 " --verify-key=KEY Specify FSS verification key\n"
105 , program_invocation_short_name);
110 static int parse_argv(int argc, char *argv[]) {
124 static const struct option options[] = {
125 { "help", no_argument, NULL, 'h' },
126 { "version" , no_argument, NULL, ARG_VERSION },
127 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
128 { "follow", no_argument, NULL, 'f' },
129 { "output", required_argument, NULL, 'o' },
130 { "all", no_argument, NULL, 'a' },
131 { "lines", required_argument, NULL, 'n' },
132 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
133 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
134 { "quiet", no_argument, NULL, 'q' },
135 { "local", no_argument, NULL, 'l' },
136 { "this-boot", no_argument, NULL, 'b' },
137 { "directory", required_argument, NULL, 'D' },
138 { "header", no_argument, NULL, ARG_HEADER },
139 { "priority", no_argument, NULL, 'p' },
140 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
141 { "interval", required_argument, NULL, ARG_INTERVAL },
142 { "verify", no_argument, NULL, ARG_VERIFY },
143 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
152 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
161 puts(PACKAGE_STRING);
163 puts(SYSTEMD_FEATURES);
175 arg_output = output_mode_from_string(optarg);
176 if (arg_output < 0) {
177 log_error("Unknown output '%s'.", optarg);
188 r = safe_atoi(optarg, &arg_lines);
189 if (r < 0 || arg_lines < 0) {
190 log_error("Failed to parse lines '%s'", optarg);
200 arg_action = ACTION_NEW_ID128;
212 arg_this_boot = true;
216 arg_directory = optarg;
220 arg_action = ACTION_PRINT_HEADER;
224 arg_action = ACTION_VERIFY;
229 arg_action = ACTION_SETUP_KEYS;
234 arg_action = ACTION_VERIFY;
235 arg_verify_key = optarg;
240 r = parse_usec(optarg, &arg_interval);
241 if (r < 0 || arg_interval <= 0) {
242 log_error("Failed to parse sealing key change interval: %s", optarg);
250 log_error("Forward-secure sealing not available.");
257 dots = strstr(optarg, "..");
263 a = strndup(optarg, dots - optarg);
267 from = log_level_from_string(a);
268 to = log_level_from_string(dots + 2);
271 if (from < 0 || to < 0) {
272 log_error("Failed to parse log level range %s", optarg);
279 for (i = from; i <= to; i++)
280 arg_priorities |= 1 << i;
282 for (i = to; i <= from; i++)
283 arg_priorities |= 1 << i;
289 p = log_level_from_string(optarg);
291 log_error("Unknown log level %s", optarg);
297 for (i = 0; i <= p; i++)
298 arg_priorities |= 1 << i;
308 log_error("Unknown option code %c", c);
313 if (arg_follow && !arg_no_tail && arg_lines < 0)
319 static bool on_tty(void) {
322 /* Note that this is invoked relatively early, before we start
323 * the pager. That means the value we return reflects whether
324 * we originally were started on a tty, not if we currently
325 * are. But this is intended, since we want colour and so on
326 * when run in our own pager. */
328 if (_unlikely_(t < 0))
329 t = isatty(STDOUT_FILENO) > 0;
334 static int generate_new_id128(void) {
339 r = sd_id128_randomize(&id);
341 log_error("Failed to generate ID: %s", strerror(-r));
345 printf("As string:\n"
346 SD_ID128_FORMAT_STR "\n\n"
348 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
350 "#define MESSAGE_XYZ SD_ID128_MAKE(",
351 SD_ID128_FORMAT_VAL(id),
352 SD_ID128_FORMAT_VAL(id));
354 for (i = 0; i < 16; i++)
355 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
357 fputs(")\n", stdout);
362 static int add_matches(sd_journal *j, char **args) {
368 STRV_FOREACH(i, args) {
371 r = sd_journal_add_disjunction(j);
372 else if (path_is_absolute(*i)) {
377 p = canonicalize_file_name(*i);
380 if (stat(path, &st) < 0) {
382 log_error("Couldn't stat file: %m");
386 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
387 t = strappend("_EXE=", path);
388 else if (S_ISCHR(st.st_mode))
389 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
390 else if (S_ISBLK(st.st_mode))
391 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
394 log_error("File is not a device node, regular file or is not executable: %s", *i);
403 r = sd_journal_add_match(j, t, 0);
406 r = sd_journal_add_match(j, *i, 0);
409 log_error("Failed to add match '%s': %s", *i, strerror(-r));
417 static int add_this_boot(sd_journal *j) {
418 char match[9+32+1] = "_BOOT_ID=";
427 r = sd_id128_get_boot(&boot_id);
429 log_error("Failed to get boot id: %s", strerror(-r));
433 sd_id128_to_string(boot_id, match + 9);
434 r = sd_journal_add_match(j, match, strlen(match));
436 log_error("Failed to add match: %s", strerror(-r));
443 static int add_priorities(sd_journal *j) {
444 char match[] = "PRIORITY=0";
449 if (arg_priorities == 0xFF)
452 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
453 if (arg_priorities & (1 << i)) {
454 match[sizeof(match)-2] = '0' + i;
456 log_info("adding match %s", match);
458 r = sd_journal_add_match(j, match, strlen(match));
460 log_error("Failed to add match: %s", strerror(-r));
468 static int setup_keys(void) {
470 size_t mpk_size, seed_size, state_size, i;
471 uint8_t *mpk, *seed, *state;
473 int fd = -1, r, attr = 0;
474 sd_id128_t machine, boot;
475 char *p = NULL, *k = NULL;
479 r = sd_id128_get_machine(&machine);
481 log_error("Failed to get machine ID: %s", strerror(-r));
485 r = sd_id128_get_boot(&boot);
487 log_error("Failed to get boot ID: %s", strerror(-r));
491 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
492 SD_ID128_FORMAT_VAL(machine)) < 0)
495 if (access(p, F_OK) >= 0) {
496 log_error("Sealing key file %s exists already.", p);
501 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
502 SD_ID128_FORMAT_VAL(machine)) < 0) {
507 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
508 mpk = alloca(mpk_size);
510 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
511 seed = alloca(seed_size);
513 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
514 state = alloca(state_size);
516 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
518 log_error("Failed to open /dev/random: %m");
523 log_info("Generating seed...");
524 l = loop_read(fd, seed, seed_size, true);
525 if (l < 0 || (size_t) l != seed_size) {
526 log_error("Failed to read random seed: %s", strerror(EIO));
531 log_info("Generating key pair...");
532 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
534 log_info("Generating sealing key...");
535 FSPRG_GenState0(state, mpk, seed, seed_size);
537 assert(arg_interval > 0);
539 n = now(CLOCK_REALTIME);
542 close_nointr_nofail(fd);
543 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
545 log_error("Failed to open %s: %m", k);
550 /* Enable secure remove, exclusion from dump, synchronous
551 * writing and in-place updating */
552 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
553 log_warning("FS_IOC_GETFLAGS failed: %m");
555 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
557 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
558 log_warning("FS_IOC_SETFLAGS failed: %m");
561 memcpy(h.signature, "KSHHRHLP", 8);
562 h.machine_id = machine;
564 h.header_size = htole64(sizeof(h));
565 h.start_usec = htole64(n * arg_interval);
566 h.interval_usec = htole64(arg_interval);
567 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
568 h.fsprg_state_size = htole64(state_size);
570 l = loop_write(fd, &h, sizeof(h), false);
571 if (l < 0 || (size_t) l != sizeof(h)) {
572 log_error("Failed to write header: %s", strerror(EIO));
577 l = loop_write(fd, state, state_size, false);
578 if (l < 0 || (size_t) l != state_size) {
579 log_error("Failed to write state: %s", strerror(EIO));
584 if (link(k, p) < 0) {
585 log_error("Failed to link file: %m");
590 if (isatty(STDOUT_FILENO)) {
593 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
594 "the following local file. This key file is automatically updated when the\n"
595 "sealing key is advanced. It should not be used on multiple hosts.\n"
599 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
600 "at a safe location and should not be saved locally on disk.\n"
601 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
604 for (i = 0; i < seed_size; i++) {
605 if (i > 0 && i % 3 == 0)
607 printf("%02x", ((uint8_t*) seed)[i]);
610 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
612 if (isatty(STDOUT_FILENO)) {
613 char tsb[FORMAT_TIMESPAN_MAX], *hn;
616 ANSI_HIGHLIGHT_OFF "\n"
617 "The sealing key is automatically changed every %s.\n",
618 format_timespan(tsb, sizeof(tsb), arg_interval));
620 hn = gethostname_malloc();
623 hostname_cleanup(hn);
624 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
626 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
629 fprintf(stderr, "\nTo transfer the verification key to your phone please scan the QR code below:\n\n");
630 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
639 close_nointr_nofail(fd);
650 log_error("Forward-secure sealing not available.");
655 static int verify(sd_journal *j) {
662 log_show_color(true);
664 HASHMAP_FOREACH(f, j->files, i) {
666 usec_t from, to, total;
669 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
670 log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
673 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
675 /* If the key was invalid give up right-away. */
678 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
681 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
682 log_info("PASS: %s", f->path);
684 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
685 log_info("=> Validated from %s to %s, %s missing",
686 format_timestamp(a, sizeof(a), from),
687 format_timestamp(b, sizeof(b), to),
688 format_timespan(c, sizeof(c), total > to ? total - to : 0));
695 int main(int argc, char *argv[]) {
697 sd_journal *j = NULL;
699 bool need_seek = false;
700 sd_id128_t previous_boot_id;
701 bool previous_boot_id_valid = false;
704 log_parse_environment();
707 r = parse_argv(argc, argv);
711 if (arg_action == ACTION_NEW_ID128) {
712 r = generate_new_id128();
716 if (arg_action == ACTION_SETUP_KEYS) {
722 r = sd_journal_open_directory(&j, arg_directory, 0);
724 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
727 log_error("Failed to open journal: %s", strerror(-r));
731 if (arg_action == ACTION_VERIFY) {
736 if (arg_action == ACTION_PRINT_HEADER) {
737 journal_print_header(j);
743 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
744 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
747 r = add_this_boot(j);
751 r = add_matches(j, argv + optind);
755 r = add_priorities(j);
761 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
763 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
765 log_error("Failed to get cutoff: %s", strerror(-r));
771 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
773 printf("Logs begin at %s, end at %s.\n",
774 format_timestamp(start_buf, sizeof(start_buf), start),
775 format_timestamp(end_buf, sizeof(end_buf), end));
779 if (arg_lines >= 0) {
780 r = sd_journal_seek_tail(j);
782 log_error("Failed to seek to tail: %s", strerror(-r));
786 r = sd_journal_previous_skip(j, arg_lines);
788 r = sd_journal_seek_head(j);
790 log_error("Failed to seek to head: %s", strerror(-r));
794 r = sd_journal_next(j);
798 log_error("Failed to iterate through journal: %s", strerror(-r));
803 have_pager = !arg_no_pager && !arg_follow && pager_open();
805 if (arg_output == OUTPUT_JSON) {
814 arg_show_all * OUTPUT_SHOW_ALL |
815 have_pager * OUTPUT_FULL_WIDTH |
816 on_tty() * OUTPUT_COLOR;
819 r = sd_journal_next(j);
821 log_error("Failed to iterate through journal: %s", strerror(-r));
829 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
831 if (previous_boot_id_valid &&
832 !sd_id128_equal(boot_id, previous_boot_id))
833 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
835 previous_boot_id = boot_id;
836 previous_boot_id_valid = true;
841 r = output_journal(j, arg_output, line, 0, flags);
851 r = sd_journal_wait(j, (uint64_t) -1);
853 log_error("Couldn't wait for log event: %s", strerror(-r));
858 if (arg_output == OUTPUT_JSON)
859 fputs("\n]\n", stdout);
867 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;