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:
+117
-13
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user