]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
selinux: use LabelContext in label callbacks
authorSimon de Vlieger <cmdr@supakeen.com>
Sat, 4 Jul 2026 10:44:53 +0000 (12:44 +0200)
committerSimon de Vlieger <cmdr@supakeen.com>
Fri, 31 Jul 2026 08:06:13 +0000 (10:06 +0200)
With the plumbing and context type in place, make the SELinux pre/post
callbacks use the alternate context when label_context is non-NULL, so
files get labeled according to the target image's policy rather than the
host's.

Errors from the host kernel not recognising image-specific contexts
(EINVAL from setfscreatecon_raw) are logged at debug level and skipped
gracefully, since this is expected when the image carries labels the host
policy doesn't define.

Signed-off-by: Simon de Vlieger <cmdr@supakeen.com>
src/shared/selinux-util.c

index 7f959c253559d62592996a7de8192baf6abf7330..61bd8ba7b9cc7220a230874787b39bfe11287769 100644 (file)
@@ -67,7 +67,11 @@ static int mac_selinux_label_pre(int dir_fd, const char *path, mode_t mode, Labe
 }
 
 static int mac_selinux_label_post(int dir_fd, const char *path, bool created, LabelContext *label_context) {
-        mac_selinux_create_file_clear();
+        if (label_context) {
+                PROTECT_ERRNO;
+                (void) sym_setfscreatecon_raw(NULL);
+        } else
+                mac_selinux_create_file_clear();
         return 0;
 }
 
@@ -396,12 +400,32 @@ static int setfilecon_idempotent(int fd, const char *context) {
         return RET_NERRNO(sym_setfilecon_raw(FORMAT_PROC_FD_PATH(fd), context));
 }
 
+static const char* strip_root_path(const char *path, const char *root) {
+        assert(path);
+        assert(root);
+
+        const char *suffix = path_startswith(path, root);
+        if (!suffix)
+                return NULL;
+
+        if (isempty(suffix))
+                return "/";
+
+        assert(suffix > path);
+        assert(suffix[-1] == '/');
+
+        return suffix - 1; /* back up to include the leading / */
+}
+
 static int selinux_fix_fd(
                 int fd,
                 const char *label_path,
-                LabelFixFlags flags) {
+                LabelFixFlags flags,
+                LabelContext *c) {
 
         _cleanup_freecon_ char* fcon = NULL;
+        struct selabel_handle *hnd;
+        const char *lookup_path;
         struct stat st;
         int r;
 
@@ -412,16 +436,30 @@ static int selinux_fix_fd(
         if (fstat(fd, &st) < 0)
                 return -errno;
 
-        /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
-        mac_selinux_maybe_reload();
-        if (!label_hnd)
-                return 0;
+        if (c) {
+                hnd = c->label_hnd;
 
-        if (sym_selabel_lookup_raw(label_hnd, &fcon, label_path, st.st_mode) < 0) {
+                lookup_path = strip_root_path(label_path, c->root);
+                if (!lookup_path)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Path '%s' is not under root '%s', refusing SELinux label fix.", label_path, c->root);
+        } else {
+                /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+                mac_selinux_maybe_reload();
+                if (!label_hnd)
+                        return 0;
+                hnd = label_hnd;
+                lookup_path = label_path;
+        }
+
+        if (sym_selabel_lookup_raw(hnd, &fcon, lookup_path, st.st_mode) < 0) {
                 /* If there's no label to set, then exit without warning */
                 if (errno == ENOENT)
                         return 0;
 
+                if (c)
+                        return log_debug_errno(errno, "Unable to look up intended SELinux security context of %s (looked up as %s): %m", label_path, lookup_path);
+
                 return log_selinux_enforcing_errno(errno, "Unable to lookup intended SELinux security context of %s: %m", label_path);
         }
 
@@ -437,6 +475,16 @@ static int selinux_fix_fd(
         if (r == -EROFS && (flags & LABEL_IGNORE_EROFS))
                 return 0;
 
+        if (c) {
+                /* EINVAL means the host kernel doesn't know this context, degrade gracefully */
+                if (r == -EINVAL) {
+                        log_debug_errno(r, "Unable to fix SELinux security context of %s, ignoring: %m", label_path);
+                        return 0;
+                }
+
+                return log_debug_errno(r, "Unable to fix SELinux security context of %s: %m", label_path);
+        }
+
         return log_selinux_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", label_path);
 }
 #endif
@@ -456,12 +504,19 @@ int mac_selinux_fix_full(
         _cleanup_free_ char *p = NULL;
         int inode_fd, r;
 
-        r = selinux_init(/* force= */ false);
-        if (r <= 0)
-                return r;
+        LabelContext *c = label_context;
 
-        if (!label_hnd)
-                return 0;
+        if (c) {
+                if (!c->label_hnd)
+                        return 0;
+        } else {
+                r = selinux_init(/* force= */ false);
+                if (r <= 0)
+                        return r;
+
+                if (!label_hnd)
+                        return 0;
+        }
 
         if (inode_path) {
                 opened_fd = openat(atfd, inode_path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
@@ -488,7 +543,7 @@ int mac_selinux_fix_full(
                 }
         }
 
-        return selinux_fix_fd(inode_fd, label_path, flags);
+        return selinux_fix_fd(inode_fd, label_path, flags, c);
 #else
         return 0;
 #endif
@@ -714,6 +769,52 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode)
 
         return 0;
 }
+
+static int selinux_create_file_prepare_context(int dir_fd, const char *path, mode_t mode, LabelContext *c) {
+        _cleanup_free_ char *abspath = NULL;
+        _cleanup_freecon_ char *filecon = NULL;
+        const char *lookup_path;
+        int r;
+
+        assert(c);
+        assert(c->label_hnd);
+
+        /* Resolve the path to absolute if needed */
+        if (isempty(path) || !path_is_absolute(path)) {
+                r = fd_get_path(dir_fd, &abspath);
+                if (r < 0)
+                        return r;
+
+                if (!isempty(path) && !path_extend(&abspath, path))
+                        return -ENOMEM;
+
+                path = abspath;
+        }
+
+        /* Strip the root prefix so we look up the path as it would appear inside the image */
+        lookup_path = strip_root_path(path, c->root);
+        if (!lookup_path)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Path '%s' is not under root '%s', refusing SELinux labeling.", path, c->root);
+
+        r = RET_NERRNO(sym_selabel_lookup_raw(c->label_hnd, &filecon, lookup_path, mode));
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_debug_errno(r, "Failed to determine SELinux security context for %s (looked up as %s): %m", path, lookup_path);
+
+        if (sym_setfscreatecon_raw(filecon) < 0) {
+                /* EINVAL means the host kernel doesn't know this context, degrade gracefully */
+                if (errno == EINVAL) {
+                        log_debug_errno(errno, "Failed to set SELinux security context %s for %s, ignoring: %m", filecon, path);
+                        return 0;
+                }
+
+                return log_debug_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, path);
+        }
+
+        return 0;
+}
 #endif
 
 int mac_selinux_create_file_prepare_at(
@@ -728,6 +829,9 @@ int mac_selinux_create_file_prepare_at(
 
         assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
 
+        if (label_context)
+                return selinux_create_file_prepare_context(dir_fd, path, mode, label_context);
+
         r = selinux_init(/* force= */ false);
         if (r <= 0)
                 return r;