]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/analyze/analyze-security.c
strv: make iterator in STRV_FOREACH() declaread in the loop
[thirdparty/systemd.git] / src / analyze / analyze-security.c
index 400eebbda8a99781b6205b587ac0b58fc38dc140..458b681143a12a7b3b81de499f8fd9adfedb8f6e 100644 (file)
@@ -3,13 +3,17 @@
 #include <sys/utsname.h>
 
 #include "af-list.h"
+#include "analyze.h"
 #include "analyze-security.h"
 #include "analyze-verify.h"
 #include "bus-error.h"
 #include "bus-map-properties.h"
 #include "bus-unit-util.h"
 #include "bus-util.h"
+#include "copy.h"
 #include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
 #include "format-table.h"
 #include "in-addr-prefix-util.h"
 #include "locale-util.h"
@@ -17,6 +21,7 @@
 #include "manager.h"
 #include "missing_capability.h"
 #include "missing_sched.h"
+#include "mkdir.h"
 #include "nulstr-util.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -1764,6 +1769,11 @@ static int assess(const SecurityInfo *info,
                         d = strdup("Service runs in special boot phase, option is not appropriate");
                         if (!d)
                                 return log_oom();
+                } else if (weight == 0) {
+                        badness = UINT64_MAX;
+                        d = strdup("Option excluded by policy, skipping");
+                        if (!d)
+                                return log_oom();
                 } else {
                         r = a->assess(a, info, data, &badness, &d);
                         if (r < 0)
@@ -1895,7 +1905,7 @@ static int assess(const SecurityInfo *info,
                         name = info->id;
 
                 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64 ".%" PRIu64 " %s%s %s\n",
-                       special_glyph(SPECIAL_GLYPH_ARROW),
+                       special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
                        ansi_highlight(),
                        name,
                        ansi_normal(),
@@ -2641,6 +2651,7 @@ static int offline_security_checks(char **filenames,
                                    bool run_generators,
                                    unsigned threshold,
                                    const char *root,
+                                   const char *profile,
                                    PagerFlags pager_flags,
                                    JsonFormatFlags json_format_flags) {
 
@@ -2655,7 +2666,6 @@ static int offline_security_checks(char **filenames,
         _cleanup_free_ char *var = NULL;
         int r, k;
         size_t count = 0;
-        char **filename;
 
         if (strv_isempty(filenames))
                 return 0;
@@ -2677,6 +2687,13 @@ static int offline_security_checks(char **filenames,
         if (r < 0)
                 return r;
 
+        if (profile) {
+                /* Ensure the temporary directory is in the search path, so that we can add drop-ins. */
+                r = strv_extend(&m->lookup_paths.search_path, m->lookup_paths.temporary_dir);
+                if (r < 0)
+                        return log_oom();
+        }
+
         log_debug("Loading remaining units from the command line...");
 
         STRV_FOREACH(filename, filenames) {
@@ -2692,6 +2709,33 @@ static int offline_security_checks(char **filenames,
                         continue;
                 }
 
+                /* 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. */
+                if (profile) {
+                        _cleanup_free_ char *unit_name = NULL, *dropin = NULL, *profile_path = NULL;
+
+                        r = path_extract_filename(prepared, &unit_name);
+                        if (r < 0)
+                                return log_oom();
+
+                        dropin = strjoin(m->lookup_paths.temporary_dir, "/", unit_name, ".d/profile.conf");
+                        if (!dropin)
+                                return log_oom();
+                        (void) mkdir_parents(dropin, 0755);
+
+                        if (!is_path(profile)) {
+                                r = find_portable_profile(profile, unit_name, &profile_path);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to find portable profile %s: %m", profile);
+                                profile = profile_path;
+                        }
+
+                        r = copy_file(profile, dropin, 0, 0644, 0, 0, 0);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to copy: %m");
+                }
+
                 k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]);
                 if (k < 0) {
                         if (r == 0)
@@ -2711,7 +2755,7 @@ static int offline_security_checks(char **filenames,
         return r;
 }
 
-int analyze_security(sd_bus *bus,
+static int analyze_security(sd_bus *bus,
                      char **units,
                      JsonVariant *policy,
                      UnitFileScope scope,
@@ -2720,6 +2764,7 @@ int analyze_security(sd_bus *bus,
                      bool offline,
                      unsigned threshold,
                      const char *root,
+                     const char *profile,
                      JsonFormatFlags json_format_flags,
                      PagerFlags pager_flags,
                      AnalyzeSecurityFlags flags) {
@@ -2727,10 +2772,10 @@ int analyze_security(sd_bus *bus,
         _cleanup_(table_unrefp) Table *overview_table = NULL;
         int ret = 0, r;
 
-        assert(bus);
+        assert(!!bus != offline);
 
         if (offline)
-                return offline_security_checks(units, policy, scope, check_man, run_generators, threshold, root, pager_flags, json_format_flags);
+                return offline_security_checks(units, policy, scope, check_man, run_generators, threshold, root, profile, pager_flags, json_format_flags);
 
         if (strv_length(units) != 1) {
                 overview_table = table_new("unit", "exposure", "predicate", "happy");
@@ -2743,7 +2788,6 @@ int analyze_security(sd_bus *bus,
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
                 _cleanup_strv_free_ char **list = NULL;
                 size_t n = 0;
-                char **i;
 
                 r = sd_bus_call_method(
                                 bus,
@@ -2795,9 +2839,7 @@ int analyze_security(sd_bus *bus,
                                 ret = r;
                 }
 
-        } else {
-                char **i;
-
+        } else
                 STRV_FOREACH(i, units) {
                         _cleanup_free_ char *mangled = NULL, *instance = NULL;
                         const char *name;
@@ -2829,7 +2871,6 @@ int analyze_security(sd_bus *bus,
                         if (r < 0 && ret >= 0)
                                 ret = r;
                 }
-        }
 
         if (overview_table) {
                 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
@@ -2843,3 +2884,51 @@ int analyze_security(sd_bus *bus,
         }
         return ret;
 }
+
+int verb_security(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *policy = NULL;
+        int r;
+        unsigned line, column;
+
+        if (!arg_offline) {
+                r = acquire_bus(&bus, NULL);
+                if (r < 0)
+                        return bus_log_connect_error(r, arg_transport);
+        }
+
+        pager_open(arg_pager_flags);
+
+        if (arg_security_policy) {
+                r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
+        } else {
+                _cleanup_fclose_ FILE *f = NULL;
+                _cleanup_free_ char *pp = NULL;
+
+                r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp);
+                if (r < 0 && r != -ENOENT)
+                        return r;
+
+                if (f) {
+                        r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column);
+                        if (r < 0)
+                                return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
+                }
+        }
+
+        return analyze_security(bus,
+                                strv_skip(argv, 1),
+                                policy,
+                                arg_scope,
+                                arg_man,
+                                arg_generators,
+                                arg_offline,
+                                arg_threshold,
+                                arg_root,
+                                arg_profile,
+                                arg_json_format_flags,
+                                arg_pager_flags,
+                                /*flags=*/ 0);
+}