]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
namespace helpers: Allow entering a UID namespace 977/head
authorRichard Maw <richard.maw@codethink.co.uk>
Mon, 17 Aug 2015 08:52:13 +0000 (08:52 +0000)
committerRichard Maw <richard.maw@codethink.co.uk>
Mon, 17 Aug 2015 08:52:13 +0000 (08:52 +0000)
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.

src/basic/util.c
src/basic/util.h
src/core/machine-id-setup.c
src/libsystemd/sd-bus/bus-container.c
src/machine/machine-dbus.c
src/shared/logs-show.c

index d4d3d3c83a88536aab3c5e57c6f440753fab48d3..381146089506124d3925b6fc2584bd8cbc07232a 100644 (file)
@@ -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);
 
index 426b7f7d1659652eb1d4787b8884f91df453ad8e..1667b35f0049cfc2efac625a526ee3b6c699faf7 100644 (file)
@@ -803,8 +803,8 @@ int get_proc_cmdline_key(const char *parameter, char **value);
 
 int container_get_leader(const char *machine, pid_t *pid);
 
-int namespace_open(pid_t pid, 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 root_fd);
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd);
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
 
 int getpeercred(int fd, struct ucred *ucred);
 int getpeersec(int fd, char **ret);
index 8e26362546ba675510336b53743504a2a585c054..2d5ae3b3b92618f369b8dba8ad3f4cfa6b413730 100644 (file)
@@ -325,7 +325,7 @@ int machine_id_commit(const char *root) {
         fd = safe_close(fd);
 
         /* Store current mount namespace */
-        r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL);
+        r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Can't fetch current mount namespace: %m");
 
@@ -351,7 +351,7 @@ int machine_id_commit(const char *root) {
         fd = safe_close(fd);
 
         /* Return to initial namespace and proceed a lazy tmpfs unmount */
-        r = namespace_enter(-1, initial_mntns_fd, -1, -1);
+        r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
         if (r < 0)
                 return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id);
 
index fa7a207448806f8e5cc3e5efd2a3a5217d05a7e3..101e4af18d6b78546728f29b841c724aa1989a60 100644 (file)
@@ -29,7 +29,7 @@
 #include "bus-container.h"
 
 int bus_container_connect_socket(sd_bus *b) {
-        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
         pid_t child;
         siginfo_t si;
         int r;
@@ -45,7 +45,7 @@ int bus_container_connect_socket(sd_bus *b) {
                         return r;
         }
 
-        r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &rootfd);
+        r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
         if (r < 0)
                 return r;
 
@@ -64,7 +64,7 @@ int bus_container_connect_socket(sd_bus *b) {
         if (child == 0) {
                 pid_t grandchild;
 
-                r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
+                r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
                 if (r < 0)
                         _exit(255);
 
@@ -120,7 +120,7 @@ int bus_container_connect_socket(sd_bus *b) {
 
 int bus_container_connect_kernel(sd_bus *b) {
         _cleanup_close_pair_ int pair[2] = { -1, -1 };
-        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
         union {
                 struct cmsghdr cmsghdr;
                 uint8_t buf[CMSG_SPACE(sizeof(int))];
@@ -146,7 +146,7 @@ int bus_container_connect_kernel(sd_bus *b) {
                         return r;
         }
 
-        r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &rootfd);
+        r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
         if (r < 0)
                 return r;
 
@@ -162,7 +162,7 @@ int bus_container_connect_kernel(sd_bus *b) {
 
                 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);
 
index 7658d7146db892fd0865cb8ba9c2a407b410bc75..b62a9bd81309ac75a340fc03dd85f52fd829d61b 100644 (file)
@@ -212,7 +212,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
         if (streq(us, them))
                 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
 
-        r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
+        r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL);
         if (r < 0)
                 return r;
 
@@ -230,7 +230,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
 
                 pair[0] = safe_close(pair[0]);
 
-                r = namespace_enter(-1, -1, netns_fd, -1);
+                r = namespace_enter(-1, -1, netns_fd, -1, -1);
                 if (r < 0)
                         _exit(EXIT_FAILURE);
 
@@ -346,7 +346,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
         if (m->class != MACHINE_CONTAINER)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
 
-        r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
+        r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
         if (r < 0)
                 return r;
 
@@ -362,7 +362,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
 
                 pair[0] = safe_close(pair[0]);
 
-                r = namespace_enter(-1, mntns_fd, -1, root_fd);
+                r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
                 if (r < 0)
                         _exit(EXIT_FAILURE);
 
index 068da465d9fcdb816c9230cbe6cc6d251b110939..b78cb7678c458ac8280fbb26ab07382459473640 100644 (file)
@@ -1141,7 +1141,7 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
         if (r < 0)
                 return r;
 
-        r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
+        r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, NULL, &rootfd);
         if (r < 0)
                 return r;
 
@@ -1157,7 +1157,7 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
 
                 pair[0] = safe_close(pair[0]);
 
-                r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
+                r = namespace_enter(pidnsfd, mntnsfd, -1, -1, rootfd);
                 if (r < 0)
                         _exit(EXIT_FAILURE);