nxt_clone.c (1201:563e00547881) nxt_clone.c (1306:3604d05e48be)
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>

--- 11 unchanged lines hidden (view full) ---

20#endif
21}
22
23#endif
24
25
26#if (NXT_HAVE_CLONE_NEWUSER)
27
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>

--- 11 unchanged lines hidden (view full) ---

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
31nxt_int_t nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid,
28nxt_int_t nxt_clone_credential_setgroups(nxt_task_t *task, pid_t child_pid,
32 const char *str);
29 const char *str);
33nxt_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);
35nxt_int_t nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile,
30nxt_int_t nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile,
31 pid_t pid, nxt_int_t default_container, nxt_int_t default_host,
32 nxt_clone_credential_map_t *map);
33nxt_int_t nxt_clone_credential_map_write(nxt_task_t *task, const char *mapfile,
36 pid_t pid, u_char *mapinfo);
37
38
34 pid_t pid, u_char *mapinfo);
35
36
39typedef struct {
40 nxt_int_t container;
41 nxt_int_t host;
42 nxt_int_t size;
43} nxt_clone_procmap_t;
44
45
46nxt_int_t
37nxt_int_t
47nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid, const char *str)
38nxt_clone_credential_setgroups(nxt_task_t *task, pid_t child_pid,
39 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';

--- 28 unchanged lines hidden (view full) ---

84 return NXT_ERROR;
85 }
86
87 return NXT_OK;
88}
89
90
91nxt_int_t
40{
41 int fd, n;
42 u_char *p, *end;
43 u_char path[PATH_MAX];
44
45 end = path + PATH_MAX;
46 p = nxt_sprintf(path, end, "/proc/%d/setgroups", child_pid);
47 *p = '\0';

--- 28 unchanged lines hidden (view full) ---

76 return NXT_ERROR;
77 }
78
79 return NXT_OK;
80}
81
82
83nxt_int_t
92nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile, pid_t pid,
93 u_char *mapinfo)
84nxt_clone_credential_map_write(nxt_task_t *task, const char *mapfile,
85 pid_t pid, 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

--- 32 unchanged lines hidden (view full) ---

134
135 close(mapfd);
136
137 return NXT_OK;
138}
139
140
141nxt_int_t
86{
87 int len, mapfd;
88 u_char *p, *end;
89 ssize_t n;
90 u_char buf[256];
91
92 end = buf + sizeof(buf);
93

--- 32 unchanged lines hidden (view full) ---

126
127 close(mapfd);
128
129 return NXT_OK;
130}
131
132
133nxt_int_t
142nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, pid_t pid,
143 nxt_int_t defval, nxt_conf_value_t *mapobj)
134nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile, pid_t pid,
135 nxt_int_t default_container, nxt_int_t default_host,
136 nxt_clone_credential_map_t *map)
144{
137{
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;
138 u_char *p, *end, *mapinfo;
139 nxt_int_t ret, len;
140 nxt_uint_t i;
149
141
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
142 /*
143 * uid_map one-entry size:
144 * alloc space for 3 numbers (32bit) plus 2 spaces and \n.
145 */
146 len = sizeof(u_char) * (10 + 10 + 10 + 2 + 1);
147
160 if (mapobj != NULL) {
161 count = nxt_conf_array_elements_count(mapobj);
148 if (map->size > 0) {
149 len = len * map->size + 1;
162
150
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)) {
151 mapinfo = nxt_malloc(len);
152 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
153 return NXT_ERROR;
154 }
155
156 p = mapinfo;
157 end = mapinfo + len;
158
178 for (i = 0; i < count; i++) {
179 obj = nxt_conf_get_array_element(mapobj, i);
159 for (i = 0; i < map->size; i++) {
160 p = nxt_sprintf(p, end, "%d %d %d", map->map[i].container,
161 map->map[i].host, map->map[i].size);
180
162
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)) {
163 if (nxt_slow_path(p == end)) {
192 nxt_alert(task, "write past the uid_map buffer");
164 nxt_alert(task, "write past the mapinfo buffer");
193 nxt_free(mapinfo);
194 return NXT_ERROR;
195 }
196
165 nxt_free(mapinfo);
166 return NXT_ERROR;
167 }
168
197 if (i+1 < count) {
169 if (i+1 < map->size) {
198 *p++ = '\n';
199
200 } else {
201 *p = '\0';
202 }
203 }
204
205 } else {
170 *p++ = '\n';
171
172 } else {
173 *p = '\0';
174 }
175 }
176
177 } else {
206
207default_map:
208
209 mapinfo = nxt_malloc(len);
210 if (nxt_slow_path(mapinfo == NULL)) {
178 mapinfo = nxt_malloc(len);
179 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;
180 return NXT_ERROR;
181 }
182
183 end = mapinfo + len;
216 p = nxt_sprintf(mapinfo, end, NXT_DEFAULT_UNPRIV_MAP, defval);
184 p = nxt_sprintf(mapinfo, end, "%d %d 1",
185 default_container, default_host);
217 *p = '\0';
218
219 if (nxt_slow_path(p == end)) {
186 *p = '\0';
187
188 if (nxt_slow_path(p == end)) {
220 nxt_alert(task, "write past the %s buffer", mapfile);
189 nxt_alert(task, "write past mapinfo buffer");
221 nxt_free(mapinfo);
222 return NXT_ERROR;
223 }
224 }
225
190 nxt_free(mapinfo);
191 return NXT_ERROR;
192 }
193 }
194
226 ret = nxt_clone_proc_map_write(task, mapfile, pid, mapinfo);
195 ret = nxt_clone_credential_map_write(task, mapfile, pid, mapinfo);
227
228 nxt_free(mapinfo);
229
230 return ret;
231}
232
233
234nxt_int_t
196
197 nxt_free(mapinfo);
198
199 return ret;
200}
201
202
203nxt_int_t
235nxt_clone_proc_map(nxt_task_t *task, pid_t pid, nxt_process_clone_t *clone)
204nxt_clone_credential_map(nxt_task_t *task, pid_t pid,
205 nxt_credential_t *app_creds, nxt_clone_t *clone)
236{
237 nxt_int_t ret;
206{
207 nxt_int_t ret;
238 nxt_int_t uid, gid;
208 nxt_int_t default_host_uid;
209 nxt_int_t default_host_gid;
239 const char *rule;
240 nxt_runtime_t *rt;
241
242 rt = task->thread->runtime;
210 const char *rule;
211 nxt_runtime_t *rt;
212
213 rt = task->thread->runtime;
243 uid = geteuid();
244 gid = getegid();
245
214
246 rule = rt->capabilities.setid ? "allow" : "deny";
215 if (rt->capabilities.setid) {
216 rule = "allow";
247
217
248 ret = nxt_clone_proc_map_set(task, "uid_map", pid, uid, clone->uidmap);
218 /*
219 * By default we don't map a privileged user
220 */
221 default_host_uid = app_creds->uid;
222 default_host_gid = app_creds->base_gid;
223 } else {
224 rule = "deny";
225
226 default_host_uid = nxt_euid;
227 default_host_gid = nxt_egid;
228 }
229
230 ret = nxt_clone_credential_map_set(task, "uid_map", pid, app_creds->uid,
231 default_host_uid,
232 &clone->uidmap);
233
249 if (nxt_slow_path(ret != NXT_OK)) {
250 return NXT_ERROR;
251 }
252
234 if (nxt_slow_path(ret != NXT_OK)) {
235 return NXT_ERROR;
236 }
237
253 ret = nxt_clone_proc_setgroups(task, pid, rule);
238 ret = nxt_clone_credential_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
239 if (nxt_slow_path(ret != NXT_OK)) {
240 nxt_alert(task, "failed to write /proc/%d/setgroups", pid);
241 return NXT_ERROR;
242 }
243
259 ret = nxt_clone_proc_map_set(task, "gid_map", pid, gid, clone->gidmap);
244 ret = nxt_clone_credential_map_set(task, "gid_map", pid, app_creds->base_gid,
245 default_host_gid,
246 &clone->gidmap);
247
260 if (nxt_slow_path(ret != NXT_OK)) {
261 return NXT_ERROR;
262 }
263
264 return NXT_OK;
265}
266
248 if (nxt_slow_path(ret != NXT_OK)) {
249 return NXT_ERROR;
250 }
251
252 return NXT_OK;
253}
254
255
256nxt_int_t
257nxt_clone_vldt_credential_uidmap(nxt_task_t *task,
258 nxt_clone_credential_map_t *map, nxt_credential_t *creds)
259{
260 nxt_int_t id;
261 nxt_uint_t i;
262 nxt_runtime_t *rt;
263 nxt_clone_map_entry_t m;
264
265 if (map->size == 0) {
266 return NXT_OK;
267 }
268
269 rt = task->thread->runtime;
270
271 if (!rt->capabilities.setid) {
272 if (nxt_slow_path(map->size > 1)) {
273 nxt_log(task, NXT_LOG_NOTICE, "\"uidmap\" field has %d entries "
274 "but unprivileged unit has a maximum of 1 map.",
275 map->size);
276
277 return NXT_ERROR;
278 }
279
280 id = map->map[0].host;
281
282 if (nxt_slow_path((nxt_uid_t) id != nxt_euid)) {
283 nxt_log(task, NXT_LOG_NOTICE, "\"uidmap\" field has an entry for "
284 "host uid %d but unprivileged unit can only map itself "
285 "(uid %d) into child namespaces.", id, nxt_euid);
286
287 return NXT_ERROR;
288 }
289
290 return NXT_OK;
291 }
292
293 for (i = 0; i < map->size; i++) {
294 m = map->map[i];
295
296 if (creds->uid >= (nxt_uid_t) m.container
297 && creds->uid < (nxt_uid_t) (m.container + m.size))
298 {
299 return NXT_OK;
300 }
301 }
302
303 nxt_log(task, NXT_LOG_NOTICE, "\"uidmap\" field has no \"container\" "
304 "entry for user \"%s\" (uid %d)", creds->user, creds->uid);
305
306 return NXT_ERROR;
307}
308
309
310nxt_int_t
311nxt_clone_vldt_credential_gidmap(nxt_task_t *task,
312 nxt_clone_credential_map_t *map, nxt_credential_t *creds)
313{
314 nxt_uint_t base_ok, gid_ok, gids_ok;
315 nxt_uint_t i, j;
316 nxt_runtime_t *rt;
317 nxt_clone_map_entry_t m;
318
319 rt = task->thread->runtime;
320
321 if (!rt->capabilities.setid) {
322 if (creds->ngroups > 0
323 && !(creds->ngroups == 1 && creds->gids[0] == creds->base_gid)) {
324 nxt_log(task, NXT_LOG_NOTICE,
325 "unprivileged unit disallow supplementary groups for "
326 "new namespace (user \"%s\" has %d group%s).",
327 creds->user, creds->ngroups,
328 creds->ngroups > 1 ? "s" : "");
329
330 return NXT_ERROR;
331 }
332
333 if (map->size == 0) {
334 return NXT_OK;
335 }
336
337 if (nxt_slow_path(map->size > 1)) {
338 nxt_log(task, NXT_LOG_NOTICE, "\"gidmap\" field has %d entries "
339 "but unprivileged unit has a maximum of 1 map.",
340 map->size);
341
342 return NXT_ERROR;
343 }
344
345 m = map->map[0];
346
347 if (nxt_slow_path((nxt_gid_t) m.host != nxt_egid)) {
348 nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry for "
349 "host gid %d but unprivileged unit can only map itself "
350 "(gid %d) into child namespaces.", m.host, nxt_egid);
351
352 return NXT_ERROR;
353 }
354
355 if (nxt_slow_path(m.size > 1)) {
356 nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry with "
357 "\"size\": %d, but for unprivileged unit it must be 1.",
358 m.size);
359
360 return NXT_ERROR;
361 }
362
363 if (nxt_slow_path((nxt_gid_t) m.container != creds->base_gid)) {
364 nxt_log(task, NXT_LOG_ERR,
365 "\"gidmap\" field has no \"container\" entry for gid %d.",
366 creds->base_gid);
367
368 return NXT_ERROR;
369 }
370
371 return NXT_OK;
372 }
373
374 if (map->size == 0) {
375 if (creds->ngroups > 0
376 && !(creds->ngroups == 1 && creds->gids[0] == creds->base_gid))
377 {
378 nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has no entries "
379 "but user \"%s\" has %d suplementary group%s.",
380 creds->user, creds->ngroups,
381 creds->ngroups > 1 ? "s" : "");
382
383 return NXT_ERROR;
384 }
385
386 return NXT_OK;
387 }
388
389 base_ok = 0;
390 gids_ok = 0;
391
392 for (i = 0; i < creds->ngroups; i++) {
393 gid_ok = 0;
394
395 for (j = 0; j < map->size; j++) {
396 m = map->map[j];
397
398 if (!base_ok && creds->base_gid >= (nxt_gid_t) m.container
399 && creds->base_gid < (nxt_gid_t) (m.container+m.size))
400 {
401 base_ok = 1;
402 }
403
404 if (creds->gids[i] >= (nxt_gid_t) m.container
405 && creds->gids[i] < (nxt_gid_t) (m.container+m.size))
406 {
407 gid_ok = 1;
408 break;
409 }
410 }
411
412 if (nxt_fast_path(gid_ok)) {
413 gids_ok++;
414 }
415 }
416
417 if (!base_ok) {
418 for (i = 0; i < map->size; i++) {
419 m = map->map[i];
420
421 if (creds->base_gid >= (nxt_gid_t) m.container
422 && creds->base_gid < (nxt_gid_t) (m.container+m.size))
423 {
424 base_ok = 1;
425 break;
426 }
427 }
428 }
429
430 if (nxt_slow_path(!base_ok)) {
431 nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has no \"container\" "
432 "entry for gid %d.", creds->base_gid);
433
434 return NXT_ERROR;
435 }
436
437 if (nxt_slow_path(gids_ok < creds->ngroups)) {
438 nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has missing "
439 "suplementary gid mappings (found %d out of %d).", gids_ok,
440 creds->ngroups);
441
442 return NXT_ERROR;
443 }
444
445 return NXT_OK;
446}
447
267#endif
448#endif