]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: load IPE policy on boot
authorLuca Boccassi <bluca@debian.org>
Sat, 14 Sep 2024 12:27:53 +0000 (14:27 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 2 Oct 2024 16:29:43 +0000 (18:29 +0200)
IPE is a new LSM being introduced in 6.12. Like IMA, it works based on a
policy file that has to be loaded at boot, the earlier the better. So
like IMA, if such a policy is present, load it and activate it.

If there are any .p7b files in /etc/ipe/, load them as policies.
The files have to be inline signed in DER format as per IPE documentation.

For more information on the details of IPE:

https://microsoft.github.io/ipe/

meson.build
meson_options.txt
src/basic/build.c
src/core/ipe-setup.c [new file with mode: 0644]
src/core/ipe-setup.h [new file with mode: 0644]
src/core/main.c
src/core/meson.build

index 724c48adc81f0371f1e524ab559fcbf649c02c48..e258a65a1ac2c08a0cb3ddf422c8972f7353545c 100644 (file)
@@ -1661,6 +1661,7 @@ foreach term : ['analyze',
                 'hwdb',
                 'idn',
                 'ima',
+                'ipe',
                 'initrd',
                 'kernel-install',
                 'ldconfig',
index 46e3ac55f7890ab784f85902c427c9536ac1b078..ec3688ab33c9a9d9504e22eaa53660436a457c22 100644 (file)
@@ -388,6 +388,8 @@ option('polkit', type : 'feature', deprecated : { 'true' : 'enabled', 'false' :
        description : 'polkit support')
 option('ima', type : 'boolean',
        description : 'IMA support')
+option('ipe', type : 'boolean',
+       description : 'IPE support')
 
 option('acl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libacl support')
index bb9b4185f01e51e9e83181bbc27eb523624ca2d3..97249ba14f1cb14693f25d4400a1a6b59362773c 100644 (file)
@@ -45,6 +45,12 @@ const char* const systemd_features =
         " -IMA"
 #endif
 
+#if ENABLE_IMA
+        " +IPE"
+#else
+        " -IPE"
+#endif
+
 #if ENABLE_SMACK
         " +SMACK"
 #else
diff --git a/src/core/ipe-setup.c b/src/core/ipe-setup.c
new file mode 100644 (file)
index 0000000..4648d43
--- /dev/null
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "conf-files.h"
+#include "copy.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "ipe-setup.h"
+#include "nulstr-util.h"
+#include "path-util.h"
+
+#define IPE_SECFS_DIR "/sys/kernel/security/ipe"
+#define IPE_SECFS_NEW_POLICY IPE_SECFS_DIR "/new_policy"
+#define IPE_SECFS_POLICIES IPE_SECFS_DIR "/policies/"
+
+int ipe_setup(void) {
+#if ENABLE_IPE
+        _cleanup_strv_free_ char **policies = NULL;
+        int r;
+
+        /* Very quick smoke tests first: this is in the citical, sequential boot path, and in most cases it
+         * is unlikely this will be configured, so do the fastest existence checks first and immediately
+         * return if there's nothing to do. */
+
+        if (access(IPE_SECFS_DIR, F_OK) < 0) {
+                log_debug_errno(errno, "IPE support is disabled in the kernel, ignoring: %m");
+                return 0;
+        }
+
+        r = conf_files_list_nulstr(
+                        &policies,
+                        ".p7b",
+                        /* root= */ NULL,
+                        CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED,
+                        CONF_PATHS_NULSTR("ipe"));
+        if (r < 0)
+                return log_error_errno(r, "Failed to assemble list of IPE policies: %m");
+
+        STRV_FOREACH(policy, policies) {
+                _cleanup_free_ char *policy_name = NULL, *file_name = NULL, *output_path = NULL, *activate_path = NULL;
+                _cleanup_close_ int input = -EBADF, output = -EBADF;
+                const char *suffix;
+
+                r = path_extract_filename(*policy, &file_name);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to extract filename from IPE policy path %s: %m", *policy);
+
+                /* Filtered by conf_files_list_nulstr() */
+                suffix = ASSERT_PTR(endswith(file_name, ".p7b"));
+
+                policy_name = strndup(file_name, suffix - file_name);
+                if (!policy_name)
+                        return log_oom();
+
+                if (!filename_is_valid(policy_name))
+                        return log_error_errno(
+                                        SYNTHETIC_ERRNO(EINVAL),
+                                        "Invalid IPE policy name %s",
+                                        policy_name);
+
+                input = open(*policy, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
+                if (input < 0)
+                        return log_error_errno(
+                                        errno,
+                                        "Failed to open the IPE policy file %s: %m",
+                                        *policy);
+
+                /* If policy is already installed, try to update it */
+                output_path = path_join(IPE_SECFS_POLICIES, policy_name, "update");
+                if (!output_path)
+                        return log_oom();
+
+                output = open(output_path, O_WRONLY|O_CLOEXEC);
+                if (output < 0 && errno == ENOENT)
+                        /* Policy is not installed, install it and activate it */
+                        output = open(IPE_SECFS_NEW_POLICY, O_WRONLY|O_CLOEXEC);
+                if (output < 0)
+                        return log_error_errno(
+                                        errno,
+                                        "Failed to open the IPE policy handle for writing: %m");
+
+                /* The policy is inline signed in binary format, so it has to be copied in one go, otherwise the
+                 * kernel will reject partial inputs with -EBADMSG. */
+                r = copy_bytes(input, output, UINT64_MAX, /* copy_flags= */ 0);
+                if (r < 0)
+                        return log_error_errno(
+                                        r,
+                                        "Failed to copy the IPE policy %s to %s: %m",
+                                        *policy,
+                                        output_path);
+
+                output = safe_close(output);
+
+                activate_path = path_join(IPE_SECFS_POLICIES, policy_name, "active");
+                if (!activate_path)
+                        return log_oom();
+
+                r = write_string_file(activate_path, "1", WRITE_STRING_FILE_DISABLE_BUFFER);
+                if (r == -ESTALE) {
+                        log_debug_errno(r,
+                                        "IPE policy %s is already loaded with a version that is equal or higher, skipping.",
+                                        policy_name);
+                        continue;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to activate the IPE policy %s: %m", policy_name);
+
+                log_info("Successfully loaded and activated the IPE policy %s.", policy_name);
+        }
+
+#endif /* ENABLE_IPE */
+        return 0;
+}
diff --git a/src/core/ipe-setup.h b/src/core/ipe-setup.h
new file mode 100644 (file)
index 0000000..dae1fad
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int ipe_setup(void);
index b9dc4506366b5177bddac9f816ccdecf01cd58d1..e526c37e6a5cc1d5122eff17bfefe32d57a895f6 100644 (file)
@@ -57,6 +57,7 @@
 #include "ima-setup.h"
 #include "import-creds.h"
 #include "initrd-util.h"
+#include "ipe-setup.h"
 #include "killall.h"
 #include "kmod-setup.h"
 #include "limits-util.h"
@@ -2920,6 +2921,12 @@ static int initialize_security(
                 return r;
         }
 
+        r = ipe_setup();
+        if (r < 0) {
+                *ret_error_message = "Failed to load IPE policy";
+                return r;
+        }
+
         dual_timestamp_now(security_finish_timestamp);
         return 0;
 }
index 28c10703ea0020b0a461ec478da5b11f1b0f5b0b..84cade9ae7500d66208406ef80a9fa0b1d3135df 100644 (file)
@@ -146,6 +146,7 @@ systemd_sources = files(
         'kmod-setup.c',
         'apparmor-setup.c',
         'ima-setup.c',
+        'ipe-setup.c',
         'selinux-setup.c',
         'smack-setup.c',
 )