summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_clone.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nxt_clone.c')
-rw-r--r--src/nxt_clone.c311
1 files changed, 246 insertions, 65 deletions
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