selinux: use LabelContext in label callbacks

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>
This commit is contained in:
Simon de Vlieger
2026-07-31 10:06:13 +02:00
parent f2b82e5c20
commit f4f6a56db4
+117 -13
View 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;