/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include #include #include #include #if (NXT_HAVE_CLONE) pid_t nxt_clone(nxt_int_t flags) { #if defined(__s390x__) || defined(__s390__) || defined(__CRIS__) return syscall(__NR_clone, NULL, flags); #else return syscall(__NR_clone, flags, NULL); #endif } #endif #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, 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, 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) { int fd, n; u_char *p, *end; u_char path[PATH_MAX]; end = path + PATH_MAX; p = nxt_sprintf(path, end, "/proc/%d/setgroups", child_pid); *p = '\0'; if (nxt_slow_path(p == end)) { nxt_alert(task, "error write past the buffer: %s", path); return NXT_ERROR; } fd = open((char *)path, O_RDWR); if (fd == -1) { /* * If the /proc/pid/setgroups doesn't exists, we are * safe to set uid/gid maps. But if the error is anything * other than ENOENT, then we should abort and let user know. */ if (errno != ENOENT) { nxt_alert(task, "open(%s): %E", path, nxt_errno); return NXT_ERROR; } return NXT_OK; } n = write(fd, str, strlen(str)); close(fd); if (nxt_slow_path(n == -1)) { nxt_alert(task, "write(%s): %E", path, nxt_errno); return NXT_ERROR; } return NXT_OK; } nxt_int_t nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile, pid_t pid, u_char *mapinfo) { int len, mapfd; u_char *p, *end; ssize_t n; u_char buf[256]; end = buf + sizeof(buf); p = nxt_sprintf(buf, end, "/proc/%d/%s", pid, mapfile); if (nxt_slow_path(p == end)) { nxt_alert(task, "writing past the buffer"); return NXT_ERROR; } *p = '\0'; mapfd = open((char*)buf, O_RDWR); if (nxt_slow_path(mapfd == -1)) { nxt_alert(task, "failed to open proc map (%s) %E", buf, nxt_errno); return NXT_ERROR; } len = nxt_strlen(mapinfo); n = write(mapfd, (char *)mapinfo, len); if (nxt_slow_path(n != len)) { if (n == -1 && nxt_errno == EINVAL) { nxt_alert(task, "failed to write %s: Check kernel maximum " \ "allowed lines %E", buf, nxt_errno); } else { nxt_alert(task, "failed to write proc map (%s) %E", buf, nxt_errno); } close(mapfd); return NXT_ERROR; } close(mapfd); return NXT_OK; } 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) { 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"); /* * uid_map one-entry size: * alloc space for 3 numbers (32bit) plus 2 spaces and \n. */ 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; 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); 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_free(mapinfo); return NXT_ERROR; } if (i+1 < count) { *p++ = '\n'; } else { *p = '\0'; } } } 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 = '\0'; if (nxt_slow_path(p == end)) { nxt_alert(task, "write past the %s buffer", mapfile); nxt_free(mapinfo); return NXT_ERROR; } } ret = nxt_clone_proc_map_write(task, mapfile, pid, mapinfo); nxt_free(mapinfo); return ret; } nxt_int_t nxt_clone_proc_map(nxt_task_t *task, pid_t pid, nxt_process_clone_t *clone) { nxt_int_t ret; nxt_int_t uid, gid; const char *rule; nxt_runtime_t *rt; rt = task->thread->runtime; uid = geteuid(); gid = getegid(); rule = rt->capabilities.setid ? "allow" : "deny"; 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); 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); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } return NXT_OK; } #endif