1 2 /* 3 * Copyright (C) Igor Sysoev 4 * Copyright (C) NGINX, Inc. 5 */ 6 7 #include <nxt_main.h> 8 9 10 static nxt_int_t nxt_user_groups_get(nxt_user_cred_t *uc); 11 12 13 /* A cached process pid. */ 14 nxt_pid_t nxt_pid; 15 16 /* An original parent process pid. */ 17 nxt_pid_t nxt_ppid; 18 19 20 nxt_pid_t 21 nxt_process_create(nxt_process_start_t start, void *data, const char *name) 22 { 23 nxt_pid_t pid; 24 nxt_thread_t *thr; 25 26 thr = nxt_thread(); 27 28 pid = fork(); 29 30 switch (pid) { 31 32 case -1: 33 nxt_log_alert(thr->log, "fork() failed while creating \"%s\" %E", 34 name, nxt_errno); 35 break; 36 37 case 0: 38 /* A child. */ 39 nxt_pid = getpid(); 40 41 /* Clean inherited cached thread tid. */ 42 thr->tid = 0; 43 44 start(data); 45 break; 46 47 default: 48 /* A parent. */ 49 nxt_log_debug(thr->log, "fork(): %PI", pid); 50 break; 51 } 52 53 return pid; 54 } 55 56 57 #if (NXT_HAVE_POSIX_SPAWN) 58 59 /* 60 * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve(). 61 * Linux glibc 2.4 posix_spawn() without file actions and spawn 62 * attributes uses vfork()/execve(). 63 * 64 * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve(). 65 * 66 * Solaris 10: 67 * In the Solaris 10 OS, posix_spawn() is currently implemented using 68 * private-to-libc vfork(), execve(), and exit() functions. They are 69 * identical to regular vfork(), execve(), and exit() in functionality, 70 * but they are not exported from libc and therefore don't cause the 71 * deadlock-in-the-dynamic-linker problem that any multithreaded code 72 * outside of libc that calls vfork() can cause. 73 * 74 * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented 75 * as syscall. 76 */ 77 78 nxt_pid_t 79 nxt_process_execute(char *name, char **argv, char **envp) 80 { 81 nxt_pid_t pid; 82 83 nxt_thread_log_debug("posix_spawn(\"%s\")", name); 84 85 if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) { 86 nxt_thread_log_alert("posix_spawn(\"%s\") failed %E", name, nxt_errno); 87 return -1; 88 } 89 90 return pid; 91 } 92 93 #else 94 95 nxt_pid_t 96 nxt_process_execute(char *name, char **argv, char **envp) 97 { 98 nxt_pid_t pid; 99 100 /* 101 * vfork() is better than fork() because: 102 * it is faster several times; 103 * its execution time does not depend on private memory mapping size; 104 * it has lesser chances to fail due to the ENOMEM error. 105 */ 106 107 pid = vfork(); 108 109 switch (pid) { 110 111 case -1: 112 nxt_thread_log_alert("vfork() failed while executing \"%s\" %E", 113 name, nxt_errno); 114 break; 115 116 case 0: 117 /* A child. */ 118 nxt_thread_log_debug("execve(\"%s\")", name); 119 120 (void) execve(name, argv, envp); 121 122 nxt_thread_log_alert("execve(\"%s\") failed %E", name, nxt_errno); 123 124 exit(1); 125 break; 126 127 default: 128 /* A parent. */ 129 nxt_thread_log_debug("vfork(): %PI", pid); 130 break; 131 } 132 133 return pid; 134 } 135 136 #endif 137 138 139 nxt_int_t 140 nxt_process_daemon(void) 141 { 142 nxt_fd_t fd; 143 nxt_pid_t pid; 144 const char *msg; 145 nxt_thread_t *thr; 146 147 thr = nxt_thread(); 148 149 /* 150 * fork() followed by a parent process's exit() detaches a child process 151 * from an init script or terminal shell process which has started the 152 * parent process and allows the child process to run in background. 153 */ 154 155 pid = fork(); 156 157 switch (pid) { 158 159 case -1: 160 msg = "fork() failed %E"; 161 goto fail; 162 163 case 0: 164 /* A child. */ 165 break; 166 167 default: 168 /* A parent. */ 169 nxt_log_debug(thr->log, "fork(): %PI", pid); 170 exit(0); 171 nxt_unreachable(); 172 } 173 174 nxt_pid = getpid(); 175 176 /* Clean inherited cached thread tid. */ 177 thr->tid = 0; 178 179 nxt_log_debug(thr->log, "daemon"); 180 181 /* Detach from controlling terminal. */ 182 183 if (setsid() == -1) { 184 nxt_log_emerg(thr->log, "setsid() failed %E", nxt_errno); 185 return NXT_ERROR; 186 } 187 188 /* 189 * Reset file mode creation mask: any access 190 * rights can be set on file creation. 191 */ 192 umask(0); 193 194 /* Redirect STDIN and STDOUT to the "/dev/null". */ 195 196 fd = open("/dev/null", O_RDWR); 197 if (fd == -1) { 198 msg = "open(\"/dev/null\") failed %E"; 199 goto fail; 200 } 201 202 if (dup2(fd, STDIN_FILENO) == -1) { 203 msg = "dup2(\"/dev/null\", STDIN) failed %E"; 204 goto fail; 205 } 206 207 if (dup2(fd, STDOUT_FILENO) == -1) { 208 msg = "dup2(\"/dev/null\", STDOUT) failed %E"; 209 goto fail; 210 } 211 212 if (fd > STDERR_FILENO) { 213 nxt_fd_close(fd); 214 } 215 216 return NXT_OK; 217 218 fail: 219 220 nxt_log_emerg(thr->log, msg, nxt_errno); 221 222 return NXT_ERROR; 223 } 224 225 226 void 227 nxt_nanosleep(nxt_nsec_t ns) 228 { 229 struct timespec ts; 230 231 ts.tv_sec = ns / 1000000000; 232 ts.tv_nsec = ns % 1000000000; 233 234 (void) nanosleep(&ts, NULL); 235 } 236 237 238 nxt_int_t 239 nxt_user_cred_get(nxt_user_cred_t *uc, const char *group) 240 { 241 struct group *grp; 242 struct passwd *pwd; 243 244 pwd = getpwnam(uc->user); 245 246 if (nxt_slow_path(pwd == NULL)) { 247 nxt_thread_log_emerg("getpwnam(%s) failed %E", uc->user, nxt_errno); 248 return NXT_ERROR; 249 } 250 251 uc->uid = pwd->pw_uid; 252 uc->base_gid = pwd->pw_gid; 253 254 if (group != NULL) { 255 grp = getgrnam(group); 256 257 if (nxt_slow_path(grp == NULL)) { 258 nxt_thread_log_emerg("getgrnam(%s) failed %E", group, nxt_errno); 259 return NXT_ERROR; 260 } 261 262 uc->base_gid = grp->gr_gid; 263 } 264 265 if (getuid() == 0) { 266 return nxt_user_groups_get(uc); 267 } 268 269 return NXT_OK; 270 } 271 272 273 /* 274 * nxt_user_groups_get() stores an array of groups IDs which should be 275 * set by the initgroups() function for a given user. The initgroups() 276 * may block a just forked worker process for some time if LDAP or NDIS+ 277 * is used, so nxt_user_groups_get() allows to get worker user groups in 278 * master process. In a nutshell the initgroups() calls getgrouplist() 279 * followed by setgroups(). However Solaris lacks the getgrouplist(). 280 * Besides getgrouplist() does not allow to query the exact number of 281 * groups while NGROUPS_MAX can be quite large (e.g. 65536 on Linux). 282 * So nxt_user_groups_get() emulates getgrouplist(): at first the function 283 * saves the super-user groups IDs, then calls initgroups() and saves the 284 * specified user groups IDs, and then restores the super-user groups IDs. 285 * This works at least on Linux, FreeBSD, and Solaris, but does not work 286 * on MacOSX, getgroups(2): 287 * 288 * To provide compatibility with applications that use getgroups() in 289 * environments where users may be in more than {NGROUPS_MAX} groups, 290 * a variant of getgroups(), obtained when compiling with either the 291 * macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can 292 * be used that is not limited to {NGROUPS_MAX} groups. However, this 293 * variant only returns the user's default group access list and not 294 * the group list modified by a call to setgroups(2). 295 * 296 * For such cases initgroups() is used in worker process as fallback. 297 */ 298 299 static nxt_int_t 300 nxt_user_groups_get(nxt_user_cred_t *uc) 301 { 302 int nsaved, ngroups; 303 nxt_int_t ret; 304 nxt_gid_t *saved; 305 306 nsaved = getgroups(0, NULL); 307 308 if (nsaved == -1) { 309 nxt_thread_log_emerg("getgroups(0, NULL) failed %E", nxt_errno); 310 return NXT_ERROR; 311 } 312 313 nxt_thread_log_debug("getgroups(0, NULL): %d", nsaved); 314 315 if (nsaved > NGROUPS_MAX) { 316 /* MacOSX case. */ 317 return NXT_OK; 318 } 319 320 saved = nxt_malloc(nsaved * sizeof(nxt_gid_t)); 321 322 if (saved == NULL) { 323 return NXT_ERROR; 324 } 325 326 ret = NXT_ERROR; 327 328 nsaved = getgroups(nsaved, saved); 329 330 if (nsaved == -1) { 331 nxt_thread_log_emerg("getgroups(%d) failed %E", nsaved, nxt_errno); 332 goto fail; 333 } 334 335 nxt_thread_log_debug("getgroups(): %d", nsaved); 336 337 if (initgroups(uc->user, uc->base_gid) != 0) { 338 nxt_thread_log_emerg("initgroups(%s, %d) failed", 339 uc->user, uc->base_gid); 340 goto restore; 341 } 342 343 ngroups = getgroups(0, NULL); 344 345 if (ngroups == -1) { 346 nxt_thread_log_emerg("getgroups(0, NULL) failed %E", nxt_errno); 347 goto restore; 348 } 349 350 nxt_thread_log_debug("getgroups(0, NULL): %d", ngroups); 351 352 uc->gids = nxt_malloc(ngroups * sizeof(nxt_gid_t)); 353 354 if (uc->gids == NULL) { 355 goto restore; 356 } 357 358 ngroups = getgroups(ngroups, uc->gids); 359 360 if (ngroups == -1) { 361 nxt_thread_log_emerg("getgroups(%d) failed %E", ngroups, nxt_errno); 362 goto restore; 363 } 364 365 uc->ngroups = ngroups; 366 367 #if (NXT_DEBUG) 368 { 369 u_char *p, *end; 370 nxt_uint_t i; 371 u_char msg[NXT_MAX_ERROR_STR]; 372 373 p = msg; 374 end = msg + NXT_MAX_ERROR_STR; 375 376 for (i = 0; i < uc->ngroups; i++) { 377 p = nxt_sprintf(p, end, "%uL:", (uint64_t) uc->gids[i]); 378 } 379 380 nxt_thread_log_debug("user \"%s\" cred: uid:%uL base gid:%uL, gids:%*s", 381 uc->user, (uint64_t) uc->uid, 382 (uint64_t) uc->base_gid, p - msg, msg); 383 } 384 #endif 385 386 ret = NXT_OK; 387 388 restore: 389 390 if (setgroups(nsaved, saved) != 0) { 391 nxt_thread_log_emerg("setgroups(%d) failed %E", nsaved, nxt_errno); 392 ret = NXT_ERROR; 393 } 394 395 fail: 396 397 nxt_free(saved); 398 399 return ret; 400 } 401 402 403 nxt_int_t 404 nxt_user_cred_set(nxt_user_cred_t *uc) 405 { 406 nxt_thread_log_debug("user cred set: \"%s\" uid:%uL base gid:%uL", 407 uc->user, (uint64_t) uc->uid, uc->base_gid); 408 409 if (setgid(uc->base_gid) != 0) { 410 nxt_thread_log_emerg("setgid(%d) failed %E", uc->base_gid, nxt_errno); 411 return NXT_ERROR; 412 } 413 414 if (uc->gids != NULL) { 415 if (setgroups(uc->ngroups, uc->gids) != 0) { 416 nxt_thread_log_emerg("setgroups(%i) failed %E", 417 uc->ngroups, nxt_errno); 418 return NXT_ERROR; 419 } 420 421 } else { 422 /* MacOSX fallback. */ 423 if (initgroups(uc->user, uc->base_gid) != 0) { 424 nxt_thread_log_emerg("initgroups(%s, %d) failed", 425 uc->user, uc->base_gid); 426 return NXT_ERROR; 427 } 428 } 429 430 if (setuid(uc->uid) != 0) { 431 nxt_thread_log_emerg("setuid(%d) failed %E", uc->uid, nxt_errno); 432 return NXT_ERROR; 433 } 434 435 return NXT_OK; 436 } 437