Support more complex uid and gid mappings

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.
This commit is contained in:
Stephen Röttger 2016-09-25 14:30:19 +02:00 committed by Stephen Röttger
parent ee7de33531
commit 1c950391a1
3 changed files with 150 additions and 7 deletions

View File

@ -114,10 +114,24 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
logYesNo(nsjconf->clone_newuts), logYesNo(nsjconf->clone_newcgroup), logYesNo(nsjconf->clone_newuts), logYesNo(nsjconf->clone_newcgroup),
logYesNo(nsjconf->apply_sandbox), logYesNo(nsjconf->keep_caps), nsjconf->tmpfs_size); logYesNo(nsjconf->apply_sandbox), logYesNo(nsjconf->keep_caps), nsjconf->tmpfs_size);
struct mounts_t *p; {
TAILQ_FOREACH(p, &nsjconf->mountpts, pointers) { struct mounts_t *p;
LOG_I("Mount point: src:'%s' dst:'%s' type:'%s' flags:0x%tx options:'%s'", TAILQ_FOREACH(p, &nsjconf->mountpts, pointers) {
p->src, p->dst, p->fs_type, p->flags, p->options); 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->pids);
TAILQ_INIT(&nsjconf->mountpts); TAILQ_INIT(&nsjconf->mountpts);
TAILQ_INIT(&nsjconf->open_fds); TAILQ_INIT(&nsjconf->open_fds);
TAILQ_INIT(&nsjconf->uid_mappings);
TAILQ_INIT(&nsjconf->gid_mappings);
char *user = NULL; char *user = NULL;
char *group = 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_newipc", no_argument, NULL, 0x0405}, "Don't use CLONE_NEWIPC"},
{{"disable_clone_newuts", no_argument, NULL, 0x0406}, "Don't use CLONE_NEWUTS"}, {{"disable_clone_newuts", no_argument, NULL, 0x0406}, "Don't use CLONE_NEWUTS"},
{{"enable_clone_newcgroup", no_argument, NULL, 0x0407}, "Use CLONE_NEWCGROUP"}, {{"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_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'"}, {{"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"}, {{"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; int opt_index = 0;
for (;;) { 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); &opt_index);
if (c == -1) { if (c == -1) {
break; break;
@ -539,6 +557,21 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
TAILQ_INSERT_TAIL(&nsjconf->envs, p, pointers); TAILQ_INSERT_TAIL(&nsjconf->envs, p, pointers);
} }
break; 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': case 'R':
{ {
struct mounts_t *p = utilMalloc(sizeof(struct mounts_t)); struct mounts_t *p = utilMalloc(sizeof(struct mounts_t));

View File

@ -71,6 +71,13 @@ struct mounts_t {
TAILQ_ENTRY(mounts_t) pointers; 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 { struct fds_t {
int fd; int fd;
TAILQ_ENTRY(fds_t) pointers; TAILQ_ENTRY(fds_t) pointers;
@ -139,6 +146,8 @@ struct nsjconf_t {
TAILQ_HEAD(pidslist, pids_t) pids; TAILQ_HEAD(pidslist, pids_t) pids;
TAILQ_HEAD(mountptslist, mounts_t) mountpts; TAILQ_HEAD(mountptslist, mounts_t) mountpts;
TAILQ_HEAD(fdslistt, fds_t) open_fds; 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 */ #endif /* NS_COMMON_H */

105
user.c
View File

@ -54,8 +54,7 @@ static bool userSetGroups(pid_t pid)
return true; 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 fname[PATH_MAX];
char map[128]; char map[128];
@ -68,6 +67,13 @@ static bool userUidGidMap(struct nsjconf_t *nsjconf, pid_t pid)
return false; 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(fname, sizeof(fname), "/proc/%d/gid_map", pid);
snprintf(map, sizeof(map), "%lu %lu 1", (unsigned long)nsjconf->inside_gid, snprintf(map, sizeof(map), "%lu %lu 1", (unsigned long)nsjconf->inside_gid,
(unsigned long)nsjconf->outside_gid); (unsigned long)nsjconf->outside_gid);
@ -79,6 +85,101 @@ static bool userUidGidMap(struct nsjconf_t *nsjconf, pid_t pid)
return true; 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) bool userInitNsFromParent(struct nsjconf_t * nsjconf, pid_t pid)
{ {
if (nsjconf->clone_newuser == false) { if (nsjconf->clone_newuser == false) {