summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMike Yuan <me@yhndnzj.com>2025-01-11 15:42:15 +0100
committerMike Yuan <me@yhndnzj.com>2025-01-11 15:53:14 +0100
commitc7704ecd0425a7ba5230275bdd4f06f1713c5cf8 (patch)
tree4ba3adf00949d5072f961eee3fef34b140fcb702 /src
parentREADME: document kernel version for idmapped mounts (diff)
downloadsystemd-c7704ecd0425a7ba5230275bdd4f06f1713c5cf8.tar.xz
systemd-c7704ecd0425a7ba5230275bdd4f06f1713c5cf8.zip
namespace-util: group userns functions together
Diffstat (limited to '')
-rw-r--r--src/basic/namespace-util.c237
-rw-r--r--src/basic/namespace-util.h10
2 files changed, 124 insertions, 123 deletions
diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c
index c7b1bcca2d..2ae5003459 100644
--- a/src/basic/namespace-util.c
+++ b/src/basic/namespace-util.c
@@ -459,6 +459,44 @@ int detach_mount_namespace_userns(int userns_fd) {
return detach_mount_namespace();
}
+int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range) {
+ _cleanup_free_ char *buffer = NULL;
+ const char *range, *shift;
+ int r;
+ uid_t uid_shift, uid_range = 65536;
+
+ assert(s);
+
+ range = strchr(s, ':');
+ if (range) {
+ buffer = strndup(s, range - s);
+ if (!buffer)
+ return log_oom();
+ shift = buffer;
+
+ range++;
+ r = safe_atou32(range, &uid_range);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range);
+ } else
+ shift = s;
+
+ r = parse_uid(shift, &uid_shift);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID \"%s\": %m", s);
+
+ if (!userns_shift_range_valid(uid_shift, uid_range))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID range cannot be empty or go beyond " UID_FMT ".", UID_INVALID);
+
+ if (ret_uid_shift)
+ *ret_uid_shift = uid_shift;
+
+ if (ret_uid_range)
+ *ret_uid_range = uid_range;
+
+ return 0;
+}
+
int userns_acquire_empty(void) {
_cleanup_(sigkill_waitp) pid_t pid = 0;
_cleanup_close_ int userns_fd = -EBADF;
@@ -520,124 +558,6 @@ int userns_acquire(const char *uid_map, const char *gid_map) {
return TAKE_FD(userns_fd);
}
-int netns_acquire(void) {
- _cleanup_(sigkill_waitp) pid_t pid = 0;
- _cleanup_close_ int netns_fd = -EBADF;
- int r;
-
- /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills
- * the process again. This way we have a netns fd that is not bound to any process. */
-
- r = safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid);
- if (r < 0)
- return log_debug_errno(r, "Failed to fork process (sd-mknetns): %m");
- if (r == 0)
- /* Child. We do nothing here, just freeze until somebody kills us. */
- freeze();
-
- r = namespace_open(pid,
- /* ret_pidns_fd = */ NULL,
- /* ret_mntns_fd = */ NULL,
- &netns_fd,
- /* ret_userns_fd = */ NULL,
- /* ret_root_fd = */ NULL);
- if (r < 0)
- return log_debug_errno(r, "Failed to open netns fd: %m");
-
- return TAKE_FD(netns_fd);
-}
-
-int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range) {
- _cleanup_free_ char *buffer = NULL;
- const char *range, *shift;
- int r;
- uid_t uid_shift, uid_range = 65536;
-
- assert(s);
-
- range = strchr(s, ':');
- if (range) {
- buffer = strndup(s, range - s);
- if (!buffer)
- return log_oom();
- shift = buffer;
-
- range++;
- r = safe_atou32(range, &uid_range);
- if (r < 0)
- return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range);
- } else
- shift = s;
-
- r = parse_uid(shift, &uid_shift);
- if (r < 0)
- return log_error_errno(r, "Failed to parse UID \"%s\": %m", s);
-
- if (!userns_shift_range_valid(uid_shift, uid_range))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID range cannot be empty or go beyond " UID_FMT ".", UID_INVALID);
-
- if (ret_uid_shift)
- *ret_uid_shift = uid_shift;
-
- if (ret_uid_range)
- *ret_uid_range = uid_range;
-
- return 0;
-}
-
-int is_idmapping_supported(const char *path) {
- _cleanup_close_ int mount_fd = -EBADF, userns_fd = -EBADF, dir_fd = -EBADF;
- _cleanup_free_ char *uid_map = NULL, *gid_map = NULL;
- int r;
-
- assert(path);
-
- if (!mount_new_api_supported())
- return false;
-
- r = strextendf(&uid_map, UID_FMT " " UID_FMT " " UID_FMT "\n", UID_NOBODY, UID_NOBODY, 1u);
- if (r < 0)
- return r;
-
- r = strextendf(&gid_map, GID_FMT " " GID_FMT " " GID_FMT "\n", GID_NOBODY, GID_NOBODY, 1u);
- if (r < 0)
- return r;
-
- userns_fd = r = userns_acquire(uid_map, gid_map);
- if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
- return false;
- if (r == -ENOSPC) {
- log_debug_errno(r, "Failed to acquire new user namespace, user.max_user_namespaces seems to be exhausted or maybe even zero, assuming ID-mapping is not supported: %m");
- return false;
- }
- if (r < 0)
- return log_debug_errno(r, "Failed to acquire new user namespace for checking if '%s' supports ID-mapping: %m", path);
-
- dir_fd = r = RET_NERRNO(open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
- if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
- return false;
- if (r < 0)
- return log_debug_errno(r, "Failed to open '%s', cannot determine if ID-mapping is supported: %m", path);
-
- mount_fd = r = RET_NERRNO(open_tree(dir_fd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC));
- if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
- return false;
- if (r < 0)
- return log_debug_errno(r, "Failed to open mount tree '%s', cannot determine if ID-mapping is supported: %m", path);
-
- r = RET_NERRNO(mount_setattr(mount_fd, "", AT_EMPTY_PATH,
- &(struct mount_attr) {
- .attr_set = MOUNT_ATTR_IDMAP | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RDONLY | MOUNT_ATTR_NODEV,
- .userns_fd = userns_fd,
- }, sizeof(struct mount_attr)));
- if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
- return false;
- if (r < 0)
- return log_debug_errno(r, "Failed to set mount attribute to '%s', cannot determine if ID-mapping is supported: %m", path);
-
- return true;
-}
-
int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid) {
_cleanup_(close_pairp) int pfd[2] = EBADF_PAIR;
_cleanup_(sigkill_waitp) pid_t pid = 0;
@@ -763,3 +683,84 @@ int process_is_owned_by_uid(const PidRef *pidref, uid_t uid) {
close_and_replace(userns_fd, parent_fd);
}
}
+
+int is_idmapping_supported(const char *path) {
+ _cleanup_close_ int mount_fd = -EBADF, userns_fd = -EBADF, dir_fd = -EBADF;
+ _cleanup_free_ char *uid_map = NULL, *gid_map = NULL;
+ int r;
+
+ assert(path);
+
+ if (!mount_new_api_supported())
+ return false;
+
+ r = strextendf(&uid_map, UID_FMT " " UID_FMT " " UID_FMT "\n", UID_NOBODY, UID_NOBODY, 1u);
+ if (r < 0)
+ return r;
+
+ r = strextendf(&gid_map, GID_FMT " " GID_FMT " " GID_FMT "\n", GID_NOBODY, GID_NOBODY, 1u);
+ if (r < 0)
+ return r;
+
+ userns_fd = r = userns_acquire(uid_map, gid_map);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
+ return false;
+ if (r == -ENOSPC) {
+ log_debug_errno(r, "Failed to acquire new user namespace, user.max_user_namespaces seems to be exhausted or maybe even zero, assuming ID-mapping is not supported: %m");
+ return false;
+ }
+ if (r < 0)
+ return log_debug_errno(r, "Failed to acquire new user namespace for checking if '%s' supports ID-mapping: %m", path);
+
+ dir_fd = r = RET_NERRNO(open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ return false;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open '%s', cannot determine if ID-mapping is supported: %m", path);
+
+ mount_fd = r = RET_NERRNO(open_tree(dir_fd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC));
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
+ return false;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open mount tree '%s', cannot determine if ID-mapping is supported: %m", path);
+
+ r = RET_NERRNO(mount_setattr(mount_fd, "", AT_EMPTY_PATH,
+ &(struct mount_attr) {
+ .attr_set = MOUNT_ATTR_IDMAP | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RDONLY | MOUNT_ATTR_NODEV,
+ .userns_fd = userns_fd,
+ }, sizeof(struct mount_attr)));
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
+ return false;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to set mount attribute to '%s', cannot determine if ID-mapping is supported: %m", path);
+
+ return true;
+}
+
+int netns_acquire(void) {
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ _cleanup_close_ int netns_fd = -EBADF;
+ int r;
+
+ /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills
+ * the process again. This way we have a netns fd that is not bound to any process. */
+
+ r = safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to fork process (sd-mknetns): %m");
+ if (r == 0)
+ /* Child. We do nothing here, just freeze until somebody kills us. */
+ freeze();
+
+ r = namespace_open(pid,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
+ &netns_fd,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open netns fd: %m");
+
+ return TAKE_FD(netns_fd);
+}
+
diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h
index d1ac4f6ed4..58347cbf88 100644
--- a/src/basic/namespace-util.h
+++ b/src/basic/namespace-util.h
@@ -83,15 +83,15 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
return true;
}
+int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
+
int userns_acquire_empty(void);
int userns_acquire(const char *uid_map, const char *gid_map);
-int netns_acquire(void);
+int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid);
-int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
+int process_is_owned_by_uid(const PidRef *pidref, uid_t uid);
int is_idmapping_supported(const char *path);
-int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid);
-
-int process_is_owned_by_uid(const PidRef *pidref, uid_t uid);
+int netns_acquire(void);