]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
nsenter: add --all option
authorKarel Zak <kzak@redhat.com>
Tue, 13 Dec 2016 13:44:33 +0000 (14:44 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 13 Dec 2016 13:56:48 +0000 (14:56 +0100)
Let's make it easy for users to enter target process namespaces.

Addresses: https://github.com/karelzak/util-linux/issues/382
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/nsenter.1
sys-utils/nsenter.c

index c85592630fae93c34be1881922c0d1b374aedfdc..9e3e60fe25bafa5f2085a641135d78cee2bcc7ab 100644 (file)
@@ -103,6 +103,18 @@ This should be one of the
 files described in
 .BR namespaces (7).
 .TP
+\fB\-a\fR, \fB\-\-all\fR
+Enter all namespaces of the target process by the default
+.IR /proc/[pid]/ns/*
+namespace paths. The default paths to the target process namespaces may be
+overwritten by namespace specific options (e.g. --all --mount=[path]).
+
+The user namespace will be ignored if the same as the caller's current user
+namespace. It prevents a caller that has dropped capabilities from regaining
+those capabilities via a call to setns().  See
+.BR setns (2)
+for more details.
+.TP
 \fB\-t\fR, \fB\-\-target\fR \fIpid\fP
 Specify a target process to get contexts from.  The paths to the contexts
 specified by
index f85ace9fcece993d2511310b8e058f8aabeec5f8..fc301d9f3cdd5659e04ab0872ae66680243396a9 100644 (file)
@@ -29,6 +29,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <grp.h>
+#include <sys/stat.h>
 
 #ifdef HAVE_LIBSELINUX
 # include <selinux/selinux.h>
@@ -76,6 +77,7 @@ static void usage(int status)
        fputs(_("Run a program with namespaces of other processes.\n"), out);
 
        fputs(USAGE_OPTIONS, out);
+       fputs(_(" -a, --all              enter all namespaces\n"), out);
        fputs(_(" -t, --target <pid>     target process to get namespaces from\n"), out);
        fputs(_(" -m, --mount[=<file>]   enter mount namespace\n"), out);
        fputs(_(" -u, --uts[=<file>]     enter UTS namespace (hostname etc)\n"), out);
@@ -143,6 +145,32 @@ static void open_namespace_fd(int nstype, const char *path)
        assert(nsfile->nstype);
 }
 
+static int get_ns_ino(const char *path, ino_t *ino)
+{
+       struct stat st;
+
+       if (stat(path, &st) != 0)
+               return -errno;
+       *ino = st.st_ino;
+       return 0;
+}
+
+static int is_same_namespace(pid_t a, pid_t b, const char *type)
+{
+       char path[PATH_MAX];
+       ino_t a_ino, b_ino;
+
+       snprintf(path, sizeof(path), "/proc/%u/%s", a, type);
+       if (get_ns_ino(path, &a_ino) != 0)
+               err(EXIT_FAILURE, _("stat of %s failed"), path);
+
+       snprintf(path, sizeof(path), "/proc/%u/%s", b, type);
+       if (get_ns_ino(path, &b_ino) != 0)
+               err(EXIT_FAILURE, _("stat of %s failed"), path);
+
+       return a_ino == b_ino;
+}
+
 static void continue_as_child(void)
 {
        pid_t child = fork();
@@ -181,6 +209,7 @@ int main(int argc, char *argv[])
                OPT_PRESERVE_CRED = CHAR_MAX + 1
        };
        static const struct option longopts[] = {
+               { "all", no_argument, NULL, 'a' },
                { "help", no_argument, NULL, 'h' },
                { "version", no_argument, NULL, 'V'},
                { "target", required_argument, NULL, 't' },
@@ -206,6 +235,7 @@ int main(int argc, char *argv[])
        struct namespace_file *nsfile;
        int c, pass, namespaces = 0, setgroups_nerrs = 0, preserve_cred = 0;
        bool do_rd = false, do_wd = false, force_uid = false, force_gid = false;
+       bool do_all = false;
        int do_fork = -1; /* unknown yet */
        uid_t uid = 0;
        gid_t gid = 0;
@@ -219,7 +249,7 @@ int main(int argc, char *argv[])
        atexit(close_stdout);
 
        while ((c =
-               getopt_long(argc, argv, "+hVt:m::u::i::n::p::C::U::S:G:r::w::FZ",
+               getopt_long(argc, argv, "+ahVt:m::u::i::n::p::C::U::S:G:r::w::FZ",
                            longopts, NULL)) != -1) {
                switch (c) {
                case 'h':
@@ -227,6 +257,9 @@ int main(int argc, char *argv[])
                case 'V':
                        printf(UTIL_LINUX_VERSION);
                        return EXIT_SUCCESS;
+               case 'a':
+                       do_all = true;
+                       break;
                case 't':
                        namespace_target_pid =
                            strtoul_or_err(optarg, _("failed to parse pid"));
@@ -323,6 +356,25 @@ int main(int argc, char *argv[])
                freecon(scon);
        }
 #endif
+
+       if (do_all) {
+               if (!namespace_target_pid)
+                       errx(EXIT_FAILURE, _("no target PID specified for --all"));
+               for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
+                       if (nsfile->fd >= 0)
+                               continue;       /* namespace already specified */
+
+                       /* It is not permitted to use setns(2) to reenter the caller's
+                        * current user namespace; see setns(2) man page for more details.
+                        */
+                       if (nsfile->nstype & CLONE_NEWUSER
+                           && is_same_namespace(getpid(), namespace_target_pid, nsfile->name))
+                               continue;
+
+                       namespaces |= nsfile->nstype;
+               }
+       }
+
        /*
         * Open remaining namespace and directory descriptors.
         */