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;
239 r = parse_usec(optarg, &arg_interval);
240 if (r < 0 || arg_interval <= 0) {
241 log_error("Failed to parse sealing key change interval: %s", optarg);
249 log_error("Forward-secure sealing not available.");
256 dots = strstr(optarg, "..");
262 a = strndup(optarg, dots - optarg);
266 from = log_level_from_string(a);
267 to = log_level_from_string(dots + 2);
270 if (from < 0 || to < 0) {
271 log_error("Failed to parse log level range %s", optarg);
278 for (i = from; i <= to; i++)
279 arg_priorities |= 1 << i;
281 for (i = to; i <= from; i++)
282 arg_priorities |= 1 << i;
288 p = log_level_from_string(optarg);
290 log_error("Unknown log level %s", optarg);
296 for (i = 0; i <= p; i++)
297 arg_priorities |= 1 << i;
307 log_error("Unknown option code %c", c);
312 if (arg_follow && !arg_no_tail && arg_lines < 0)
318 static bool on_tty(void) {
321 /* Note that this is invoked relatively early, before we start
322 * the pager. That means the value we return reflects whether
323 * we originally were started on a tty, not if we currently
324 * are. But this is intended, since we want colour and so on
325 * when run in our own pager. */
327 if (_unlikely_(t < 0))
328 t = isatty(STDOUT_FILENO) > 0;
333 static int generate_new_id128(void) {
338 r = sd_id128_randomize(&id);
340 log_error("Failed to generate ID: %s", strerror(-r));
344 printf("As string:\n"
345 SD_ID128_FORMAT_STR "\n\n"
347 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
349 "#define MESSAGE_XYZ SD_ID128_MAKE(",
350 SD_ID128_FORMAT_VAL(id),
351 SD_ID128_FORMAT_VAL(id));
353 for (i = 0; i < 16; i++)
354 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
356 fputs(")\n", stdout);
361 static int add_matches(sd_journal *j, char **args) {
367 STRV_FOREACH(i, args) {
370 r = sd_journal_add_disjunction(j);
371 else if (path_is_absolute(*i)) {
376 p = canonicalize_file_name(*i);
379 if (stat(path, &st) < 0) {
381 log_error("Couldn't stat file: %m");
385 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
386 t = strappend("_EXE=", path);
387 else if (S_ISCHR(st.st_mode))
388 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
389 else if (S_ISBLK(st.st_mode))
390 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
393 log_error("File is not a device node, regular file or is not executable: %s", *i);
402 r = sd_journal_add_match(j, t, 0);
405 r = sd_journal_add_match(j, *i, 0);
408 log_error("Failed to add match '%s': %s", *i, strerror(-r));
416 static int add_this_boot(sd_journal *j) {
417 char match[9+32+1] = "_BOOT_ID=";
426 r = sd_id128_get_boot(&boot_id);
428 log_error("Failed to get boot id: %s", strerror(-r));
432 sd_id128_to_string(boot_id, match + 9);
433 r = sd_journal_add_match(j, match, strlen(match));
435 log_error("Failed to add match: %s", strerror(-r));
442 static int add_priorities(sd_journal *j) {
443 char match[] = "PRIORITY=0";
448 if (arg_priorities == 0xFF)
451 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
452 if (arg_priorities & (1 << i)) {
453 match[sizeof(match)-2] = '0' + i;
455 log_info("adding match %s", match);
457 r = sd_journal_add_match(j, match, strlen(match));
459 log_error("Failed to add match: %s", strerror(-r));
467 static int setup_keys(void) {
469 size_t mpk_size, seed_size, state_size, i;
470 uint8_t *mpk, *seed, *state;
472 int fd = -1, r, attr = 0;
473 sd_id128_t machine, boot;
474 char *p = NULL, *k = NULL;
478 r = sd_id128_get_machine(&machine);
480 log_error("Failed to get machine ID: %s", strerror(-r));
484 r = sd_id128_get_boot(&boot);
486 log_error("Failed to get boot ID: %s", strerror(-r));
490 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
491 SD_ID128_FORMAT_VAL(machine)) < 0)
494 if (access(p, F_OK) >= 0) {
495 log_error("Sealing key file %s exists already.", p);
500 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
501 SD_ID128_FORMAT_VAL(machine)) < 0) {
506 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
507 mpk = alloca(mpk_size);
509 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
510 seed = alloca(seed_size);
512 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
513 state = alloca(state_size);
515 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
517 log_error("Failed to open /dev/random: %m");
522 log_info("Generating seed...");
523 l = loop_read(fd, seed, seed_size, true);
524 if (l < 0 || (size_t) l != seed_size) {
525 log_error("Failed to read random seed: %s", strerror(EIO));
530 log_info("Generating key pair...");
531 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
533 log_info("Generating sealing key...");
534 FSPRG_GenState0(state, mpk, seed, seed_size);
536 assert(arg_interval > 0);
538 n = now(CLOCK_REALTIME);
541 close_nointr_nofail(fd);
542 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
544 log_error("Failed to open %s: %m", k);
549 /* Enable secure remove, exclusion from dump, synchronous
550 * writing and in-place updating */
551 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
552 log_warning("FS_IOC_GETFLAGS failed: %m");
554 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
556 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
557 log_warning("FS_IOC_SETFLAGS failed: %m");
560 memcpy(h.signature, "KSHHRHLP", 8);
561 h.machine_id = machine;
563 h.header_size = htole64(sizeof(h));
564 h.start_usec = htole64(n * arg_interval);
565 h.interval_usec = htole64(arg_interval);
566 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
567 h.fsprg_state_size = htole64(state_size);
569 l = loop_write(fd, &h, sizeof(h), false);
570 if (l < 0 || (size_t) l != sizeof(h)) {
571 log_error("Failed to write header: %s", strerror(EIO));
576 l = loop_write(fd, state, state_size, false);
577 if (l < 0 || (size_t) l != state_size) {
578 log_error("Failed to write state: %s", strerror(EIO));
583 if (link(k, p) < 0) {
584 log_error("Failed to link file: %m");
589 if (isatty(STDOUT_FILENO)) {
592 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
593 "the following local file. This key file is automatically updated when the\n"
594 "sealing key is advanced. It should not be used on multiple hosts.\n"
598 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
599 "at a safe location and should not be saved locally on disk.\n"
600 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
603 for (i = 0; i < seed_size; i++) {
604 if (i > 0 && i % 3 == 0)
606 printf("%02x", ((uint8_t*) seed)[i]);
609 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
611 if (isatty(STDOUT_FILENO)) {
612 char tsb[FORMAT_TIMESPAN_MAX], *hn;
615 ANSI_HIGHLIGHT_OFF "\n"
616 "The sealing key is automatically changed every %s.\n",
617 format_timespan(tsb, sizeof(tsb), arg_interval));
619 hn = gethostname_malloc();
622 hostname_cleanup(hn);
623 fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
625 fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
628 fprintf(stderr, "\nTo transfer the verification key to your phone please scan the QR code below:\n\n");
629 print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
638 close_nointr_nofail(fd);
649 log_error("Forward-secure sealing not available.");
654 static int verify(sd_journal *j) {
661 HASHMAP_FOREACH(f, j->files, i) {
663 usec_t from, to, total;
666 if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
667 log_warning("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
670 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
672 /* If the key was invalid give up right-away. */
675 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
678 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
679 log_info("PASS: %s", f->path);
681 if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
682 log_info("=> Validated from %s to %s, %s missing",
683 format_timestamp(a, sizeof(a), from),
684 format_timestamp(b, sizeof(b), to),
685 format_timespan(c, sizeof(c), total > to ? total - to : 0));
692 int main(int argc, char *argv[]) {
694 sd_journal *j = NULL;
696 bool need_seek = false;
697 sd_id128_t previous_boot_id;
698 bool previous_boot_id_valid = false;
701 log_parse_environment();
704 r = parse_argv(argc, argv);
708 if (arg_action == ACTION_NEW_ID128) {
709 r = generate_new_id128();
713 if (arg_action == ACTION_SETUP_KEYS) {
719 r = sd_journal_open_directory(&j, arg_directory, 0);
721 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
724 log_error("Failed to open journal: %s", strerror(-r));
728 if (arg_action == ACTION_VERIFY) {
733 if (arg_action == ACTION_PRINT_HEADER) {
734 journal_print_header(j);
740 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
741 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
744 r = add_this_boot(j);
748 r = add_matches(j, argv + optind);
752 r = add_priorities(j);
758 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
760 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
762 log_error("Failed to get cutoff: %s", strerror(-r));
768 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
770 printf("Logs begin at %s, end at %s.\n",
771 format_timestamp(start_buf, sizeof(start_buf), start),
772 format_timestamp(end_buf, sizeof(end_buf), end));
776 if (arg_lines >= 0) {
777 r = sd_journal_seek_tail(j);
779 log_error("Failed to seek to tail: %s", strerror(-r));
783 r = sd_journal_previous_skip(j, arg_lines);
785 r = sd_journal_seek_head(j);
787 log_error("Failed to seek to head: %s", strerror(-r));
791 r = sd_journal_next(j);
795 log_error("Failed to iterate through journal: %s", strerror(-r));
800 have_pager = !arg_no_pager && !arg_follow && pager_open();
802 if (arg_output == OUTPUT_JSON) {
811 arg_show_all * OUTPUT_SHOW_ALL |
812 have_pager * OUTPUT_FULL_WIDTH |
813 on_tty() * OUTPUT_COLOR;
816 r = sd_journal_next(j);
818 log_error("Failed to iterate through journal: %s", strerror(-r));
826 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
828 if (previous_boot_id_valid &&
829 !sd_id128_equal(boot_id, previous_boot_id))
830 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
832 previous_boot_id = boot_id;
833 previous_boot_id_valid = true;
838 r = output_journal(j, arg_output, line, 0, flags);
848 r = sd_journal_wait(j, (uint64_t) -1);
850 log_error("Couldn't wait for log event: %s", strerror(-r));
855 if (arg_output == OUTPUT_JSON)
856 fputs("\n]\n", stdout);
864 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;