mac: rework labelling code to be simpler, and less racy

This merges the various labelling calls into a single label_fix_full(),
which can operate on paths, on inode fds, and in a dirfd/fname style
(i.e. like openat()). It also systematically separates the path to look
up in the db from the path we actually use to reference the inode to
relabel.

This then ports tmpfiles over to labelling by fd. This should make the
code a bit less racy, as we'll try hard to always operate on the very
same inode, pinning it via an fd.

User-visibly the behaviour should not change.
This commit is contained in:
Lennart Poettering 2022-07-08 10:05:57 +02:00
parent 05c4affe04
commit 03bc11d1c4
11 changed files with 136 additions and 124 deletions

View file

@ -3081,7 +3081,7 @@ static int setup_credentials_internal(
assert(!must_mount || workspace_mounted > 0);
where = workspace_mounted ? workspace : final;
(void) label_fix_container(where, final, 0);
(void) label_fix_full(AT_FDCWD, where, final, 0);
r = acquire_credentials(context, params, unit, where, uid, workspace_mounted);
if (r < 0)

View file

@ -928,7 +928,7 @@ static int mount_private_dev(MountEntry *m) {
if (r < 0)
goto fail;
r = label_fix_container(dev, "/dev", 0);
r = label_fix_full(AT_FDCWD, dev, "/dev", 0);
if (r < 0) {
log_debug_errno(r, "Failed to fix label of '%s' as /dev: %m", dev);
goto fail;
@ -1158,7 +1158,7 @@ static int mount_tmpfs(const MountEntry *m) {
if (r < 0)
return r;
r = label_fix_container(entry_path, inner_path, 0);
r = label_fix_full(AT_FDCWD, entry_path, inner_path, 0);
if (r < 0)
return log_debug_errno(r, "Failed to fix label of '%s' as '%s': %m", entry_path, inner_path);
@ -2758,7 +2758,7 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
if (mkdir(y, 0777 | S_ISVTX) < 0)
return -errno;
r = label_fix_container(y, prefix, 0);
r = label_fix_full(AT_FDCWD, y, prefix, 0);
if (r < 0)
return r;

View file

@ -11,12 +11,29 @@
#include "selinux-util.h"
#include "smack-util.h"
int label_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
int label_fix_full(
int atfd,
const char *inode_path, /* path of inode to apply label to */
const char *label_path, /* path to use as database lookup key in label database (typically same as inode_path, but not always) */
LabelFixFlags flags) {
int r, q;
r = mac_selinux_fix_container(path, inside_path, flags);
q = mac_smack_fix_container(path, inside_path, flags);
if (atfd < 0 && atfd != AT_FDCWD)
return -EBADF;
if (!inode_path && atfd < 0) /* We need at least one of atfd and an inode path */
return -EINVAL;
/* If both atfd and inode_path are specified, we take the specified path relative to atfd which must be an fd to a dir.
*
* If only atfd is specified (and inode_path is NULL), we'll operated on the inode the atfd refers to.
*
* If atfd is AT_FDCWD then we'll operate on the inode the path refers to.
*/
r = mac_selinux_fix_full(atfd, inode_path, label_path, flags);
q = mac_smack_fix_full(atfd, inode_path, label_path, flags);
if (r < 0)
return r;
if (q < 0)

View file

@ -10,9 +10,10 @@ typedef enum LabelFixFlags {
LABEL_IGNORE_EROFS = 1 << 1,
} LabelFixFlags;
int label_fix_container(const char *path, const char *inside_path, LabelFixFlags flags);
int label_fix_full(int atfd, const char *inode_path, const char *label_path, LabelFixFlags flags);
static inline int label_fix(const char *path, LabelFixFlags flags) {
return label_fix_container(path, path, flags);
return label_fix_full(AT_FDCWD, path, path, flags);
}
int symlink_label(const char *old_path, const char *new_path);

View file

@ -22,7 +22,7 @@ int mkdirat_label(int dirfd, const char *path, mode_t mode) {
if (r < 0)
return r;
return mac_smack_fix_at(dirfd, path, 0);
return mac_smack_fix_full(dirfd, path, NULL, 0);
}
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {

View file

@ -233,46 +233,19 @@ static int mac_selinux_reload(int seqno) {
}
#endif
int mac_selinux_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
assert(path);
assert(inside_path);
#if HAVE_SELINUX
_cleanup_close_ int fd = -1;
static int selinux_fix_fd(
int fd,
const char *label_path,
LabelFixFlags flags) {
/* if mac_selinux_init() wasn't called before we are a NOOP */
if (!label_hnd)
return 0;
/* Open the file as O_PATH, to pin it while we determine and adjust the label */
fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (fd < 0) {
if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
return 0;
return -errno;
}
return mac_selinux_fix_container_fd(fd, path, inside_path, flags);
#endif
return 0;
}
int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_path, LabelFixFlags flags) {
assert(fd >= 0);
assert(inside_path);
#if HAVE_SELINUX
_cleanup_freecon_ char* fcon = NULL;
struct stat st;
int r;
/* if mac_selinux_init() wasn't called before we are a NOOP */
if (!label_hnd)
return 0;
assert(fd >= 0);
assert(label_path);
assert(path_is_absolute(label_path));
if (fstat(fd, &st) < 0)
return -errno;
@ -282,42 +255,85 @@ int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_pa
if (!label_hnd)
return 0;
if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) < 0) {
if (selabel_lookup_raw(label_hnd, &fcon, label_path, st.st_mode) < 0) {
/* If there's no label to set, then exit without warning */
if (errno == ENOENT)
return 0;
r = -errno;
goto fail;
return log_enforcing_errno(errno, "Unable to lookup intended SELinux security context of %s: %m", label_path);
}
if (setfilecon_raw(FORMAT_PROC_FD_PATH(fd), fcon) < 0) {
_cleanup_freecon_ char *oldcon = NULL;
r = -errno;
/* If the FS doesn't support labels, then exit without warning */
if (ERRNO_IS_NOT_SUPPORTED(errno))
if (ERRNO_IS_NOT_SUPPORTED(r))
return 0;
/* It the FS is read-only and we were told to ignore failures caused by that, suppress error */
if (errno == EROFS && (flags & LABEL_IGNORE_EROFS))
if (r == -EROFS && (flags & LABEL_IGNORE_EROFS))
return 0;
r = -errno;
/* If the old label is identical to the new one, suppress any kind of error */
if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq(fcon, oldcon))
return 0;
goto fail;
return log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", label_path);
}
return 0;
fail:
return log_enforcing_errno(r, "Unable to fix SELinux security context of %s (%s): %m", strna(path), strna(inside_path));
}
#endif
int mac_selinux_fix_full(
int atfd,
const char *inode_path,
const char *label_path,
LabelFixFlags flags) {
assert(atfd >= 0 || atfd == AT_FDCWD);
assert(atfd >= 0 || inode_path);
#if HAVE_SELINUX
_cleanup_close_ int opened_fd = -1;
_cleanup_free_ char *p = NULL;
int inode_fd, r;
/* if mac_selinux_init() wasn't called before we are a NOOP */
if (!label_hnd)
return 0;
if (inode_path) {
opened_fd = openat(atfd, inode_path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (opened_fd < 0) {
if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
return 0;
return -errno;
}
inode_fd = opened_fd;
} else
inode_fd = atfd;
if (!label_path) {
if (path_is_absolute(inode_path))
label_path = inode_path;
else {
r = fd_get_path(inode_fd, &p);
if (r < 0)
return r;
label_path = p;
}
}
return selinux_fix_fd(inode_fd, label_path, flags);
#else
return 0;
#endif
}
int mac_selinux_apply(const char *path, const char *label) {

View file

@ -24,15 +24,7 @@ int mac_selinux_init(void);
void mac_selinux_maybe_reload(void);
void mac_selinux_finish(void);
int mac_selinux_fix_container(const char *path, const char *inside_path, LabelFixFlags flags);
static inline int mac_selinux_fix(const char *path, LabelFixFlags flags) {
return mac_selinux_fix_container(path, path, flags);
}
int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_path, LabelFixFlags flags);
static inline int mac_selinux_fix_fd(int fd, const char *path, LabelFixFlags flags) {
return mac_selinux_fix_container_fd(fd, path, path, flags);
}
int mac_selinux_fix_full(int atfd, const char *inode_path, const char *label_path, LabelFixFlags flags);
int mac_selinux_apply(const char *path, const char *label);
int mac_selinux_apply_fd(int fd, const char *path, const char *label);

View file

@ -122,17 +122,22 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
return r;
}
static int smack_fix_fd(int fd, const char *abspath, LabelFixFlags flags) {
static int smack_fix_fd(
int fd,
const char *label_path,
LabelFixFlags flags) {
const char *label;
struct stat st;
int r;
/* The caller should have done the sanity checks. */
assert(abspath);
assert(path_is_absolute(abspath));
assert(fd >= 0);
assert(label_path);
assert(path_is_absolute(label_path));
/* Path must be in /dev. */
if (!path_startswith(abspath, "/dev"))
if (!path_startswith(label_path, "/dev"))
return 0;
if (fstat(fd, &st) < 0)
@ -171,70 +176,53 @@ static int smack_fix_fd(int fd, const char *abspath, LabelFixFlags flags) {
streq(old_label, label))
return 0;
return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", abspath);
return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", label_path);
}
return 0;
}
int mac_smack_fix_at(int dir_fd, const char *path, LabelFixFlags flags) {
int mac_smack_fix_full(
int atfd,
const char *inode_path,
const char *label_path,
LabelFixFlags flags) {
_cleanup_close_ int opened_fd = -1;
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
int r;
int r, inode_fd;
assert(path);
assert(atfd >= 0 || atfd == AT_FDCWD);
assert(atfd >= 0 || inode_path);
if (!mac_smack_use())
return 0;
if (dir_fd < 0) {
if (dir_fd != AT_FDCWD)
return -EBADF;
if (inode_path) {
opened_fd = openat(atfd, inode_path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (opened_fd < 0) {
if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
return 0;
return mac_smack_fix(path, flags);
return -errno;
}
inode_fd = opened_fd;
} else
inode_fd = atfd;
if (!label_path) {
if (path_is_absolute(inode_path))
label_path = inode_path;
else {
r = fd_get_path(inode_fd, &p);
if (r < 0)
return r;
label_path = p;
}
}
fd = openat(dir_fd, path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (fd < 0) {
if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
return 0;
return -errno;
}
if (!path_is_absolute(path)) {
r = fd_get_path(fd, &p);
if (r < 0)
return r;
path = p;
}
return smack_fix_fd(fd, path, flags);
}
int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
_cleanup_free_ char *abspath = NULL;
_cleanup_close_ int fd = -1;
int r;
assert(path);
if (!mac_smack_use())
return 0;
r = path_make_absolute_cwd(path, &abspath);
if (r < 0)
return r;
fd = open(abspath, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (fd < 0) {
if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
return 0;
return -errno;
}
return smack_fix_fd(fd, inside_path, flags);
return smack_fix_fd(inode_fd, label_path, flags);
}
int mac_smack_copy(const char *dest, const char *src) {

View file

@ -29,13 +29,11 @@ typedef enum SmackAttr {
bool mac_smack_use(void);
int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags);
int mac_smack_fix_full(int atfd, const char *inode_path, const char *label_path, LabelFixFlags flags);
static inline int mac_smack_fix(const char *path, LabelFixFlags flags) {
return mac_smack_fix_container(path, path, flags);
return mac_smack_fix_full(AT_FDCWD, path, path, flags);
}
int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags);
const char* smack_attr_to_string(SmackAttr i) _const_;
SmackAttr smack_attr_from_string(const char *s) _pure_;
int mac_smack_read(const char *path, SmackAttr attr, char **label);

View file

@ -923,7 +923,7 @@ static int fd_set_perms(Item *i, int fd, const char *path, const struct stat *st
}
shortcut:
return label_fix(path, 0);
return label_fix_full(fd, /* inode_path= */ NULL, /* label_path= */ path, 0);
}
static int path_open_parent_safe(const char *path) {

View file

@ -677,7 +677,7 @@ static int udev_node_apply_permissions_impl(
/* set the defaults */
if (!selinux)
(void) mac_selinux_fix_fd(node_fd, devnode, LABEL_IGNORE_ENOENT);
(void) mac_selinux_fix_full(node_fd, NULL, devnode, LABEL_IGNORE_ENOENT);
if (!smack)
(void) mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, NULL);
}