268 lines
6.3 KiB
C
268 lines
6.3 KiB
C
/*
|
|
* Copyright (C) Igor Sysoev
|
|
* Copyright (C) NGINX, Inc.
|
|
*/
|
|
|
|
#include <nxt_main.h>
|
|
#include <sys/types.h>
|
|
#include <nxt_conf.h>
|
|
#include <nxt_clone.h>
|
|
|
|
#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
|