diff options
Diffstat (limited to 'src/analyze')
-rw-r--r-- | src/analyze/analyze-architectures.c | 88 | ||||
-rw-r--r-- | src/analyze/analyze-architectures.h | 4 | ||||
-rw-r--r-- | src/analyze/analyze-capability.c | 6 | ||||
-rw-r--r-- | src/analyze/analyze-cat-config.c | 5 | ||||
-rw-r--r-- | src/analyze/analyze-critical-chain.c | 13 | ||||
-rw-r--r-- | src/analyze/analyze-dot.c | 31 | ||||
-rw-r--r-- | src/analyze/analyze-exit-status.c | 6 | ||||
-rw-r--r-- | src/analyze/analyze-fdstore.c | 4 | ||||
-rw-r--r-- | src/analyze/analyze-image-policy.c | 2 | ||||
-rw-r--r-- | src/analyze/analyze-pcrs.c | 2 | ||||
-rw-r--r-- | src/analyze/analyze-plot.c | 20 | ||||
-rw-r--r-- | src/analyze/analyze-security.c | 115 | ||||
-rw-r--r-- | src/analyze/analyze-srk.c | 2 | ||||
-rw-r--r-- | src/analyze/analyze-time-data.c | 56 | ||||
-rw-r--r-- | src/analyze/analyze-time-data.h | 3 | ||||
-rw-r--r-- | src/analyze/analyze-unit-files.c | 2 | ||||
-rw-r--r-- | src/analyze/analyze-unit-paths.c | 2 | ||||
-rw-r--r-- | src/analyze/analyze-verify-util.c | 18 | ||||
-rw-r--r-- | src/analyze/analyze.c | 26 | ||||
-rw-r--r-- | src/analyze/meson.build | 1 |
20 files changed, 280 insertions, 126 deletions
diff --git a/src/analyze/analyze-architectures.c b/src/analyze/analyze-architectures.c new file mode 100644 index 0000000..2d155d5 --- /dev/null +++ b/src/analyze/analyze-architectures.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-architectures.h" +#include "format-table.h" + +static int add_arch(Table *t, Architecture a) { + const char *c, *color; + int r; + + assert(t); + + if (a == native_architecture()) { + c = "native"; + color = ANSI_HIGHLIGHT_GREEN; + } else if (a == uname_architecture()) { + c = "uname"; + color = ANSI_HIGHLIGHT; +#ifdef ARCHITECTURE_SECONDARY + } else if (a == ARCHITECTURE_SECONDARY) { + c = "secondary"; + color = NULL; +#endif + } else { + c = "foreign"; + color = ANSI_GREY; + } + + r = table_add_many(t, + TABLE_INT, (int) a, + TABLE_STRING, architecture_to_string(a), + TABLE_STRING, c, + TABLE_SET_COLOR, color); + if (r < 0) + return table_log_add_error(r); + + return 0; +} + +int verb_architectures(int argc, char *argv[], void *userdata) { + _cleanup_(table_unrefp) Table *table = NULL; + int r; + + table = table_new("id", "name", "support"); + if (!table) + return log_oom(); + + (void) table_hide_column_from_display(table, (size_t) 0); + + if (strv_isempty(strv_skip(argv, 1))) + for (Architecture a = 0; a < _ARCHITECTURE_MAX; a++) { + r = add_arch(table, a); + if (r < 0) + return r; + } + else { + STRV_FOREACH(as, strv_skip(argv, 1)) { + Architecture a; + + if (streq(*as, "native")) + a = native_architecture(); + else if (streq(*as, "uname")) + a = uname_architecture(); + else if (streq(*as, "secondary")) { +#ifdef ARCHITECTURE_SECONDARY + a = ARCHITECTURE_SECONDARY; +#else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No secondary architecture."); +#endif + } else + a = architecture_from_string(*as); + if (a < 0) + return log_error_errno(a, "Architecture \"%s\" not known.", *as); + + r = add_arch(table, a); + if (r < 0) + return r; + } + + (void) table_set_sort(table, (size_t) 0); + } + + r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); + if (r < 0) + return log_error_errno(r, "Failed to output table: %m"); + + return EXIT_SUCCESS; +} diff --git a/src/analyze/analyze-architectures.h b/src/analyze/analyze-architectures.h new file mode 100644 index 0000000..06b9473 --- /dev/null +++ b/src/analyze/analyze-architectures.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_architectures(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-capability.c b/src/analyze/analyze-capability.c index 8072175..7cdc0e3 100644 --- a/src/analyze/analyze-capability.c +++ b/src/analyze/analyze-capability.c @@ -46,11 +46,9 @@ int verb_capabilities(int argc, char *argv[], void *userdata) { (void) table_set_sort(table, (size_t) 1); } - pager_open(arg_pager_flags); - - r = table_print(table, NULL); + r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); if (r < 0) - return r; + return log_error_errno(r, "Failed to output table: %m"); return EXIT_SUCCESS; } diff --git a/src/analyze/analyze-cat-config.c b/src/analyze/analyze-cat-config.c index 66bbbc1..b480d4a 100644 --- a/src/analyze/analyze-cat-config.c +++ b/src/analyze/analyze-cat-config.c @@ -4,7 +4,6 @@ #include "analyze-cat-config.h" #include "conf-files.h" #include "constants.h" -#include "nulstr-util.h" #include "path-util.h" #include "pretty-print.h" #include "strv.h" @@ -23,7 +22,7 @@ int verb_cat_config(int argc, char *argv[], void *userdata) { print_separator(); if (path_is_absolute(*arg)) { - NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) { + FOREACH_STRING(dir, CONF_PATHS("")) { t = path_startswith(*arg, dir); if (t) break; @@ -35,7 +34,7 @@ int verb_cat_config(int argc, char *argv[], void *userdata) { } else t = *arg; - r = conf_files_cat(arg_root, t, arg_cat_flags); + r = conf_files_cat(arg_root, t, arg_cat_flags | CAT_FORMAT_HAS_SECTIONS); if (r < 0) return r; } diff --git a/src/analyze/analyze-critical-chain.c b/src/analyze/analyze-critical-chain.c index 4a7f452..7d78de3 100644 --- a/src/analyze/analyze-critical-chain.c +++ b/src/analyze/analyze-critical-chain.c @@ -23,23 +23,20 @@ static int list_dependencies_print( UnitTimes *times, BootTimes *boot) { - for (unsigned i = level; i != 0; i--) + for (unsigned i = level; i > 0; i--) printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE)); printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH)); - if (times) { + if (times && times->activating >= boot->userspace_time) { if (timestamp_is_set(times->time)) - printf("%s%s @%s +%s%s", ansi_highlight_red(), name, + printf("%s%s @%s +%s%s\n", ansi_highlight_red(), name, FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC), FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); - else if (times->activated > boot->userspace_time) - printf("%s @%s", name, FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC)); else - printf("%s", name); + printf("%s @%s\n", name, FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC)); } else - printf("%s", name); - printf("\n"); + printf("%s\n", name); return 0; } diff --git a/src/analyze/analyze-dot.c b/src/analyze/analyze-dot.c index bf8aa81..9e92d59 100644 --- a/src/analyze/analyze-dot.c +++ b/src/analyze/analyze-dot.c @@ -13,14 +13,15 @@ static int graph_one_property( const UnitInfo *u, const char *prop, const char *color, - char *patterns[], - char *from_patterns[], - char *to_patterns[]) { + char **patterns, + char **from_patterns, + char **to_patterns) { _cleanup_strv_free_ char **units = NULL; bool match_patterns; int r; + assert(bus); assert(u); assert(prop); assert(color); @@ -51,7 +52,13 @@ static int graph_one_property( return 0; } -static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) { +static int graph_one( + sd_bus *bus, + const UnitInfo *u, + char **patterns, + char **from_patterns, + char **to_patterns) { + int r; assert(bus); @@ -67,12 +74,19 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *fro r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns); if (r < 0) return r; + r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns); if (r < 0) return r; + + r = graph_one_property(bus, u, "BindsTo", "gold", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns); if (r < 0) return r; + r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); if (r < 0) return r; @@ -85,6 +99,9 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { _cleanup_strv_free_ char **expanded_patterns = NULL; int r; + assert(bus); + assert(ret); + STRV_FOREACH(pattern, patterns) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *unit = NULL, *unit_id = NULL; @@ -110,10 +127,9 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { if (r < 0) return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); - if (!streq(*pattern, unit_id)) { + if (!streq(*pattern, unit_id)) if (strv_extend(&expanded_patterns, unit_id) < 0) return log_oom(); - } } *ret = TAKE_PTR(expanded_patterns); /* do not free */ @@ -128,8 +144,8 @@ int verb_dot(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **expanded_patterns = NULL; _cleanup_strv_free_ char **expanded_from_patterns = NULL; _cleanup_strv_free_ char **expanded_to_patterns = NULL; - int r; UnitInfo u; + int r; r = acquire_bus(&bus, NULL); if (r < 0) @@ -170,6 +186,7 @@ int verb_dot(int argc, char *argv[], void *userdata) { log_info(" Color legend: black = Requires\n" " dark blue = Requisite\n" + " gold = BindsTo\n" " dark grey = Wants\n" " red = Conflicts\n" " green = After\n"); diff --git a/src/analyze/analyze-exit-status.c b/src/analyze/analyze-exit-status.c index 3a8d3f4..1032f1a 100644 --- a/src/analyze/analyze-exit-status.c +++ b/src/analyze/analyze-exit-status.c @@ -46,11 +46,9 @@ int verb_exit_status(int argc, char *argv[], void *userdata) { return table_log_add_error(r); } - pager_open(arg_pager_flags); - - r = table_print(table, NULL); + r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); if (r < 0) - return r; + return log_error_errno(r, "Failed to output table: %m"); return EXIT_SUCCESS; } diff --git a/src/analyze/analyze-fdstore.c b/src/analyze/analyze-fdstore.c index 13db7f5..8ada6d4 100644 --- a/src/analyze/analyze-fdstore.c +++ b/src/analyze/analyze-fdstore.c @@ -81,12 +81,12 @@ static int dump_fdstore(sd_bus *bus, const char *arg) { if (r < 0) return r; - if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && table_get_rows(table) <= 0) + if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && table_isempty(table)) log_info("No file descriptors in fdstore of '%s'.", unit); else { r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */true); if (r < 0) - return log_error_errno(r, "Failed to output table: %m"); + return r; } return EXIT_SUCCESS; diff --git a/src/analyze/analyze-image-policy.c b/src/analyze/analyze-image-policy.c index 0146b50..7d4f549 100644 --- a/src/analyze/analyze-image-policy.c +++ b/src/analyze/analyze-image-policy.c @@ -94,6 +94,8 @@ int verb_image_policy(int argc, char *argv[], void *userdata) { p = &image_policy_sysext_strict; else if (streq(argv[i], "@confext")) p = &image_policy_confext; + else if (streq(argv[i], "@confext-strict")) + p = &image_policy_confext_strict; else if (streq(argv[i], "@container")) p = &image_policy_container; else if (streq(argv[i], "@service")) diff --git a/src/analyze/analyze-pcrs.c b/src/analyze/analyze-pcrs.c index ed907f7..43e415f 100644 --- a/src/analyze/analyze-pcrs.c +++ b/src/analyze/analyze-pcrs.c @@ -48,7 +48,7 @@ static int get_current_pcr(const char *alg, uint32_t pcr, void **ret, size_t *re if (r < 0) return log_error_errno(r, "Failed to read '%s': %m", p); - r = unhexmem(s, ss, &buf, &bufsize); + r = unhexmem_full(s, ss, /* secure = */ false, &buf, &bufsize); if (r < 0) return log_error_errno(r, "Failed to decode hex PCR data '%s': %m", s); diff --git a/src/analyze/analyze-plot.c b/src/analyze/analyze-plot.c index 81fc25b..e271296 100644 --- a/src/analyze/analyze-plot.c +++ b/src/analyze/analyze-plot.c @@ -168,7 +168,7 @@ static void plot_tooltip(const UnitTimes *ut) { svg("%s:\n", ut->name); UnitDependency i; - VA_ARGS_FOREACH(i, UNIT_AFTER, UNIT_BEFORE, UNIT_REQUIRES, UNIT_REQUISITE, UNIT_WANTS, UNIT_CONFLICTS, UNIT_UPHOLDS) + FOREACH_ARGUMENT(i, UNIT_AFTER, UNIT_BEFORE, UNIT_REQUIRES, UNIT_REQUISITE, UNIT_WANTS, UNIT_CONFLICTS, UNIT_UPHOLDS) if (!strv_isempty(ut->deps[i])) { svg("\n%s:\n", unit_dependency_to_string(i)); STRV_FOREACH(s, ut->deps[i]) @@ -316,7 +316,10 @@ static int produce_plot_as_svg( strempty(host->virtualization)); svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time)); - svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time); + if (boot->soft_reboots_count > 0) + svg_graph_box(m, 0, boot->finish_time); + else + svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time); if (timestamp_is_set(boot->firmware_time)) { svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y); @@ -344,6 +347,11 @@ static int produce_plot_as_svg( svg_text(true, boot->initrd_time, y, "initrd"); y++; } + if (boot->soft_reboots_count > 0) { + svg_bar("soft-reboot", 0, boot->userspace_time, y); + svg_text(true, 0, y, "soft-reboot"); + y++; + } for (u = times; u->has_data; u++) { if (u->activating >= boot->userspace_time) @@ -402,7 +410,7 @@ static int show_table(Table *table, const char *word) { assert(table); assert(word); - if (table_get_rows(table) > 1) { + if (!table_isempty(table)) { table_set_header(table, arg_legend); if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) @@ -414,10 +422,10 @@ static int show_table(Table *table, const char *word) { } if (arg_legend) { - if (table_get_rows(table) > 1) - printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word); - else + if (table_isempty(table)) printf("No %s.\n", word); + else + printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word); } return 0; diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c index 5f1b5e6..75508f4 100644 --- a/src/analyze/analyze-security.c +++ b/src/analyze/analyze-security.c @@ -215,20 +215,21 @@ static int assess_user( uint64_t *ret_badness, char **ret_description) { - _cleanup_free_ char *d = NULL; + const char *d; uint64_t b; + int r; assert(ret_badness); assert(ret_description); if (streq_ptr(info->user, NOBODY_USER_NAME)) { - d = strdup("Service runs under as '" NOBODY_USER_NAME "' user, which should not be used for services"); + d = "Service runs under as '" NOBODY_USER_NAME "' user, which should not be used for services"; b = 9; } else if (info->dynamic_user && !STR_IN_SET(info->user, "0", "root")) { - d = strdup("Service runs under a transient non-root user identity"); + d = "Service runs under a transient non-root user identity"; b = 0; } else if (info->user && !STR_IN_SET(info->user, "0", "root", "")) { - d = strdup("Service runs under a static non-root user identity"); + d = "Service runs under a static non-root user identity"; b = 0; } else { *ret_badness = 10; @@ -236,12 +237,11 @@ static int assess_user( return 0; } - if (!d) - return log_oom(); + r = strdup_to(ret_description, d); + if (r < 0) + return r; *ret_badness = b; - *ret_description = TAKE_PTR(d); - return 0; } @@ -254,7 +254,6 @@ static int assess_protect_home( const char *description; uint64_t badness; - char *copy; int r; assert(ret_badness); @@ -277,13 +276,11 @@ static int assess_protect_home( description = "Service has no access to home directories"; } - copy = strdup(description); - if (!copy) - return log_oom(); + r = strdup_to(ret_description, description); + if (r < 0) + return r; *ret_badness = badness; - *ret_description = copy; - return 0; } @@ -296,7 +293,6 @@ static int assess_protect_system( const char *description; uint64_t badness; - char *copy; int r; assert(ret_badness); @@ -319,13 +315,11 @@ static int assess_protect_system( description = "Service has limited write access to the OS file hierarchy"; } - copy = strdup(description); - if (!copy) - return log_oom(); + r = strdup_to(ret_description, description); + if (r < 0) + return r; *ret_badness = badness; - *ret_description = copy; - return 0; } @@ -370,9 +364,9 @@ static int assess_umask( uint64_t *ret_badness, char **ret_description) { - char *copy = NULL; const char *d; uint64_t b; + int r; assert(ret_badness); assert(ret_description); @@ -394,13 +388,11 @@ static int assess_umask( b = 0; } - copy = strdup(d); - if (!copy) - return log_oom(); + r = strdup_to(ret_description, d); + if (r < 0) + return r; *ret_badness = b; - *ret_description = copy; - return 0; } @@ -537,30 +529,30 @@ static int assess_system_call_architectures( uint64_t *ret_badness, char **ret_description) { - char *d; + const char *d; uint64_t b; + int r; assert(ret_badness); assert(ret_description); if (set_isempty(info->system_call_architectures)) { b = 10; - d = strdup("Service may execute system calls with all ABIs"); + d = "Service may execute system calls with all ABIs"; } else if (set_contains(info->system_call_architectures, "native") && set_size(info->system_call_architectures) == 1) { b = 0; - d = strdup("Service may execute system calls only with native ABI"); + d = "Service may execute system calls only with native ABI"; } else { b = 8; - d = strdup("Service may execute system calls with multiple ABIs"); + d = "Service may execute system calls with multiple ABIs"; } - if (!d) - return log_oom(); + r = strdup_to(ret_description, d); + if (r < 0) + return r; *ret_badness = b; - *ret_description = d; - return 0; } @@ -607,12 +599,12 @@ static int assess_system_call_filter( assert(a->parameter < _SYSCALL_FILTER_SET_MAX); const SyscallFilterSet *f = syscall_filter_sets + a->parameter; - _cleanup_free_ char *d = NULL; + char *d; uint64_t b; int r; if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) { - r = free_and_strdup(&d, "Service does not filter system calls"); + r = strdup_to(&d, "Service does not filter system calls"); b = 10; } else { bool bad; @@ -649,8 +641,8 @@ static int assess_system_call_filter( if (r < 0) return log_oom(); + *ret_description = d; *ret_badness = b; - *ret_description = TAKE_PTR(d); return 0; } @@ -664,36 +656,36 @@ static int assess_ip_address_allow( uint64_t *ret_badness, char **ret_description) { - char *d = NULL; + const char *d; uint64_t b; + int r; assert(info); assert(ret_badness); assert(ret_description); if (info->ip_filters_custom_ingress || info->ip_filters_custom_egress) { - d = strdup("Service defines custom ingress/egress IP filters with BPF programs"); + d = "Service defines custom ingress/egress IP filters with BPF programs"; b = 0; } else if (!info->ip_address_deny_all) { - d = strdup("Service does not define an IP address allow list"); + d = "Service does not define an IP address allow list"; b = 10; } else if (info->ip_address_allow_other) { - d = strdup("Service defines IP address allow list with non-localhost entries"); + d = "Service defines IP address allow list with non-localhost entries"; b = 5; } else if (info->ip_address_allow_localhost) { - d = strdup("Service defines IP address allow list with only localhost entries"); + d = "Service defines IP address allow list with only localhost entries"; b = 2; } else { - d = strdup("Service blocks all IP address ranges"); + d = "Service blocks all IP address ranges"; b = 0; } - if (!d) - return log_oom(); + r = strdup_to(ret_description, d); + if (r < 0) + return r; *ret_badness = b; - *ret_description = d; - return 0; } @@ -704,7 +696,7 @@ static int assess_device_allow( uint64_t *ret_badness, char **ret_description) { - char *d = NULL; + char *d; uint64_t b; assert(info); @@ -1651,7 +1643,7 @@ static uint64_t access_weight(const struct security_assessor *a, JsonVariant *po assert(a); val = security_assessor_find_in_policy(a, policy, "weight"); - if (val) { + if (val) { if (json_variant_is_unsigned(val)) return json_variant_unsigned(val); log_debug("JSON field 'weight' of policy for %s is not an unsigned integer, ignoring.", a->id); @@ -1666,7 +1658,7 @@ static uint64_t access_range(const struct security_assessor *a, JsonVariant *pol assert(a); val = security_assessor_find_in_policy(a, policy, "range"); - if (val) { + if (val) { if (json_variant_is_unsigned(val)) return json_variant_unsigned(val); log_debug("JSON field 'range' of policy for %s is not an unsigned integer, ignoring.", a->id); @@ -1681,7 +1673,7 @@ static const char *access_description_na(const struct security_assessor *a, Json assert(a); val = security_assessor_find_in_policy(a, policy, "description_na"); - if (val) { + if (val) { if (json_variant_is_string(val)) return json_variant_string(val); log_debug("JSON field 'description_na' of policy for %s is not a string, ignoring.", a->id); @@ -1696,7 +1688,7 @@ static const char *access_description_good(const struct security_assessor *a, Js assert(a); val = security_assessor_find_in_policy(a, policy, "description_good"); - if (val) { + if (val) { if (json_variant_is_string(val)) return json_variant_string(val); log_debug("JSON field 'description_good' of policy for %s is not a string, ignoring.", a->id); @@ -1711,7 +1703,7 @@ static const char *access_description_bad(const struct security_assessor *a, Jso assert(a); val = security_assessor_find_in_policy(a, policy, "description_bad"); - if (val) { + if (val) { if (json_variant_is_string(val)) return json_variant_string(val); log_debug("JSON field 'description_bad' of policy for %s is not a string, ignoring.", a->id); @@ -1764,15 +1756,14 @@ static int assess(const SecurityInfo *info, (void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 7); } - for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) { - const struct security_assessor *a = security_assessor_table + i; + FOREACH_ELEMENT(a, security_assessor_table) { _cleanup_free_ char *d = NULL; uint64_t badness; void *data; uint64_t weight = access_weight(a, policy); uint64_t range = access_range(a, policy); - data = (uint8_t *) info + a->offset; + data = (uint8_t*) info + a->offset; if (a->default_dependencies_only && !info->default_dependencies) { badness = UINT64_MAX; @@ -2738,7 +2729,7 @@ static int offline_security_checks( /* When a portable image is analyzed, the profile is what provides a good chunk of * the security-related settings, but they are obviously not shipped with the image. - * This allows to take them in consideration. */ + * This allows them to be taken into consideration. */ if (profile) { _cleanup_free_ char *unit_name = NULL, *dropin = NULL, *profile_path = NULL; @@ -2828,7 +2819,6 @@ static int analyze_security(sd_bus *bus, for (;;) { UnitInfo info; - char *copy = NULL; r = bus_parse_unit_info(reply, &info); if (r < 0) @@ -2842,12 +2832,11 @@ static int analyze_security(sd_bus *bus, if (!GREEDY_REALLOC(list, n + 2)) return log_oom(); - copy = strdup(info.id); - if (!copy) - return log_oom(); + r = strdup_to(&list[n], info.id); + if (r < 0) + return r; - list[n++] = copy; - list[n] = NULL; + list[++n] = NULL; } strv_sort(list); diff --git a/src/analyze/analyze-srk.c b/src/analyze/analyze-srk.c index 6faf2c2..acfd8b0 100644 --- a/src/analyze/analyze-srk.c +++ b/src/analyze/analyze-srk.c @@ -38,7 +38,7 @@ int verb_srk(int argc, char *argv[], void *userdata) { "Refusing to write binary data to TTY, please redirect output to file."); if (fwrite(marshalled, 1, marshalled_size, stdout) != marshalled_size) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write SRK to stdout: %m"); + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write SRK to stdout."); r = fflush_and_check(stdout); if (r < 0) diff --git a/src/analyze/analyze-time-data.c b/src/analyze/analyze-time-data.c index 741cab3..1a26991 100644 --- a/src/analyze/analyze-time-data.c +++ b/src/analyze/analyze-time-data.c @@ -38,6 +38,7 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) { { "FinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, finish_time) }, { "SecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_start_time) }, { "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_finish_time) }, + { "ShutdownStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, shutdown_start_time) }, { "GeneratorsStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_start_time) }, { "GeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_finish_time) }, { "UnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, unitsload_start_time) }, @@ -48,6 +49,7 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) { { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_finish_time) }, { "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_start_time) }, { "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_finish_time) }, + { "SoftRebootsCount", "t", NULL, offsetof(BootTimes, soft_reboots_count) }, {}, }; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -81,7 +83,25 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) { if (require_finished && times.finish_time <= 0) return log_not_finished(times.finish_time); - if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && times.security_start_time > 0) { + if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && times.soft_reboots_count > 0) { + /* On soft-reboot ignore kernel/firmware/initrd times as they are from the previous boot */ + times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = + times.initrd_security_start_time = times.initrd_security_finish_time = + times.initrd_generators_start_time = times.initrd_generators_finish_time = + times.initrd_unitsload_start_time = times.initrd_unitsload_finish_time = 0; + times.reverse_offset = times.shutdown_start_time; + + /* Clamp all timestamps to avoid showing huge graphs */ + if (timestamp_is_set(times.finish_time)) + subtract_timestamp(×.finish_time, times.reverse_offset); + subtract_timestamp(×.userspace_time, times.reverse_offset); + + subtract_timestamp(×.generators_start_time, times.reverse_offset); + subtract_timestamp(×.generators_finish_time, times.reverse_offset); + + subtract_timestamp(×.unitsload_start_time, times.reverse_offset); + subtract_timestamp(×.unitsload_finish_time, times.reverse_offset); + } else if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && timestamp_is_set(times.security_start_time)) { /* security_start_time is set when systemd is not running under container environment. */ if (times.initrd_time > 0) times.kernel_done_time = times.initrd_time; @@ -183,6 +203,8 @@ int pretty_boot_time(sd_bus *bus, char **ret) { return log_oom(); if (timestamp_is_set(t->initrd_time) && !strextend(&text, FORMAT_TIMESPAN(t->userspace_time - t->initrd_time, USEC_PER_MSEC), " (initrd) + ")) return log_oom(); + if (t->soft_reboots_count > 0 && strextendf(&text, "%s (soft reboot #%" PRIu64 ") + ", FORMAT_TIMESPAN(t->userspace_time, USEC_PER_MSEC), t->soft_reboots_count) < 0) + return log_oom(); if (!strextend(&text, FORMAT_TIMESPAN(t->finish_time - t->userspace_time, USEC_PER_MSEC), " (userspace) ")) return log_oom(); @@ -192,7 +214,13 @@ int pretty_boot_time(sd_bus *bus, char **ret) { return log_oom(); if (unit_id && timestamp_is_set(activated_time)) { - usec_t base = timestamp_is_set(t->userspace_time) ? t->userspace_time : t->reverse_offset; + usec_t base; + + /* On soft-reboot times are clamped to avoid showing huge graphs */ + if (t->soft_reboots_count > 0 && timestamp_is_set(t->userspace_time)) + base = t->userspace_time + t->reverse_offset; + else + base = timestamp_is_set(t->userspace_time) ? t->userspace_time : t->reverse_offset; if (!strextend(&text, "\n", unit_id, " reached after ", FORMAT_TIMESPAN(activated_time - base, USEC_PER_MSEC), " in userspace.")) return log_oom(); @@ -221,7 +249,7 @@ void unit_times_clear(UnitTimes *t) { if (!t) return; - FOREACH_ARRAY(d, t->deps, ELEMENTSOF(t->deps)) + FOREACH_ELEMENT(d, t->deps) *d = strv_free(*d); t->name = mfree(t->name); @@ -299,10 +327,28 @@ int acquire_time_data(sd_bus *bus, bool require_finished, UnitTimes **out) { return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s", u.id, bus_error_message(&error, r)); + /* Activated in the previous soft-reboot iteration? Ignore it, we want new activations */ + if ((t->activated > 0 && t->activated < boot_times->shutdown_start_time) || + (t->activating > 0 && t->activating < boot_times->shutdown_start_time)) + continue; + subtract_timestamp(&t->activating, boot_times->reverse_offset); subtract_timestamp(&t->activated, boot_times->reverse_offset); - subtract_timestamp(&t->deactivating, boot_times->reverse_offset); - subtract_timestamp(&t->deactivated, boot_times->reverse_offset); + + /* If the last deactivation was in the previous soft-reboot, ignore it */ + if (boot_times->soft_reboots_count > 0) { + if (t->deactivating < boot_times->reverse_offset) + t->deactivating = 0; + else + subtract_timestamp(&t->deactivating, boot_times->reverse_offset); + if (t->deactivated < boot_times->reverse_offset) + t->deactivated = 0; + else + subtract_timestamp(&t->deactivated, boot_times->reverse_offset); + } else { + subtract_timestamp(&t->deactivating, boot_times->reverse_offset); + subtract_timestamp(&t->deactivated, boot_times->reverse_offset); + } if (t->activated >= t->activating) t->time = t->activated - t->activating; diff --git a/src/analyze/analyze-time-data.h b/src/analyze/analyze-time-data.h index 9049d87..e7ffd85 100644 --- a/src/analyze/analyze-time-data.h +++ b/src/analyze/analyze-time-data.h @@ -14,6 +14,7 @@ typedef struct BootTimes { usec_t initrd_time; usec_t userspace_time; usec_t finish_time; + usec_t shutdown_start_time; usec_t security_start_time; usec_t security_finish_time; usec_t generators_start_time; @@ -26,6 +27,8 @@ typedef struct BootTimes { usec_t initrd_generators_finish_time; usec_t initrd_unitsload_start_time; usec_t initrd_unitsload_finish_time; + /* Not strictly a timestamp, but we are going to show it next to the other timestamps */ + uint64_t soft_reboots_count; /* * If we're analyzing the user instance, all timestamps will be offset by its own start-up timestamp, diff --git a/src/analyze/analyze-unit-files.c b/src/analyze/analyze-unit-files.c index d9b3313..e0c4867 100644 --- a/src/analyze/analyze-unit-files.c +++ b/src/analyze/analyze-unit-files.c @@ -15,7 +15,7 @@ static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int f int verb_unit_files(int argc, char *argv[], void *userdata) { _cleanup_hashmap_free_ Hashmap *unit_ids = NULL, *unit_names = NULL; - _cleanup_(lookup_paths_free) LookupPaths lp = {}; + _cleanup_(lookup_paths_done) LookupPaths lp = {}; char **patterns = strv_skip(argv, 1); const char *k, *dst; char **v; diff --git a/src/analyze/analyze-unit-paths.c b/src/analyze/analyze-unit-paths.c index bb00a4f..17f18e0 100644 --- a/src/analyze/analyze-unit-paths.c +++ b/src/analyze/analyze-unit-paths.c @@ -6,7 +6,7 @@ #include "strv.h" int verb_unit_paths(int argc, char *argv[], void *userdata) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; + _cleanup_(lookup_paths_done) LookupPaths paths = {}; int r; r = lookup_paths_init_or_warn(&paths, arg_runtime_scope, 0, NULL); diff --git a/src/analyze/analyze-verify-util.c b/src/analyze/analyze-verify-util.c index 6fbd6fa..8e83c9a 100644 --- a/src/analyze/analyze-verify-util.c +++ b/src/analyze/analyze-verify-util.c @@ -152,7 +152,7 @@ int verify_set_unit_path(char **filenames) { * Treat explicit empty path to mean that nothing should be appended. */ old = getenv("SYSTEMD_UNIT_PATH"); if (!streq_ptr(old, "") && - !strextend_with_separator(&joined, ":", old ?: "")) + !strextend_with_separator(&joined, ":", strempty(old))) return -ENOMEM; assert_se(set_unit_path(joined) >= 0); @@ -201,19 +201,15 @@ static int verify_executables(Unit *u, const char *root) { assert(u); - ExecCommand *exec = - u->type == UNIT_SOCKET ? SOCKET(u)->control_command : - u->type == UNIT_MOUNT ? MOUNT(u)->control_command : - u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL; - RET_GATHER(r, verify_executable(u, exec, root)); - if (u->type == UNIT_SERVICE) - FOREACH_ARRAY(i, SERVICE(u)->exec_command, ELEMENTSOF(SERVICE(u)->exec_command)) - RET_GATHER(r, verify_executable(u, *i, root)); + FOREACH_ELEMENT(i, SERVICE(u)->exec_command) + LIST_FOREACH(command, j, *i) + RET_GATHER(r, verify_executable(u, j, root)); if (u->type == UNIT_SOCKET) - FOREACH_ARRAY(i, SOCKET(u)->exec_command, ELEMENTSOF(SOCKET(u)->exec_command)) - RET_GATHER(r, verify_executable(u, *i, root)); + FOREACH_ELEMENT(i, SOCKET(u)->exec_command) + LIST_FOREACH(command, j, *i) + RET_GATHER(r, verify_executable(u, j, root)); return r; } diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 021de65..cf4894a 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -13,6 +13,7 @@ #include "alloc-util.h" #include "analyze.h" +#include "analyze-architectures.h" #include "analyze-blame.h" #include "analyze-calendar.h" #include "analyze-capability.h" @@ -25,6 +26,7 @@ #include "analyze-exit-status.h" #include "analyze-fdstore.h" #include "analyze-filesystems.h" +#include "analyze-image-policy.h" #include "analyze-inspect-elf.h" #include "analyze-log-control.h" #include "analyze-malloc.h" @@ -41,7 +43,6 @@ #include "analyze-unit-files.h" #include "analyze-unit-paths.h" #include "analyze-verify.h" -#include "analyze-image-policy.h" #include "build.h" #include "bus-error.h" #include "bus-locator.h" @@ -224,6 +225,7 @@ static int help(int argc, char *argv[], void *userdata) { " capability [CAP...] List capability definitions\n" " syscall-filter [NAME...] List syscalls in seccomp filters\n" " filesystems [NAME...] List known filesystems\n" + " architectures [NAME...] List known architectures\n" " condition CONDITION... Evaluate conditions and asserts\n" " compare-versions VERSION1 [OP] VERSION2\n" " Compare two version strings\n" @@ -238,7 +240,7 @@ static int help(int argc, char *argv[], void *userdata) { " fdstore SERVICE... Show file descriptor store contents of service\n" " image-policy POLICY... Analyze image policy string\n" " pcrs [PCR...] Show TPM2 PCRs and their names\n" - " srk > FILE Write TPM2 SRK to stdout\n" + " srk [>FILE] Write TPM2 SRK (to FILE)\n" "\nOptions:\n" " --recursive-errors=MODE Control which units are verified\n" " --offline=BOOL Perform a security review on unit file(s)\n" @@ -270,6 +272,7 @@ static int help(int argc, char *argv[], void *userdata) { " specified time\n" " --profile=name|PATH Include the specified profile in the\n" " security review of the unit(s)\n" + " --unit=UNIT Evaluate conditions and asserts of unit\n" " --table Output plot's raw time data as a table\n" " -h --help Show this help\n" " --version Show package version\n" @@ -557,18 +560,21 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --offline= is only supported for security right now."); - if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs")) + if (arg_offline && optind >= argc - 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Option --offline= requires one or more units to perform a security review."); + + if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs", "architectures", "capability", "exit-status")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Option --json= is only supported for security, inspect-elf, plot, fdstore, pcrs right now."); + "Option --json= is only supported for security, inspect-elf, plot, fdstore, pcrs, architectures, capability, exit-status right now."); if (arg_threshold != 100 && !streq_ptr(argv[optind], "security")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --threshold= is only supported for security right now."); - if (arg_runtime_scope == RUNTIME_SCOPE_GLOBAL && - !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify")) + if (arg_runtime_scope == RUNTIME_SCOPE_GLOBAL && !streq_ptr(argv[optind], "unit-paths")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Option --global only makes sense with verbs dot, unit-paths, verify."); + "Option --global only makes sense with verb unit-paths."); if (streq_ptr(argv[optind], "cat-config") && arg_runtime_scope == RUNTIME_SCOPE_USER) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -596,7 +602,7 @@ static int parse_argv(int argc, char *argv[]) { if (streq_ptr(argv[optind], "condition") && arg_unit && optind < argc - 1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used."); - if ((!arg_legend && !streq_ptr(argv[optind], "plot")) || + if ((!arg_legend && !STRPTR_IN_SET(argv[optind], "plot", "architectures")) || (streq_ptr(argv[optind], "plot") && !arg_legend && !arg_table && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --no-legend is only supported for plot with either --table or --json=."); @@ -650,6 +656,7 @@ static int run(int argc, char *argv[]) { { "image-policy", 2, 2, 0, verb_image_policy }, { "pcrs", VERB_ANY, VERB_ANY, 0, verb_pcrs }, { "srk", VERB_ANY, 1, 0, verb_srk }, + { "architectures", VERB_ANY, VERB_ANY, 0, verb_architectures }, {} }; @@ -673,7 +680,8 @@ static int run(int argc, char *argv[]) { arg_image_policy, DISSECT_IMAGE_GENERIC_ROOT | DISSECT_IMAGE_RELAX_VAR_CHECK | - DISSECT_IMAGE_READ_ONLY, + DISSECT_IMAGE_READ_ONLY | + DISSECT_IMAGE_ALLOW_USERSPACE_VERITY, &mounted_dir, /* ret_dir_fd= */ NULL, &loop_device); diff --git a/src/analyze/meson.build b/src/analyze/meson.build index a505447..f150ed7 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1-or-later systemd_analyze_sources = files( + 'analyze-architectures.c', 'analyze-blame.c', 'analyze-calendar.c', 'analyze-capability.c', |