xref: /unit/src/nxt_clone.c (revision 1201:563e00547881)
1 /*
2  * Copyright (C) Igor Sysoev
3  * Copyright (C) NGINX, Inc.
4  */
5 
6 #include <nxt_main.h>
7 #include <sys/types.h>
8 #include <nxt_conf.h>
9 #include <nxt_clone.h>
10 
11 #if (NXT_HAVE_CLONE)
12 
13 pid_t
14 nxt_clone(nxt_int_t flags)
15 {
16 #if defined(__s390x__) || defined(__s390__) || defined(__CRIS__)
17     return syscall(__NR_clone, NULL, flags);
18 #else
19     return syscall(__NR_clone, flags, NULL);
20 #endif
21 }
22 
23 #endif
24 
25 
26 #if (NXT_HAVE_CLONE_NEWUSER)
27 
28 /* map uid 65534 to unit pid */
29 #define NXT_DEFAULT_UNPRIV_MAP "65534 %d 1"
30 
31 nxt_int_t nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid,
32     const char *str);
33 nxt_int_t nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile,
34     pid_t pid, nxt_int_t defval, nxt_conf_value_t *mapobj);
35 nxt_int_t nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile,
36     pid_t pid, u_char *mapinfo);
37 
38 
39 typedef struct {
40     nxt_int_t container;
41     nxt_int_t host;
42     nxt_int_t size;
43 } nxt_clone_procmap_t;
44 
45 
46 nxt_int_t
47 nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid, const char *str)
48 {
49     int     fd, n;
50     u_char  *p, *end;
51     u_char  path[PATH_MAX];
52 
53     end = path + PATH_MAX;
54     p = nxt_sprintf(path, end, "/proc/%d/setgroups", child_pid);
55     *p = '\0';
56 
57     if (nxt_slow_path(p == end)) {
58         nxt_alert(task, "error write past the buffer: %s", path);
59         return NXT_ERROR;
60     }
61 
62     fd = open((char *)path, O_RDWR);
63 
64     if (fd == -1) {
65         /*
66          * If the /proc/pid/setgroups doesn't exists, we are
67          * safe to set uid/gid maps. But if the error is anything
68          * other than ENOENT, then we should abort and let user know.
69          */
70 
71         if (errno != ENOENT) {
72             nxt_alert(task, "open(%s): %E", path, nxt_errno);
73             return NXT_ERROR;
74         }
75 
76         return NXT_OK;
77     }
78 
79     n = write(fd, str, strlen(str));
80     close(fd);
81 
82     if (nxt_slow_path(n == -1)) {
83         nxt_alert(task, "write(%s): %E", path, nxt_errno);
84         return NXT_ERROR;
85     }
86 
87     return NXT_OK;
88 }
89 
90 
91 nxt_int_t
92 nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile, pid_t pid,
93     u_char *mapinfo)
94 {
95     int      len, mapfd;
96     u_char   *p, *end;
97     ssize_t  n;
98     u_char   buf[256];
99 
100     end = buf + sizeof(buf);
101 
102     p = nxt_sprintf(buf, end, "/proc/%d/%s", pid, mapfile);
103     if (nxt_slow_path(p == end)) {
104         nxt_alert(task, "writing past the buffer");
105         return NXT_ERROR;
106     }
107 
108     *p = '\0';
109 
110     mapfd = open((char*)buf, O_RDWR);
111     if (nxt_slow_path(mapfd == -1)) {
112         nxt_alert(task, "failed to open proc map (%s) %E", buf, nxt_errno);
113         return NXT_ERROR;
114     }
115 
116     len = nxt_strlen(mapinfo);
117 
118     n = write(mapfd, (char *)mapinfo, len);
119     if (nxt_slow_path(n != len)) {
120 
121         if (n == -1 && nxt_errno == EINVAL) {
122             nxt_alert(task, "failed to write %s: Check kernel maximum " \
123                       "allowed lines %E", buf, nxt_errno);
124 
125         } else {
126             nxt_alert(task, "failed to write proc map (%s) %E", buf,
127                       nxt_errno);
128         }
129 
130         close(mapfd);
131 
132         return NXT_ERROR;
133     }
134 
135     close(mapfd);
136 
137     return NXT_OK;
138 }
139 
140 
141 nxt_int_t
142 nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, pid_t pid,
143     nxt_int_t defval, nxt_conf_value_t *mapobj)
144 {
145     u_char            *p, *end, *mapinfo;
146     nxt_int_t         container, host, size;
147     nxt_int_t         ret, len, count, i;
148     nxt_conf_value_t  *obj, *value;
149 
150     static nxt_str_t str_cont = nxt_string("container");
151     static nxt_str_t str_host = nxt_string("host");
152     static nxt_str_t str_size = nxt_string("size");
153 
154     /*
155      * uid_map one-entry size:
156      *   alloc space for 3 numbers (32bit) plus 2 spaces and \n.
157      */
158     len = sizeof(u_char) * (10 + 10 + 10 + 2 + 1);
159 
160     if (mapobj != NULL) {
161         count = nxt_conf_array_elements_count(mapobj);
162 
163         if (count == 0) {
164             goto default_map;
165         }
166 
167         len = len * count + 1;
168 
169         mapinfo = nxt_malloc(len);
170         if (nxt_slow_path(mapinfo == NULL)) {
171             nxt_alert(task, "failed to allocate uid_map buffer");
172             return NXT_ERROR;
173         }
174 
175         p = mapinfo;
176         end = mapinfo + len;
177 
178         for (i = 0; i < count; i++) {
179             obj = nxt_conf_get_array_element(mapobj, i);
180 
181             value = nxt_conf_get_object_member(obj, &str_cont, NULL);
182             container = nxt_conf_get_integer(value);
183 
184             value = nxt_conf_get_object_member(obj, &str_host, NULL);
185             host = nxt_conf_get_integer(value);
186 
187             value = nxt_conf_get_object_member(obj, &str_size, NULL);
188             size = nxt_conf_get_integer(value);
189 
190             p = nxt_sprintf(p, end, "%d %d %d", container, host, size);
191             if (nxt_slow_path(p == end)) {
192                 nxt_alert(task, "write past the uid_map buffer");
193                 nxt_free(mapinfo);
194                 return NXT_ERROR;
195             }
196 
197             if (i+1 < count) {
198                 *p++ = '\n';
199 
200             } else {
201                 *p = '\0';
202             }
203         }
204 
205     } else {
206 
207 default_map:
208 
209         mapinfo = nxt_malloc(len);
210         if (nxt_slow_path(mapinfo == NULL)) {
211             nxt_alert(task, "failed to allocate uid_map buffer");
212             return NXT_ERROR;
213         }
214 
215         end = mapinfo + len;
216         p = nxt_sprintf(mapinfo, end, NXT_DEFAULT_UNPRIV_MAP, defval);
217         *p = '\0';
218 
219         if (nxt_slow_path(p == end)) {
220             nxt_alert(task, "write past the %s buffer", mapfile);
221             nxt_free(mapinfo);
222             return NXT_ERROR;
223         }
224     }
225 
226     ret = nxt_clone_proc_map_write(task, mapfile, pid, mapinfo);
227 
228     nxt_free(mapinfo);
229 
230     return ret;
231 }
232 
233 
234 nxt_int_t
235 nxt_clone_proc_map(nxt_task_t *task, pid_t pid, nxt_process_clone_t *clone)
236 {
237     nxt_int_t      ret;
238     nxt_int_t      uid, gid;
239     const char     *rule;
240     nxt_runtime_t  *rt;
241 
242     rt  = task->thread->runtime;
243     uid = geteuid();
244     gid = getegid();
245 
246     rule = rt->capabilities.setid ? "allow" : "deny";
247 
248     ret = nxt_clone_proc_map_set(task, "uid_map", pid, uid, clone->uidmap);
249     if (nxt_slow_path(ret != NXT_OK)) {
250         return NXT_ERROR;
251     }
252 
253     ret = nxt_clone_proc_setgroups(task, pid, rule);
254     if (nxt_slow_path(ret != NXT_OK)) {
255         nxt_alert(task, "failed to write /proc/%d/setgroups", pid);
256         return NXT_ERROR;
257     }
258 
259     ret = nxt_clone_proc_map_set(task, "gid_map", pid, gid, clone->gidmap);
260     if (nxt_slow_path(ret != NXT_OK)) {
261         return NXT_ERROR;
262     }
263 
264     return NXT_OK;
265 }
266 
267 #endif
268