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 nxt_int_t nxt_clone_credential_setgroups(nxt_task_t *task, pid_t child_pid, 29 const char *str); 30 nxt_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); 33 nxt_int_t nxt_clone_credential_map_write(nxt_task_t *task, const char *mapfile, 34 pid_t pid, u_char *mapinfo); 35 36 37 nxt_int_t 38 nxt_clone_credential_setgroups(nxt_task_t *task, pid_t child_pid, 39 const char *str) 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'; 48 49 if (nxt_slow_path(p == end)) { 50 nxt_alert(task, "error write past the buffer: %s", path); 51 return NXT_ERROR; 52 } 53 54 fd = open((char *)path, O_RDWR); 55 56 if (fd == -1) { 57 /* 58 * If the /proc/pid/setgroups doesn't exists, we are 59 * safe to set uid/gid maps. But if the error is anything 60 * other than ENOENT, then we should abort and let user know. 61 */ 62 63 if (errno != ENOENT) { 64 nxt_alert(task, "open(%s): %E", path, nxt_errno); 65 return NXT_ERROR; 66 } 67 68 return NXT_OK; 69 } 70 71 n = write(fd, str, strlen(str)); 72 close(fd); 73 74 if (nxt_slow_path(n == -1)) { 75 nxt_alert(task, "write(%s): %E", path, nxt_errno); 76 return NXT_ERROR; 77 } 78 79 return NXT_OK; 80 } 81 82 83 nxt_int_t 84 nxt_clone_credential_map_write(nxt_task_t *task, const char *mapfile, 85 pid_t pid, u_char *mapinfo) 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 94 p = nxt_sprintf(buf, end, "/proc/%d/%s", pid, mapfile); 95 if (nxt_slow_path(p == end)) { 96 nxt_alert(task, "writing past the buffer"); 97 return NXT_ERROR; 98 } 99 100 *p = '\0'; 101 102 mapfd = open((char*)buf, O_RDWR); 103 if (nxt_slow_path(mapfd == -1)) { 104 nxt_alert(task, "failed to open proc map (%s) %E", buf, nxt_errno); 105 return NXT_ERROR; 106 } 107 108 len = nxt_strlen(mapinfo); 109 110 n = write(mapfd, (char *)mapinfo, len); 111 if (nxt_slow_path(n != len)) { 112 113 if (n == -1 && nxt_errno == EINVAL) { 114 nxt_alert(task, "failed to write %s: Check kernel maximum " \ 115 "allowed lines %E", buf, nxt_errno); 116 117 } else { 118 nxt_alert(task, "failed to write proc map (%s) %E", buf, 119 nxt_errno); 120 } 121 122 close(mapfd); 123 124 return NXT_ERROR; 125 } 126 127 close(mapfd); 128 129 return NXT_OK; 130 } 131 132 133 nxt_int_t 134 nxt_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) 137 { 138 u_char *p, *end, *mapinfo; 139 nxt_int_t ret, len; 140 nxt_uint_t i; 141 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 148 if (map->size > 0) { 149 len = len * map->size + 1; 150 151 mapinfo = nxt_malloc(len); 152 if (nxt_slow_path(mapinfo == NULL)) { 153 return NXT_ERROR; 154 } 155 156 p = mapinfo; 157 end = mapinfo + len; 158 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); 162 163 if (nxt_slow_path(p == end)) { 164 nxt_alert(task, "write past the mapinfo buffer"); 165 nxt_free(mapinfo); 166 return NXT_ERROR; 167 } 168 169 if (i+1 < map->size) { 170 *p++ = '\n'; 171 172 } else { 173 *p = '\0'; 174 } 175 } 176 177 } else { 178 mapinfo = nxt_malloc(len); 179 if (nxt_slow_path(mapinfo == NULL)) { 180 return NXT_ERROR; 181 } 182 183 end = mapinfo + len; 184 p = nxt_sprintf(mapinfo, end, "%d %d 1", 185 default_container, default_host); 186 *p = '\0'; 187 188 if (nxt_slow_path(p == end)) { 189 nxt_alert(task, "write past mapinfo buffer"); 190 nxt_free(mapinfo); 191 return NXT_ERROR; 192 } 193 } 194 195 ret = nxt_clone_credential_map_write(task, mapfile, pid, mapinfo); 196 197 nxt_free(mapinfo); 198 199 return ret; 200 } 201 202 203 nxt_int_t 204 nxt_clone_credential_map(nxt_task_t *task, pid_t pid, 205 nxt_credential_t *app_creds, nxt_clone_t *clone) 206 { 207 nxt_int_t ret; 208 nxt_int_t default_host_uid; 209 nxt_int_t default_host_gid; 210 const char *rule; 211 nxt_runtime_t *rt; 212 213 rt = task->thread->runtime; 214 215 if (rt->capabilities.setid) { 216 rule = "allow"; 217 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 234 if (nxt_slow_path(ret != NXT_OK)) { 235 return NXT_ERROR; 236 } 237 238 ret = nxt_clone_credential_setgroups(task, pid, rule); 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 244 ret = nxt_clone_credential_map_set(task, "gid_map", pid, app_creds->base_gid, 245 default_host_gid, 246 &clone->gidmap); 247 248 if (nxt_slow_path(ret != NXT_OK)) { 249 return NXT_ERROR; 250 } 251 252 return NXT_OK; 253 } 254 255 256 nxt_int_t 257 nxt_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 310 nxt_int_t 311 nxt_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 448 #endif 449