basic,shared: move a bunch of files to src/shared/
The goal is to move everything that requires selinux or smack away from src/basic/. This means that src/basic/label.[ch] must move, which implies btrfs-util.[ch], copy.[ch], and a bunch of other files which form a cluster of internal use. This is just moving text around, so there should be no functional difference. test-blockdev-util is new, because path_is_encrypted() is moved to blockdev-util.c, and so far we didn't have any tests for code there.
This commit is contained in:
@@ -1,258 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "btrfs-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "missing_magic.h"
|
||||
#include "parse-util.h"
|
||||
#include "stat-util.h"
|
||||
|
||||
int block_get_whole_disk(dev_t d, dev_t *ret) {
|
||||
char p[SYS_BLOCK_PATH_MAX("/partition")];
|
||||
_cleanup_free_ char *s = NULL;
|
||||
dev_t devt;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (major(d) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* If it has a queue this is good enough for us */
|
||||
xsprintf_sys_block_path(p, "/queue", d);
|
||||
if (access(p, F_OK) >= 0) {
|
||||
*ret = d;
|
||||
return 0;
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
/* If it is a partition find the originating device */
|
||||
xsprintf_sys_block_path(p, "/partition", d);
|
||||
if (access(p, F_OK) < 0)
|
||||
return -errno;
|
||||
|
||||
/* Get parent dev_t */
|
||||
xsprintf_sys_block_path(p, "/../dev", d);
|
||||
r = read_one_line_file(p, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_dev(s, &devt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Only return this if it is really good enough for us. */
|
||||
xsprintf_sys_block_path(p, "/queue", devt);
|
||||
if (access(p, F_OK) < 0)
|
||||
return -errno;
|
||||
|
||||
*ret = devt;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int get_block_device(const char *path, dev_t *ret) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(ret);
|
||||
|
||||
/* Gets the block device directly backing a file system. If the block device is encrypted, returns
|
||||
* the device mapper block device. */
|
||||
|
||||
fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (fstat(fd, &st))
|
||||
return -errno;
|
||||
|
||||
if (major(st.st_dev) != 0) {
|
||||
*ret = st.st_dev;
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = btrfs_get_block_device_fd(fd, ret);
|
||||
if (r > 0)
|
||||
return 1;
|
||||
if (r != -ENOTTY) /* not btrfs */
|
||||
return r;
|
||||
|
||||
*ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int block_get_originating(dev_t dt, dev_t *ret) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
_cleanup_free_ char *t = NULL;
|
||||
char p[SYS_BLOCK_PATH_MAX("/slaves")];
|
||||
_cleanup_free_ char *first_found = NULL;
|
||||
struct dirent *de;
|
||||
const char *q;
|
||||
dev_t devt;
|
||||
int r;
|
||||
|
||||
/* For the specified block device tries to chase it through the layers, in case LUKS-style DM stacking is used,
|
||||
* trying to find the next underlying layer. */
|
||||
|
||||
xsprintf_sys_block_path(p, "/slaves", dt);
|
||||
d = opendir(p);
|
||||
if (!d)
|
||||
return -errno;
|
||||
|
||||
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
||||
|
||||
if (dot_or_dot_dot(de->d_name))
|
||||
continue;
|
||||
|
||||
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
|
||||
continue;
|
||||
|
||||
if (first_found) {
|
||||
_cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
|
||||
|
||||
/* We found a device backed by multiple other devices. We don't really support
|
||||
* automatic discovery on such setups, with the exception of dm-verity partitions. In
|
||||
* this case there are two backing devices: the data partition and the hash
|
||||
* partition. We are fine with such setups, however, only if both partitions are on
|
||||
* the same physical device. Hence, let's verify this by iterating over every node
|
||||
* in the 'slaves/' directory and comparing them with the first that gets returned by
|
||||
* readdir(), to ensure they all point to the same device. */
|
||||
|
||||
u = path_join(p, de->d_name, "../dev");
|
||||
if (!u)
|
||||
return -ENOMEM;
|
||||
|
||||
v = path_join(p, first_found, "../dev");
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
|
||||
r = read_one_line_file(u, &a);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read %s: %m", u);
|
||||
|
||||
r = read_one_line_file(v, &b);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read %s: %m", v);
|
||||
|
||||
/* Check if the parent device is the same. If not, then the two backing devices are on
|
||||
* different physical devices, and we don't support that. */
|
||||
if (!streq(a, b))
|
||||
return -ENOTUNIQ;
|
||||
} else {
|
||||
first_found = strdup(de->d_name);
|
||||
if (!first_found)
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (!first_found)
|
||||
return -ENOENT;
|
||||
|
||||
q = strjoina(p, "/", first_found, "/dev");
|
||||
|
||||
r = read_one_line_file(q, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_dev(t, &devt);
|
||||
if (r < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (major(devt) == 0)
|
||||
return -ENOENT;
|
||||
|
||||
*ret = devt;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int get_block_device_harder(const char *path, dev_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(ret);
|
||||
|
||||
/* Gets the backing block device for a file system, and handles LUKS encrypted file systems, looking for its
|
||||
* immediate parent, if there is one. */
|
||||
|
||||
r = get_block_device(path, ret);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = block_get_originating(*ret, ret);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to chase block device '%s', ignoring: %m", path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lock_whole_block_device(dev_t devt, int operation) {
|
||||
_cleanup_free_ char *whole_node = NULL;
|
||||
_cleanup_close_ int lock_fd = -1;
|
||||
dev_t whole_devt;
|
||||
int r;
|
||||
|
||||
/* Let's get a BSD file lock on the whole block device, as per: https://systemd.io/BLOCK_DEVICE_LOCKING */
|
||||
|
||||
r = block_get_whole_disk(devt, &whole_devt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = device_path_make_major_minor(S_IFBLK, whole_devt, &whole_node);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
lock_fd = open(whole_node, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
|
||||
if (lock_fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (flock(lock_fd, operation) < 0)
|
||||
return -errno;
|
||||
|
||||
return TAKE_FD(lock_fd);
|
||||
}
|
||||
|
||||
int blockdev_partscan_enabled(int fd) {
|
||||
_cleanup_free_ char *p = NULL, *buf = NULL;
|
||||
unsigned long long ull;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
/* Checks if partition scanning is correctly enabled on the block device */
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!S_ISBLK(st.st_mode))
|
||||
return -ENOTBLK;
|
||||
|
||||
if (asprintf(&p, "/sys/dev/block/%u:%u/capability", major(st.st_rdev), minor(st.st_rdev)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
r = read_one_line_file(p, &buf);
|
||||
if (r == -ENOENT) /* If the capability file doesn't exist then we are most likely looking at a
|
||||
* partition block device, not the whole block device. And that means we have no
|
||||
* partition scanning on for it (we do for its parent, but not for the partition
|
||||
* itself). */
|
||||
return false;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = safe_atollu_full(buf, 16, &ull);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
#ifndef GENHD_FL_NO_PART_SCAN
|
||||
#define GENHD_FL_NO_PART_SCAN (0x0200)
|
||||
#endif
|
||||
|
||||
return !FLAGS_SET(ull, GENHD_FL_NO_PART_SCAN);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
#define SYS_BLOCK_PATH_MAX(suffix) \
|
||||
(STRLEN("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix))
|
||||
#define xsprintf_sys_block_path(buf, suffix, devno) \
|
||||
xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix))
|
||||
|
||||
int block_get_whole_disk(dev_t d, dev_t *ret);
|
||||
int block_get_originating(dev_t d, dev_t *ret);
|
||||
|
||||
int get_block_device(const char *path, dev_t *dev);
|
||||
|
||||
int get_block_device_harder(const char *path, dev_t *dev);
|
||||
|
||||
int lock_whole_block_device(dev_t devt, int operation);
|
||||
|
||||
int blockdev_partscan_enabled(int fd);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,136 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "copy.h"
|
||||
#include "time-util.h"
|
||||
|
||||
typedef struct BtrfsSubvolInfo {
|
||||
uint64_t subvol_id;
|
||||
usec_t otime;
|
||||
|
||||
sd_id128_t uuid;
|
||||
sd_id128_t parent_uuid;
|
||||
|
||||
bool read_only;
|
||||
} BtrfsSubvolInfo;
|
||||
|
||||
typedef struct BtrfsQuotaInfo {
|
||||
uint64_t referenced;
|
||||
uint64_t exclusive;
|
||||
uint64_t referenced_max;
|
||||
uint64_t exclusive_max;
|
||||
} BtrfsQuotaInfo;
|
||||
|
||||
typedef enum BtrfsSnapshotFlags {
|
||||
BTRFS_SNAPSHOT_FALLBACK_COPY = 1 << 0, /* If the source isn't a subvolume, reflink everything */
|
||||
BTRFS_SNAPSHOT_READ_ONLY = 1 << 1,
|
||||
BTRFS_SNAPSHOT_RECURSIVE = 1 << 2,
|
||||
BTRFS_SNAPSHOT_QUOTA = 1 << 3,
|
||||
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 1 << 4, /* If the destination doesn't support subvolumes, reflink/copy instead */
|
||||
BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 1 << 5, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */
|
||||
BTRFS_SNAPSHOT_SIGINT = 1 << 6, /* Check for SIGINT regularly, and return EINTR if seen */
|
||||
} BtrfsSnapshotFlags;
|
||||
|
||||
typedef enum BtrfsRemoveFlags {
|
||||
BTRFS_REMOVE_RECURSIVE = 1 << 0,
|
||||
BTRFS_REMOVE_QUOTA = 1 << 1,
|
||||
} BtrfsRemoveFlags;
|
||||
|
||||
int btrfs_is_subvol_fd(int fd);
|
||||
int btrfs_is_subvol(const char *path);
|
||||
|
||||
int btrfs_reflink(int infd, int outfd);
|
||||
int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz);
|
||||
|
||||
int btrfs_get_block_device_fd(int fd, dev_t *dev);
|
||||
int btrfs_get_block_device(const char *path, dev_t *dev);
|
||||
|
||||
int btrfs_defrag(const char *p);
|
||||
|
||||
int btrfs_quota_enable_fd(int fd, bool b);
|
||||
int btrfs_quota_enable(const char *path, bool b);
|
||||
|
||||
int btrfs_quota_scan_start(int fd);
|
||||
int btrfs_quota_scan_wait(int fd);
|
||||
int btrfs_quota_scan_ongoing(int fd);
|
||||
|
||||
int btrfs_subvol_make(const char *path);
|
||||
int btrfs_subvol_make_fd(int fd, const char *subvolume);
|
||||
|
||||
int btrfs_subvol_make_fallback(const char *path, mode_t);
|
||||
|
||||
int btrfs_subvol_snapshot_fd_full(int old_fd, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
|
||||
return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int btrfs_subvol_snapshot_full(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
|
||||
return btrfs_subvol_snapshot_full(old_path, new_path, flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags);
|
||||
int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags);
|
||||
|
||||
int btrfs_subvol_set_read_only_fd(int fd, bool b);
|
||||
int btrfs_subvol_set_read_only(const char *path, bool b);
|
||||
int btrfs_subvol_get_read_only_fd(int fd);
|
||||
|
||||
int btrfs_subvol_get_id(int fd, const char *subvolume, uint64_t *ret);
|
||||
int btrfs_subvol_get_id_fd(int fd, uint64_t *ret);
|
||||
int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret);
|
||||
|
||||
int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *info);
|
||||
|
||||
int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret);
|
||||
|
||||
int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *quota);
|
||||
int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *quota);
|
||||
|
||||
int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max);
|
||||
int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max);
|
||||
|
||||
int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool new_qgroup);
|
||||
int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup);
|
||||
|
||||
int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret);
|
||||
int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id);
|
||||
|
||||
int btrfs_qgroup_create(int fd, uint64_t qgroupid);
|
||||
int btrfs_qgroup_destroy(int fd, uint64_t qgroupid);
|
||||
int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid);
|
||||
|
||||
int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max);
|
||||
int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max);
|
||||
|
||||
int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid);
|
||||
|
||||
int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent);
|
||||
int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent);
|
||||
|
||||
int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret);
|
||||
|
||||
int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *quota);
|
||||
int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *quota);
|
||||
|
||||
static inline int btrfs_log_dev_root(int level, int ret, const char *p) {
|
||||
return log_full_errno(level, ret,
|
||||
"File system behind %s is reported by btrfs to be backed by pseudo-device /dev/root, which is not a valid userspace accessible device node. "
|
||||
"Cannot determine correct backing block device.", p);
|
||||
}
|
||||
|
||||
static inline bool btrfs_might_be_subvol(const struct stat *st) {
|
||||
if (!st)
|
||||
return false;
|
||||
|
||||
/* Returns true if this 'struct stat' looks like it could refer to a btrfs subvolume. To make a final
|
||||
* decision, needs to be combined with an fstatfs() check to see if this is actually btrfs. */
|
||||
|
||||
return S_ISDIR(st->st_mode) && st->st_ino == 256;
|
||||
}
|
||||
-1286
File diff suppressed because it is too large
Load Diff
@@ -1,71 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef enum CopyFlags {
|
||||
COPY_REFLINK = 1 << 0, /* Try to reflink */
|
||||
COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
|
||||
COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
|
||||
COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
|
||||
COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
|
||||
COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
|
||||
COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
|
||||
COPY_MAC_CREATE = 1 << 7, /* Create files with the correct MAC label (currently SELinux only) */
|
||||
COPY_HARDLINKS = 1 << 8, /* Try to reproduce hard links */
|
||||
} CopyFlags;
|
||||
|
||||
typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
|
||||
typedef int (*copy_progress_path_t)(const char *path, const struct stat *st, void *userdata);
|
||||
|
||||
int copy_file_fd_full(const char *from, int to, CopyFlags copy_flags, copy_progress_bytes_t progress, void *userdata);
|
||||
static inline int copy_file_fd(const char *from, int to, CopyFlags copy_flags) {
|
||||
return copy_file_fd_full(from, to, copy_flags, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_file_full(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, unsigned chattr_mask, CopyFlags copy_flags, copy_progress_bytes_t progress, void *userdata);
|
||||
static inline int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, unsigned chattr_mask, CopyFlags copy_flags) {
|
||||
return copy_file_full(from, to, open_flags, mode, chattr_flags, chattr_mask, copy_flags, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_file_atomic_full(const char *from, const char *to, mode_t mode, unsigned chattr_flags, unsigned chattr_mask, CopyFlags copy_flags, copy_progress_bytes_t progress, void *userdata);
|
||||
static inline int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, unsigned chattr_mask, CopyFlags copy_flags) {
|
||||
return copy_file_atomic_full(from, to, mode, chattr_flags, chattr_mask, copy_flags, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
|
||||
return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
|
||||
}
|
||||
static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
|
||||
return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_directory_fd_full(int dirfd, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
|
||||
return copy_directory_fd_full(dirfd, to, copy_flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_directory_full(const char *from, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
|
||||
return copy_directory_full(from, to, copy_flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_bytes_full(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags, void **ret_remains, size_t *ret_remains_size, copy_progress_bytes_t progress, void *userdata);
|
||||
static inline int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
|
||||
return copy_bytes_full(fdf, fdt, max_bytes, copy_flags, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_times(int fdf, int fdt, CopyFlags flags);
|
||||
int copy_access(int fdf, int fdt);
|
||||
int copy_rights_with_fallback(int fdf, int fdt, const char *patht);
|
||||
static inline int copy_rights(int fdf, int fdt) {
|
||||
return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */
|
||||
}
|
||||
int copy_xattr(int fdf, int fdt);
|
||||
@@ -1,350 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "copy.h"
|
||||
#include "data-fd-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "io-util.h"
|
||||
#include "memfd-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
|
||||
#define DATA_FD_MEMORY_LIMIT (64U*1024U)
|
||||
|
||||
/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */
|
||||
#define DATA_FD_TMP_LIMIT (1024U*1024U)
|
||||
|
||||
int acquire_data_fd(const void *data, size_t size, unsigned flags) {
|
||||
_cleanup_close_pair_ int pipefds[2] = { -1, -1 };
|
||||
char pattern[] = "/dev/shm/data-fd-XXXXXX";
|
||||
_cleanup_close_ int fd = -1;
|
||||
int isz = 0, r;
|
||||
ssize_t n;
|
||||
off_t f;
|
||||
|
||||
assert(data || size == 0);
|
||||
|
||||
/* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
|
||||
* complex than I wish it was. But here's why:
|
||||
*
|
||||
* a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
|
||||
* read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14.
|
||||
*
|
||||
* b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
|
||||
* a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
|
||||
* clients can only bump their size to a system-wide limit, which might be quite low.
|
||||
*
|
||||
* c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
|
||||
* earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
|
||||
* /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
|
||||
*
|
||||
* d) Finally, we try creating a regular file in /dev/shm, which we then delete.
|
||||
*
|
||||
* It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
|
||||
* figure. */
|
||||
|
||||
if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
|
||||
/* As a special case, return /dev/null if we have been called for an empty data block */
|
||||
r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((flags & ACQUIRE_NO_MEMFD) == 0) {
|
||||
fd = memfd_new("data-fd");
|
||||
if (fd < 0)
|
||||
goto try_pipe;
|
||||
|
||||
n = write(fd, data, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if ((size_t) n != size)
|
||||
return -EIO;
|
||||
|
||||
f = lseek(fd, 0, SEEK_SET);
|
||||
if (f != 0)
|
||||
return -errno;
|
||||
|
||||
r = memfd_set_sealed(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return TAKE_FD(fd);
|
||||
}
|
||||
|
||||
try_pipe:
|
||||
if ((flags & ACQUIRE_NO_PIPE) == 0) {
|
||||
if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
|
||||
return -errno;
|
||||
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
|
||||
if ((size_t) isz < size) {
|
||||
isz = (int) size;
|
||||
if (isz < 0 || (size_t) isz != size)
|
||||
return -E2BIG;
|
||||
|
||||
/* Try to bump the pipe size */
|
||||
(void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
|
||||
|
||||
/* See if that worked */
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
|
||||
if ((size_t) isz < size)
|
||||
goto try_dev_shm;
|
||||
}
|
||||
|
||||
n = write(pipefds[1], data, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if ((size_t) n != size)
|
||||
return -EIO;
|
||||
|
||||
(void) fd_nonblock(pipefds[0], false);
|
||||
|
||||
return TAKE_FD(pipefds[0]);
|
||||
}
|
||||
|
||||
try_dev_shm:
|
||||
if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
|
||||
fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
|
||||
if (fd < 0)
|
||||
goto try_dev_shm_without_o_tmpfile;
|
||||
|
||||
n = write(fd, data, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if ((size_t) n != size)
|
||||
return -EIO;
|
||||
|
||||
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
|
||||
return fd_reopen(fd, O_RDONLY|O_CLOEXEC);
|
||||
}
|
||||
|
||||
try_dev_shm_without_o_tmpfile:
|
||||
if ((flags & ACQUIRE_NO_REGULAR) == 0) {
|
||||
fd = mkostemp_safe(pattern);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
n = write(fd, data, size);
|
||||
if (n < 0) {
|
||||
r = -errno;
|
||||
goto unlink_and_return;
|
||||
}
|
||||
if ((size_t) n != size) {
|
||||
r = -EIO;
|
||||
goto unlink_and_return;
|
||||
}
|
||||
|
||||
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
|
||||
r = open(pattern, O_RDONLY|O_CLOEXEC);
|
||||
if (r < 0)
|
||||
r = -errno;
|
||||
|
||||
unlink_and_return:
|
||||
(void) unlink(pattern);
|
||||
return r;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int copy_data_fd(int fd) {
|
||||
_cleanup_close_ int copy_fd = -1, tmp_fd = -1;
|
||||
_cleanup_free_ void *remains = NULL;
|
||||
size_t remains_size = 0;
|
||||
const char *td;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
/* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but
|
||||
* independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be
|
||||
* somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported
|
||||
* uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in
|
||||
* /var/tmp. */
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
/* For now, let's only accept regular files, sockets, pipes and char devices */
|
||||
if (S_ISDIR(st.st_mode))
|
||||
return -EISDIR;
|
||||
if (S_ISLNK(st.st_mode))
|
||||
return -ELOOP;
|
||||
if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode))
|
||||
return -EBADFD;
|
||||
|
||||
/* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note
|
||||
* that we use the reported regular file size only as a hint, given that there are plenty special files in
|
||||
* /proc and /sys which report a zero file size but can be read from. */
|
||||
|
||||
if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) {
|
||||
|
||||
/* Try a memfd first */
|
||||
copy_fd = memfd_new("data-fd");
|
||||
if (copy_fd >= 0) {
|
||||
off_t f;
|
||||
|
||||
r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
f = lseek(copy_fd, 0, SEEK_SET);
|
||||
if (f != 0)
|
||||
return -errno;
|
||||
|
||||
if (r == 0) {
|
||||
/* Did it fit into the limit? If so, we are done. */
|
||||
r = memfd_set_sealed(copy_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return TAKE_FD(copy_fd);
|
||||
}
|
||||
|
||||
/* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */
|
||||
|
||||
} else {
|
||||
_cleanup_(close_pairp) int pipefds[2] = { -1, -1 };
|
||||
int isz;
|
||||
|
||||
/* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather
|
||||
* then block indefinitely when we hit the pipe size limit */
|
||||
|
||||
if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
|
||||
return -errno;
|
||||
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
|
||||
/* Try to enlarge the pipe size if necessary */
|
||||
if ((size_t) isz < DATA_FD_MEMORY_LIMIT) {
|
||||
|
||||
(void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT);
|
||||
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
|
||||
|
||||
r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
|
||||
if (r < 0 && r != -EAGAIN)
|
||||
return r; /* If we get EAGAIN it could be because of the source or because of
|
||||
* the destination fd, we can't know, as sendfile() and friends won't
|
||||
* tell us. Hence, treat this as reason to fall back, just to be
|
||||
* sure. */
|
||||
if (r == 0) {
|
||||
/* Everything fit in, yay! */
|
||||
(void) fd_nonblock(pipefds[0], false);
|
||||
|
||||
return TAKE_FD(pipefds[0]);
|
||||
}
|
||||
|
||||
/* Things didn't fit in. But we read data into the pipe, let's remember that, so that
|
||||
* when writing the new file we incorporate this first. */
|
||||
copy_fd = TAKE_FD(pipefds[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */
|
||||
if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) &&
|
||||
(DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) {
|
||||
off_t f;
|
||||
|
||||
tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC);
|
||||
if (tmp_fd < 0)
|
||||
return tmp_fd;
|
||||
|
||||
if (copy_fd >= 0) {
|
||||
/* If we tried a memfd/pipe first and it ended up being too large, then copy this into the
|
||||
* temporary file first. */
|
||||
|
||||
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
if (remains_size > 0) {
|
||||
/* If there were remaining bytes (i.e. read into memory, but not written out yet) from the
|
||||
* failed copy operation, let's flush them out next. */
|
||||
|
||||
r = loop_write(tmp_fd, remains, remains_size, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
goto finish; /* Yay, it fit in */
|
||||
|
||||
/* It didn't fit in. Let's not forget to use what we already used */
|
||||
f = lseek(tmp_fd, 0, SEEK_SET);
|
||||
if (f != 0)
|
||||
return -errno;
|
||||
|
||||
CLOSE_AND_REPLACE(copy_fd, tmp_fd);
|
||||
|
||||
remains = mfree(remains);
|
||||
remains_size = 0;
|
||||
}
|
||||
|
||||
/* As last fallback use /var/tmp */
|
||||
r = var_tmp_dir(&td);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC);
|
||||
if (tmp_fd < 0)
|
||||
return tmp_fd;
|
||||
|
||||
if (copy_fd >= 0) {
|
||||
/* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this
|
||||
* into the temporary file first. */
|
||||
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
if (remains_size > 0) {
|
||||
/* Then, copy in any read but not yet written bytes. */
|
||||
r = loop_write(tmp_fd, remains, remains_size, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Copy in the rest */
|
||||
r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(r == 0);
|
||||
|
||||
finish:
|
||||
/* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the
|
||||
* file again */
|
||||
|
||||
return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
int acquire_data_fd(const void *data, size_t size, unsigned flags);
|
||||
int copy_data_fd(int fd);
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@@ -1504,91 +1503,6 @@ int open_parent(const char *path, int flags, mode_t mode) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
|
||||
_cleanup_free_ char *p = NULL, *uuids = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int r, found_encrypted = false;
|
||||
|
||||
assert(sysfs_path);
|
||||
|
||||
if (depth_left == 0)
|
||||
return -EINVAL;
|
||||
|
||||
p = path_join(sysfs_path, "dm/uuid");
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = read_one_line_file(p, &uuids);
|
||||
if (r != -ENOENT) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */
|
||||
if (startswith(uuids, "CRYPT-"))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/"
|
||||
* subdir. */
|
||||
|
||||
p = mfree(p);
|
||||
p = path_join(sysfs_path, "slaves");
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
d = opendir(p);
|
||||
if (!d) {
|
||||
if (errno == ENOENT) /* Doesn't have underlying devices */
|
||||
return false;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *q = NULL;
|
||||
struct dirent *de;
|
||||
|
||||
errno = 0;
|
||||
de = readdir_no_dot(d);
|
||||
if (!de) {
|
||||
if (errno != 0)
|
||||
return -errno;
|
||||
|
||||
break; /* No more underlying devices */
|
||||
}
|
||||
|
||||
q = path_join(p, de->d_name);
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
|
||||
r = blockdev_is_encrypted(q, depth_left - 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* we found one that is not encrypted? then propagate that immediately */
|
||||
return false;
|
||||
|
||||
found_encrypted = true;
|
||||
}
|
||||
|
||||
return found_encrypted;
|
||||
}
|
||||
|
||||
int path_is_encrypted(const char *path) {
|
||||
char p[SYS_BLOCK_PATH_MAX(NULL)];
|
||||
dev_t devt;
|
||||
int r;
|
||||
|
||||
r = get_block_device(path, &devt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* doesn't have a block device */
|
||||
return false;
|
||||
|
||||
xsprintf_sys_block_path(p, NULL, devt);
|
||||
|
||||
return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
|
||||
}
|
||||
|
||||
int conservative_renameat(
|
||||
int olddirfd, const char *oldpath,
|
||||
int newdirfd, const char *newpath) {
|
||||
|
||||
@@ -145,8 +145,6 @@ int syncfs_path(int atfd, const char *path);
|
||||
|
||||
int open_parent(const char *path, int flags, mode_t mode);
|
||||
|
||||
int path_is_encrypted(const char *path);
|
||||
|
||||
int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
|
||||
static inline int conservative_rename(const char *oldpath, const char *newpath) {
|
||||
return conservative_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath);
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "btrfs-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "label.h"
|
||||
#include "macro.h"
|
||||
#include "selinux-util.h"
|
||||
#include "smack-util.h"
|
||||
|
||||
int label_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
|
||||
int r, q;
|
||||
|
||||
r = mac_selinux_fix_container(path, inside_path, flags);
|
||||
q = mac_smack_fix_container(path, inside_path, flags);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (q < 0)
|
||||
return q;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int symlink_label(const char *old_path, const char *new_path) {
|
||||
int r;
|
||||
|
||||
assert(old_path);
|
||||
assert(new_path);
|
||||
|
||||
r = mac_selinux_create_file_prepare(new_path, S_IFLNK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (symlink(old_path, new_path) < 0)
|
||||
r = -errno;
|
||||
|
||||
mac_selinux_create_file_clear();
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mac_smack_fix(new_path, 0);
|
||||
}
|
||||
|
||||
int symlink_atomic_label(const char *from, const char *to) {
|
||||
int r;
|
||||
|
||||
assert(from);
|
||||
assert(to);
|
||||
|
||||
r = mac_selinux_create_file_prepare(to, S_IFLNK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (symlink_atomic(from, to) < 0)
|
||||
r = -errno;
|
||||
|
||||
mac_selinux_create_file_clear();
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mac_smack_fix(to, 0);
|
||||
}
|
||||
|
||||
int mknod_label(const char *pathname, mode_t mode, dev_t dev) {
|
||||
int r;
|
||||
|
||||
assert(pathname);
|
||||
|
||||
r = mac_selinux_create_file_prepare(pathname, mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (mknod(pathname, mode, dev) < 0)
|
||||
r = -errno;
|
||||
|
||||
mac_selinux_create_file_clear();
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mac_smack_fix(pathname, 0);
|
||||
}
|
||||
|
||||
int btrfs_subvol_make_label(const char *path) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
r = mac_selinux_create_file_prepare(path, S_IFDIR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = btrfs_subvol_make(path);
|
||||
mac_selinux_create_file_clear();
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mac_smack_fix(path, 0);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef enum LabelFixFlags {
|
||||
LABEL_IGNORE_ENOENT = 1 << 0,
|
||||
LABEL_IGNORE_EROFS = 1 << 1,
|
||||
} LabelFixFlags;
|
||||
|
||||
int label_fix_container(const char *path, const char *inside_path, LabelFixFlags flags);
|
||||
static inline int label_fix(const char *path, LabelFixFlags flags) {
|
||||
return label_fix_container(path, path, flags);
|
||||
}
|
||||
|
||||
int mkdir_label(const char *path, mode_t mode);
|
||||
int mkdirat_label(int dirfd, const char *path, mode_t mode);
|
||||
int symlink_label(const char *old_path, const char *new_path);
|
||||
int symlink_atomic_label(const char *from, const char *to);
|
||||
int mknod_label(const char *pathname, mode_t mode, dev_t dev);
|
||||
|
||||
int btrfs_subvol_make_label(const char *path);
|
||||
@@ -15,10 +15,6 @@ basic_sources = files('''
|
||||
async.h
|
||||
audit-util.c
|
||||
audit-util.h
|
||||
blockdev-util.c
|
||||
blockdev-util.h
|
||||
btrfs-util.c
|
||||
btrfs-util.h
|
||||
build.c
|
||||
build.h
|
||||
bus-label.c
|
||||
@@ -33,12 +29,8 @@ basic_sources = files('''
|
||||
chattr-util.h
|
||||
conf-files.c
|
||||
conf-files.h
|
||||
copy.c
|
||||
copy.h
|
||||
creds-util.c
|
||||
creds-util.h
|
||||
data-fd-util.c
|
||||
data-fd-util.h
|
||||
def.h
|
||||
dirent-util.c
|
||||
dirent-util.h
|
||||
@@ -85,8 +77,6 @@ basic_sources = files('''
|
||||
ioprio.h
|
||||
khash.c
|
||||
khash.h
|
||||
label.c
|
||||
label.h
|
||||
limits-util.c
|
||||
limits-util.h
|
||||
linux/btrfs.h
|
||||
@@ -157,7 +147,6 @@ basic_sources = files('''
|
||||
missing_syscall.h
|
||||
missing_timerfd.h
|
||||
missing_type.h
|
||||
mkdir-label.c
|
||||
mkdir.c
|
||||
mkdir.h
|
||||
mountpoint-util.c
|
||||
@@ -200,10 +189,6 @@ basic_sources = files('''
|
||||
replace-var.h
|
||||
rlimit-util.c
|
||||
rlimit-util.h
|
||||
rm-rf.c
|
||||
rm-rf.h
|
||||
selinux-util.c
|
||||
selinux-util.h
|
||||
set.h
|
||||
sigbus.c
|
||||
sigbus.h
|
||||
@@ -211,9 +196,6 @@ basic_sources = files('''
|
||||
signal-util.h
|
||||
siphash24.c
|
||||
siphash24.h
|
||||
smack-util.c
|
||||
smack-util.h
|
||||
socket-label.c
|
||||
socket-util.c
|
||||
socket-util.h
|
||||
sort-util.c
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "label.h"
|
||||
#include "macro.h"
|
||||
#include "mkdir.h"
|
||||
#include "selinux-util.h"
|
||||
#include "smack-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
int mkdir_label(const char *path, mode_t mode) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
r = mac_selinux_create_file_prepare(path, S_IFDIR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mkdir_errno_wrapper(path, mode);
|
||||
mac_selinux_create_file_clear();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mac_smack_fix(path, 0);
|
||||
}
|
||||
|
||||
int mkdirat_label(int dirfd, const char *path, mode_t mode) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
r = mac_selinux_create_file_prepare_at(dirfd, path, S_IFDIR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mkdirat_errno_wrapper(dirfd, path, mode);
|
||||
mac_selinux_create_file_clear();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mac_smack_fix_at(dirfd, path, 0);
|
||||
}
|
||||
|
||||
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
|
||||
return mkdir_safe_internal(path, mode, uid, gid, flags, mkdir_label);
|
||||
}
|
||||
|
||||
int mkdir_parents_label(const char *path, mode_t mode) {
|
||||
return mkdir_parents_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_label);
|
||||
}
|
||||
|
||||
int mkdir_p_label(const char *path, mode_t mode) {
|
||||
return mkdir_p_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_label);
|
||||
}
|
||||
@@ -1,316 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "btrfs-util.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "path-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static bool is_physical_fs(const struct statfs *sfs) {
|
||||
return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
|
||||
}
|
||||
|
||||
static int patch_dirfd_mode(
|
||||
int dfd,
|
||||
mode_t *ret_old_mode) {
|
||||
|
||||
struct stat st;
|
||||
|
||||
assert(dfd >= 0);
|
||||
assert(ret_old_mode);
|
||||
|
||||
if (fstat(dfd, &st) < 0)
|
||||
return -errno;
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
return -ENOTDIR;
|
||||
if (FLAGS_SET(st.st_mode, 0700)) /* Already set? */
|
||||
return -EACCES; /* original error */
|
||||
if (st.st_uid != geteuid()) /* this only works if the UID matches ours */
|
||||
return -EACCES;
|
||||
|
||||
if (fchmod(dfd, (st.st_mode | 0700) & 07777) < 0)
|
||||
return -errno;
|
||||
|
||||
*ret_old_mode = st.st_mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags) {
|
||||
|
||||
mode_t old_mode;
|
||||
int r;
|
||||
|
||||
/* Like unlinkat(), but tries harder: if we get EACCESS we'll try to set the r/w/x bits on the
|
||||
* directory. This is useful if we run unprivileged and have some files where the w bit is
|
||||
* missing. */
|
||||
|
||||
if (unlinkat(dfd, filename, unlink_flags) >= 0)
|
||||
return 0;
|
||||
if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD))
|
||||
return -errno;
|
||||
|
||||
r = patch_dirfd_mode(dfd, &old_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (unlinkat(dfd, filename, unlink_flags) < 0) {
|
||||
r = -errno;
|
||||
/* Try to restore the original access mode if this didn't work */
|
||||
(void) fchmod(dfd, old_mode);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(remove_flags, REMOVE_CHMOD_RESTORE) && fchmod(dfd, old_mode) < 0)
|
||||
return -errno;
|
||||
|
||||
/* If this worked, we won't reset the old mode by default, since we'll need it for other entries too,
|
||||
* and we should destroy the whole thing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fstatat_harder(int dfd,
|
||||
const char *filename,
|
||||
struct stat *ret,
|
||||
int fstatat_flags,
|
||||
RemoveFlags remove_flags) {
|
||||
|
||||
mode_t old_mode;
|
||||
int r;
|
||||
|
||||
/* Like unlink_harder() but does the same for fstatat() */
|
||||
|
||||
if (fstatat(dfd, filename, ret, fstatat_flags) >= 0)
|
||||
return 0;
|
||||
if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD))
|
||||
return -errno;
|
||||
|
||||
r = patch_dirfd_mode(dfd, &old_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (fstatat(dfd, filename, ret, fstatat_flags) < 0) {
|
||||
r = -errno;
|
||||
(void) fchmod(dfd, old_mode);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(remove_flags, REMOVE_CHMOD_RESTORE) && fchmod(dfd, old_mode) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct dirent *de;
|
||||
int ret = 0, r;
|
||||
struct statfs sfs;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
|
||||
* fd, in all cases, including on failure.. */
|
||||
|
||||
if (!(flags & REMOVE_PHYSICAL)) {
|
||||
|
||||
r = fstatfs(fd, &sfs);
|
||||
if (r < 0) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (is_physical_fs(&sfs)) {
|
||||
/* We refuse to clean physical file systems with this call,
|
||||
* unless explicitly requested. This is extra paranoia just
|
||||
* to be sure we never ever remove non-state data. */
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
(void) fd_get_path(fd, &path);
|
||||
log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.",
|
||||
strna(path));
|
||||
|
||||
safe_close(fd);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
d = fdopendir(fd);
|
||||
if (!d) {
|
||||
safe_close(fd);
|
||||
return errno == ENOENT ? 0 : -errno;
|
||||
}
|
||||
|
||||
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
||||
bool is_dir;
|
||||
struct stat st;
|
||||
|
||||
if (dot_or_dot_dot(de->d_name))
|
||||
continue;
|
||||
|
||||
if (de->d_type == DT_UNKNOWN ||
|
||||
(de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
|
||||
r = fstatat_harder(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW, flags);
|
||||
if (r < 0) {
|
||||
if (ret == 0 && r != -ENOENT)
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
|
||||
is_dir = S_ISDIR(st.st_mode);
|
||||
} else
|
||||
is_dir = de->d_type == DT_DIR;
|
||||
|
||||
if (is_dir) {
|
||||
_cleanup_close_ int subdir_fd = -1;
|
||||
|
||||
/* if root_dev is set, remove subdirectories only if device is same */
|
||||
if (root_dev && st.st_dev != root_dev->st_dev)
|
||||
continue;
|
||||
|
||||
subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||
if (subdir_fd < 0) {
|
||||
if (ret == 0 && errno != ENOENT)
|
||||
ret = -errno;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Stop at mount points */
|
||||
r = fd_is_mount_point(fd, de->d_name, 0);
|
||||
if (r < 0) {
|
||||
if (ret == 0 && r != -ENOENT)
|
||||
ret = r;
|
||||
|
||||
continue;
|
||||
}
|
||||
if (r > 0)
|
||||
continue;
|
||||
|
||||
if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
|
||||
|
||||
/* This could be a subvolume, try to remove it */
|
||||
|
||||
r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
|
||||
if (r < 0) {
|
||||
if (!IN_SET(r, -ENOTTY, -EINVAL)) {
|
||||
if (ret == 0)
|
||||
ret = r;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ENOTTY, then it wasn't a btrfs subvolume, continue below. */
|
||||
} else
|
||||
/* It was a subvolume, continue. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file
|
||||
* system type again for each directory */
|
||||
r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
|
||||
if (r < 0 && ret == 0)
|
||||
ret = r;
|
||||
|
||||
r = unlinkat_harder(fd, de->d_name, AT_REMOVEDIR, flags);
|
||||
if (r < 0 && r != -ENOENT && ret == 0)
|
||||
ret = r;
|
||||
|
||||
} else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
|
||||
|
||||
r = unlinkat_harder(fd, de->d_name, 0, flags);
|
||||
if (r < 0 && r != -ENOENT && ret == 0)
|
||||
ret = r;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rm_rf(const char *path, RemoveFlags flags) {
|
||||
int fd, r;
|
||||
struct statfs s;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* For now, don't support dropping subvols when also only dropping directories, since we can't do
|
||||
* this race-freely. */
|
||||
if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
|
||||
return -EINVAL;
|
||||
|
||||
/* We refuse to clean the root file system with this call. This is extra paranoia to never cause a
|
||||
* really seriously broken system. */
|
||||
if (path_equal_or_files_same(path, "/", AT_SYMLINK_NOFOLLOW))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
|
||||
"Attempted to remove entire root file system (\"%s\"), and we can't allow that.",
|
||||
path);
|
||||
|
||||
if (FLAGS_SET(flags, REMOVE_SUBVOLUME | REMOVE_ROOT | REMOVE_PHYSICAL)) {
|
||||
/* Try to remove as subvolume first */
|
||||
r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
||||
if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT)
|
||||
return 0;
|
||||
|
||||
if (!IN_SET(r, -ENOTTY, -EINVAL, -ENOTDIR))
|
||||
return r;
|
||||
|
||||
/* Not btrfs or not a subvolume */
|
||||
}
|
||||
|
||||
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||
if (fd < 0) {
|
||||
if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
if (!IN_SET(errno, ENOTDIR, ELOOP))
|
||||
return -errno;
|
||||
|
||||
if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES))
|
||||
return 0;
|
||||
|
||||
if (FLAGS_SET(flags, REMOVE_ROOT)) {
|
||||
|
||||
if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
|
||||
if (statfs(path, &s) < 0)
|
||||
return -errno;
|
||||
|
||||
if (is_physical_fs(&s))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
|
||||
"Attempted to remove files from a disk file system under \"%s\", refusing.",
|
||||
path);
|
||||
}
|
||||
|
||||
if (unlink(path) < 0) {
|
||||
if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = rm_rf_children(fd, flags, NULL);
|
||||
|
||||
if (FLAGS_SET(flags, REMOVE_ROOT) &&
|
||||
rmdir(path) < 0 &&
|
||||
r >= 0 &&
|
||||
(!FLAGS_SET(flags, REMOVE_MISSING_OK) || errno != ENOENT))
|
||||
r = -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
|
||||
typedef enum RemoveFlags {
|
||||
REMOVE_ONLY_DIRECTORIES = 1 << 0, /* Only remove empty directories, no files */
|
||||
REMOVE_ROOT = 1 << 1, /* Remove the specified directory itself too, not just the contents of it */
|
||||
REMOVE_PHYSICAL = 1 << 2, /* If not set, only removes files on tmpfs, never physical file systems */
|
||||
REMOVE_SUBVOLUME = 1 << 3, /* Drop btrfs subvolumes in the tree too */
|
||||
REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
|
||||
REMOVE_CHMOD = 1 << 5, /* chmod() for write access if we cannot delete or access something */
|
||||
REMOVE_CHMOD_RESTORE = 1 << 6, /* Restore the old mode before returning */
|
||||
} RemoveFlags;
|
||||
|
||||
int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags);
|
||||
int fstatat_harder(int dfd,
|
||||
const char *filename,
|
||||
struct stat *ret,
|
||||
int fstatat_flags,
|
||||
RemoveFlags remove_flags);
|
||||
|
||||
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
|
||||
int rm_rf(const char *path, RemoveFlags flags);
|
||||
|
||||
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
|
||||
static inline char *rm_rf_physical_and_free(char *p) {
|
||||
PROTECT_ERRNO;
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK|REMOVE_CHMOD);
|
||||
return mfree(p);
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free);
|
||||
|
||||
/* Similar as above, but also has magic btrfs subvolume powers */
|
||||
static inline char *rm_rf_subvolume_and_free(char *p) {
|
||||
PROTECT_ERRNO;
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_MISSING_OK|REMOVE_CHMOD);
|
||||
return mfree(p);
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_subvolume_and_free);
|
||||
@@ -1,686 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#if HAVE_SELINUX
|
||||
#include <selinux/avc.h>
|
||||
#include <selinux/context.h>
|
||||
#include <selinux/label.h>
|
||||
#include <selinux/selinux.h>
|
||||
#endif
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "path-util.h"
|
||||
#include "selinux-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
#if HAVE_SELINUX
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(context_t, context_free, NULL);
|
||||
#define _cleanup_context_free_ _cleanup_(context_freep)
|
||||
|
||||
static int mac_selinux_reload(int seqno);
|
||||
|
||||
static int cached_use = -1;
|
||||
static bool initialized = false;
|
||||
static int (*enforcing_status_func)(void) = security_getenforce;
|
||||
static int last_policyload = 0;
|
||||
static struct selabel_handle *label_hnd = NULL;
|
||||
|
||||
#define log_enforcing(...) \
|
||||
log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
|
||||
|
||||
#define log_enforcing_errno(error, ...) \
|
||||
({ \
|
||||
bool _enforcing = mac_selinux_enforcing(); \
|
||||
int _level = _enforcing ? LOG_ERR : LOG_WARNING; \
|
||||
int _e = (error); \
|
||||
\
|
||||
int _r = (log_get_max_level() >= LOG_PRI(_level)) \
|
||||
? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
|
||||
: -ERRNO_VALUE(_e); \
|
||||
_enforcing ? _r : 0; \
|
||||
})
|
||||
#endif
|
||||
|
||||
bool mac_selinux_use(void) {
|
||||
#if HAVE_SELINUX
|
||||
if (_unlikely_(cached_use < 0)) {
|
||||
cached_use = is_selinux_enabled() > 0;
|
||||
log_debug("SELinux enabled state cached to: %s", cached_use ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
return cached_use;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool mac_selinux_enforcing(void) {
|
||||
#if HAVE_SELINUX
|
||||
return enforcing_status_func() != 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mac_selinux_retest(void) {
|
||||
#if HAVE_SELINUX
|
||||
cached_use = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_SELINUX
|
||||
# if HAVE_MALLINFO2
|
||||
# define HAVE_GENERIC_MALLINFO 1
|
||||
typedef struct mallinfo2 generic_mallinfo;
|
||||
static generic_mallinfo generic_mallinfo_get(void) {
|
||||
return mallinfo2();
|
||||
}
|
||||
# elif HAVE_MALLINFO
|
||||
# define HAVE_GENERIC_MALLINFO 1
|
||||
typedef struct mallinfo generic_mallinfo;
|
||||
static generic_mallinfo generic_mallinfo_get(void) {
|
||||
/* glibc has deprecated mallinfo(), let's suppress the deprecation warning if mallinfo2() doesn't
|
||||
* exist yet. */
|
||||
DISABLE_WARNING_DEPRECATED_DECLARATIONS
|
||||
return mallinfo();
|
||||
REENABLE_WARNING
|
||||
}
|
||||
# else
|
||||
# define HAVE_GENERIC_MALLINFO 0
|
||||
# endif
|
||||
|
||||
static int open_label_db(void) {
|
||||
struct selabel_handle *hnd;
|
||||
usec_t before_timestamp, after_timestamp;
|
||||
char timespan[FORMAT_TIMESPAN_MAX];
|
||||
|
||||
# if HAVE_GENERIC_MALLINFO
|
||||
generic_mallinfo before_mallinfo = generic_mallinfo_get();
|
||||
# endif
|
||||
before_timestamp = now(CLOCK_MONOTONIC);
|
||||
|
||||
hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
|
||||
if (!hnd)
|
||||
return log_enforcing_errno(errno, "Failed to initialize SELinux labeling handle: %m");
|
||||
|
||||
after_timestamp = now(CLOCK_MONOTONIC);
|
||||
# if HAVE_GENERIC_MALLINFO
|
||||
generic_mallinfo after_mallinfo = generic_mallinfo_get();
|
||||
size_t l = LESS_BY((size_t) after_mallinfo.uordblks, (size_t) before_mallinfo.uordblks);
|
||||
log_debug("Successfully loaded SELinux database in %s, size on heap is %zuK.",
|
||||
format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
|
||||
DIV_ROUND_UP(l, 1024));
|
||||
# else
|
||||
log_debug("Successfully loaded SELinux database in %s.",
|
||||
format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0));
|
||||
# endif
|
||||
|
||||
/* release memory after measurement */
|
||||
if (label_hnd)
|
||||
selabel_close(label_hnd);
|
||||
label_hnd = TAKE_PTR(hnd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mac_selinux_init(void) {
|
||||
#if HAVE_SELINUX
|
||||
int r;
|
||||
bool have_status_page = false;
|
||||
|
||||
if (initialized)
|
||||
return 0;
|
||||
|
||||
if (!mac_selinux_use())
|
||||
return 0;
|
||||
|
||||
r = selinux_status_open(/* netlink fallback */ 1);
|
||||
if (r < 0) {
|
||||
if (!ERRNO_IS_PRIVILEGE(errno))
|
||||
return log_enforcing_errno(errno, "Failed to open SELinux status page: %m");
|
||||
log_warning_errno(errno, "selinux_status_open() with netlink fallback failed, not checking for policy reloads: %m");
|
||||
} else if (r == 1)
|
||||
log_warning("selinux_status_open() failed to open the status page, using the netlink fallback.");
|
||||
else
|
||||
have_status_page = true;
|
||||
|
||||
r = open_label_db();
|
||||
if (r < 0) {
|
||||
selinux_status_close();
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Save the current policyload sequence number, so mac_selinux_maybe_reload() does not trigger on
|
||||
* first call without any actual change. */
|
||||
last_policyload = selinux_status_policyload();
|
||||
|
||||
if (have_status_page)
|
||||
/* Now that the SELinux status page has been successfully opened, retrieve the enforcing
|
||||
* status over it (to avoid system calls in security_getenforce()). */
|
||||
enforcing_status_func = selinux_status_getenforce;
|
||||
|
||||
initialized = true;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mac_selinux_maybe_reload(void) {
|
||||
#if HAVE_SELINUX
|
||||
int policyload;
|
||||
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
/* Do not use selinux_status_updated(3), cause since libselinux 3.2 selinux_check_access(3),
|
||||
* called in core and user instances, does also use it under the hood.
|
||||
* That can cause changes to be consumed by selinux_check_access(3) and not being visible here.
|
||||
* Also do not use selinux callbacks, selinux_set_callback(3), cause they are only automatically
|
||||
* invoked since libselinux 3.2 by selinux_status_updated(3).
|
||||
* Relevant libselinux commit: https://github.com/SELinuxProject/selinux/commit/05bdc03130d741e53e1fb45a958d0a2c184be503
|
||||
* Debian Bullseye is going to ship libselinux 3.1, so stay compatible for backports. */
|
||||
policyload = selinux_status_policyload();
|
||||
if (policyload < 0) {
|
||||
log_debug_errno(errno, "Failed to get SELinux policyload from status page: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
if (policyload != last_policyload) {
|
||||
mac_selinux_reload(policyload);
|
||||
last_policyload = policyload;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void mac_selinux_finish(void) {
|
||||
|
||||
#if HAVE_SELINUX
|
||||
if (label_hnd) {
|
||||
selabel_close(label_hnd);
|
||||
label_hnd = NULL;
|
||||
}
|
||||
|
||||
enforcing_status_func = security_getenforce;
|
||||
|
||||
selinux_status_close();
|
||||
|
||||
initialized = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_SELINUX
|
||||
static int mac_selinux_reload(int seqno) {
|
||||
log_debug("SELinux reload %d", seqno);
|
||||
|
||||
(void) open_label_db();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#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;
|
||||
|
||||
/* 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
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
_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;
|
||||
|
||||
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 (selabel_lookup_raw(label_hnd, &fcon, inside_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;
|
||||
}
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
if (setfilecon_raw(procfs_path, fcon) < 0) {
|
||||
_cleanup_freecon_ char *oldcon = NULL;
|
||||
|
||||
/* If the FS doesn't support labels, then exit without warning */
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
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))
|
||||
return 0;
|
||||
|
||||
r = -errno;
|
||||
|
||||
/* If the old label is identical to the new one, suppress any kind of error */
|
||||
if (getfilecon_raw(procfs_path, &oldcon) >= 0 && streq(fcon, oldcon))
|
||||
return 0;
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return log_enforcing_errno(r, "Unable to fix SELinux security context of %s (%s): %m", strna(path), strna(inside_path));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_selinux_apply(const char *path, const char *label) {
|
||||
|
||||
assert(path);
|
||||
|
||||
#if HAVE_SELINUX
|
||||
if (!mac_selinux_use())
|
||||
return 0;
|
||||
|
||||
assert(label);
|
||||
|
||||
if (setfilecon(path, label) < 0)
|
||||
return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_selinux_apply_fd(int fd, const char *path, const char *label) {
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
#if HAVE_SELINUX
|
||||
if (!mac_selinux_use())
|
||||
return 0;
|
||||
|
||||
assert(label);
|
||||
|
||||
if (fsetfilecon(fd, label) < 0)
|
||||
return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, strna(path));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
|
||||
#if HAVE_SELINUX
|
||||
_cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
|
||||
security_class_t sclass;
|
||||
int r;
|
||||
|
||||
assert(exe);
|
||||
assert(label);
|
||||
|
||||
if (!mac_selinux_use())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = getcon_raw(&mycon);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
r = getfilecon_raw(exe, &fcon);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
sclass = string_to_security_class("process");
|
||||
if (sclass == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
r = security_compute_create_raw(mycon, fcon, sclass, label);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int mac_selinux_get_our_label(char **label) {
|
||||
#if HAVE_SELINUX
|
||||
int r;
|
||||
|
||||
assert(label);
|
||||
|
||||
if (!mac_selinux_use())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = getcon_raw(label);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
|
||||
#if HAVE_SELINUX
|
||||
_cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
|
||||
_cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
|
||||
security_class_t sclass;
|
||||
const char *range = NULL;
|
||||
int r;
|
||||
|
||||
assert(socket_fd >= 0);
|
||||
assert(exe);
|
||||
assert(label);
|
||||
|
||||
if (!mac_selinux_use())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = getcon_raw(&mycon);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
r = getpeercon_raw(socket_fd, &peercon);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
if (!exec_label) {
|
||||
/* If there is no context set for next exec let's use context
|
||||
of target executable */
|
||||
r = getfilecon_raw(exe, &fcon);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
bcon = context_new(mycon);
|
||||
if (!bcon)
|
||||
return -ENOMEM;
|
||||
|
||||
pcon = context_new(peercon);
|
||||
if (!pcon)
|
||||
return -ENOMEM;
|
||||
|
||||
range = context_range_get(pcon);
|
||||
if (!range)
|
||||
return -errno;
|
||||
|
||||
r = context_range_set(bcon, range);
|
||||
if (r)
|
||||
return -errno;
|
||||
|
||||
freecon(mycon);
|
||||
mycon = strdup(context_str(bcon));
|
||||
if (!mycon)
|
||||
return -ENOMEM;
|
||||
|
||||
sclass = string_to_security_class("process");
|
||||
if (sclass == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
r = security_compute_create_raw(mycon, fcon, sclass, label);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
char* mac_selinux_free(char *label) {
|
||||
|
||||
#if HAVE_SELINUX
|
||||
freecon(label);
|
||||
#else
|
||||
assert(!label);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if HAVE_SELINUX
|
||||
static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
|
||||
_cleanup_freecon_ char *filecon = NULL;
|
||||
int r;
|
||||
|
||||
assert(abspath);
|
||||
assert(path_is_absolute(abspath));
|
||||
|
||||
/* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
|
||||
mac_selinux_maybe_reload();
|
||||
if (!label_hnd)
|
||||
return 0;
|
||||
|
||||
r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
|
||||
if (r < 0) {
|
||||
/* No context specified by the policy? Proceed without setting it. */
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
|
||||
}
|
||||
|
||||
if (setfscreatecon_raw(filecon) < 0)
|
||||
return log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
|
||||
#if HAVE_SELINUX
|
||||
_cleanup_free_ char *abspath = NULL;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (!label_hnd)
|
||||
return 0;
|
||||
|
||||
if (!path_is_absolute(path)) {
|
||||
if (dirfd == AT_FDCWD)
|
||||
r = safe_getcwd(&abspath);
|
||||
else
|
||||
r = fd_get_path(dirfd, &abspath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_extend(&abspath, path))
|
||||
return -ENOMEM;
|
||||
|
||||
path = abspath;
|
||||
}
|
||||
|
||||
return selinux_create_file_prepare_abspath(path, mode);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
|
||||
#if HAVE_SELINUX
|
||||
int r;
|
||||
|
||||
_cleanup_free_ char *abspath = NULL;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (!label_hnd)
|
||||
return 0;
|
||||
|
||||
r = path_make_absolute_cwd(path, &abspath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return selinux_create_file_prepare_abspath(abspath, mode);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mac_selinux_create_file_clear(void) {
|
||||
|
||||
#if HAVE_SELINUX
|
||||
PROTECT_ERRNO;
|
||||
|
||||
if (!mac_selinux_use())
|
||||
return;
|
||||
|
||||
setfscreatecon_raw(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
int mac_selinux_create_socket_prepare(const char *label) {
|
||||
|
||||
#if HAVE_SELINUX
|
||||
assert(label);
|
||||
|
||||
if (!mac_selinux_use())
|
||||
return 0;
|
||||
|
||||
if (setsockcreatecon(label) < 0)
|
||||
return log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mac_selinux_create_socket_clear(void) {
|
||||
|
||||
#if HAVE_SELINUX
|
||||
PROTECT_ERRNO;
|
||||
|
||||
if (!mac_selinux_use())
|
||||
return;
|
||||
|
||||
setsockcreatecon_raw(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
|
||||
/* Binds a socket and label its file system object according to the SELinux policy */
|
||||
|
||||
#if HAVE_SELINUX
|
||||
_cleanup_freecon_ char *fcon = NULL;
|
||||
const struct sockaddr_un *un;
|
||||
bool context_changed = false;
|
||||
char *path;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(addr);
|
||||
assert(addrlen >= sizeof(sa_family_t));
|
||||
|
||||
if (!label_hnd)
|
||||
goto skipped;
|
||||
|
||||
/* Filter out non-local sockets */
|
||||
if (addr->sa_family != AF_UNIX)
|
||||
goto skipped;
|
||||
|
||||
/* Filter out anonymous sockets */
|
||||
if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
|
||||
goto skipped;
|
||||
|
||||
/* Filter out abstract namespace sockets */
|
||||
un = (const struct sockaddr_un*) addr;
|
||||
if (un->sun_path[0] == 0)
|
||||
goto skipped;
|
||||
|
||||
path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
|
||||
|
||||
/* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
|
||||
mac_selinux_maybe_reload();
|
||||
if (!label_hnd)
|
||||
goto skipped;
|
||||
|
||||
if (path_is_absolute(path))
|
||||
r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
|
||||
else {
|
||||
_cleanup_free_ char *newpath = NULL;
|
||||
|
||||
r = path_make_absolute_cwd(path, &newpath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
/* No context specified by the policy? Proceed without setting it */
|
||||
if (errno == ENOENT)
|
||||
goto skipped;
|
||||
|
||||
r = log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
if (setfscreatecon_raw(fcon) < 0) {
|
||||
r = log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
context_changed = true;
|
||||
}
|
||||
|
||||
r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
|
||||
|
||||
if (context_changed)
|
||||
(void) setfscreatecon_raw(NULL);
|
||||
|
||||
return r;
|
||||
|
||||
skipped:
|
||||
#endif
|
||||
if (bind(fd, addr, addrlen) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "label.h"
|
||||
|
||||
#if HAVE_SELINUX
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(char*, freecon, NULL);
|
||||
#define _cleanup_freecon_ _cleanup_(freeconp)
|
||||
#endif
|
||||
|
||||
bool mac_selinux_use(void);
|
||||
void mac_selinux_retest(void);
|
||||
bool mac_selinux_enforcing(void);
|
||||
|
||||
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_apply(const char *path, const char *label);
|
||||
int mac_selinux_apply_fd(int fd, const char *path, const char *label);
|
||||
|
||||
int mac_selinux_get_create_label_from_exe(const char *exe, char **label);
|
||||
int mac_selinux_get_our_label(char **label);
|
||||
int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label);
|
||||
char* mac_selinux_free(char *label);
|
||||
|
||||
int mac_selinux_create_file_prepare(const char *path, mode_t mode);
|
||||
int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode);
|
||||
void mac_selinux_create_file_clear(void);
|
||||
|
||||
int mac_selinux_create_socket_prepare(const char *label);
|
||||
void mac_selinux_create_socket_clear(void);
|
||||
|
||||
int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, mac_selinux_free);
|
||||
@@ -1,288 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/***
|
||||
Copyright © 2013 Intel Corporation
|
||||
|
||||
Author: Auke Kok <auke-jan.h.kok@intel.com>
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "smack-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
#if ENABLE_SMACK
|
||||
bool mac_smack_use(void) {
|
||||
static int cached_use = -1;
|
||||
|
||||
if (cached_use < 0)
|
||||
cached_use = access("/sys/fs/smackfs/", F_OK) >= 0;
|
||||
|
||||
return cached_use;
|
||||
}
|
||||
|
||||
static const char* const smack_attr_table[_SMACK_ATTR_MAX] = {
|
||||
[SMACK_ATTR_ACCESS] = "security.SMACK64",
|
||||
[SMACK_ATTR_EXEC] = "security.SMACK64EXEC",
|
||||
[SMACK_ATTR_MMAP] = "security.SMACK64MMAP",
|
||||
[SMACK_ATTR_TRANSMUTE] = "security.SMACK64TRANSMUTE",
|
||||
[SMACK_ATTR_IPIN] = "security.SMACK64IPIN",
|
||||
[SMACK_ATTR_IPOUT] = "security.SMACK64IPOUT",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(smack_attr, SmackAttr);
|
||||
|
||||
int mac_smack_read(const char *path, SmackAttr attr, char **label) {
|
||||
assert(path);
|
||||
assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
|
||||
assert(label);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
return getxattr_malloc(path, smack_attr_to_string(attr), label, true);
|
||||
}
|
||||
|
||||
int mac_smack_read_fd(int fd, SmackAttr attr, char **label) {
|
||||
assert(fd >= 0);
|
||||
assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
|
||||
assert(label);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
return fgetxattr_malloc(fd, smack_attr_to_string(attr), label);
|
||||
}
|
||||
|
||||
int mac_smack_apply(const char *path, SmackAttr attr, const char *label) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
if (label)
|
||||
r = lsetxattr(path, smack_attr_to_string(attr), label, strlen(label), 0);
|
||||
else
|
||||
r = lremovexattr(path, smack_attr_to_string(attr));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
if (label)
|
||||
r = fsetxattr(fd, smack_attr_to_string(attr), label, strlen(label), 0);
|
||||
else
|
||||
r = fremovexattr(fd, smack_attr_to_string(attr));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_apply_pid(pid_t pid, const char *label) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(label);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
p = procfs_file_alloca(pid, "attr/current");
|
||||
r = write_string_file(p, label, WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) {
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
const char *label;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
/* The caller should have done the sanity checks. */
|
||||
assert(abspath);
|
||||
assert(path_is_absolute(abspath));
|
||||
|
||||
/* Path must be in /dev. */
|
||||
if (!path_startswith(abspath, "/dev"))
|
||||
return 0;
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
/*
|
||||
* Label directories and character devices "*".
|
||||
* Label symlinks "_".
|
||||
* Don't change anything else.
|
||||
*/
|
||||
|
||||
if (S_ISDIR(st.st_mode))
|
||||
label = SMACK_STAR_LABEL;
|
||||
else if (S_ISLNK(st.st_mode))
|
||||
label = SMACK_FLOOR_LABEL;
|
||||
else if (S_ISCHR(st.st_mode))
|
||||
label = SMACK_STAR_LABEL;
|
||||
else
|
||||
return 0;
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
if (setxattr(procfs_path, "security.SMACK64", label, strlen(label), 0) < 0) {
|
||||
_cleanup_free_ char *old_label = NULL;
|
||||
|
||||
r = -errno;
|
||||
|
||||
/* If the FS doesn't support labels, then exit without warning */
|
||||
if (r == -EOPNOTSUPP)
|
||||
return 0;
|
||||
|
||||
/* It the FS is read-only and we were told to ignore failures caused by that, suppress error */
|
||||
if (r == -EROFS && (flags & LABEL_IGNORE_EROFS))
|
||||
return 0;
|
||||
|
||||
/* If the old label is identical to the new one, suppress any kind of error */
|
||||
if (getxattr_malloc(procfs_path, "security.SMACK64", &old_label, false) >= 0 &&
|
||||
streq(old_label, label))
|
||||
return 0;
|
||||
|
||||
return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", abspath);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (!mac_smack_use())
|
||||
return 0;
|
||||
|
||||
fd = openat(dirfd, 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);
|
||||
}
|
||||
|
||||
int mac_smack_copy(const char *dest, const char *src) {
|
||||
int r;
|
||||
_cleanup_free_ char *label = NULL;
|
||||
|
||||
assert(dest);
|
||||
assert(src);
|
||||
|
||||
r = mac_smack_read(src, SMACK_ATTR_ACCESS, &label);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mac_smack_apply(dest, SMACK_ATTR_ACCESS, label);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#else
|
||||
bool mac_smack_use(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int mac_smack_read(const char *path, SmackAttr attr, char **label) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int mac_smack_read_fd(int fd, SmackAttr attr, char **label) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int mac_smack_apply(const char *path, SmackAttr attr, const char *label) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_apply_pid(pid_t pid, const char *label) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mac_smack_copy(const char *dest, const char *src) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1,46 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
Copyright © 2013 Intel Corporation
|
||||
|
||||
Author: Auke Kok <auke-jan.h.kok@intel.com>
|
||||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "label.h"
|
||||
#include "macro.h"
|
||||
|
||||
#define SMACK_FLOOR_LABEL "_"
|
||||
#define SMACK_STAR_LABEL "*"
|
||||
|
||||
typedef enum SmackAttr {
|
||||
SMACK_ATTR_ACCESS,
|
||||
SMACK_ATTR_EXEC,
|
||||
SMACK_ATTR_MMAP,
|
||||
SMACK_ATTR_TRANSMUTE,
|
||||
SMACK_ATTR_IPIN,
|
||||
SMACK_ATTR_IPOUT,
|
||||
_SMACK_ATTR_MAX,
|
||||
_SMACK_ATTR_INVALID = -EINVAL,
|
||||
} SmackAttr;
|
||||
|
||||
bool mac_smack_use(void);
|
||||
|
||||
int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags);
|
||||
static inline int mac_smack_fix(const char *path, LabelFixFlags flags) {
|
||||
return mac_smack_fix_container(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);
|
||||
int mac_smack_read_fd(int fd, SmackAttr attr, char **label);
|
||||
int mac_smack_apply(const char *path, SmackAttr attr, const char *label);
|
||||
int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label);
|
||||
int mac_smack_apply_pid(pid_t pid, const char *label);
|
||||
int mac_smack_copy(const char *dest, const char *src);
|
||||
@@ -1,133 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing_socket.h"
|
||||
#include "mkdir.h"
|
||||
#include "selinux-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "umask-util.h"
|
||||
|
||||
int socket_address_listen(
|
||||
const SocketAddress *a,
|
||||
int flags,
|
||||
int backlog,
|
||||
SocketAddressBindIPv6Only only,
|
||||
const char *bind_to_device,
|
||||
bool reuse_port,
|
||||
bool free_bind,
|
||||
bool transparent,
|
||||
mode_t directory_mode,
|
||||
mode_t socket_mode,
|
||||
const char *label) {
|
||||
|
||||
_cleanup_close_ int fd = -1;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
|
||||
r = socket_address_verify(a, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
if (label) {
|
||||
r = mac_selinux_create_socket_prepare(label);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
fd = socket(socket_address_family(a), a->type | flags, a->protocol);
|
||||
r = fd < 0 ? -errno : 0;
|
||||
|
||||
if (label)
|
||||
mac_selinux_create_socket_clear();
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
|
||||
r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_V6ONLY, only == SOCKET_ADDRESS_IPV6_ONLY);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (IN_SET(socket_address_family(a), AF_INET, AF_INET6)) {
|
||||
if (bind_to_device) {
|
||||
r = socket_bind_to_ifname(fd, bind_to_device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (reuse_port) {
|
||||
r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEPORT, true);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "SO_REUSEPORT failed: %m");
|
||||
}
|
||||
|
||||
if (free_bind) {
|
||||
r = socket_set_freebind(fd, socket_address_family(a), true);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "IP_FREEBIND/IPV6_FREEBIND failed: %m");
|
||||
}
|
||||
|
||||
if (transparent) {
|
||||
r = socket_set_transparent(fd, socket_address_family(a), true);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "IP_TRANSPARENT/IPV6_TRANSPARENT failed: %m");
|
||||
}
|
||||
}
|
||||
|
||||
r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = socket_address_get_path(a);
|
||||
if (p) {
|
||||
/* Create parents */
|
||||
(void) mkdir_parents_label(p, directory_mode);
|
||||
|
||||
/* Enforce the right access mode for the socket */
|
||||
RUN_WITH_UMASK(~socket_mode) {
|
||||
r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
|
||||
if (r == -EADDRINUSE) {
|
||||
/* Unlink and try again */
|
||||
|
||||
if (unlink(p) < 0)
|
||||
return r; /* didn't work, return original error */
|
||||
|
||||
r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
if (bind(fd, &a->sockaddr.sa, a->size) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (socket_address_can_accept(a))
|
||||
if (listen(fd, backlog) < 0)
|
||||
return -errno;
|
||||
|
||||
/* Let's trigger an inotify event on the socket node, so that anyone waiting for this socket to be connectable
|
||||
* gets notified */
|
||||
if (p)
|
||||
(void) touch(p);
|
||||
|
||||
return TAKE_FD(fd);
|
||||
}
|
||||
Reference in New Issue
Block a user