]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
unshare: Add possibility to add mapping into root user in user namespace
authorLubomir Rintel <lkundrak@v3.sk>
Fri, 27 Dec 2013 21:14:48 +0000 (22:14 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 7 Jan 2014 10:04:42 +0000 (11:04 +0100)
This makes it very convenient to use make use of privileged actions
on CONFIG_USER_NS enabled kernels, without having to manually tinker
with uid_map and gid_map to obtain required credentials (as those
given upon unshare() vanish with call to execve() and lot of userspace
checks for euid==0 anyway).

Usage example:

$ unshare --uts
unshare: unshare failed: Operation not permitted

$ unshare --user --uts
[nfsnobody@odvarok ~]$ hostname swag
hostname: you must be root to change the host name

$ unshare -r --uts
[root@odvarok util-linux]# hostname swag
[root@odvarok util-linux]#

[kzak@redhat.com: - move code to map_id()
                  - use all-io.h
                  - add paths to pathnames.h]

Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
Signed-off-by: Karel Zak <kzak@redhat.com>
include/pathnames.h
sys-utils/unshare.1
sys-utils/unshare.c

index dce98d2a42e5ee4cef63c2c4adc9682c0182fad3..2957dacb56a9f3f8de303681c2c06f2527a19bae 100644 (file)
@@ -87,6 +87,9 @@
 #define _PATH_PROC_LOCKS        "/proc/locks"
 #define _PATH_PROC_CDROMINFO   "/proc/sys/dev/cdrom/info"
 
+#define _PATH_PROC_UIDMAP      "/proc/self/uid_map"
+#define _PATH_PROC_GIDMAP      "/proc/self/gid_map"
+
 #define _PATH_PROC_ATTR_CURRENT        "/proc/self/attr/current"
 #define _PATH_PROC_ATTR_EXEC   "/proc/self/attr/exec"
 #define _PATH_PROC_CAPLASTCAP  "/proc/sys/kernel/cap_last_cap"
index 1f5273eba85430ce15380415ac2a770b47382395..41ea2ecbf964d563aee28ccaf8ced7cc1f12d6d0 100644 (file)
@@ -80,6 +80,14 @@ Just before running the program, mount the proc filesystem at the \fImountpoint\
 implies creating a new mount namespace since the /proc mount would otherwise
 mess up existing programs on the system. The new proc filesystem is explicitly
 mounted as private (by MS_PRIVATE|MS_REC).
+.TP
+.BR \-r , " \-\-map-root-user"
+Run the program only after current effective user and group ID have been mapped to
+superuser UID and GID in newly created user namespace. This makes it possible to
+conveniently gain capabilities needed to manage various aspects of newly created
+namespaces (such as configure interfaces in network namespace or mount filesystems in
+mount) even when run unprivileged. As a convenience feature, it does not support
+more sophisticated use cases, such as mapping multiple ranges of UIDs and GIDs.
 .SH SEE ALSO
 .BR unshare (2),
 .BR clone (2),
index 114f17da0ebdfcab500e2a9e711c1de536f8bb32..91e0ec74a813ce97a541e1a66f8e6c0bbea4503f 100644 (file)
 #include "closestream.h"
 #include "namespace.h"
 #include "exec_shell.h"
+#include "xalloc.h"
+#include "pathnames.h"
+#include "all-io.h"
+
+static void map_id(const char *file, uint32_t from, uint32_t to)
+{
+       char *buf;
+       int fd;
+
+       fd = open(file, O_WRONLY);
+       if (fd < 0)
+                err(EXIT_FAILURE, _("cannot open %s"), file);
+
+       xasprintf(&buf, "%u %u 1", from, to);
+       if (write_all(fd, buf, strlen(buf)))
+               err(EXIT_FAILURE, _("write failed %s"), file);
+       free(buf);
+       close(fd);
+}
 
 static void usage(int status)
 {
@@ -50,6 +69,7 @@ static void usage(int status)
        fputs(_(" -U, --user                unshare user namespace\n"), out);
        fputs(_(" -f, --fork                fork before launching <program>\n"), out);
        fputs(_("     --mount-proc[=<dir>]  mount proc filesystem first (implies --mount)\n"), out);
+       fputs(_(" -r, --map-root-user       map current user to root (implies --user)\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
        fputs(USAGE_HELP, out);
@@ -75,19 +95,22 @@ int main(int argc, char *argv[])
                { "user", no_argument, 0, 'U' },
                { "fork", no_argument, 0, 'f' },
                { "mount-proc", optional_argument, 0, OPT_MOUNTPROC },
+               { "map-root-user", no_argument, 0, 'r' },
                { NULL, 0, 0, 0 }
        };
 
        int unshare_flags = 0;
-       int c, forkit = 0;
+       int c, forkit = 0, maproot = 0;
        const char *procmnt = NULL;
+       uid_t real_euid = geteuid();
+       gid_t real_egid = getegid();;
 
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
        atexit(close_stdout);
 
-       while ((c = getopt_long(argc, argv, "fhVmuinpU", longopts, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "fhVmuinpUr", longopts, NULL)) != -1) {
                switch (c) {
                case 'f':
                        forkit = 1;
@@ -119,6 +142,10 @@ int main(int argc, char *argv[])
                        unshare_flags |= CLONE_NEWNS;
                        procmnt = optarg ? optarg : "/proc";
                        break;
+               case 'r':
+                       unshare_flags |= CLONE_NEWUSER;
+                       maproot = 1;
+                       break;
                default:
                        usage(EXIT_FAILURE);
                }
@@ -147,6 +174,11 @@ int main(int argc, char *argv[])
                }
        }
 
+       if (maproot) {
+               map_id(_PATH_PROC_UIDMAP, 0, real_euid);
+               map_id(_PATH_PROC_GIDMAP, 0, real_egid);
+       }
+
        if (procmnt &&
            (mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL) != 0 ||
             mount("proc", procmnt, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0))