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/>.
34 #include <systemd/sd-journal.h>
38 #include "path-util.h"
41 #include "logs-show.h"
43 #include "journal-internal.h"
44 #include "journal-def.h"
45 #include "journal-verify.h"
46 #include "journal-authenticate.h"
49 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
51 static OutputMode arg_output = OUTPUT_SHORT;
52 static bool arg_follow = false;
53 static bool arg_show_all = false;
54 static bool arg_no_pager = false;
55 static int arg_lines = -1;
56 static bool arg_no_tail = false;
57 static bool arg_quiet = false;
58 static bool arg_local = false;
59 static bool arg_this_boot = false;
60 static const char *arg_directory = NULL;
61 static int arg_priorities = 0xFF;
62 static const char *arg_verify_key = NULL;
63 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
71 } arg_action = ACTION_SHOW;
73 static int help(void) {
75 printf("%s [OPTIONS...] [MATCH]\n\n"
76 "Send control commands to or query the journal.\n\n"
77 " -h --help Show this help\n"
78 " --version Show package version\n"
79 " --no-pager Do not pipe output into a pager\n"
80 " -a --all Show all fields, including long and unprintable\n"
81 " -f --follow Follow journal\n"
82 " -n --lines=INTEGER Journal entries to show\n"
83 " --no-tail Show all lines, even in follow mode\n"
84 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
85 " verbose, export, json, cat)\n"
86 " -q --quiet Don't show privilege warning\n"
87 " -l --local Only local entries\n"
88 " -b --this-boot Show data only from current boot\n"
89 " -D --directory=PATH Show journal files from directory\n"
90 " -p --priority=RANGE Show only messages within the specified priority range\n\n"
92 " --new-id128 Generate a new 128 Bit ID\n"
93 " --header Show journal header information\n"
94 " --setup-keys Generate new FSS key pair\n"
95 " --interval=TIME Time interval for changing the FSS sealing key\n"
96 " --verify Verify journal file consistency\n"
97 " --verify-key=KEY Specify FSS verification key\n",
98 program_invocation_short_name);
103 static int parse_argv(int argc, char *argv[]) {
117 static const struct option options[] = {
118 { "help", no_argument, NULL, 'h' },
119 { "version" , no_argument, NULL, ARG_VERSION },
120 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
121 { "follow", no_argument, NULL, 'f' },
122 { "output", required_argument, NULL, 'o' },
123 { "all", no_argument, NULL, 'a' },
124 { "lines", required_argument, NULL, 'n' },
125 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
126 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
127 { "quiet", no_argument, NULL, 'q' },
128 { "local", no_argument, NULL, 'l' },
129 { "this-boot", no_argument, NULL, 'b' },
130 { "directory", required_argument, NULL, 'D' },
131 { "header", no_argument, NULL, ARG_HEADER },
132 { "priority", no_argument, NULL, 'p' },
133 { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
134 { "interval", required_argument, NULL, ARG_INTERVAL },
135 { "verify", no_argument, NULL, ARG_VERIFY },
136 { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
145 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
154 puts(PACKAGE_STRING);
156 puts(SYSTEMD_FEATURES);
168 arg_output = output_mode_from_string(optarg);
169 if (arg_output < 0) {
170 log_error("Unknown output '%s'.", optarg);
181 r = safe_atoi(optarg, &arg_lines);
182 if (r < 0 || arg_lines < 0) {
183 log_error("Failed to parse lines '%s'", optarg);
193 arg_action = ACTION_NEW_ID128;
205 arg_this_boot = true;
209 arg_directory = optarg;
213 arg_action = ACTION_PRINT_HEADER;
217 arg_action = ACTION_SETUP_KEYS;
221 arg_action = ACTION_VERIFY;
225 arg_action = ACTION_VERIFY;
226 arg_verify_key = optarg;
230 r = parse_usec(optarg, &arg_interval);
231 if (r < 0 || arg_interval <= 0) {
232 log_error("Failed to parse sealing key change interval: %s", optarg);
240 dots = strstr(optarg, "..");
246 a = strndup(optarg, dots - optarg);
250 from = log_level_from_string(a);
251 to = log_level_from_string(dots + 2);
254 if (from < 0 || to < 0) {
255 log_error("Failed to parse log level range %s", optarg);
262 for (i = from; i <= to; i++)
263 arg_priorities |= 1 << i;
265 for (i = to; i <= from; i++)
266 arg_priorities |= 1 << i;
272 p = log_level_from_string(optarg);
274 log_error("Unknown log level %s", optarg);
280 for (i = 0; i <= p; i++)
281 arg_priorities |= 1 << i;
291 log_error("Unknown option code %c", c);
296 if (arg_follow && !arg_no_tail && arg_lines < 0)
302 static bool on_tty(void) {
305 /* Note that this is invoked relatively early, before we start
306 * the pager. That means the value we return reflects whether
307 * we originally were started on a tty, not if we currently
308 * are. But this is intended, since we want colour and so on
309 * when run in our own pager. */
311 if (_unlikely_(t < 0))
312 t = isatty(STDOUT_FILENO) > 0;
317 static int generate_new_id128(void) {
322 r = sd_id128_randomize(&id);
324 log_error("Failed to generate ID: %s", strerror(-r));
328 printf("As string:\n"
329 SD_ID128_FORMAT_STR "\n\n"
331 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
333 "#define MESSAGE_XYZ SD_ID128_MAKE(",
334 SD_ID128_FORMAT_VAL(id),
335 SD_ID128_FORMAT_VAL(id));
337 for (i = 0; i < 16; i++)
338 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
340 fputs(")\n", stdout);
345 static int add_matches(sd_journal *j, char **args) {
351 STRV_FOREACH(i, args) {
354 r = sd_journal_add_disjunction(j);
355 else if (path_is_absolute(*i)) {
360 p = canonicalize_file_name(*i);
363 if (stat(path, &st) < 0) {
365 log_error("Couldn't stat file: %m");
369 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
370 t = strappend("_EXE=", path);
371 else if (S_ISCHR(st.st_mode))
372 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
373 else if (S_ISBLK(st.st_mode))
374 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
377 log_error("File is not a device node, regular file or is not executable: %s", *i);
386 r = sd_journal_add_match(j, t, 0);
389 r = sd_journal_add_match(j, *i, 0);
392 log_error("Failed to add match '%s': %s", *i, strerror(-r));
400 static int add_this_boot(sd_journal *j) {
401 char match[9+32+1] = "_BOOT_ID=";
410 r = sd_id128_get_boot(&boot_id);
412 log_error("Failed to get boot id: %s", strerror(-r));
416 sd_id128_to_string(boot_id, match + 9);
417 r = sd_journal_add_match(j, match, strlen(match));
419 log_error("Failed to add match: %s", strerror(-r));
426 static int add_priorities(sd_journal *j) {
427 char match[] = "PRIORITY=0";
432 if (arg_priorities == 0xFF)
435 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
436 if (arg_priorities & (1 << i)) {
437 match[sizeof(match)-2] = '0' + i;
439 log_info("adding match %s", match);
441 r = sd_journal_add_match(j, match, strlen(match));
443 log_error("Failed to add match: %s", strerror(-r));
451 static int setup_keys(void) {
453 size_t mpk_size, seed_size, state_size, i;
454 uint8_t *mpk, *seed, *state;
457 sd_id128_t machine, boot;
458 char *p = NULL, *k = NULL;
462 r = sd_id128_get_machine(&machine);
464 log_error("Failed to get machine ID: %s", strerror(-r));
468 r = sd_id128_get_boot(&boot);
470 log_error("Failed to get boot ID: %s", strerror(-r));
474 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
475 SD_ID128_FORMAT_VAL(machine)) < 0)
478 if (access(p, F_OK) >= 0) {
479 log_error("Evolving key file %s exists already.", p);
484 if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
485 SD_ID128_FORMAT_VAL(machine)) < 0) {
490 mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
491 mpk = alloca(mpk_size);
493 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
494 seed = alloca(seed_size);
496 state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
497 state = alloca(state_size);
499 fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
501 log_error("Failed to open /dev/random: %m");
506 log_info("Generating seed...");
507 l = loop_read(fd, seed, seed_size, true);
508 if (l < 0 || (size_t) l != seed_size) {
509 log_error("Failed to read random seed: %s", strerror(EIO));
514 log_info("Generating key pair...");
515 FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
517 log_info("Generating sealing key...");
518 FSPRG_GenState0(state, mpk, seed, seed_size);
520 assert(arg_interval > 0);
522 n = now(CLOCK_REALTIME);
525 close_nointr_nofail(fd);
526 fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
528 log_error("Failed to open %s: %m", k);
534 memcpy(h.signature, "KSHHRHLP", 8);
535 h.machine_id = machine;
537 h.header_size = htole64(sizeof(h));
538 h.start_usec = htole64(n * arg_interval);
539 h.interval_usec = htole64(arg_interval);
540 h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
541 h.fsprg_state_size = htole64(state_size);
543 l = loop_write(fd, &h, sizeof(h), false);
544 if (l < 0 || (size_t) l != sizeof(h)) {
545 log_error("Failed to write header: %s", strerror(EIO));
550 l = loop_write(fd, state, state_size, false);
551 if (l < 0 || (size_t) l != state_size) {
552 log_error("Failed to write state: %s", strerror(EIO));
557 if (link(k, p) < 0) {
558 log_error("Failed to link file: %m");
563 if (isatty(STDOUT_FILENO)) {
566 "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
567 "the following local file. It should not be used on multiple hosts.\n"
571 "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
572 "at a safe location and should not be saved locally on disk.\n"
573 "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
576 for (i = 0; i < seed_size; i++) {
577 if (i > 0 && i % 3 == 0)
579 printf("%02x", ((uint8_t*) seed)[i]);
582 printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
584 if (isatty(STDOUT_FILENO)) {
585 char tsb[FORMAT_TIMESPAN_MAX];
588 ANSI_HIGHLIGHT_OFF "\n"
589 "The sealing key is automatically changed every %s.\n",
590 format_timespan(tsb, sizeof(tsb), arg_interval));
597 close_nointr_nofail(fd);
608 log_error("Forward-secure journal verification not available.");
612 static int verify(sd_journal *j) {
619 HASHMAP_FOREACH(f, j->files, i) {
623 if (!arg_verify_key && journal_file_fss_enabled(f))
624 log_warning("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
627 k = journal_file_verify(f, arg_verify_key);
629 /* If the key was invalid give up right-away. */
632 log_warning("FAIL: %s (%s)", f->path, strerror(-k));
635 log_info("PASS: %s", f->path);
641 int main(int argc, char *argv[]) {
643 sd_journal *j = NULL;
645 bool need_seek = false;
646 sd_id128_t previous_boot_id;
647 bool previous_boot_id_valid = false;
650 log_parse_environment();
653 r = parse_argv(argc, argv);
657 if (arg_action == ACTION_NEW_ID128) {
658 r = generate_new_id128();
662 if (arg_action == ACTION_SETUP_KEYS) {
668 r = sd_journal_open_directory(&j, arg_directory, 0);
670 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
673 log_error("Failed to open journal: %s", strerror(-r));
677 if (arg_action == ACTION_VERIFY) {
682 if (arg_action == ACTION_PRINT_HEADER) {
683 journal_print_header(j);
689 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
690 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
693 r = add_this_boot(j);
697 r = add_matches(j, argv + optind);
701 r = add_priorities(j);
707 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
709 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
711 log_error("Failed to get cutoff: %s", strerror(-r));
717 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
719 printf("Logs begin at %s, end at %s.\n",
720 format_timestamp(start_buf, sizeof(start_buf), start),
721 format_timestamp(end_buf, sizeof(end_buf), end));
725 if (arg_lines >= 0) {
726 r = sd_journal_seek_tail(j);
728 log_error("Failed to seek to tail: %s", strerror(-r));
732 r = sd_journal_previous_skip(j, arg_lines);
734 r = sd_journal_seek_head(j);
736 log_error("Failed to seek to head: %s", strerror(-r));
740 r = sd_journal_next(j);
744 log_error("Failed to iterate through journal: %s", strerror(-r));
749 have_pager = !arg_no_pager && !arg_follow && pager_open();
751 if (arg_output == OUTPUT_JSON) {
760 arg_show_all * OUTPUT_SHOW_ALL |
761 have_pager * OUTPUT_FULL_WIDTH |
762 on_tty() * OUTPUT_COLOR;
765 r = sd_journal_next(j);
767 log_error("Failed to iterate through journal: %s", strerror(-r));
775 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
777 if (previous_boot_id_valid &&
778 !sd_id128_equal(boot_id, previous_boot_id))
779 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
781 previous_boot_id = boot_id;
782 previous_boot_id_valid = true;
787 r = output_journal(j, arg_output, line, 0, flags);
797 r = sd_journal_wait(j, (uint64_t) -1);
799 log_error("Couldn't wait for log event: %s", strerror(-r));
804 if (arg_output == OUTPUT_JSON)
805 fputs("\n]\n", stdout);
813 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;