]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
nsenter: enter namespaces in two passes
authorJames Bottomley <James.Bottomley@HansenPartnership.com>
Fri, 15 Apr 2016 15:10:20 +0000 (08:10 -0700)
committerKarel Zak <kzak@redhat.com>
Fri, 22 Apr 2016 09:03:01 +0000 (11:03 +0200)
We have two use cases for user namespaces, one to elevate the
privilege of an unprivileged user, in which case we have to enter the
user namespace before all other namespaces (otherwise there isn't
enough permission to enter any other namespace).  And the other one is
where we're deprivileging a user and thus have to enter the user
namespace last (because that's the point at which we lose the
privileges).  On the first pass, we start at the position one after
the user namespace clearing the file descriptors as we close them
after calling setns().  If setns() fails on the first pass, ignore the
failure assuming that it will succeed after we enter the user
namespace.

Addresses: https://github.com/karelzak/util-linux/issues/315
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/nsenter.c

index d8690db367ea018c1e8a26201cd6c5cb792106b8..8dbd22b2ecd714b503e618e72c51642d0376a8ab 100644 (file)
@@ -48,9 +48,11 @@ static struct namespace_file {
 } namespace_files[] = {
        /* Careful the order is significant in this array.
         *
-        * The user namespace comes first, so that it is entered
-        * first.  This gives an unprivileged user the potential to
-        * enter the other namespaces.
+        * The user namespace comes either first or last: first if
+        * you're using it to increase your privilege and last if
+        * you're using it to decrease.  We enter the namespaces in
+        * two passes starting initially from offset 1 and then offset
+        * 0 if that fails.
         */
        { .nstype = CLONE_NEWUSER,  .name = "ns/user", .fd = -1 },
        { .nstype = CLONE_NEWCGROUP,.name = "ns/cgroup", .fd = -1 },
@@ -202,7 +204,7 @@ int main(int argc, char *argv[])
        };
 
        struct namespace_file *nsfile;
-       int c, namespaces = 0, setgroups_nerrs = 0, preserve_cred = 0;
+       int c, pass, namespaces = 0, setgroups_nerrs = 0, preserve_cred = 0;
        bool do_rd = false, do_wd = false, force_uid = false, force_gid = false;
        int do_fork = -1; /* unknown yet */
        uid_t uid = 0;
@@ -353,19 +355,31 @@ int main(int argc, char *argv[])
        }
 
        /*
-        * Now that we know which namespaces we want to enter, enter them.
+        * Now that we know which namespaces we want to enter, enter
+        * them.  Do this in two passes, not entering the user
+        * namespace on the first pass.  So if we're deprivileging the
+        * container we'll enter the user namespace last and if we're
+        * privileging it then we enter the usernamespace first
+        * (because the initial setns will fail).
         */
-       for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
-               if (nsfile->fd < 0)
-                       continue;
-               if (nsfile->nstype == CLONE_NEWPID && do_fork == -1)
-                       do_fork = 1;
-               if (setns(nsfile->fd, nsfile->nstype))
-                       err(EXIT_FAILURE,
-                           _("reassociate to namespace '%s' failed"),
-                           nsfile->name);
-               close(nsfile->fd);
-               nsfile->fd = -1;
+       for (pass = 0; pass < 2; pass ++) {
+               for (nsfile = namespace_files + 1 - pass; nsfile->nstype; nsfile++) {
+                       if (nsfile->fd < 0)
+                               continue;
+                       if (nsfile->nstype == CLONE_NEWPID && do_fork == -1)
+                               do_fork = 1;
+                       if (setns(nsfile->fd, nsfile->nstype)) {
+                               if (pass != 0)
+                                       err(EXIT_FAILURE,
+                                           _("reassociate to namespace '%s' failed"),
+                                           nsfile->name);
+                               else
+                                       continue;
+                       }
+
+                       close(nsfile->fd);
+                       nsfile->fd = -1;
+               }
        }
 
        /* Remember the current working directory if I'm not changing it */