Rewrite uid mapping system

This commit is contained in:
Robert Swiecki 2017-05-26 23:07:47 +02:00
parent a2a497f089
commit 4eaa6cc9d3
7 changed files with 238 additions and 173 deletions

View File

@ -102,7 +102,7 @@ indent:
# DO NOT DELETE THIS LINE -- make depend depends on it.
nsjail.o: nsjail.h common.h cmdline.h log.h net.h subproc.h
cmdline.o: cmdline.h common.h config.h log.h mount.h util.h
cmdline.o: cmdline.h common.h config.h log.h mount.h util.h user.h
config.o: common.h config.h log.h util.h
contain.o: contain.h common.h cgroup.h log.h mount.h net.h pid.h user.h
contain.o: util.h uts.h

224
cmdline.c
View File

@ -45,6 +45,7 @@
#include "log.h"
#include "mount.h"
#include "util.h"
#include "user.h"
struct custom_option {
struct option opt;
@ -67,8 +68,8 @@ struct custom_option custom_opts[] = {
{{"config", required_argument, NULL, 'C'}, "Configuration file in the config.proto ProtoBuf format"},
{{"chroot", required_argument, NULL, 'c'}, "Directory containing / of the jail (default: none)"},
{{"rw", no_argument, NULL, 0x601}, "Mount / and /proc 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. 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"},
{{"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:count 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:count 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)"},
@ -233,13 +234,15 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
{
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);
LOG_I("Uid map: inside_uid:%d outside_uid:%d count:%zu", p->inside_id,
p->outside_id, p->count);
if (p->outside_id == 0) {
LOG_W("Process will be UID/EUID=0 in the global user namespace");
}
}
TAILQ_FOREACH(p, &nsjconf->gids, pointers) {
LOG_I("Gid map: inside_gid:%d outside_gid:%d", p->inside_id, p->outside_id);
LOG_I("Gid map: inside_gid:%d outside_gid:%d count:%zu", p->inside_id,
p->outside_id, p->count);
if (p->outside_id == 0) {
LOG_W("Process will be GID/EGID=0 in the global user namespace");
}
@ -247,17 +250,17 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
}
{
struct mapping_t *p;
TAILQ_FOREACH(p, &nsjconf->uid_mappings, pointers) {
LOG_I("Newuid mapping: inside_uid:'%s' outside_uid:'%s' count:'%s'",
struct idmap_t *p;
TAILQ_FOREACH(p, &nsjconf->uid_newuidmap, pointers) {
LOG_I("Newuid mapping: inside_uid:%u outside_uid:%u count:%zu",
p->inside_id, p->outside_id, p->count);
if (p->outside_id == 0) {
LOG_W("Process will be UID/EUID=0 in the global user namespace");
}
}
TAILQ_FOREACH(p, &nsjconf->gid_mappings, pointers) {
LOG_I("Newgid mapping: inside_uid:'%s' outside_uid:'%s' count:'%s'",
TAILQ_FOREACH(p, &nsjconf->gid_newuidmap, pointers) {
LOG_I("Newgid mapping: inside_uid:%u outside_uid:%u count:%zu",
p->inside_id, p->outside_id, p->count);
if (p->outside_id == 0) {
LOG_W("Process will be GID/EGID=0 in the global user namespace");
@ -266,16 +269,6 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
}
}
static bool cmdlineIsANumber(const char *s)
{
for (int i = 0; s[i]; s++) {
if (!isdigit(s[i]) && s[i] != 'x') {
return false;
}
}
return true;
}
__rlim64_t cmdlineParseRLimit(int res, const char *optarg, unsigned long mul)
{
struct rlimit64 cur;
@ -288,7 +281,7 @@ __rlim64_t cmdlineParseRLimit(int res, const char *optarg, unsigned long mul)
if (strcasecmp(optarg, "def") == 0) {
return cur.rlim_cur;
}
if (cmdlineIsANumber(optarg) == false) {
if (utilIsANumber(optarg) == false) {
LOG_F("RLIMIT %d needs a numeric or 'max'/'def' value ('%s' provided)", res,
optarg);
}
@ -304,6 +297,10 @@ __rlim64_t cmdlineParseRLimit(int res, const char *optarg, unsigned long mul)
* string. */
static char *cmdlineSplitStrByColon(char *spec)
{
if (spec == NULL) {
return NULL;
}
char *dest = spec;
while (*dest != ':' && *dest != '\0') {
dest++;
@ -314,97 +311,13 @@ static char *cmdlineSplitStrByColon(char *spec)
*dest = '\0';
return dest + 1;
case '\0':
return spec;
return NULL;
default:
// not reached
return spec;
LOG_F("Impossible condition in cmdlineSplitStrByColon()");
return NULL;
}
}
static bool cmdlineParseUid(struct nsjconf_t *nsjconf, char *str)
{
if (str == NULL) {
return true;
}
char *second = cmdlineSplitStrByColon(str);
pid_t inside_uid;
pid_t outside_uid;
struct passwd *pw = getpwnam(str);
if (pw != NULL) {
inside_uid = pw->pw_uid;
} else if (cmdlineIsANumber(str)) {
inside_uid = (uid_t) strtoull(str, NULL, 0);
} else {
LOG_E("No such user '%s'", str);
return false;
}
if (str == second) {
outside_uid = getuid();
} 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;
}
}
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;
}
static bool cmdlineParseGid(struct nsjconf_t *nsjconf, char *str)
{
if (str == NULL) {
return true;
}
char *second = cmdlineSplitStrByColon(str);
gid_t inside_gid;
gid_t outside_gid;
struct group *gr = getgrnam(str);
if (gr != NULL) {
inside_gid = gr->gr_gid;
} else if (cmdlineIsANumber(str)) {
inside_gid = (gid_t) strtoull(str, NULL, 0);
} else {
LOG_E("No such group '%s'", str);
return false;
}
if (str == second) {
outside_gid = getgid();
} 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;
}
}
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;
}
bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
{
/* *INDENT-OFF* */
@ -460,14 +373,14 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
};
/* *INDENT-ON* */
TAILQ_INIT(&nsjconf->uids);
TAILQ_INIT(&nsjconf->gids);
TAILQ_INIT(&nsjconf->envs);
TAILQ_INIT(&nsjconf->pids);
TAILQ_INIT(&nsjconf->mountpts);
TAILQ_INIT(&nsjconf->open_fds);
TAILQ_INIT(&nsjconf->uid_mappings);
TAILQ_INIT(&nsjconf->gid_mappings);
TAILQ_INIT(&nsjconf->envs);
TAILQ_INIT(&nsjconf->uids);
TAILQ_INIT(&nsjconf->gids);
TAILQ_INIT(&nsjconf->uid_newuidmap);
TAILQ_INIT(&nsjconf->gid_newuidmap);
static char cmdlineTmpfsSz[PATH_MAX] = "size=4194304";
@ -528,16 +441,6 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
case 'i':
nsjconf->max_conns_per_ip = strtoul(optarg, NULL, 0);
break;
case 'u':
if (cmdlineParseUid(nsjconf, optarg) == false) {
LOG_F("cmdlineParseUid('%s')", optarg);
}
break;
case 'g':
if (cmdlineParseGid(nsjconf, optarg) == false) {
LOG_F("cmdlineParseGid('%s')", optarg);
}
break;
case 'l':
nsjconf->logfile = optarg;
if (logInitLogFile(nsjconf) == false) {
@ -663,17 +566,72 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
p->val = optarg;
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);
case 'u':{
char *i_id = optarg;
char *o_id = cmdlineSplitStrByColon(i_id);
char *cnt = cmdlineSplitStrByColon(o_id);
size_t count = (cnt == NULL
|| strlen(cnt) == 0) ? 1U : (size_t) strtoull(cnt,
NULL,
0);
struct idmap_t *p =
userParseId(i_id, o_id, count, false /* is_gid */ );
if (p) {
TAILQ_INSERT_TAIL(&nsjconf->uids, p, pointers);
} else {
TAILQ_INSERT_TAIL(&nsjconf->gid_mappings, p, pointers);
return false;
}
}
break;
case 'g':{
char *i_id = optarg;
char *o_id = cmdlineSplitStrByColon(i_id);
char *cnt = cmdlineSplitStrByColon(o_id);
size_t count = (cnt == NULL
|| strlen(cnt) == 0) ? 1U : (size_t) strtoull(cnt,
NULL,
0);
struct idmap_t *p =
userParseId(i_id, o_id, count, true /* is_gid */ );
if (p) {
TAILQ_INSERT_TAIL(&nsjconf->gids, p, pointers);
} else {
return false;
}
}
break;
case 'U':{
char *i_id = optarg;
char *o_id = cmdlineSplitStrByColon(i_id);
char *cnt = cmdlineSplitStrByColon(o_id);
size_t count = (cnt == NULL
|| strlen(cnt) == 0) ? 1U : (size_t) strtoull(cnt,
NULL,
0);
struct idmap_t *p =
userParseId(i_id, o_id, count, false /* is_gid */ );
if (p) {
TAILQ_INSERT_TAIL(&nsjconf->uid_newuidmap, p, pointers);
} else {
return false;
}
}
break;
case 'G':{
char *i_id = optarg;
char *o_id = cmdlineSplitStrByColon(i_id);
char *cnt = cmdlineSplitStrByColon(o_id);
size_t count = (cnt == NULL
|| strlen(cnt) == 0) ? 1U : (size_t) strtoull(cnt,
NULL,
0);
struct idmap_t *p =
userParseId(i_id, o_id, count, true /* is_gid */ );
if (p) {
TAILQ_INSERT_TAIL(&nsjconf->gid_newuidmap, p, pointers);
} else {
return false;
}
}
break;
@ -822,12 +780,14 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
struct idmap_t *p = utilMalloc(sizeof(struct idmap_t));
p->inside_id = getuid();
p->outside_id = getuid();
p->count = 1U;
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();
p->count = 1U;
TAILQ_INSERT_HEAD(&nsjconf->gids, p, pointers);
}

View File

@ -74,16 +74,10 @@ 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 idmap_t {
uid_t inside_id;
uid_t outside_id;
size_t count;
TAILQ_ENTRY(idmap_t) pointers;
};
@ -164,14 +158,14 @@ struct nsjconf_t {
size_t cgroup_pids_max;
FILE *kafel_file;
char *kafel_string;
TAILQ_HEAD(uidlist, idmap_t) uids;
TAILQ_HEAD(gidlist, idmap_t) gids;
TAILQ_HEAD(udmaplist, idmap_t) uids;
TAILQ_HEAD(gdmaplist, idmap_t) gids;
TAILQ_HEAD(newuidmaplist, idmap_t) uid_newuidmap;
TAILQ_HEAD(newgidmaplist, idmap_t) gid_newuidmap;
TAILQ_HEAD(envlist, charptr_t) envs;
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 */

154
user.c
View File

@ -24,12 +24,14 @@
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include "log.h"
@ -65,8 +67,8 @@ static bool userUidMapSelf(struct nsjconf_t *nsjconf, pid_t pid)
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);
utilSSnPrintf(map, sizeof(map), "%lu %lu %zu\n", (unsigned long)p->inside_id,
(unsigned long)p->outside_id, p->count);
}
LOG_D("Writing '%s' to '%s'", map, fname);
@ -87,8 +89,8 @@ static bool userGidMapSelf(struct nsjconf_t *nsjconf, pid_t pid)
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);
utilSSnPrintf(map, sizeof(map), "%lu %lu %zu\n", (unsigned long)p->inside_id,
(unsigned long)p->outside_id, p->count);
}
LOG_D("Writing '%s' to '%s'", map, fname);
@ -103,23 +105,38 @@ static bool userGidMapSelf(struct nsjconf_t *nsjconf, pid_t pid)
/* Use /usr/bin/newgidmap for writing the gid map */
static bool userGidMapExternal(struct nsjconf_t *nsjconf, pid_t pid UNUSED)
{
char pid_str[128];
snprintf(pid_str, sizeof(pid_str), "%d", (int)pid);
const char *argv[1024] = { "/usr/bin/newgidmap", pid_str };
size_t argv_idx = 2;
static size_t idx = 0;
struct mapping_t *p;
TAILQ_FOREACH(p, &nsjconf->gid_mappings, pointers) {
if (argv_idx >= ARRAYSIZE(argv)) {
const char *argv[1024];
char parms[1024][256];
argv[idx++] = "/usr/bin/newgidmap";
snprintf(parms[idx], sizeof(parms[idx]), "%u", (unsigned)pid);
argv[idx] = parms[idx];
idx++;
struct idmap_t *p;
TAILQ_FOREACH(p, &nsjconf->gid_newuidmap, pointers) {
if ((idx + 4) >= ARRAYSIZE(argv)) {
LOG_W("Number of arguments to '/usr/bin/newgidmap' too big");
return false;
}
argv[argv_idx++] = p->inside_id;
argv[argv_idx++] = p->outside_id;
argv[argv_idx++] = p->count;
snprintf(parms[idx], sizeof(parms[idx]), "%u", (unsigned)p->inside_id);
argv[idx] = parms[idx];
idx++;
snprintf(parms[idx], sizeof(parms[idx]), "%u", (unsigned)p->outside_id);
argv[idx] = parms[idx];
idx++;
snprintf(parms[idx], sizeof(parms[idx]), "%zu", p->count);
argv[idx] = parms[idx];
idx++;
}
argv[argv_idx++] = NULL;
argv[idx] = NULL;
if (subprocSystem(argv, environ) != 0) {
LOG_E("'/usr/bin/newgidmap' failed");
@ -132,23 +149,38 @@ static bool userGidMapExternal(struct nsjconf_t *nsjconf, pid_t pid UNUSED)
/* Use /usr/bin/newuidmap for writing the uid map */
static bool userUidMapExternal(struct nsjconf_t *nsjconf, pid_t pid UNUSED)
{
char pid_str[128];
snprintf(pid_str, sizeof(pid_str), "%d", (int)pid);
const char *argv[1024] = { "/usr/bin/newuidmap", pid_str };
size_t argv_idx = 2;
static size_t idx = 0;
struct mapping_t *p;
TAILQ_FOREACH(p, &nsjconf->uid_mappings, pointers) {
if (argv_idx >= ARRAYSIZE(argv)) {
const char *argv[1024];
char parms[1024][256];
argv[idx++] = "/usr/bin/newuidmap";
snprintf(parms[idx], sizeof(parms[idx]), "%u", (unsigned)pid);
argv[idx] = parms[idx];
idx++;
struct idmap_t *p;
TAILQ_FOREACH(p, &nsjconf->uid_newuidmap, pointers) {
if ((idx + 4) >= ARRAYSIZE(argv)) {
LOG_W("Number of arguments to '/usr/bin/newuidmap' too big");
return false;
}
argv[argv_idx++] = p->inside_id;
argv[argv_idx++] = p->outside_id;
argv[argv_idx++] = p->count;
snprintf(parms[idx], sizeof(parms[idx]), "%u", (unsigned)p->inside_id);
argv[idx] = parms[idx];
idx++;
snprintf(parms[idx], sizeof(parms[idx]), "%u", (unsigned)p->outside_id);
argv[idx] = parms[idx];
idx++;
snprintf(parms[idx], sizeof(parms[idx]), "%zu", p->count);
argv[idx] = parms[idx];
idx++;
}
argv[argv_idx++] = NULL;
argv[idx] = NULL;
if (subprocSystem(argv, environ) != 0) {
LOG_E("'/usr/bin/newuidmap' failed");
@ -160,7 +192,7 @@ static bool userUidMapExternal(struct nsjconf_t *nsjconf, pid_t pid UNUSED)
static bool userUidGidMap(struct nsjconf_t *nsjconf, pid_t pid)
{
if (TAILQ_EMPTY(&nsjconf->gid_mappings)) {
if (TAILQ_EMPTY(&nsjconf->gid_newuidmap)) {
if (!userGidMapSelf(nsjconf, pid)) {
return false;
}
@ -169,7 +201,7 @@ static bool userUidGidMap(struct nsjconf_t *nsjconf, pid_t pid)
return false;
}
}
if (TAILQ_EMPTY(&nsjconf->uid_mappings)) {
if (TAILQ_EMPTY(&nsjconf->gid_newuidmap)) {
return userUidMapSelf(nsjconf, pid);
} else {
return userUidMapExternal(nsjconf, pid);
@ -221,3 +253,69 @@ bool userInitNsFromChild(struct nsjconf_t * nsjconf)
return true;
}
static uid_t cmdParseUid(const char *id)
{
if (id == NULL || strlen(id) == 0) {
return getuid();
}
struct passwd *pw = getpwnam(id);
if (pw != NULL) {
return pw->pw_uid;
}
if (utilIsANumber(id)) {
return (uid_t) strtoull(id, NULL, 0);
}
return -1;
}
static gid_t cmdParseGid(const char *id)
{
if (id == NULL || strlen(id) == 0) {
return getgid();
}
struct group *gr = getgrnam(id);
if (gr != NULL) {
return gr->gr_gid;
}
if (utilIsANumber(id)) {
return (gid_t) strtoull(id, NULL, 0);
}
return -1;
}
struct idmap_t *userParseId(const char *i_id, const char *o_id, size_t cnt, bool is_gid)
{
uid_t inside_id;
uid_t outside_id;
if (is_gid) {
inside_id = cmdParseGid(i_id);
if (inside_id == (uid_t) - 1) {
LOG_E("Cannot parse '%s' as GID", i_id);
return NULL;
}
outside_id = cmdParseGid(o_id);
if (inside_id == (uid_t) - 1) {
LOG_E("Cannot parse '%s' as GID", o_id);
return NULL;
}
} else {
inside_id = cmdParseUid(i_id);
if (inside_id == (uid_t) - 1) {
LOG_E("Cannot parse '%s' as UID", i_id);
return NULL;
}
outside_id = cmdParseUid(o_id);
if (inside_id == (uid_t) - 1) {
LOG_E("Cannot parse '%s' as UID", o_id);
return NULL;
}
}
struct idmap_t *p = utilMalloc(sizeof(struct idmap_t));
p->inside_id = inside_id;
p->outside_id = outside_id;
p->count = cnt;
return p;
}

3
user.h
View File

@ -27,7 +27,8 @@
#include "common.h"
bool userInitNsFromParent(struct nsjconf_t *nsjconf, pid_t pid);
bool userInitNsFromChild(struct nsjconf_t *nsjconf);
struct idmap_t *userParseId(const char *i_id, const char *o_id, size_t cnt, bool is_gid);
#endif /* NS_USER_H */

11
util.c
View File

@ -21,6 +21,7 @@
#include "util.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
@ -190,3 +191,13 @@ int utilSSnPrintf(char *str, size_t size, const char *format, ...)
return snprintf(str, size, "%s%s", buf1, buf2);
}
bool utilIsANumber(const char *s)
{
for (int i = 0; s[i]; s++) {
if (!isdigit(s[i]) && s[i] != 'x') {
return false;
}
}
return true;
}

1
util.h
View File

@ -36,5 +36,6 @@ 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, ...);
bool utilIsANumber(const char *s);
#endif /* NS_UTIL_H */