summaryrefslogtreecommitdiff
path: root/src/basic/util.c
diff options
context:
space:
mode:
authorRichard Maw <richard.maw@codethink.co.uk>2015-08-17 08:52:13 +0000
committerRichard Maw <richard.maw@codethink.co.uk>2015-08-17 08:52:13 +0000
commit671c34195896e89f935d842b13f52a748bd8a5b4 (patch)
tree6003e1b80caf7351c1e723e63f54855eec78e91c /src/basic/util.c
parentaae68db9b441963b0d1b8519e0aee8e2fc05e20c (diff)
namespace helpers: Allow entering a UID namespace
To be able to use `systemd-run` or `machinectl login` on a container that is in a private user namespace, the sub-process must have entered the user namespace before connecting to the container's D-Bus, otherwise the UID and GID in the peer credentials are garbage. So we extend namespace_open and namespace_enter to support UID namespaces, and we enter the UID namespace in bus_container_connect_{socket,kernel}. namespace_open will degrade to a no-op if user namespaces are not enabled in the kernel. Special handling is required for the setns call in namespace_enter with a user namespace, since transitioning to your own namespace is forbidden, as it would result in re-entering your user namespace as root. Arguably it may be valid to check this at the call site, rather than inside namespace_enter, but it is less code to do it inside, and if the intention of calling namespace_enter is to *be* in the target namespace, rather than to transition to the target namespace, it is a reasonable approach. The check for whether the user namespace is the same must happen before entering namespaces, as we may not be able to access /proc during the intermediate transition stage. We can't instead attempt to enter the user namespace and then ignore the failure from it being the same namespace, since the error code is not distinct, and we can't compare namespaces while mid-transition.
Diffstat (limited to 'src/basic/util.c')
-rw-r--r--src/basic/util.c45
1 files changed, 38 insertions, 7 deletions
diff --git a/src/basic/util.c b/src/basic/util.c
index d4d3d3c83a..3811460895 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -4950,8 +4950,8 @@ int container_get_leader(const char *machine, pid_t *pid) {
return 0;
}
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd) {
- _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1;
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
int rfd = -1;
assert(pid >= 0);
@@ -4983,6 +4983,15 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *
return -errno;
}
+ if (userns_fd) {
+ const char *userns;
+
+ userns = procfs_file_alloca(pid, "ns/user");
+ usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (usernsfd < 0 && errno != ENOENT)
+ return -errno;
+ }
+
if (root_fd) {
const char *root;
@@ -5001,15 +5010,33 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *
if (netns_fd)
*netns_fd = netnsfd;
+ if (userns_fd)
+ *userns_fd = usernsfd;
+
if (root_fd)
*root_fd = rfd;
- pidnsfd = mntnsfd = netnsfd = -1;
+ pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
return 0;
}
-int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
+ if (userns_fd >= 0) {
+ /* Can't setns to your own userns, since then you could
+ * escalate from non-root to root in your own namespace, so
+ * check if namespaces equal before attempting to enter. */
+ _cleanup_free_ char *userns_fd_path = NULL;
+ int r;
+ if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
+ return -ENOMEM;
+
+ r = files_same(userns_fd_path, "/proc/self/ns/user");
+ if (r < 0)
+ return r;
+ if (r)
+ userns_fd = -1;
+ }
if (pidns_fd >= 0)
if (setns(pidns_fd, CLONE_NEWPID) < 0)
@@ -5023,6 +5050,10 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
if (setns(netns_fd, CLONE_NEWNET) < 0)
return -errno;
+ if (userns_fd >= 0)
+ if (setns(userns_fd, CLONE_NEWUSER) < 0)
+ return -errno;
+
if (root_fd >= 0) {
if (fchdir(root_fd) < 0)
return -errno;
@@ -6012,7 +6043,7 @@ int ptsname_malloc(int fd, char **ret) {
}
int openpt_in_namespace(pid_t pid, int flags) {
- _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
union {
struct cmsghdr cmsghdr;
@@ -6029,7 +6060,7 @@ int openpt_in_namespace(pid_t pid, int flags) {
assert(pid > 0);
- r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
if (r < 0)
return r;
@@ -6045,7 +6076,7 @@ int openpt_in_namespace(pid_t pid, int flags) {
pair[0] = safe_close(pair[0]);
- r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
+ r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
if (r < 0)
_exit(EXIT_FAILURE);