Support more complex uid and gid mappings
authorStephen Röttger <stephen.roettger@gmail.com>
Sun, 25 Sep 2016 12:30:19 +0000 (14:30 +0200)
committerStephen Röttger <sroettger@google.com>
Fri, 30 Sep 2016 13:30:15 +0000 (15:30 +0200)
Introduces the new options uid_mapping and gid_mapping that specify
arbitrary custom mappings. If these options are used, nsjail will
use newuidmap/newgidmap to write the map files.

cmdline.c
common.h
user.c

index e59dbe32e06c9a282445ed2f1e8c7d89f63f2f09..c60ca7b4bf57d7de5bed0b62c6e6f27ba40646e3 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -114,10 +114,24 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
             logYesNo(nsjconf->clone_newuts), logYesNo(nsjconf->clone_newcgroup),
             logYesNo(nsjconf->apply_sandbox), logYesNo(nsjconf->keep_caps), nsjconf->tmpfs_size);
 
-       struct mounts_t *p;
-       TAILQ_FOREACH(p, &nsjconf->mountpts, pointers) {
-               LOG_I("Mount point: src:'%s' dst:'%s' type:'%s' flags:0x%tx options:'%s'",
-                     p->src, p->dst, p->fs_type, p->flags, p->options);
+       {
+               struct mounts_t *p;
+               TAILQ_FOREACH(p, &nsjconf->mountpts, pointers) {
+                       LOG_I("Mount point: src:'%s' dst:'%s' type:'%s' flags:0x%tx options:'%s'",
+                                               p->src, p->dst, p->fs_type, p->flags, p->options);
+               }
+       }
+       {
+               struct mapping_t *p;
+               TAILQ_FOREACH(p, &nsjconf->uid_mappings, pointers) {
+                       LOG_I("Uid mapping: inside_uid:'%s' outside_uid:'%s' count:'%s'",
+                                               p->inside_id, p->outside_id, p->count);
+               }
+
+               TAILQ_FOREACH(p, &nsjconf->gid_mappings, pointers) {
+                       LOG_I("Gid mapping: inside_uid:'%s' outside_uid:'%s' count:'%s'",
+                                               p->inside_id, p->outside_id, p->count);
+               }
        }
 }
 
@@ -306,6 +320,8 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
        TAILQ_INIT(&nsjconf->pids);
        TAILQ_INIT(&nsjconf->mountpts);
        TAILQ_INIT(&nsjconf->open_fds);
+       TAILQ_INIT(&nsjconf->uid_mappings);
+       TAILQ_INIT(&nsjconf->gid_mappings);
 
        char *user = NULL;
        char *group = NULL;
@@ -370,6 +386,8 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                {{"disable_clone_newipc", no_argument, NULL, 0x0405}, "Don't use CLONE_NEWIPC"},
                {{"disable_clone_newuts", no_argument, NULL, 0x0406}, "Don't use CLONE_NEWUTS"},
                {{"enable_clone_newcgroup", no_argument, NULL, 0x0407}, "Use CLONE_NEWCGROUP"},
+               {{"uid_mapping", required_argument, NULL, 'U'}, "Add a custom uid mapping of the form inside_uid:outside_uid:count. Setting this requires newuidmap to be present"},
+               {{"gid_mapping", required_argument, NULL, 'G'}, "Add a custom gid mapping of the form inside_gid:outside_gid:count. Setting this requires newuidmap to be present"},
                {{"bindmount_ro", required_argument, NULL, 'R'}, "List of mountpoints to be mounted --bind (ro) inside the container. Can be specified multiple times. Supports 'source' syntax, or 'source:dest'"},
                {{"bindmount", required_argument, NULL, 'B'}, "List of mountpoints to be mounted --bind (rw) inside the container. Can be specified multiple times. Supports 'source' syntax, or 'source:dest'"},
                {{"tmpfsmount", required_argument, NULL, 'T'}, "List of mountpoints to be mounted as RW/tmpfs inside the container. Can be specified multiple times. Supports 'dest' syntax"},
