diff options
-rw-r--r-- | auto/isolation | 5 | ||||
-rw-r--r-- | auto/sources | 6 | ||||
-rw-r--r-- | src/nxt_clone.c | 311 | ||||
-rw-r--r-- | src/nxt_clone.h | 40 | ||||
-rw-r--r-- | src/nxt_credential.c | 66 | ||||
-rw-r--r-- | src/nxt_credential.h | 4 | ||||
-rw-r--r-- | src/nxt_lib.c | 2 | ||||
-rw-r--r-- | src/nxt_main_process.c | 213 | ||||
-rw-r--r-- | src/nxt_process.c | 32 | ||||
-rw-r--r-- | src/nxt_process.h | 18 | ||||
-rw-r--r-- | src/test/nxt_clone_test.c | 601 | ||||
-rw-r--r-- | src/test/nxt_tests.c | 6 | ||||
-rw-r--r-- | src/test/nxt_tests.h | 1 |
13 files changed, 1170 insertions, 135 deletions
diff --git a/auto/isolation b/auto/isolation index c26a4991..d231de12 100644 --- a/auto/isolation +++ b/auto/isolation @@ -5,6 +5,7 @@ NXT_ISOLATION=NO NXT_HAVE_CLONE=NO +NXT_HAVE_CLONE_NEWUSER=NO nsflags="USER NS PID NET UTS CGROUP" @@ -42,6 +43,10 @@ if [ $nxt_found = yes ]; then . auto/feature if [ $nxt_found = yes ]; then + if [ $flag = "USER" ]; then + NXT_HAVE_CLONE_NEWUSER=YES + fi + if [ "$NXT_ISOLATION" = "NO" ]; then NXT_ISOLATION=$flag else diff --git a/auto/sources b/auto/sources index 33c1dcd4..70d6eb8d 100644 --- a/auto/sources +++ b/auto/sources @@ -163,6 +163,12 @@ NXT_TEST_SRCS=" \ src/test/nxt_strverscmp_test.c \ " + +if [ $NXT_HAVE_CLONE_NEWUSER = YES ]; then + NXT_TEST_SRCS="$NXT_TEST_SRCS src/test/nxt_clone_test.c" +fi + + NXT_LIB_UTF8_FILE_NAME_TEST_SRCS=" \ src/test/nxt_utf8_file_name_test.c \ " diff --git a/src/nxt_clone.c b/src/nxt_clone.c index a2c376a3..9ee3c012 100644 --- a/src/nxt_clone.c +++ b/src/nxt_clone.c @@ -25,26 +25,18 @@ nxt_clone(nxt_int_t flags) #if (NXT_HAVE_CLONE_NEWUSER) -/* map uid 65534 to unit pid */ -#define NXT_DEFAULT_UNPRIV_MAP "65534 %d 1" - -nxt_int_t nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid, +nxt_int_t nxt_clone_credential_setgroups(nxt_task_t *task, pid_t child_pid, const char *str); -nxt_int_t nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, - pid_t pid, nxt_int_t defval, nxt_conf_value_t *mapobj); -nxt_int_t nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile, +nxt_int_t nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile, + pid_t pid, nxt_int_t default_container, nxt_int_t default_host, + nxt_clone_credential_map_t *map); +nxt_int_t nxt_clone_credential_map_write(nxt_task_t *task, const char *mapfile, pid_t pid, u_char *mapinfo); -typedef struct { - nxt_int_t container; - nxt_int_t host; - nxt_int_t size; -} nxt_clone_procmap_t; - - nxt_int_t -nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid, const char *str) +nxt_clone_credential_setgroups(nxt_task_t *task, pid_t child_pid, + const char *str) { int fd, n; u_char *p, *end; @@ -89,8 +81,8 @@ nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid, const char *str) nxt_int_t -nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile, pid_t pid, - u_char *mapinfo) +nxt_clone_credential_map_write(nxt_task_t *task, const char *mapfile, + pid_t pid, u_char *mapinfo) { int len, mapfd; u_char *p, *end; @@ -139,17 +131,13 @@ nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile, pid_t pid, nxt_int_t -nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, pid_t pid, - nxt_int_t defval, nxt_conf_value_t *mapobj) +nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile, pid_t pid, + nxt_int_t default_container, nxt_int_t default_host, + nxt_clone_credential_map_t *map) { - u_char *p, *end, *mapinfo; - nxt_int_t container, host, size; - nxt_int_t ret, len, count, i; - nxt_conf_value_t *obj, *value; - - static nxt_str_t str_cont = nxt_string("container"); - static nxt_str_t str_host = nxt_string("host"); - static nxt_str_t str_size = nxt_string("size"); + u_char *p, *end, *mapinfo; + nxt_int_t ret, len; + nxt_uint_t i; /* * uid_map one-entry size: @@ -157,44 +145,28 @@ nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, pid_t pid, */ len = sizeof(u_char) * (10 + 10 + 10 + 2 + 1); - if (mapobj != NULL) { - count = nxt_conf_array_elements_count(mapobj); - - if (count == 0) { - goto default_map; - } - - len = len * count + 1; + if (map->size > 0) { + len = len * map->size + 1; mapinfo = nxt_malloc(len); if (nxt_slow_path(mapinfo == NULL)) { - nxt_alert(task, "failed to allocate uid_map buffer"); return NXT_ERROR; } p = mapinfo; end = mapinfo + len; - for (i = 0; i < count; i++) { - obj = nxt_conf_get_array_element(mapobj, i); + for (i = 0; i < map->size; i++) { + p = nxt_sprintf(p, end, "%d %d %d", map->map[i].container, + map->map[i].host, map->map[i].size); - value = nxt_conf_get_object_member(obj, &str_cont, NULL); - container = nxt_conf_get_integer(value); - - value = nxt_conf_get_object_member(obj, &str_host, NULL); - host = nxt_conf_get_integer(value); - - value = nxt_conf_get_object_member(obj, &str_size, NULL); - size = nxt_conf_get_integer(value); - - p = nxt_sprintf(p, end, "%d %d %d", container, host, size); if (nxt_slow_path(p == end)) { - nxt_alert(task, "write past the uid_map buffer"); + nxt_alert(task, "write past the mapinfo buffer"); nxt_free(mapinfo); return NXT_ERROR; } - if (i+1 < count) { + if (i+1 < map->size) { *p++ = '\n'; } else { @@ -203,27 +175,24 @@ nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, pid_t pid, } } else { - -default_map: - mapinfo = nxt_malloc(len); if (nxt_slow_path(mapinfo == NULL)) { - nxt_alert(task, "failed to allocate uid_map buffer"); return NXT_ERROR; } end = mapinfo + len; - p = nxt_sprintf(mapinfo, end, NXT_DEFAULT_UNPRIV_MAP, defval); + p = nxt_sprintf(mapinfo, end, "%d %d 1", + default_container, default_host); *p = '\0'; if (nxt_slow_path(p == end)) { - nxt_alert(task, "write past the %s buffer", mapfile); + nxt_alert(task, "write past mapinfo buffer"); nxt_free(mapinfo); return NXT_ERROR; } } - ret = nxt_clone_proc_map_write(task, mapfile, pid, mapinfo); + ret = nxt_clone_credential_map_write(task, mapfile, pid, mapinfo); nxt_free(mapinfo); @@ -232,31 +201,50 @@ default_map: nxt_int_t -nxt_clone_proc_map(nxt_task_t *task, pid_t pid, nxt_process_clone_t *clone) +nxt_clone_credential_map(nxt_task_t *task, pid_t pid, + nxt_credential_t *app_creds, nxt_clone_t *clone) { nxt_int_t ret; - nxt_int_t uid, gid; + nxt_int_t default_host_uid; + nxt_int_t default_host_gid; const char *rule; nxt_runtime_t *rt; rt = task->thread->runtime; - uid = geteuid(); - gid = getegid(); - rule = rt->capabilities.setid ? "allow" : "deny"; + if (rt->capabilities.setid) { + rule = "allow"; + + /* + * By default we don't map a privileged user + */ + default_host_uid = app_creds->uid; + default_host_gid = app_creds->base_gid; + } else { + rule = "deny"; + + default_host_uid = nxt_euid; + default_host_gid = nxt_egid; + } + + ret = nxt_clone_credential_map_set(task, "uid_map", pid, app_creds->uid, + default_host_uid, + &clone->uidmap); - ret = nxt_clone_proc_map_set(task, "uid_map", pid, uid, clone->uidmap); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } - ret = nxt_clone_proc_setgroups(task, pid, rule); + ret = nxt_clone_credential_setgroups(task, pid, rule); if (nxt_slow_path(ret != NXT_OK)) { nxt_alert(task, "failed to write /proc/%d/setgroups", pid); return NXT_ERROR; } - ret = nxt_clone_proc_map_set(task, "gid_map", pid, gid, clone->gidmap); + ret = nxt_clone_credential_map_set(task, "gid_map", pid, app_creds->base_gid, + default_host_gid, + &clone->gidmap); + if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } @@ -264,4 +252,197 @@ nxt_clone_proc_map(nxt_task_t *task, pid_t pid, nxt_process_clone_t *clone) return NXT_OK; } + +nxt_int_t +nxt_clone_vldt_credential_uidmap(nxt_task_t *task, + nxt_clone_credential_map_t *map, nxt_credential_t *creds) +{ + nxt_int_t id; + nxt_uint_t i; + nxt_runtime_t *rt; + nxt_clone_map_entry_t m; + + if (map->size == 0) { + return NXT_OK; + } + + rt = task->thread->runtime; + + if (!rt->capabilities.setid) { + if (nxt_slow_path(map->size > 1)) { + nxt_log(task, NXT_LOG_NOTICE, "\"uidmap\" field has %d entries " + "but unprivileged unit has a maximum of 1 map.", + map->size); + + return NXT_ERROR; + } + + id = map->map[0].host; + + if (nxt_slow_path((nxt_uid_t) id != nxt_euid)) { + nxt_log(task, NXT_LOG_NOTICE, "\"uidmap\" field has an entry for " + "host uid %d but unprivileged unit can only map itself " + "(uid %d) into child namespaces.", id, nxt_euid); + + return NXT_ERROR; + } + + return NXT_OK; + } + + for (i = 0; i < map->size; i++) { + m = map->map[i]; + + if (creds->uid >= (nxt_uid_t) m.container + && creds->uid < (nxt_uid_t) (m.container + m.size)) + { + return NXT_OK; + } + } + + nxt_log(task, NXT_LOG_NOTICE, "\"uidmap\" field has no \"container\" " + "entry for user \"%s\" (uid %d)", creds->user, creds->uid); + + return NXT_ERROR; +} + + +nxt_int_t +nxt_clone_vldt_credential_gidmap(nxt_task_t *task, + nxt_clone_credential_map_t *map, nxt_credential_t *creds) +{ + nxt_uint_t base_ok, gid_ok, gids_ok; + nxt_uint_t i, j; + nxt_runtime_t *rt; + nxt_clone_map_entry_t m; + + rt = task->thread->runtime; + + if (!rt->capabilities.setid) { + if (creds->ngroups > 0 + && !(creds->ngroups == 1 && creds->gids[0] == creds->base_gid)) { + nxt_log(task, NXT_LOG_NOTICE, + "unprivileged unit disallow supplementary groups for " + "new namespace (user \"%s\" has %d group%s).", + creds->user, creds->ngroups, + creds->ngroups > 1 ? "s" : ""); + + return NXT_ERROR; + } + + if (map->size == 0) { + return NXT_OK; + } + + if (nxt_slow_path(map->size > 1)) { + nxt_log(task, NXT_LOG_NOTICE, "\"gidmap\" field has %d entries " + "but unprivileged unit has a maximum of 1 map.", + map->size); + + return NXT_ERROR; + } + + m = map->map[0]; + + if (nxt_slow_path((nxt_gid_t) m.host != nxt_egid)) { + nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry for " + "host gid %d but unprivileged unit can only map itself " + "(gid %d) into child namespaces.", m.host, nxt_egid); + + return NXT_ERROR; + } + + if (nxt_slow_path(m.size > 1)) { + nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry with " + "\"size\": %d, but for unprivileged unit it must be 1.", + m.size); + + return NXT_ERROR; + } + + if (nxt_slow_path((nxt_gid_t) m.container != creds->base_gid)) { + nxt_log(task, NXT_LOG_ERR, + "\"gidmap\" field has no \"container\" entry for gid %d.", + creds->base_gid); + + return NXT_ERROR; + } + + return NXT_OK; + } + + if (map->size == 0) { + if (creds->ngroups > 0 + && !(creds->ngroups == 1 && creds->gids[0] == creds->base_gid)) + { + nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has no entries " + "but user \"%s\" has %d suplementary group%s.", + creds->user, creds->ngroups, + creds->ngroups > 1 ? "s" : ""); + + return NXT_ERROR; + } + + return NXT_OK; + } + + base_ok = 0; + gids_ok = 0; + + for (i = 0; i < creds->ngroups; i++) { + gid_ok = 0; + + for (j = 0; j < map->size; j++) { + m = map->map[j]; + + if (!base_ok && creds->base_gid >= (nxt_gid_t) m.container + && creds->base_gid < (nxt_gid_t) (m.container+m.size)) + { + base_ok = 1; + } + + if (creds->gids[i] >= (nxt_gid_t) m.container + && creds->gids[i] < (nxt_gid_t) (m.container+m.size)) + { + gid_ok = 1; + break; + } + } + + if (nxt_fast_path(gid_ok)) { + gids_ok++; + } + } + + if (!base_ok) { + for (i = 0; i < map->size; i++) { + m = map->map[i]; + + if (creds->base_gid >= (nxt_gid_t) m.container + && creds->base_gid < (nxt_gid_t) (m.container+m.size)) + { + base_ok = 1; + break; + } + } + } + + if (nxt_slow_path(!base_ok)) { + nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has no \"container\" " + "entry for gid %d.", creds->base_gid); + + return NXT_ERROR; + } + + if (nxt_slow_path(gids_ok < creds->ngroups)) { + nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has missing " + "suplementary gid mappings (found %d out of %d).", gids_ok, + creds->ngroups); + + return NXT_ERROR; + } + + return NXT_OK; +} + #endif diff --git a/src/nxt_clone.h b/src/nxt_clone.h index 50dec0b4..dcccf1db 100644 --- a/src/nxt_clone.h +++ b/src/nxt_clone.h @@ -7,11 +7,47 @@ #define _NXT_CLONE_INCLUDED_ +#if (NXT_HAVE_CLONE_NEWUSER) + +typedef struct { + nxt_int_t container; + nxt_int_t host; + nxt_int_t size; +} nxt_clone_map_entry_t; + +typedef struct { + nxt_uint_t size; + nxt_clone_map_entry_t *map; +} nxt_clone_credential_map_t; + +#endif + +typedef struct { + nxt_int_t flags; + +#if (NXT_HAVE_CLONE_NEWUSER) + nxt_clone_credential_map_t uidmap; + nxt_clone_credential_map_t gidmap; +#endif + +} nxt_clone_t; + + pid_t nxt_clone(nxt_int_t flags); + #if (NXT_HAVE_CLONE_NEWUSER) -nxt_int_t nxt_clone_proc_map(nxt_task_t *task, pid_t pid, - nxt_process_clone_t *clone); + +#define NXT_CLONE_USER(flags) \ + ((flags & CLONE_NEWUSER) == CLONE_NEWUSER) + +NXT_EXPORT nxt_int_t nxt_clone_credential_map(nxt_task_t *task, pid_t pid, + nxt_credential_t *creds, nxt_clone_t *clone); +NXT_EXPORT nxt_int_t nxt_clone_vldt_credential_uidmap(nxt_task_t *task, + nxt_clone_credential_map_t *map, nxt_credential_t *creds); +NXT_EXPORT nxt_int_t nxt_clone_vldt_credential_gidmap(nxt_task_t *task, + nxt_clone_credential_map_t *map, nxt_credential_t *creds); + #endif #endif /* _NXT_CLONE_INCLUDED_ */ diff --git a/src/nxt_credential.c b/src/nxt_credential.c index 9f275b7d..168db9cf 100644 --- a/src/nxt_credential.c +++ b/src/nxt_credential.c @@ -280,61 +280,69 @@ free: nxt_int_t -nxt_credential_set(nxt_task_t *task, nxt_credential_t *uc) +nxt_credential_setuid(nxt_task_t *task, nxt_credential_t *uc) { - nxt_debug(task, "user cred set: \"%s\" uid:%d base gid:%d", - uc->user, uc->uid, uc->base_gid); + nxt_debug(task, "user cred set: \"%s\" uid:%d", uc->user, uc->uid); - if (setgid(uc->base_gid) != 0) { + if (setuid(uc->uid) != 0) { #if (NXT_HAVE_CLONE) if (nxt_errno == EINVAL) { - nxt_log(task, NXT_LOG_ERR, "The gid %d isn't valid in the " - "application namespace.", uc->base_gid); + nxt_log(task, NXT_LOG_ERR, "The uid %d (user \"%s\") isn't " + "valid in the application namespace.", uc->uid, uc->user); return NXT_ERROR; } #endif - nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno); + nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno); return NXT_ERROR; } - if (uc->gids != NULL) { - if (setgroups(uc->ngroups, uc->gids) != 0) { + return NXT_OK; +} -#if (NXT_HAVE_CLONE) - if (nxt_errno == EINVAL) { - nxt_log(task, NXT_LOG_ERR, "The user \"%s\" (uid: %d) has " - "supplementary group ids not valid in the application " - "namespace.", uc->user, uc->uid); - return NXT_ERROR; - } -#endif - nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno); - return NXT_ERROR; - } +nxt_int_t +nxt_credential_setgids(nxt_task_t *task, nxt_credential_t *uc) +{ + nxt_runtime_t *rt; - } else { - /* MacOSX fallback. */ - if (initgroups(uc->user, uc->base_gid) != 0) { - nxt_alert(task, "initgroups(%s, %d) failed %E", - uc->user, uc->base_gid, nxt_errno); + nxt_debug(task, "user cred set gids: base gid:%d, ngroups: %d", + uc->base_gid, uc->ngroups); + + rt = task->thread->runtime; + + if (setgid(uc->base_gid) != 0) { + +#if (NXT_HAVE_CLONE) + if (nxt_errno == EINVAL) { + nxt_log(task, NXT_LOG_ERR, "The gid %d isn't valid in the " + "application namespace.", uc->base_gid); return NXT_ERROR; } +#endif + + nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno); + return NXT_ERROR; } - if (setuid(uc->uid) != 0) { + if (!rt->capabilities.setid) { + return NXT_OK; + } + + if (nxt_slow_path(uc->ngroups > 0 + && setgroups(uc->ngroups, uc->gids) != 0)) { #if (NXT_HAVE_CLONE) if (nxt_errno == EINVAL) { - nxt_log(task, NXT_LOG_ERR, "The uid %d (user \"%s\") isn't " - "valid in the application namespace.", uc->uid, uc->user); + nxt_log(task, NXT_LOG_ERR, "The user \"%s\" (uid: %d) has " + "supplementary group ids not valid in the application " + "namespace.", uc->user, uc->uid); return NXT_ERROR; } #endif - nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno); + nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno); return NXT_ERROR; } diff --git a/src/nxt_credential.h b/src/nxt_credential.h index e9f59327..243eba83 100644 --- a/src/nxt_credential.h +++ b/src/nxt_credential.h @@ -21,7 +21,9 @@ typedef struct { NXT_EXPORT nxt_int_t nxt_credential_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc, const char *group); -NXT_EXPORT nxt_int_t nxt_credential_set(nxt_task_t *task, +NXT_EXPORT nxt_int_t nxt_credential_setuid(nxt_task_t *task, + nxt_credential_t *uc); +NXT_EXPORT nxt_int_t nxt_credential_setgids(nxt_task_t *task, nxt_credential_t *uc); diff --git a/src/nxt_lib.c b/src/nxt_lib.c index db3d29c1..1634a2b8 100644 --- a/src/nxt_lib.c +++ b/src/nxt_lib.c @@ -43,6 +43,8 @@ nxt_lib_start(const char *app, char **argv, char ***envp) nxt_pid = getpid(); nxt_ppid = getppid(); + nxt_euid = geteuid(); + nxt_egid = getegid(); #if (NXT_DEBUG) diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 02b0c293..79b9ee1f 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -80,10 +80,22 @@ static nxt_int_t nxt_process_init_name_set(nxt_process_init_t *init, static nxt_int_t nxt_process_init_creds_set(nxt_task_t *task, nxt_process_init_t *init, nxt_str_t *user, nxt_str_t *group); -static nxt_int_t nxt_init_set_isolation(nxt_task_t *task, - nxt_process_init_t *init, nxt_conf_value_t *isolation); -static nxt_int_t nxt_init_set_ns(nxt_task_t *task, nxt_process_init_t *init, - nxt_conf_value_t *ns); +static nxt_int_t nxt_init_isolation(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_init_t *init); +#if (NXT_HAVE_CLONE) +static nxt_int_t nxt_init_clone_flags(nxt_task_t *task, + nxt_conf_value_t *namespaces, nxt_process_init_t *init); +#endif + +#if (NXT_HAVE_CLONE_NEWUSER) +static nxt_int_t nxt_init_isolation_creds(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_init_t *init); +static nxt_int_t nxt_init_vldt_isolation_creds(nxt_task_t *task, + nxt_process_init_t *init); +static nxt_int_t nxt_init_isolation_credential_map(nxt_task_t *task, + nxt_mp_t *mem_pool, nxt_conf_value_t *map_array, + nxt_clone_credential_map_t *map); +#endif const nxt_sig_event_t nxt_main_process_signals[] = { nxt_event_signal(SIGHUP, nxt_main_process_signal_handler), @@ -641,6 +653,7 @@ static nxt_int_t nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt, nxt_common_app_conf_t *app_conf, uint32_t stream) { + nxt_int_t cap_setid; nxt_int_t ret; nxt_process_init_t *init; @@ -649,7 +662,22 @@ nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt, return NXT_ERROR; } - if (rt->capabilities.setid) { + cap_setid = rt->capabilities.setid; + + if (app_conf->isolation != NULL) { + ret = nxt_init_isolation(task, app_conf->isolation, init); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + } + +#if (NXT_HAVE_CLONE_NEWUSER) + if (NXT_CLONE_USER(init->isolation.clone.flags)) { + cap_setid = 1; + } +#endif + + if (cap_setid) { ret = nxt_process_init_creds_set(task, init, &app_conf->user, &app_conf->group); if (nxt_slow_path(ret != NXT_OK)) { @@ -679,10 +707,12 @@ nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt, init->data = app_conf; init->stream = stream; - ret = nxt_init_set_isolation(task, init, app_conf->isolation); +#if (NXT_HAVE_CLONE_NEWUSER) + ret = nxt_init_vldt_isolation_creds(task, init); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } +#endif return nxt_main_create_worker_process(task, rt, init); @@ -1487,45 +1517,178 @@ nxt_main_port_access_log_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) static nxt_int_t -nxt_init_set_isolation(nxt_task_t *task, nxt_process_init_t *init, - nxt_conf_value_t *isolation) +nxt_init_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_init_t *init) +{ +#if (NXT_HAVE_CLONE) + nxt_int_t ret; + nxt_conf_value_t *obj; + + static nxt_str_t nsname = nxt_string("namespaces"); + + obj = nxt_conf_get_object_member(isolation, &nsname, NULL); + if (obj != NULL) { + ret = nxt_init_clone_flags(task, obj, init); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } +#endif + +#if (NXT_HAVE_CLONE_NEWUSER) + ret = nxt_init_isolation_creds(task, isolation, init); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } +#endif + + return NXT_OK; +} + + +#if (NXT_HAVE_CLONE_NEWUSER) + +static nxt_int_t +nxt_init_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_init_t *init) { nxt_int_t ret; - nxt_conf_value_t *object; + nxt_clone_t *clone; + nxt_conf_value_t *array; - static nxt_str_t nsname = nxt_string("namespaces"); static nxt_str_t uidname = nxt_string("uidmap"); static nxt_str_t gidname = nxt_string("gidmap"); - if (isolation == NULL) { + clone = &init->isolation.clone; + + array = nxt_conf_get_object_member(isolation, &uidname, NULL); + if (array != NULL) { + ret = nxt_init_isolation_credential_map(task, init->mem_pool, array, + &clone->uidmap); + + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + + array = nxt_conf_get_object_member(isolation, &gidname, NULL); + if (array != NULL) { + ret = nxt_init_isolation_credential_map(task, init->mem_pool, array, + &clone->gidmap); + + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + + return NXT_OK; +} + + +static nxt_int_t +nxt_init_vldt_isolation_creds(nxt_task_t *task, nxt_process_init_t *init) +{ + nxt_int_t ret; + nxt_clone_t *clone; + + clone = &init->isolation.clone; + + if (clone->uidmap.size == 0 && clone->gidmap.size == 0) { return NXT_OK; } - object = nxt_conf_get_object_member(isolation, &nsname, NULL); - if (object != NULL) { - ret = nxt_init_set_ns(task, init, object); - if (ret != NXT_OK) { - return ret; + if (!NXT_CLONE_USER(clone->flags)) { + if (nxt_slow_path(clone->uidmap.size > 0)) { + nxt_log(task, NXT_LOG_ERR, "\"uidmap\" is set but " + "\"isolation.namespaces.credential\" is false or unset"); + + return NXT_ERROR; } + + if (nxt_slow_path(clone->gidmap.size > 0)) { + nxt_log(task, NXT_LOG_ERR, "\"gidmap\" is set but " + "\"isolation.namespaces.credential\" is false or unset"); + + return NXT_ERROR; + } + + return NXT_OK; + } + + ret = nxt_clone_vldt_credential_uidmap(task, &clone->uidmap, + init->user_cred); + + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + return nxt_clone_vldt_credential_gidmap(task, &clone->gidmap, + init->user_cred); +} + + +static nxt_int_t +nxt_init_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mem_pool, + nxt_conf_value_t *map_array, nxt_clone_credential_map_t *map) +{ + nxt_int_t ret; + nxt_uint_t i; + nxt_conf_value_t *obj; + + static nxt_conf_map_t nxt_clone_map_entry_conf[] = { + { + nxt_string("container"), + NXT_CONF_MAP_INT, + offsetof(nxt_clone_map_entry_t, container), + }, + + { + nxt_string("host"), + NXT_CONF_MAP_INT, + offsetof(nxt_clone_map_entry_t, host), + }, + + { + nxt_string("size"), + NXT_CONF_MAP_INT, + offsetof(nxt_clone_map_entry_t, size), + }, + }; + + map->size = nxt_conf_array_elements_count(map_array); + + if (map->size == 0) { + return NXT_OK; } - object = nxt_conf_get_object_member(isolation, &uidname, NULL); - if (object != NULL) { - init->isolation.clone.uidmap = object; + map->map = nxt_mp_alloc(mem_pool, + map->size * sizeof(nxt_clone_map_entry_t)); + if (nxt_slow_path(map->map == NULL)) { + return NXT_ERROR; } - object = nxt_conf_get_object_member(isolation, &gidname, NULL); - if (object != NULL) { - init->isolation.clone.gidmap = object; + for (i = 0; i < map->size; i++) { + obj = nxt_conf_get_array_element(map_array, i); + + ret = nxt_conf_map_object(mem_pool, obj, nxt_clone_map_entry_conf, + nxt_nitems(nxt_clone_map_entry_conf), + map->map + i); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "clone map entry map error"); + return NXT_ERROR; + } } return NXT_OK; } +#endif + +#if (NXT_HAVE_CLONE) static nxt_int_t -nxt_init_set_ns(nxt_task_t *task, nxt_process_init_t *init, - nxt_conf_value_t *namespaces) +nxt_init_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces, + nxt_process_init_t *init) { uint32_t index; nxt_str_t name; @@ -1592,6 +1755,8 @@ nxt_init_set_ns(nxt_task_t *task, nxt_process_init_t *init, return NXT_OK; } +#endif + static nxt_process_init_t * nxt_process_init_create(nxt_task_t *task, nxt_process_type_t type, diff --git a/src/nxt_process.c b/src/nxt_process.c index 75c6ef25..035f747f 100644 --- a/src/nxt_process.c +++ b/src/nxt_process.c @@ -23,6 +23,12 @@ nxt_pid_t nxt_pid; /* An original parent process pid. */ nxt_pid_t nxt_ppid; +/* A cached process effective uid */ +nxt_uid_t nxt_euid; + +/* A cached process effective gid */ +nxt_gid_t nxt_egid; + nxt_bool_t nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = { { 1, 1, 1, 1, 1 }, { 1, 0, 0, 0, 0 }, @@ -207,8 +213,9 @@ nxt_process_create(nxt_task_t *task, nxt_process_t *process) } #if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER) - if ((init->isolation.clone.flags & CLONE_NEWUSER) == CLONE_NEWUSER) { - ret = nxt_clone_proc_map(task, pid, &init->isolation.clone); + if (NXT_CLONE_USER(init->isolation.clone.flags)) { + ret = nxt_clone_credential_map(task, pid, init->user_cred, + &init->isolation.clone); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -257,7 +264,7 @@ cleanup: static void nxt_process_start(nxt_task_t *task, nxt_process_t *process) { - nxt_int_t ret; + nxt_int_t ret, cap_setid; nxt_port_t *port, *main_port; nxt_thread_t *thread; nxt_runtime_t *rt; @@ -276,9 +283,22 @@ nxt_process_start(nxt_task_t *task, nxt_process_t *process) nxt_random_init(&thread->random); - if (rt->capabilities.setid && init->user_cred != NULL) { - ret = nxt_credential_set(task, init->user_cred); - if (ret != NXT_OK) { + cap_setid = rt->capabilities.setid; + +#if (NXT_HAVE_CLONE_NEWUSER) + if (!cap_setid && NXT_CLONE_USER(init->isolation.clone.flags)) { + cap_setid = 1; + } +#endif + + if (cap_setid) { + ret = nxt_credential_setgids(task, init->user_cred); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + + ret = nxt_credential_setuid(task, init->user_cred); + if (nxt_slow_path(ret != NXT_OK)) { goto fail; } } diff --git a/src/nxt_process.h b/src/nxt_process.h index 457d2299..343fffb8 100644 --- a/src/nxt_process.h +++ b/src/nxt_process.h @@ -7,18 +7,14 @@ #ifndef _NXT_PROCESS_H_INCLUDED_ #define _NXT_PROCESS_H_INCLUDED_ -#include <nxt_conf.h> +#if (NXT_HAVE_CLONE) +#include <nxt_clone.h> +#endif typedef pid_t nxt_pid_t; -typedef struct { - nxt_int_t flags; - nxt_conf_value_t *uidmap; - nxt_conf_value_t *gidmap; -} nxt_process_clone_t; - typedef struct nxt_process_init_s nxt_process_init_t; typedef nxt_int_t (*nxt_process_start_t)(nxt_task_t *task, void *data); typedef nxt_int_t (*nxt_process_restart_t)(nxt_task_t *task, nxt_runtime_t *rt, @@ -39,7 +35,9 @@ struct nxt_process_init_s { uint32_t stream; union { - nxt_process_clone_t clone; +#if (NXT_HAVE_CLONE) + nxt_clone_t clone; +#endif } isolation; }; @@ -118,6 +116,8 @@ nxt_port_t *nxt_process_connected_port_find(nxt_process_t *process, void nxt_worker_process_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); +void nxt_init_destroy(nxt_runtime_t *rt, nxt_process_init_t *init); + #if (NXT_HAVE_SETPROCTITLE) @@ -146,6 +146,8 @@ NXT_EXPORT void nxt_process_title(nxt_task_t *task, const char *fmt, ...); NXT_EXPORT extern nxt_pid_t nxt_pid; NXT_EXPORT extern nxt_pid_t nxt_ppid; +NXT_EXPORT extern nxt_uid_t nxt_euid; +NXT_EXPORT extern nxt_gid_t nxt_egid; NXT_EXPORT extern char **nxt_process_argv; NXT_EXPORT extern char ***nxt_process_environ; diff --git a/src/test/nxt_clone_test.c b/src/test/nxt_clone_test.c new file mode 100644 index 00000000..15d36557 --- /dev/null +++ b/src/test/nxt_clone_test.c @@ -0,0 +1,601 @@ +/* + * Copyright (C) NGINX, Inc. + * Copyright (C) Valentin V. Bartenev + */ + +#include <nxt_main.h> +#include <nxt_conf.h> +#include "nxt_tests.h" + + +#define UIDMAP 1 +#define GIDMAP 2 + + +typedef struct { + nxt_int_t map_type; + nxt_str_t map_data; + nxt_int_t setid; + nxt_credential_t creds; + nxt_uid_t unit_euid; + nxt_gid_t unit_egid; + nxt_int_t result; + nxt_str_t errmsg; +} nxt_clone_creds_testcase_t; + +typedef struct { + nxt_clone_creds_testcase_t *tc; +} nxt_clone_creds_ctx_t; + + +nxt_int_t nxt_clone_test_mappings(nxt_task_t *task, nxt_mp_t *mp, + nxt_clone_creds_ctx_t *ctx, nxt_clone_creds_testcase_t *tc); +void nxt_cdecl nxt_clone_test_log_handler(nxt_uint_t level, nxt_log_t *log, + const char *fmt, ...); +nxt_int_t nxt_clone_test_map_assert(nxt_task_t *task, + nxt_clone_creds_testcase_t *tc, nxt_clone_credential_map_t *map); +static nxt_int_t nxt_clone_test_parse_map(nxt_task_t *task, + nxt_str_t *map_str, nxt_clone_credential_map_t *map); + + +nxt_log_t *test_log; + +static nxt_gid_t gids[] = {1000, 10000, 60000}; + +static nxt_clone_creds_testcase_t testcases[] = { + { + /* + * Unprivileged unit + * + * if no uid mapping and app creds and unit creds are the same, + * then we automatically add a map for the creds->uid. + * Then, child process can safely setuid(creds->uid) in + * the new namespace. + */ + UIDMAP, + nxt_string(""), + 0, + {"nobody", 65534, 65534, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string(""), + 0, + {"johndoe", 10000, 10000, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string("[{\"container\": 1000, \"host\": 1000, \"size\": 1}]"), + 0, + {"johndoe", 1000, 1000, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1}]"), + 0, + {"root", 0, 0, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string("[{\"container\": 65534, \"host\": 1000, \"size\": 1}]"), + 0, + {"nobody", 65534, 0, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1}," + " {\"container\": 1000, \"host\": 2000, \"size\": 1}]"), + 0, + {"root", 0, 0, 0, NULL}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"uidmap\" field has 2 entries but unprivileged unit has " + "a maximum of 1 map.") + }, + { + UIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1}," + " {\"container\": 1000, \"host\": 2000, \"size\": 1}]"), + 1, /* privileged */ + {"root", 0, 0, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000}," + " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"), + 1, /* privileged */ + {"johndoe", 500, 0, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000}," + " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"), + 1, /* privileged */ + {"johndoe", 1000, 0, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000}," + " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"), + 1, /* privileged */ + {"johndoe", 1500, 0, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000}," + " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"), + 1, /* privileged */ + {"johndoe", 1999, 0, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + UIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1000}," + " {\"container\": 1000, \"host\": 2000, \"size\": 1000}]"), + 1, /* privileged */ + {"johndoe", 2000, 0, 0, NULL}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"uidmap\" field has no \"container\" entry for user " + "\"johndoe\" (uid 2000)") + }, + { + /* + * Unprivileged unit + * + * if no gid mapping and app creds and unit creds are the same, + * then we automatically add a map for the creds->base_gid. + * Then, child process can safely setgid(creds->base_gid) in + * the new namespace. + */ + GIDMAP, + nxt_string("[]"), + 0, + {"nobody", 65534, 65534, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + /* + * Unprivileged unit + * + * Inside the new namespace, we can have any gid but it + * should map to parent gid (in this case 1000) in parent + * namespace. + */ + GIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1}]"), + 0, + {"root", 0, 0, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + GIDMAP, + nxt_string("[{\"container\": 65534, \"host\": 1000, \"size\": 1}]"), + 0, + {"nobody", 65534, 65534, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + /* + * Unprivileged unit + * + * There's no mapping for "johndoe" (gid 1000) inside the namespace. + */ + GIDMAP, + nxt_string("[{\"container\": 65535, \"host\": 1000, \"size\": 1}]"), + 0, + {"johndoe", 1000, 1000, 0, NULL}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"gidmap\" field has no \"container\" entry for " + "gid 1000.") + }, + { + GIDMAP, + nxt_string("[{\"container\": 1000, \"host\": 1000, \"size\": 2}]"), + 0, + {"johndoe", 1000, 1000, 0, NULL}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"gidmap\" field has an entry with \"size\": 2, but " + "for unprivileged unit it must be 1.") + }, + { + GIDMAP, + nxt_string("[{\"container\": 1000, \"host\": 1001, \"size\": 1}]"), + 0, + {"johndoe", 1000, 1000, 0, NULL}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"gidmap\" field has an entry for host gid 1001 but " + "unprivileged unit can only map itself (gid 1000) " + "into child namespaces.") + }, + { + GIDMAP, + nxt_string("[{\"container\": 1000, \"host\": 1000, \"size\": 1}]"), + 0, + {"johndoe", 1000, 1000, 3, gids}, + 1000, 1000, + NXT_ERROR, + nxt_string("unprivileged unit disallow supplementary groups for " + "new namespace (user \"johndoe\" has 3 groups).") + }, + + /* privileged unit */ + + /* not root with capabilities */ + { + GIDMAP, + nxt_string("[]"), + 1, + {"johndoe", 1000, 1000, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + GIDMAP, + nxt_string(""), + 1, + {"johndoe", 1000, 1000, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + /* missing gid of {"user": "nobody"} */ + GIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1}]"), + 1, + {"nobody", 65534, 65534, 0, NULL}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"gidmap\" field has no \"container\" entry for " + "gid 65534.") + }, + { + /* solves the previous by mapping 65534 gids */ + GIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 65535}]"), + 1, + {"nobody", 65534, 65534, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + /* solves by adding a separate mapping */ + GIDMAP, + nxt_string("[{\"container\": 0, \"host\": 1000, \"size\": 1}," + " {\"container\": 65534, \"host\": 1000, \"size\": 1}]"), + 1, + {"nobody", 65534, 65534, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + /* + * Map a big range + */ + GIDMAP, + nxt_string("[{\"container\": 0, \"host\": 0, \"size\": 200000}]"), + 1, + {"johndoe", 100000, 100000, 0, NULL}, + 1000, 1000, + NXT_OK, + nxt_string("") + }, + { + /* + * Validate if supplementary groups are mapped + */ + GIDMAP, + nxt_string("[]"), + 1, + {"johndoe", 1000, 1000, 3, gids}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"gidmap\" field has no entries but user \"johndoe\" " + "has 3 suplementary groups."), + }, + { + GIDMAP, + nxt_string("[{\"container\": 0, \"host\": 0, \"size\": 1}]"), + 1, + {"johndoe", 1000, 1000, 3, gids}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"gidmap\" field has no \"container\" entry for " + "gid 1000."), + }, + { + GIDMAP, + nxt_string("[{\"container\": 1000, \"host\": 0, \"size\": 1}]"), + 1, + {"johndoe", 1000, 1000, 3, gids}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"gidmap\" field has missing suplementary gid mappings " + "(found 1 out of 3)."), + }, + { + GIDMAP, + nxt_string("[{\"container\": 1000, \"host\": 0, \"size\": 1}," + " {\"container\": 10000, \"host\": 10000, \"size\": 1}]"), + 1, + {"johndoe", 1000, 1000, 3, gids}, + 1000, 1000, + NXT_ERROR, + nxt_string("\"gidmap\" field has missing suplementary gid mappings " + "(found 2 out of 3)."), + }, + { + /* + * Fix all mappings + */ + GIDMAP, + nxt_string("[{\"container\": 1000, \"host\": 0, \"size\": 1}," + "{\"container\": 10000, \"host\": 10000, \"size\": 1}," + " {\"container\": 60000, \"host\": 60000, \"size\": 1}]"), + 1, + {"johndoe", 1000, 1000, 3, gids}, + 1000, 1000, + NXT_OK, + nxt_string(""), + }, +}; + + +void nxt_cdecl +nxt_clone_test_log_handler(nxt_uint_t level, nxt_log_t *log, + const char *fmt, ...) +{ + u_char *p, *end; + va_list args; + nxt_clone_creds_ctx_t *ctx; + nxt_clone_creds_testcase_t *tc; + u_char msg[NXT_MAX_ERROR_STR]; + + p = msg; + end = msg + NXT_MAX_ERROR_STR; + + ctx = log->ctx; + tc = ctx->tc; + + va_start(args, fmt); + p = nxt_vsprintf(p, end, fmt, args); + va_end(args); + + *p++ = '\0'; + + if (tc->result == NXT_OK && level == NXT_LOG_DEBUG) { + return; + } + + if (tc->errmsg.length == 0) { + nxt_log_error(NXT_LOG_ERR, &nxt_main_log, "unexpected log: %s", msg); + return; + } + + if (!nxt_str_eq(&tc->errmsg, msg, (nxt_uint_t) (p - msg - 1))) { + nxt_log_error(NXT_LOG_ERR, &nxt_main_log, + "error log mismatch: got [%s] but wants [%V]", + msg, &tc->errmsg); + return; + } +} + + +nxt_int_t +nxt_clone_creds_test(nxt_thread_t *thr) +{ + nxt_mp_t *mp; + nxt_int_t ret; + nxt_uint_t count, i; + nxt_task_t *task; + nxt_runtime_t rt; + nxt_clone_creds_ctx_t ctx; + + nxt_log_t nxt_clone_creds_log = { + NXT_LOG_INFO, + 0, + nxt_clone_test_log_handler, + NULL, + &ctx + }; + + nxt_thread_time_update(thr); + + thr->runtime = &rt; + + task = thr->task; + + mp = nxt_mp_create(1024, 128, 256, 32); + if (mp == NULL) { + return NXT_ERROR; + } + + rt.mem_pool = mp; + + test_log = task->log; + task->log = &nxt_clone_creds_log; + task->thread = thr; + + count = sizeof(testcases)/sizeof(nxt_clone_creds_testcase_t); + + for (i = 0; i < count; i++) { + ret = nxt_clone_test_mappings(task, mp, &ctx, &testcases[i]); + + if (ret != NXT_OK) { + goto fail; + } + } + + ret = NXT_OK; + + nxt_log_error(NXT_LOG_NOTICE, test_log, "clone creds test passed"); + +fail: + task->log = test_log; + nxt_mp_destroy(mp); + + return ret; +} + + +nxt_int_t +nxt_clone_test_mappings(nxt_task_t *task, nxt_mp_t *mp, + nxt_clone_creds_ctx_t *ctx, nxt_clone_creds_testcase_t *tc) +{ + nxt_int_t ret; + nxt_runtime_t *rt; + nxt_clone_credential_map_t map; + + rt = task->thread->runtime; + + map.size = 0; + + if (tc->map_data.length > 0) { + ret = nxt_clone_test_parse_map(task, &tc->map_data, &map); + if (ret != NXT_OK) { + return NXT_ERROR; + } + } + + rt->capabilities.setid = tc->setid; + + nxt_euid = tc->unit_euid; + nxt_egid = tc->unit_egid; + + ctx->tc = tc; + + if (nxt_clone_test_map_assert(task, tc, &map) != NXT_OK) { + return NXT_ERROR; + } + + if (tc->setid && nxt_euid != 0) { + /* + * Running as root should have the same behavior as + * passing Linux capabilities. + */ + + nxt_euid = 0; + nxt_egid = 0; + + if (nxt_clone_test_map_assert(task, tc, &map) != NXT_OK) { + return NXT_ERROR; + } + } + + return NXT_OK; +} + + +nxt_int_t +nxt_clone_test_map_assert(nxt_task_t *task, nxt_clone_creds_testcase_t *tc, + nxt_clone_credential_map_t *map) +{ + nxt_int_t ret; + + if (tc->map_type == UIDMAP) { + ret = nxt_clone_vldt_credential_uidmap(task, map, &tc->creds); + } else { + ret = nxt_clone_vldt_credential_gidmap(task, map, &tc->creds); + } + + if (ret != tc->result) { + nxt_log_error(NXT_LOG_ERR, &nxt_main_log, + "return %d instead of %d (map: %V)", ret, tc->result, + &tc->map_data); + + return NXT_ERROR; + } + + return NXT_OK; +} + + +static nxt_int_t +nxt_clone_test_parse_map(nxt_task_t *task, nxt_str_t *map_str, + nxt_clone_credential_map_t *map) +{ + nxt_uint_t i; + nxt_runtime_t *rt; + nxt_conf_value_t *array, *obj, *value; + + static nxt_str_t host_name = nxt_string("host"); + static nxt_str_t cont_name = nxt_string("container"); + static nxt_str_t size_name = nxt_string("size"); + + rt = task->thread->runtime; + + array = nxt_conf_json_parse_str(rt->mem_pool, map_str); + if (array == NULL) { + return NXT_ERROR; + } + + map->size = nxt_conf_array_elements_count(array); + + if (map->size == 0) { + return NXT_OK; + } + + map->map = nxt_mp_alloc(rt->mem_pool, + map->size * sizeof(nxt_clone_map_entry_t)); + + if (map->map == NULL) { + return NXT_ERROR; + } + + for (i = 0; i < map->size; i++) { + obj = nxt_conf_get_array_element(array, i); + + value = nxt_conf_get_object_member(obj, &host_name, NULL); + map->map[i].host = nxt_conf_get_integer(value); + + value = nxt_conf_get_object_member(obj, &cont_name, NULL); + map->map[i].container = nxt_conf_get_integer(value); + + value = nxt_conf_get_object_member(obj, &size_name, NULL); + map->map[i].size = nxt_conf_get_integer(value); + } + + return NXT_OK; +} diff --git a/src/test/nxt_tests.c b/src/test/nxt_tests.c index 7cba0f69..901d76c3 100644 --- a/src/test/nxt_tests.c +++ b/src/test/nxt_tests.c @@ -162,5 +162,11 @@ main(int argc, char **argv) return 1; } +#if (NXT_HAVE_CLONE_NEWUSER) + if (nxt_clone_creds_test(thr) != NXT_OK) { + return 1; + } +#endif + return 0; } diff --git a/src/test/nxt_tests.h b/src/test/nxt_tests.h index be4168cf..d531cc7d 100644 --- a/src/test/nxt_tests.h +++ b/src/test/nxt_tests.h @@ -64,6 +64,7 @@ nxt_int_t nxt_malloc_test(nxt_thread_t *thr); nxt_int_t nxt_utf8_test(nxt_thread_t *thr); nxt_int_t nxt_http_parse_test(nxt_thread_t *thr); nxt_int_t nxt_strverscmp_test(nxt_thread_t *thr); +nxt_int_t nxt_clone_creds_test(nxt_thread_t *thr); #endif /* _NXT_TESTS_H_INCLUDED_ */ |