Allow to specify multiple uid/gid maps

This commit is contained in:
Robert Swiecki 2017-02-08 00:36:32 +01:00
parent f7b9fede69
commit 4a154733e0
6 changed files with 143 additions and 90 deletions

View File

@ -22,6 +22,7 @@ CC ?= gcc
CFLAGS += -O2 -c -std=gnu11 \
-D_GNU_SOURCE \
-fstack-protector-all -Wformat -Wformat=2 -Wformat-security -fPIE \
-Wno-format-nonliteral \
-Wall -Wextra -Werror
LDFLAGS += -Wl,-z,now -Wl,-z,relro -pie -Wl,-z,noexecstack

123
cmdline.c
View File

@ -101,13 +101,12 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
LOG_I
("Jail parameters: hostname:'%s', chroot:'%s', process:'%s', bind:[%s]:%d, "
"max_conns_per_ip:%u, uid:(ns:%u, global:%u), gid:(ns:%u, global:%u), time_limit:%ld, personality:%#lx, daemonize:%s, "
"max_conns_per_ip:%u, time_limit:%ld, personality:%#lx, daemonize:%s, "
"clone_newnet:%s, clone_newuser:%s, clone_newns:%s, clone_newpid:%s, "
"clone_newipc:%s, clonew_newuts:%s, clone_newcgroup:%s, keep_caps:%s, "
"tmpfs_size:%zu, disable_no_new_privs:%s, pivot_root_only:%s",
nsjconf->hostname, nsjconf->chroot, nsjconf->argv[0], nsjconf->bindhost, nsjconf->port,
nsjconf->max_conns_per_ip, nsjconf->inside_uid, nsjconf->outside_uid,
nsjconf->inside_gid, nsjconf->outside_gid, nsjconf->tlimit, nsjconf->personality,
nsjconf->max_conns_per_ip, nsjconf->tlimit, nsjconf->personality,
logYesNo(nsjconf->daemonize), logYesNo(nsjconf->clone_newnet),
logYesNo(nsjconf->clone_newuser), logYesNo(nsjconf->clone_newns),
logYesNo(nsjconf->clone_newpid), logYesNo(nsjconf->clone_newipc),
@ -122,15 +121,25 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
p->src, p->dst, p->fs_type, p->flags, p->options);
}
}
{
struct idmap_t *p;
TAILQ_FOREACH(p, &nsjconf->uids, pointers) {
LOG_I("Uid map: inside_uid:%d outside_uid:%d", p->inside_id, p->outside_id);
}
TAILQ_FOREACH(p, &nsjconf->gids, pointers) {
LOG_I("Gid map: inside_gid:%d outside_gid:%d", p->inside_id, p->outside_id);
}
}
{
struct mapping_t *p;
TAILQ_FOREACH(p, &nsjconf->uid_mappings, pointers) {
LOG_I("Uid mapping: inside_uid:'%s' outside_uid:'%s' count:'%s'",
LOG_I("Newuid 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'",
LOG_I("Newgid mapping: inside_uid:'%s' outside_uid:'%s' count:'%s'",
p->inside_id, p->outside_id, p->count);
}
}
@ -204,30 +213,38 @@ static bool cmdlineParseUid(struct nsjconf_t *nsjconf, char *str)
}
char *second = cmdlineSplitStrByColon(str);
pid_t inside_uid;
pid_t outside_uid;
struct passwd *pw = getpwnam(str);
if (pw != NULL) {
nsjconf->inside_uid = pw->pw_uid;
inside_uid = pw->pw_uid;
} else if (cmdlineIsANumber(str)) {
nsjconf->inside_uid = (uid_t) strtoull(str, NULL, 0);
inside_uid = (uid_t) strtoull(str, NULL, 0);
} else {
LOG_E("No such user '%s'", str);
return false;
}
if (str == second) {
return true;
outside_uid = inside_uid;
} else {
pw = getpwnam(second);
if (pw != NULL) {
outside_uid = pw->pw_uid;
} else if (cmdlineIsANumber(second)) {
outside_uid = (uid_t) strtoull(second, NULL, 0);
} else {
LOG_E("No such user '%s'", second);
return false;
}
}
pw = getpwnam(second);
if (pw != NULL) {
nsjconf->outside_uid = pw->pw_uid;
} else if (cmdlineIsANumber(second)) {
nsjconf->outside_uid = (uid_t) strtoull(second, NULL, 0);
} else {
LOG_E("No such user '%s'", second);
return false;
}
struct idmap_t *p = utilMalloc(sizeof(struct idmap_t));
p->inside_id = inside_uid;
p->outside_id = outside_uid;
TAILQ_INSERT_TAIL(&nsjconf->uids, p, pointers);
return true;
}
@ -238,30 +255,39 @@ static bool cmdlineParseGid(struct nsjconf_t *nsjconf, char *str)
}
char *second = cmdlineSplitStrByColon(str);
gid_t inside_gid;
gid_t outside_gid;
struct group *gr = getgrnam(str);
if (gr != NULL) {
nsjconf->inside_gid = gr->gr_gid;
inside_gid = gr->gr_gid;
} else if (cmdlineIsANumber(str)) {
nsjconf->inside_gid = (gid_t) strtoull(str, NULL, 0);
inside_gid = (gid_t) strtoull(str, NULL, 0);
} else {
LOG_E("No such group '%s'", str);
return false;
}
if (str == second) {
return true;
outside_gid = inside_gid;
} else {
gr = getgrnam(second);
if (gr != NULL) {
outside_gid = gr->gr_gid;
} else if (cmdlineIsANumber(second)) {
outside_gid = (gid_t) strtoull(second, NULL, 0);
} else {
LOG_E("No such group '%s'", second);
return false;
}
}
gr = getgrnam(second);
if (gr != NULL) {
nsjconf->outside_gid = gr->gr_gid;
} else if (cmdlineIsANumber(second)) {
nsjconf->outside_gid = (gid_t) strtoull(second, NULL, 0);
} else {
LOG_E("No such group '%s'", second);
return false;
}
struct idmap_t *p = utilMalloc(sizeof(struct idmap_t));
p->inside_id = inside_gid;
p->outside_id = outside_gid;
TAILQ_INSERT_TAIL(&nsjconf->gids, p, pointers);
return true;
}
@ -300,10 +326,6 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
.is_root_rw = false,
.is_silent = false,
.skip_setsid = false,
.inside_uid = getuid(),
.inside_gid = getgid(),
.outside_uid = getuid(),
.outside_gid = getgid(),
.max_conns_per_ip = 0,
.tmpfs_size = 4 * (1024 * 1024),
.mount_proc = true,
@ -320,6 +342,8 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
};
/* *INDENT-OFF* */
TAILQ_INIT(&nsjconf->uids);
TAILQ_INIT(&nsjconf->gids);
TAILQ_INIT(&nsjconf->envs);
TAILQ_INIT(&nsjconf->pids);
TAILQ_INIT(&nsjconf->mountpts);
@ -327,8 +351,6 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
TAILQ_INIT(&nsjconf->uid_mappings);
TAILQ_INIT(&nsjconf->gid_mappings);
char *user = NULL;
char *group = NULL;
const char *logfile = NULL;
static char cmdlineTmpfsSz[PATH_MAX] = "size=4194304";
@ -353,8 +375,8 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
"\tr: Immediately launch a single process on the console, keep doing it forever [MODE_STANDALONE_RERUN]"},
{{"chroot", required_argument, NULL, 'c'}, "Directory containing / of the jail (default: none)"},
{{"rw", no_argument, NULL, 0x601}, "Mount / as RW (default: RO)"},
{{"user", required_argument, NULL, 'u'}, "Username/uid of processess inside the jail (default: your current uid). You can also use inside_ns_uid:outside_ns_uid convention here"},
{{"group", required_argument, NULL, 'g'}, "Groupname/gid of processess inside the jail (default: your current gid). You can also use inside_ns_gid:global_ns_gid convention here"},
{{"user", required_argument, NULL, 'u'}, "Username/uid of processess inside the jail (default: your current uid). You can also use inside_ns_uid:outside_ns_uid convention here. Can be specified multiple times"},
{{"group", required_argument, NULL, 'g'}, "Groupname/gid of processess inside the jail (default: your current gid). You can also use inside_ns_gid:global_ns_gid convention here. Can be specified multiple times"},
{{"hostname", required_argument, NULL, 'H'}, "UTS name (hostname) of the jail (default: 'NSJAIL')"},
{{"cwd", required_argument, NULL, 'D'}, "Directory in the namespace the process will run (default: '/')"},
{{"port", required_argument, NULL, 'p'}, "TCP port to bind to (enables MODE_LISTEN_TCP) (default: 0)"},
@ -445,10 +467,14 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
nsjconf->max_conns_per_ip = strtoul(optarg, NULL, 0);
break;
case 'u':
user = optarg;
if (cmdlineParseUid(nsjconf, optarg) == false) {
LOG_F("cmdlineParseUid('%s')", optarg);
}
break;
case 'g':
group = optarg;
if (cmdlineParseGid(nsjconf, optarg) == false) {
LOG_F("cmdlineParseGid('%s')", optarg);
}
break;
case 'l':
logfile = optarg;
@ -711,6 +737,18 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
TAILQ_INSERT_HEAD(&nsjconf->mountpts, p, pointers);
}
if (TAILQ_EMPTY(&nsjconf->uids)) {
struct idmap_t *p = utilMalloc(sizeof(struct idmap_t));
p->inside_id = getuid();
p->outside_id = getuid();
TAILQ_INSERT_HEAD(&nsjconf->uids, p, pointers);
}
if (TAILQ_EMPTY(&nsjconf->gids)) {
struct idmap_t *p = utilMalloc(sizeof(struct idmap_t));
p->inside_id = getgid();
p->outside_id = getgid();
TAILQ_INSERT_HEAD(&nsjconf->gids, p, pointers);
}
#if !defined(USE_KAFEL)
if (nsjconf->kafel_file != NULL || nsjconf->kafel_string != NULL) {
LOG_F("Kafel policy specified but the kafel/ is not compiled in");
@ -721,13 +759,6 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
return false;
}
if (cmdlineParseUid(nsjconf, user) == false) {
return false;
}
if (cmdlineParseGid(nsjconf, group) == false) {
return false;
}
nsjconf->argv = &argv[optind];
if (nsjconf->argv[0] == NULL) {
LOG_E("No command provided");

View File

@ -31,6 +31,7 @@
#include <sys/types.h>
#define ARRAYSIZE(array) (sizeof(array) / sizeof(*array))
#define UNUSED __attribute__((unused))
#if 0 /* Works, but needs -fblocks and libBlocksRuntime with clang */
/* Go-style defer implementation */
@ -79,6 +80,12 @@ struct mapping_t {
TAILQ_ENTRY(mapping_t) pointers;
};
struct idmap_t {
pid_t inside_id;
pid_t outside_id;
TAILQ_ENTRY(idmap_t) pointers;
};
struct fds_t {
int fd;
TAILQ_ENTRY(fds_t) pointers;
@ -129,10 +136,6 @@ struct nsjconf_t {
bool is_root_rw;
bool is_silent;
bool skip_setsid;
uid_t outside_uid;
gid_t outside_gid;
uid_t inside_uid;
gid_t inside_gid;
unsigned int max_conns_per_ip;
size_t tmpfs_size;
bool mount_proc;
@ -146,6 +149,8 @@ struct nsjconf_t {
size_t cgroup_mem_max;
FILE *kafel_file;
char *kafel_string;
TAILQ_HEAD(uidlist, idmap_t) uids;
TAILQ_HEAD(gidlist, idmap_t) gids;
TAILQ_HEAD(envlist, charptr_t) envs;
TAILQ_HEAD(pidslist, pids_t) pids;
TAILQ_HEAD(mountptslist, mounts_t) mountpts;

79
user.c
View File

@ -59,11 +59,16 @@ static bool userSetGroups(pid_t pid)
static bool userUidMapSelf(struct nsjconf_t *nsjconf, pid_t pid)
{
char fname[PATH_MAX];
char map[128];
snprintf(fname, sizeof(fname), "/proc/%d/uid_map", pid);
snprintf(map, sizeof(map), "%lu %lu 1", (unsigned long)nsjconf->inside_uid,
(unsigned long)nsjconf->outside_uid);
char map[4096] = {[0] = '\0' };
struct idmap_t *p;
TAILQ_FOREACH(p, &nsjconf->uids, pointers) {
utilSSnPrintf(map, sizeof(map), "%lu %lu 1\n", (unsigned long)p->inside_id,
(unsigned long)p->outside_id);
}
LOG_D("Writing '%s' to '%s'", map, fname);
if (utilWriteBufToFile(fname, map, strlen(map), O_WRONLY) == false) {
LOG_E("utilWriteBufToFile('%s', '%s') failed", fname, map);
@ -76,36 +81,34 @@ static bool userUidMapSelf(struct nsjconf_t *nsjconf, pid_t pid)
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);
char map[4096] = {[0] = '\0' };
struct idmap_t *p;
TAILQ_FOREACH(p, &nsjconf->gids, pointers) {
utilSSnPrintf(map, sizeof(map), "%lu %lu 1\n", (unsigned long)p->inside_id,
(unsigned long)p->outside_id);
}
LOG_D("Writing '%s' to '%s'", map, fname);
if (utilWriteBufToFile(fname, map, strlen(map), O_WRONLY) == false) {
LOG_E("utilWriteBufToFile('%s', '%s') failed", fname, map);
return false;
}
return true;
}
/* Use /usr/bin/newgidmap for writing the gid map */
static bool userGidMapExternal(struct nsjconf_t *nsjconf, pid_t pid)
static bool userGidMapExternal(struct nsjconf_t *nsjconf, pid_t pid UNUSED)
{
char pid_str[16];
char ins_gid_str[16];
char out_gid_str[16];
snprintf(pid_str, sizeof(pid_str), "%lu", (unsigned long)pid);
snprintf(ins_gid_str, sizeof(ins_gid_str), "%lu", (unsigned long)nsjconf->inside_gid);
snprintf(out_gid_str, sizeof(out_gid_str), "%lu", (unsigned long)nsjconf->outside_gid);
const char *argv[1024] = { "/usr/bin/newgidmap", pid_str, ins_gid_str, out_gid_str, "1" };
size_t argv_idx = 5;
const char *argv[1024] = { "/usr/bin/newgidmap" };
size_t argv_idx = 1;
struct mapping_t *p;
TAILQ_FOREACH(p, &nsjconf->gid_mappings, pointers) {
if ((argv_idx + 4) >= ARRAYSIZE(argv)) {
if (argv_idx >= ARRAYSIZE(argv)) {
LOG_W("Number of arguments to '/usr/bin/newgidmap' too big");
return false;
}
@ -125,22 +128,14 @@ static bool userGidMapExternal(struct nsjconf_t *nsjconf, pid_t pid)
}
/* Use /usr/bin/newuidmap for writing the uid map */
static bool userUidMapExternal(struct nsjconf_t *nsjconf, pid_t pid)
static bool userUidMapExternal(struct nsjconf_t *nsjconf, pid_t pid UNUSED)
{
char pid_str[16];
char ins_uid_str[16];
char out_uid_str[16];
snprintf(pid_str, sizeof(pid_str), "%lu", (unsigned long)pid);
snprintf(ins_uid_str, sizeof(ins_uid_str), "%lu", (unsigned long)nsjconf->inside_uid);
snprintf(out_uid_str, sizeof(out_uid_str), "%lu", (unsigned long)nsjconf->outside_uid);
const char *argv[1024] = { "/usr/bin/newuidmap", pid_str, ins_uid_str, out_uid_str, "1" };
size_t argv_idx = 5;
const char *argv[1024] = { "/usr/bin/newuidmap" };
size_t argv_idx = 1;
struct mapping_t *p;
TAILQ_FOREACH(p, &nsjconf->uid_mappings, pointers) {
if ((argv_idx + 4) >= ARRAYSIZE(argv)) {
if (argv_idx >= ARRAYSIZE(argv)) {
LOG_W("Number of arguments to '/usr/bin/newuidmap' too big");
return false;
}
@ -201,18 +196,22 @@ bool userInitNsFromChild(struct nsjconf_t * nsjconf)
if (setgroups(0, group_list) == -1) {
PLOG_D("setgroups(NULL) failed");
}
LOG_D("setresgid(%d, %d, %d)", nsjconf->inside_gid, nsjconf->inside_gid,
nsjconf->inside_gid);
if (syscall(__NR_setresgid, nsjconf->inside_gid, nsjconf->inside_gid, nsjconf->inside_gid)
LOG_D("setresgid(%d, %d, %d)", TAILQ_FIRST(&nsjconf->gids)->inside_id,
TAILQ_FIRST(&nsjconf->gids)->inside_id, TAILQ_FIRST(&nsjconf->gids)->inside_id);
if (syscall
(__NR_setresgid, TAILQ_FIRST(&nsjconf->gids)->inside_id,
TAILQ_FIRST(&nsjconf->gids)->inside_id, TAILQ_FIRST(&nsjconf->gids)->inside_id)
== -1) {
PLOG_E("setresgid(%u)", nsjconf->inside_gid);
PLOG_E("setresgid(%u)", TAILQ_FIRST(&nsjconf->gids)->inside_id);
return false;
}
LOG_D("setresuid(%d, %d, %d)", nsjconf->inside_uid, nsjconf->inside_uid,
nsjconf->inside_uid);
if (syscall(__NR_setresuid, nsjconf->inside_uid, nsjconf->inside_uid, nsjconf->inside_uid)
LOG_D("setresuid(%d, %d, %d)", TAILQ_FIRST(&nsjconf->uids)->inside_id,
TAILQ_FIRST(&nsjconf->uids)->inside_id, TAILQ_FIRST(&nsjconf->uids)->inside_id);
if (syscall
(__NR_setresuid, TAILQ_FIRST(&nsjconf->uids)->inside_id,
TAILQ_FIRST(&nsjconf->uids)->inside_id, TAILQ_FIRST(&nsjconf->uids)->inside_id)
== -1) {
PLOG_E("setresuid(%u)", nsjconf->inside_uid);
PLOG_E("setresuid(%u)", TAILQ_FIRST(&nsjconf->uids)->inside_id);
return false;
}

16
util.c
View File

@ -23,6 +23,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -157,3 +158,18 @@ bool utilCreateDirRecursively(const char *dir)
curr = next + 1;
}
}
int utilSSnPrintf(char *str, size_t size, const char *format, ...)
{
char buf1[size];
char buf2[size];
snprintf(buf1, sizeof(buf1), "%s", str);
va_list args;
va_start(args, format);
vsnprintf(buf2, size, format, args);
va_end(args);
return snprintf(str, size, "%s%s", buf1, buf2);
}

1
util.h
View File

@ -33,5 +33,6 @@ ssize_t utilReadFromFile(const char *fname, void *buf, size_t len);
ssize_t utilWriteToFd(int fd, const void *buf, size_t len);
bool utilWriteBufToFile(const char *filename, const void *buf, size_t len, int open_flags);
bool utilCreateDirRecursively(const char *dir);
int utilSSnPrintf(char *str, size_t size, const char *format, ...);
#endif /* NS_UTIL_H */