@@ -394,7 +412,7 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
 
        int opt_index = 0;
        for (;;) {
-               int c = getopt_long(argc, argv, "H:D:c:p:i:u:g:l:t:M:Ndveh?E:R:B:T:I:", opts,
+               int c = getopt_long(argc, argv, "H:D:c:p:i:u:g:l:t:M:Ndveh?E:R:B:T:I:U:G:", opts,
                                    &opt_index);
                if (c == -1) {
                        break;
@@ -539,6 +557,21 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                                TAILQ_INSERT_TAIL(&nsjconf->envs, p, pointers);
                        }
                        break;
+               case 'U':
+               case 'G':
+                       {
+                               struct mapping_t *p = utilMalloc(sizeof(struct mapping_t));
+                               p->inside_id = optarg;
+                               char *outside_id = cmdlineSplitStrByColon(optarg);
+                               p->outside_id = outside_id;
+                               p->count = cmdlineSplitStrByColon(outside_id);
+                               if (c == 'U') {
+                                       TAILQ_INSERT_TAIL(&nsjconf->uid_mappings, p, pointers);
+                               } else {
+                                       TAILQ_INSERT_TAIL(&nsjconf->gid_mappings, p, pointers);
+                               }
+                       }
+                       break;
                case 'R':
                        {
                                struct mounts_t *p = utilMalloc(sizeof(struct mounts_t));
index 46cf9b5a8e40d4f3822dd335291a71d6394a76ca..ca53d735086a768ff537d2bf43f57afa8364c359 100644 (file)
--- a/common.h
+++ b/common.h
@@ -71,6 +71,13 @@ struct mounts_t {
         TAILQ_ENTRY(mounts_t) pointers;
 };
 
+struct mapping_t {
+       const char *inside_id;
+       const char *outside_id;
+       const char *count;
+        TAILQ_ENTRY(mapping_t) pointers;
+};
+
 struct fds_t {
        int fd;
         TAILQ_ENTRY(fds_t) pointers;
@@ -139,6 +146,8 @@ struct nsjconf_t {
         TAILQ_HEAD(pidslist, pids_t) pids;
         TAILQ_HEAD(mountptslist, mounts_t) mountpts;
         TAILQ_HEAD(fdslistt, fds_t) open_fds;
+        TAILQ_HEAD(uidmaplistt, mapping_t) uid_mappings;
+        TAILQ_HEAD(gidmaplistt, mapping_t) gid_mappings;
 };
 
 #endif                         /* NS_COMMON_H */
diff --git a/user.c b/user.c
index 01505f5b39f4ea9a59c4b75e885938e5e35d21d1..e785ea1ff8e6bb50ea5b119c8ed656cf9ded24b1 100644 (file)
--- a/user.c
+++ b/user.c
@@ -54,8 +54,7 @@ static bool userSetGroups(pid_t pid)
        return true;
 }
 
-static bool userUidGidMap(struct nsjconf_t *nsjconf, pid_t pid)
-{
+static bool userUidMapSelf(struct nsjconf_t *nsjconf, pid_t pid) {
        char fname[PATH_MAX];
        char map[128];
 
@@ -68,6 +67,13 @@ static bool userUidGidMap(struct nsjconf_t *nsjconf, pid_t pid)
                return false;
        }
 
+       return true;
+}
+
+static bool userGidMapSelf(struct nsjconf_t *nsjconf, pid_t pid) {
+       char fname[PATH_MAX];
+       char map[128];
+
        snprintf(fname, sizeof(fname), "/proc/%d/gid_map", pid);
        snprintf(map, sizeof(map), "%lu %lu 1", (unsigned long)nsjconf->inside_gid,
                 (unsigned long)nsjconf->outside_gid);
@@ -79,6 +85,101 @@ static bool userUidGidMap(struct nsjconf_t *nsjconf, pid_t pid)
        return true;
 }
 
+// use /usr/bin/newgidmap for writing the uid and gid map
+static bool userGidMapExternal(struct nsjconf_t *nsjconf, pid_t pid) {
+       char cmd_buf[1024];
+       char *cmd_ptr = cmd_buf;
+       size_t len = sizeof(cmd_buf);
+       int write_size;
+
+       write_size = snprintf(cmd_ptr, len, "/usr/bin/newgidmap %lu %lu %lu 1",
+                       (unsigned long)pid,
+                       (unsigned long)nsjconf->inside_gid,
+                       (unsigned long)nsjconf->outside_gid);
+       if (write_size <= 0 || (size_t) write_size > len) {
+               LOG_E("snprintf writing the new{u,g}idmap command failed");
+               return false;
+       }
+       cmd_ptr += write_size;
+       len -= write_size;
+
+       struct mapping_t *p;
+       TAILQ_FOREACH(p, &nsjconf->gid_mappings, pointers) {
+               write_size = snprintf(cmd_ptr, len, " %s %s %s",
+                               p->inside_id, p->outside_id, p->count);
+       if (write_size <= 0 || (size_t) write_size > len) {
+                       LOG_E("snprintf writing the new{u,g}idmap command failed");
+                       return false;
+               }
+               cmd_ptr += write_size;
+               len -= write_size;
+       }
+
+       if (system(cmd_buf) != 0) {
+                       LOG_E("system('%s') failed", cmd_buf);
+                       while(1) ;
+                       return false;
+       }
+
+       return true;
+}
+
+// use /usr/bin/newuidmap for writing the uid and gid map
+static bool userUidMapExternal(struct nsjconf_t *nsjconf, pid_t pid) {
+       char cmd_buf[1024];
+       char *cmd_ptr = cmd_buf;
+       size_t len = sizeof(cmd_buf);
+       int write_size;
+
+       write_size = snprintf(cmd_ptr, len, "/usr/bin/newuidmap %lu %lu %lu 1",
+                       (unsigned long)pid,
+                       (unsigned long)nsjconf->inside_uid,
+                       (unsigned long)nsjconf->outside_uid);
+       if (write_size <= 0 || (size_t) write_size > len) {
+               LOG_E("snprintf writing the new{u,g}idmap command failed");
+               return false;
+       }
+       cmd_ptr += write_size;
+       len -= write_size;
+
+       struct mapping_t *p;
+       TAILQ_FOREACH(p, &nsjconf->uid_mappings, pointers) {
+               write_size = snprintf(cmd_ptr, len, " %s %s %s",
+                               p->inside_id, p->outside_id, p->count);
+       if (write_size <= 0 || (size_t) write_size > len) {
+                       LOG_E("snprintf writing the new{u,g}idmap command failed");
+                       return false;
+               }
+               cmd_ptr += write_size;
+               len -= write_size;
+       }
+
+       if (system(cmd_buf) != 0) {
+                       LOG_E("system('%s') failed", cmd_buf);
+                       return false;
+       }
+
+       return true;
+}
+
+static bool userUidGidMap(struct nsjconf_t *nsjconf, pid_t pid)
+{
+       if (TAILQ_EMPTY(&nsjconf->gid_mappings)) {
+               if (!userGidMapSelf(nsjconf, pid)) {
+                       return false;
+               }
+       } else {
+               if (!userGidMapExternal(nsjconf, pid)) {
+                       return false;
+               }
+       }
+       if (TAILQ_EMPTY(&nsjconf->uid_mappings)) {
+               return userUidMapSelf(nsjconf, pid);
+       } else {
+               return userUidMapExternal(nsjconf, pid);
+       }
+}
+
 bool userInitNsFromParent(struct nsjconf_t * nsjconf, pid_t pid)
 {
        if (nsjconf->clone_newuser == false) {