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"
51 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
53 static OutputMode arg_output = OUTPUT_SHORT;
54 static bool arg_follow = false;
55 static bool arg_show_all = false;
56 static bool arg_no_pager = false;
57 static int arg_lines = -1;
58 static bool arg_no_tail = false;
59 static bool arg_quiet = false;
60 static bool arg_local = false;
61 static bool arg_this_boot = false;
62 static const char *arg_directory = NULL;
63 static int arg_priorities = 0xFF;
64 static const char *arg_verify_key = NULL;
65 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
73 } arg_action = ACTION_SHOW;
75 static int help(void) {
77 printf("%s [OPTIONS...] [MATCH]\n\n"
78 "Send control commands to or query the journal.\n\n"
79 " -h --help Show this help\n"
80 " --version Show package version\n"
81 " --no-pager Do not pipe output into a pager\n"
82 " -a --all Show all fields, including long and unprintable\n"
83 " -f --follow Follow journal\n"
84 " -n --lines=INTEGER Journal entries to show\n"
85 " --no-tail Show all lines, even in follow mode\n"
86 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
87 " verbose, export, json, cat)\n"
88 " -q --quiet Don't show privilege warning\n"
89 " -l --local Only local entries\n"
90 " -b --this-boot Show data only from current boot\n"
91 " -D --directory=PATH Show journal files from directory\n"
92 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
94 " --new-id128 Generate a new 128 Bit ID\n"
95 " --header Show journal header information\n"
96 " --setup-keys Generate new FSS key pair\n"
97 " --interval=TIME Time interval for changing the FSS sealing key\n"
98 " --verify Verify journal file consistency\n"
99 " --verify-key=KEY Specify FSS verification key\n",
100 program_invocation_short_name);
105 static int parse_argv(int argc, char *argv[]) {
119 static const struct option options[] = {
120 { "help", no_argument, NULL, 'h' },
121 { "version" , no_argument, NULL, ARG_VERSION },
122 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
123 { "follow", no_argument, NULL, 'f' },
124 { "output", required_argument, NULL, 'o' },
125 { "all", no_argument, NULL, 'a' },
126 { "lines", required_argument, NULL, 'n' },
127 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
128 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
129 { "quiet", no_argument, NULL, 'q' },
130 { "local", no_argument, NULL, 'l' },
131 { "this-boot", no_argument, NULL, 'b' },
132 { "directory", required_argument, NULL, 'D' },
133 { "header", no_argument, NULL, ARG_HEADER },
134 { "priority", no_argument, NULL, 'p' },
135 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
136 { "interval", required_argument, NULL, ARG_INTERVAL },
137 { "verify", no_argument, NULL, ARG_VERIFY },
138 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
147 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
156 puts(PACKAGE_STRING);
158 puts(SYSTEMD_FEATURES);
170 arg_output = output_mode_from_string(optarg);
171 if (arg_output < 0) {
172 log_error("Unknown output '%s'.", optarg);
183 r = safe_atoi(optarg, &arg_lines);
184 if (r < 0 || arg_lines < 0) {
185 log_error("Failed to parse lines '%s'", optarg);
195 arg_action = ACTION_NEW_ID128;
207 arg_this_boot = true;
211 arg_directory = optarg;
215 arg_action = ACTION_PRINT_HEADER;
219 arg_action = ACTION_SETUP_KEYS;
223 arg_action = ACTION_VERIFY;
227 arg_action = ACTION_VERIFY;
228 arg_verify_key = optarg;
232 r = parse_usec(optarg, &arg_interval);
233 if (r < 0 || arg_interval <= 0) {
234 log_error("Failed to parse sealing key change interval: %s", optarg);
242 dots = strstr(optarg, "..");
248 a = strndup(optarg, dots - optarg);
252 from = log_level_from_string(a);
253 to = log_level_from_string(dots + 2);
256 if (from < 0 || to < 0) {
257 log_error("Failed to parse log level range %s", optarg);
264 for (i = from; i <= to; i++)
265 arg_priorities |= 1 << i;
267 for (i = to; i <= from; i++)
268 arg_priorities |= 1 << i;
274 p = log_level_from_string(optarg);
276 log_error("Unknown log level %s", optarg);
282 for (i = 0; i <= p; i++)
283 arg_priorities |= 1 << i;
293 log_error("Unknown option code %c", c);
298 if (arg_follow && !arg_no_tail && arg_lines < 0)
304 static bool on_tty(void) {
307 /* Note that this is invoked relatively early, before we start
308 * the pager. That means the value we return reflects whether
309 * we originally were started on a tty, not if we currently
310 * are. But this is intended, since we want colour and so on
311 * when run in our own pager. */
313 if (_unlikely_(t < 0))
314 t = isatty(STDOUT_FILENO) > 0;
319 static int generate_new_id128(void) {
324 r = sd_id128_randomize(&id);
326 log_error("Failed to generate ID: %s", strerror(-r));
330 printf("As string:\n"
331 SD_ID128_FORMAT_STR "\n\n"
333 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
335 "#define MESSAGE_XYZ SD_ID128_MAKE(",
336 SD_ID128_FORMAT_VAL(id),
337 SD_ID128_FORMAT_VAL(id));
339 for (i = 0; i < 16; i++)
340 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
342 fputs(")\n", stdout);
347 static int add_matches(sd_journal *j, char **args) {
353 STRV_FOREACH(i, args) {
356 r = sd_journal_add_disjunction(j);
357 else if (path_is_absolute(*i)) {
362 p = canonicalize_file_name(*i);
365 if (stat(path, &st) < 0) {
367 log_error("Couldn't stat file: %m");
371 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
372 t = strappend("_EXE=", path);
373 else if (S_ISCHR(st.st_mode))
374 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
375 else if (S_ISBLK(st.st_mode))
376 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
379 log_error("File is not a device node, regular file or is not executable: %s", *i);
388 r = sd_journal_add_match(j, t, 0);
391 r = sd_journal_add_match(j, *i, 0);
394 log_error("Failed to add match '%s': %s", *i, strerror(-r));
402 static int add_this_boot(sd_journal *j) {
403 char match[9+32+1] = "_BOOT_ID=";
412 r = sd_id128_get_boot(&boot_id);
414 log_error("Failed to get boot id: %s", strerror(-r));
418 sd_id128_to_string(boot_id, match + 9);
419 r = sd_journal_add_match(j, match, strlen(match));
421 log_error("Failed to add match: %s", strerror(-r));
428 static int add_priorities(sd_journal *j) {
429 char match[] = "PRIORITY=0";
434 if (arg_priorities == 0xFF)
437 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
438 if (arg_priorities & (1 << i)) {
439 match[sizeof(match)-2] = '0' + i;
441 log_info("adding match %s", match);
443 r = sd_journal_add_match(j, match, strlen(match));
445 log_error("Failed to add match: %s", strerror(-r));
453 static int setup_keys(void) {
455 size_t mpk_size, seed_size, state_size, i;
456 uint8_t *mpk, *seed, *state;
458 int fd = -1, r, attr = 0;
459 sd_id128_t machine, boot;
460 char *p = NULL, *k = NULL;
464 r = sd_id128_get_machine(&machine);
466 log_error("Failed to get machine ID: %s", strerror(-r));
470 r = sd_id128_get_boot(&boot);
472 log_error("Failed to get boot ID: %s", strerror(-r));
476 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
477 SD_ID128_FORMAT_VAL(machine)) < 0)
480 if (access(p, F_OK) >= 0) {
481 log_error("Sealing key file %s exists already.", p);
486 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
487 SD_ID128_FORMAT_VAL(machine)) < 0) {
492 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
493 mpk = alloca(mpk_size);
495 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
496 seed = alloca(seed_size);
498 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
499 state = alloca(state_size);
501 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
503 log_error("Failed to open /dev/random: %m");
508 log_info("Generating seed...");
509 l = loop_read(fd, seed, seed_size, true);
510 if (l < 0 || (size_t) l != seed_size) {
511 log_error("Failed to read random seed: %s", strerror(EIO));
516 log_info("Generating key pair...");
517 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
519 log_info("Generating sealing key...");
520 FSPRG_GenState0(state, mpk, seed, seed_size);
522 assert(arg_interval > 0);
524 n = now(CLOCK_REALTIME);
527 close_nointr_nofail(fd);
528 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
530 log_error("Failed to open %s: %m", k);
535 /* Enable secure remove, exclusion from dump, synchronous
536 * writing and in-place updating */
537 if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
538 log_warning("FS_IOC_GETFLAGS failed: %m");
540 attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
542 if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
543 log_warning("FS_IOC_SETFLAGS failed: %m");
546 memcpy(h.signature, "KSHHRHLP", 8);
547 h.machine_id = machine;
549 h.header_size = htole64(sizeof(h));
550 h.start_usec = htole64(n * arg_interval);
551 h.interval_usec = htole64(arg_interval);
552 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
553 h.fsprg_state_size = htole64(state_size);
555 l = loop_write(fd, &h, sizeof(h), false);
556 if (l < 0 || (size_t) l != sizeof(h)) {
557 log_error("Failed to write header: %s", strerror(EIO));
562 l = loop_write(fd, state, state_size, false);
563 if (l < 0 || (size_t) l != state_size) {
564 log_error("Failed to write state: %s", strerror(EIO));
569 if (link(k, p) < 0) {
570 log_error("Failed to link file: %m");
575 if (isatty(STDOUT_FILENO)) {
578 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
579 "the following local file. It should not be used on multiple hosts.\n"
583 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
584 "at a safe location and should not be saved locally on disk.\n"
585 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
588 for (i = 0; i < seed_size; i++) {
589 if (i > 0 && i % 3 == 0)
591 printf("%02x", ((uint8_t*) seed)[i]);
594 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
596 if (isatty(STDOUT_FILENO)) {
597 char tsb[FORMAT_TIMESPAN_MAX];
600 ANSI_HIGHLIGHT_OFF "\n"
601 "The sealing key is automatically changed every %s.\n",
602 format_timespan(tsb, sizeof(tsb), arg_interval));
609 close_nointr_nofail(fd);
620 log_error("Forward-secure journal verification not available.");
624 static int verify(sd_journal *j) {
631 HASHMAP_FOREACH(f, j->files, i) {
633 usec_t from, to, total;
636 if (!arg_verify_key && journal_file_fss_enabled(f))
637 log_warning("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
640 k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true);
642 /* If the key was invalid give up right-away. */
645 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
648 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
649 log_info("PASS: %s", f->path);
651 if (arg_verify_key && journal_file_fss_enabled(f))
652 log_info("=> Validated from %s to %s, %s missing",
653 format_timestamp(a, sizeof(a), from),
654 format_timestamp(b, sizeof(b), to),
655 format_timespan(c, sizeof(c), total > to ? total - to : 0));
662 int main(int argc, char *argv[]) {
664 sd_journal *j = NULL;
666 bool need_seek = false;
667 sd_id128_t previous_boot_id;
668 bool previous_boot_id_valid = false;
671 log_parse_environment();
674 r = parse_argv(argc, argv);
678 if (arg_action == ACTION_NEW_ID128) {
679 r = generate_new_id128();
683 if (arg_action == ACTION_SETUP_KEYS) {
689 r = sd_journal_open_directory(&j, arg_directory, 0);
691 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
694 log_error("Failed to open journal: %s", strerror(-r));
698 if (arg_action == ACTION_VERIFY) {
703 if (arg_action == ACTION_PRINT_HEADER) {
704 journal_print_header(j);
710 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
711 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
714 r = add_this_boot(j);
718 r = add_matches(j, argv + optind);
722 r = add_priorities(j);
728 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
730 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
732 log_error("Failed to get cutoff: %s", strerror(-r));
738 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
740 printf("Logs begin at %s, end at %s.\n",
741 format_timestamp(start_buf, sizeof(start_buf), start),
742 format_timestamp(end_buf, sizeof(end_buf), end));
746 if (arg_lines >= 0) {
747 r = sd_journal_seek_tail(j);
749 log_error("Failed to seek to tail: %s", strerror(-r));
753 r = sd_journal_previous_skip(j, arg_lines);
755 r = sd_journal_seek_head(j);
757 log_error("Failed to seek to head: %s", strerror(-r));
761 r = sd_journal_next(j);
765 log_error("Failed to iterate through journal: %s", strerror(-r));
770 have_pager = !arg_no_pager && !arg_follow && pager_open();
772 if (arg_output == OUTPUT_JSON) {
781 arg_show_all * OUTPUT_SHOW_ALL |
782 have_pager * OUTPUT_FULL_WIDTH |
783 on_tty() * OUTPUT_COLOR;
786 r = sd_journal_next(j);
788 log_error("Failed to iterate through journal: %s", strerror(-r));
796 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
798 if (previous_boot_id_valid &&
799 !sd_id128_equal(boot_id, previous_boot_id))
800 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
802 previous_boot_id = boot_id;
803 previous_boot_id_valid = true;
808 r = output_journal(j, arg_output, line, 0, flags);
818 r = sd_journal_wait(j, (uint64_t) -1);
820 log_error("Couldn't wait for log event: %s", strerror(-r));
825 if (arg_output == OUTPUT_JSON)
826 fputs("\n]\n", stdout);
834 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;