1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "ap_config.h" 18#define CORE_PRIVATE 19#include "httpd.h" 20#include "http_config.h" 21#include "http_main.h" 22#include "http_log.h" 23#include "unixd.h" 24#include "mpm_common.h" 25#include "os.h" 26#include "ap_mpm.h" 27#include "apr_thread_proc.h" 28#include "apr_strings.h" 29#include "apr_portable.h" 30#ifdef HAVE_PWD_H 31#include <pwd.h> 32#endif 33#ifdef HAVE_SYS_RESOURCE_H 34#include <sys/resource.h> 35#endif 36/* XXX */ 37#include <sys/stat.h> 38#ifdef HAVE_UNISTD_H 39#include <unistd.h> 40#endif 41#ifdef HAVE_GRP_H 42#include <grp.h> 43#endif 44#ifdef HAVE_STRINGS_H 45#include <strings.h> 46#endif 47#ifdef HAVE_SYS_SEM_H 48#include <sys/sem.h> 49#endif 50#ifdef HAVE_SYS_PRCTL_H 51#include <sys/prctl.h> 52#endif 53 54unixd_config_rec unixd_config; 55 56/* Set group privileges. 57 * 58 * Note that we use the username as set in the config files, rather than 59 * the lookup of to uid --- the same uid may have multiple passwd entries, 60 * with different sets of groups for each. 61 */ 62 63static int set_group_privs(void) 64{ 65 if (!geteuid()) { 66 const char *name; 67 68 /* Get username if passed as a uid */ 69 70 if (unixd_config.user_name[0] == '#') { 71 struct passwd *ent; 72 uid_t uid = atoi(&unixd_config.user_name[1]); 73 74 if ((ent = getpwuid(uid)) == NULL) { 75 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 76 "getpwuid: couldn't determine user name from uid %u, " 77 "you probably need to modify the User directive", 78 (unsigned)uid); 79 return -1; 80 } 81 82 name = ent->pw_name; 83 } 84 else 85 name = unixd_config.user_name; 86 87#if !defined(OS2) && !defined(TPF) 88 /* OS/2 and TPF don't support groups. */ 89 90 /* 91 * Set the GID before initgroups(), since on some platforms 92 * setgid() is known to zap the group list. 93 */ 94 if (setgid(unixd_config.group_id) == -1) { 95 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 96 "setgid: unable to set group id to Group %u", 97 (unsigned)unixd_config.group_id); 98 return -1; 99 } 100 101 /* Reset `groups' attributes. */ 102 103 if (initgroups(name, unixd_config.group_id) == -1) { 104 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 105 "initgroups: unable to set groups for User %s " 106 "and Group %u", name, (unsigned)unixd_config.group_id); 107 return -1; 108 } 109#endif /* !defined(OS2) && !defined(TPF) */ 110 } 111 return 0; 112} 113 114 115AP_DECLARE(int) unixd_setup_child(void) 116{ 117 if (set_group_privs()) { 118 return -1; 119 } 120 121 if (NULL != unixd_config.chroot_dir) { 122 if (geteuid()) { 123 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 124 "Cannot chroot when not started as root"); 125 return -1; 126 } 127 if (chdir(unixd_config.chroot_dir) != 0) { 128 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 129 "Can't chdir to %s", unixd_config.chroot_dir); 130 return -1; 131 } 132 if (chroot(unixd_config.chroot_dir) != 0) { 133 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 134 "Can't chroot to %s", unixd_config.chroot_dir); 135 return -1; 136 } 137 if (chdir("/") != 0) { 138 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 139 "Can't chdir to new root"); 140 return -1; 141 } 142 } 143 144#ifdef MPE 145 /* Only try to switch if we're running as MANAGER.SYS */ 146 if (geteuid() == 1 && unixd_config.user_id > 1) { 147 GETPRIVMODE(); 148 if (setuid(unixd_config.user_id) == -1) { 149 GETUSERMODE(); 150 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 151 "setuid: unable to change to uid: %ld", 152 (long) unixd_config.user_id); 153 exit(1); 154 } 155 GETUSERMODE(); 156 } 157#else 158 /* Only try to switch if we're running as root */ 159 if (!geteuid() && ( 160#ifdef _OSD_POSIX 161 os_init_job_environment(NULL, unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 || 162#endif 163 setuid(unixd_config.user_id) == -1)) { 164 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 165 "setuid: unable to change to uid: %ld", 166 (long) unixd_config.user_id); 167 return -1; 168 } 169#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) 170 /* this applies to Linux 2.4+ */ 171#ifdef AP_MPM_WANT_SET_COREDUMPDIR 172 if (ap_coredumpdir_configured) { 173 if (prctl(PR_SET_DUMPABLE, 1)) { 174 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, 175 "set dumpable failed - this child will not coredump" 176 " after software errors"); 177 } 178 } 179#endif 180#endif 181#endif 182 return 0; 183} 184 185 186AP_DECLARE(const char *) unixd_set_user(cmd_parms *cmd, void *dummy, 187 const char *arg) 188{ 189 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); 190 if (err != NULL) { 191 return err; 192 } 193 194 unixd_config.user_name = arg; 195 unixd_config.user_id = ap_uname2id(arg); 196#if !defined (BIG_SECURITY_HOLE) && !defined (OS2) 197 if (unixd_config.user_id == 0) { 198 return "Error:\tApache has not been designed to serve pages while\n" 199 "\trunning as root. There are known race conditions that\n" 200 "\twill allow any local user to read any file on the system.\n" 201 "\tIf you still desire to serve pages as root then\n" 202 "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n" 203 "\tand then rebuild the server.\n" 204 "\tIt is strongly suggested that you instead modify the User\n" 205 "\tdirective in your httpd.conf file to list a non-root\n" 206 "\tuser.\n"; 207 } 208#endif 209 210 return NULL; 211} 212 213AP_DECLARE(const char *) unixd_set_group(cmd_parms *cmd, void *dummy, 214 const char *arg) 215{ 216 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); 217 if (err != NULL) { 218 return err; 219 } 220 221 unixd_config.group_id = ap_gname2id(arg); 222 223 return NULL; 224} 225AP_DECLARE(const char *) unixd_set_chroot_dir(cmd_parms *cmd, void *dummy, 226 const char *arg) 227{ 228 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); 229 if (err != NULL) { 230 return err; 231 } 232 if (!ap_is_directory(cmd->pool, arg)) { 233 return "ChrootDir must be a valid directory"; 234 } 235 236 unixd_config.chroot_dir = arg; 237 return NULL; 238} 239 240AP_DECLARE(const char *) unixd_set_suexec(cmd_parms *cmd, void *dummy, 241 int arg) 242{ 243 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); 244 if (err != NULL) { 245 return err; 246 } 247 248 if (!unixd_config.suexec_enabled && arg) { 249 return "suEXEC isn't supported; check existence, owner, and " 250 "file mode of " SUEXEC_BIN; 251 } 252 253 unixd_config.suexec_enabled = arg; 254 return NULL; 255} 256 257AP_DECLARE(void) unixd_pre_config(apr_pool_t *ptemp) 258{ 259 apr_finfo_t wrapper; 260 261 unixd_config.user_name = DEFAULT_USER; 262 unixd_config.user_id = ap_uname2id(DEFAULT_USER); 263 unixd_config.group_id = ap_gname2id(DEFAULT_GROUP); 264 265 unixd_config.chroot_dir = NULL; /* none */ 266 267 /* Check for suexec */ 268 unixd_config.suexec_enabled = 0; 269 if ((apr_stat(&wrapper, SUEXEC_BIN, 270 APR_FINFO_NORM, ptemp)) != APR_SUCCESS) { 271 return; 272 } 273 274 if ((wrapper.protection & APR_USETID) && wrapper.user == 0) { 275 unixd_config.suexec_enabled = 1; 276 } 277} 278 279 280AP_DECLARE(void) unixd_set_rlimit(cmd_parms *cmd, struct rlimit **plimit, 281 const char *arg, const char * arg2, int type) 282{ 283#if (defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS)) && APR_HAVE_STRUCT_RLIMIT && APR_HAVE_GETRLIMIT 284 char *str; 285 struct rlimit *limit; 286 /* If your platform doesn't define rlim_t then typedef it in ap_config.h */ 287 rlim_t cur = 0; 288 rlim_t max = 0; 289 290 *plimit = (struct rlimit *)apr_pcalloc(cmd->pool, sizeof(**plimit)); 291 limit = *plimit; 292 if ((getrlimit(type, limit)) != 0) { 293 *plimit = NULL; 294 ap_log_error(APLOG_MARK, APLOG_ERR, errno, cmd->server, 295 "%s: getrlimit failed", cmd->cmd->name); 296 return; 297 } 298 299 if ((str = ap_getword_conf(cmd->pool, &arg))) { 300 if (!strcasecmp(str, "max")) { 301 cur = limit->rlim_max; 302 } 303 else { 304 cur = atol(str); 305 } 306 } 307 else { 308 ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server, 309 "Invalid parameters for %s", cmd->cmd->name); 310 return; 311 } 312 313 if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) { 314 max = atol(str); 315 } 316 317 /* if we aren't running as root, cannot increase max */ 318 if (geteuid()) { 319 limit->rlim_cur = cur; 320 if (max) { 321 ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server, 322 "Must be uid 0 to raise maximum %s", cmd->cmd->name); 323 } 324 } 325 else { 326 if (cur) { 327 limit->rlim_cur = cur; 328 } 329 if (max) { 330 limit->rlim_max = max; 331 } 332 } 333#else 334 335 ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server, 336 "Platform does not support rlimit for %s", cmd->cmd->name); 337#endif 338} 339 340APR_HOOK_STRUCT( 341 APR_HOOK_LINK(get_suexec_identity) 342) 343 344AP_IMPLEMENT_HOOK_RUN_FIRST(ap_unix_identity_t *, get_suexec_identity, 345 (const request_rec *r), (r), NULL) 346 347static apr_status_t ap_unix_create_privileged_process( 348 apr_proc_t *newproc, const char *progname, 349 const char * const *args, 350 const char * const *env, 351 apr_procattr_t *attr, ap_unix_identity_t *ugid, 352 apr_pool_t *p) 353{ 354 int i = 0; 355 const char **newargs; 356 char *newprogname; 357 char *execuser, *execgroup; 358 const char *argv0; 359 360 if (!unixd_config.suexec_enabled) { 361 return apr_proc_create(newproc, progname, args, env, attr, p); 362 } 363 364 argv0 = ap_strrchr_c(progname, '/'); 365 /* Allow suexec's "/" check to succeed */ 366 if (argv0 != NULL) { 367 argv0++; 368 } 369 else { 370 argv0 = progname; 371 } 372 373 374 if (ugid->userdir) { 375 execuser = apr_psprintf(p, "~%ld", (long) ugid->uid); 376 } 377 else { 378 execuser = apr_psprintf(p, "%ld", (long) ugid->uid); 379 } 380 execgroup = apr_psprintf(p, "%ld", (long) ugid->gid); 381 382 if (!execuser || !execgroup) { 383 return APR_ENOMEM; 384 } 385 386 i = 0; 387 if (args) { 388 while (args[i]) { 389 i++; 390 } 391 } 392 /* allocate space for 4 new args, the input args, and a null terminator */ 393 newargs = apr_palloc(p, sizeof(char *) * (i + 4)); 394 newprogname = SUEXEC_BIN; 395 newargs[0] = SUEXEC_BIN; 396 newargs[1] = execuser; 397 newargs[2] = execgroup; 398 newargs[3] = apr_pstrdup(p, argv0); 399 400 /* 401 ** using a shell to execute suexec makes no sense thus 402 ** we force everything to be APR_PROGRAM, and never 403 ** APR_SHELLCMD 404 */ 405 if(apr_procattr_cmdtype_set(attr, APR_PROGRAM) != APR_SUCCESS) { 406 return APR_EGENERAL; 407 } 408 409 i = 1; 410 do { 411 newargs[i + 3] = args[i]; 412 } while (args[i++]); 413 414 return apr_proc_create(newproc, newprogname, newargs, env, attr, p); 415} 416 417AP_DECLARE(apr_status_t) ap_os_create_privileged_process( 418 const request_rec *r, 419 apr_proc_t *newproc, const char *progname, 420 const char * const *args, 421 const char * const *env, 422 apr_procattr_t *attr, apr_pool_t *p) 423{ 424 ap_unix_identity_t *ugid = ap_run_get_suexec_identity(r); 425 426 if (ugid == NULL) { 427 return apr_proc_create(newproc, progname, args, env, attr, p); 428 } 429 430 return ap_unix_create_privileged_process(newproc, progname, args, env, 431 attr, ugid, p); 432} 433 434/* XXX move to APR and externalize (but implement differently :) ) */ 435static apr_lockmech_e proc_mutex_mech(apr_proc_mutex_t *pmutex) 436{ 437 const char *mechname = apr_proc_mutex_name(pmutex); 438 439 if (!strcmp(mechname, "sysvsem")) { 440 return APR_LOCK_SYSVSEM; 441 } 442 else if (!strcmp(mechname, "flock")) { 443 return APR_LOCK_FLOCK; 444 } 445 return APR_LOCK_DEFAULT; 446} 447 448AP_DECLARE(apr_status_t) unixd_set_proc_mutex_perms(apr_proc_mutex_t *pmutex) 449{ 450 if (!geteuid()) { 451 apr_lockmech_e mech = proc_mutex_mech(pmutex); 452 453 switch(mech) { 454#if APR_HAS_SYSVSEM_SERIALIZE 455 case APR_LOCK_SYSVSEM: 456 { 457 apr_os_proc_mutex_t ospmutex; 458#if !APR_HAVE_UNION_SEMUN 459 union semun { 460 long val; 461 struct semid_ds *buf; 462 unsigned short *array; 463 }; 464#endif 465 union semun ick; 466 struct semid_ds buf; 467 468 apr_os_proc_mutex_get(&ospmutex, pmutex); 469 buf.sem_perm.uid = unixd_config.user_id; 470 buf.sem_perm.gid = unixd_config.group_id; 471 buf.sem_perm.mode = 0600; 472 ick.buf = &buf; 473 if (semctl(ospmutex.crossproc, 0, IPC_SET, ick) < 0) { 474 return errno; 475 } 476 } 477 break; 478#endif 479#if APR_HAS_FLOCK_SERIALIZE 480 case APR_LOCK_FLOCK: 481 { 482 const char *lockfile = apr_proc_mutex_lockfile(pmutex); 483 484 if (lockfile) { 485 if (chown(lockfile, unixd_config.user_id, 486 -1 /* no gid change */) < 0) { 487 return errno; 488 } 489 } 490 } 491 break; 492#endif 493 default: 494 /* do nothing */ 495 break; 496 } 497 } 498 return APR_SUCCESS; 499} 500 501AP_DECLARE(apr_status_t) unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex) 502{ 503#if !APR_PROC_MUTEX_IS_GLOBAL 504 apr_os_global_mutex_t osgmutex; 505 apr_os_global_mutex_get(&osgmutex, gmutex); 506 return unixd_set_proc_mutex_perms(osgmutex.proc_mutex); 507#else /* APR_PROC_MUTEX_IS_GLOBAL */ 508 /* In this case, apr_proc_mutex_t and apr_global_mutex_t are the same. */ 509 return unixd_set_proc_mutex_perms(gmutex); 510#endif /* APR_PROC_MUTEX_IS_GLOBAL */ 511} 512 513AP_DECLARE(apr_status_t) unixd_accept(void **accepted, ap_listen_rec *lr, 514 apr_pool_t *ptrans) 515{ 516 apr_socket_t *csd; 517 apr_status_t status; 518#ifdef _OSD_POSIX 519 int sockdes; 520#endif 521 522 *accepted = NULL; 523 status = apr_socket_accept(&csd, lr->sd, ptrans); 524 if (status == APR_SUCCESS) { 525 *accepted = csd; 526#ifdef _OSD_POSIX 527 apr_os_sock_get(&sockdes, csd); 528 if (sockdes >= FD_SETSIZE) { 529 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, 530 "new file descriptor %d is too large; you probably need " 531 "to rebuild Apache with a larger FD_SETSIZE " 532 "(currently %d)", 533 sockdes, FD_SETSIZE); 534 apr_socket_close(csd); 535 return APR_EINTR; 536 } 537#endif 538 return APR_SUCCESS; 539 } 540 541 if (APR_STATUS_IS_EINTR(status)) { 542 return status; 543 } 544 /* Our old behaviour here was to continue after accept() 545 * errors. But this leads us into lots of troubles 546 * because most of the errors are quite fatal. For 547 * example, EMFILE can be caused by slow descriptor 548 * leaks (say in a 3rd party module, or libc). It's 549 * foolish for us to continue after an EMFILE. We also 550 * seem to tickle kernel bugs on some platforms which 551 * lead to never-ending loops here. So it seems best 552 * to just exit in most cases. 553 */ 554 switch (status) { 555#if defined(HPUX11) && defined(ENOBUFS) 556 /* On HPUX 11.x, the 'ENOBUFS, No buffer space available' 557 * error occurs because the accept() cannot complete. 558 * You will not see ENOBUFS with 10.20 because the kernel 559 * hides any occurrence from being returned to user space. 560 * ENOBUFS with 11.x's TCP/IP stack is possible, and could 561 * occur intermittently. As a work-around, we are going to 562 * ignore ENOBUFS. 563 */ 564 case ENOBUFS: 565#endif 566 567#ifdef EPROTO 568 /* EPROTO on certain older kernels really means 569 * ECONNABORTED, so we need to ignore it for them. 570 * See discussion in new-httpd archives nh.9701 571 * search for EPROTO. 572 * 573 * Also see nh.9603, search for EPROTO: 574 * There is potentially a bug in Solaris 2.x x<6, 575 * and other boxes that implement tcp sockets in 576 * userland (i.e. on top of STREAMS). On these 577 * systems, EPROTO can actually result in a fatal 578 * loop. See PR#981 for example. It's hard to 579 * handle both uses of EPROTO. 580 */ 581 case EPROTO: 582#endif 583#ifdef ECONNABORTED 584 case ECONNABORTED: 585#endif 586 /* Linux generates the rest of these, other tcp 587 * stacks (i.e. bsd) tend to hide them behind 588 * getsockopt() interfaces. They occur when 589 * the net goes sour or the client disconnects 590 * after the three-way handshake has been done 591 * in the kernel but before userland has picked 592 * up the socket. 593 */ 594#ifdef ECONNRESET 595 case ECONNRESET: 596#endif 597#ifdef ETIMEDOUT 598 case ETIMEDOUT: 599#endif 600#ifdef EHOSTUNREACH 601 case EHOSTUNREACH: 602#endif 603#ifdef ENETUNREACH 604 case ENETUNREACH: 605#endif 606 /* EAGAIN/EWOULDBLOCK can be returned on BSD-derived 607 * TCP stacks when the connection is aborted before 608 * we call connect, but only because our listener 609 * sockets are non-blocking (AP_NONBLOCK_WHEN_MULTI_LISTEN) 610 */ 611#ifdef EAGAIN 612 case EAGAIN: 613#endif 614#ifdef EWOULDBLOCK 615#if !defined(EAGAIN) || EAGAIN != EWOULDBLOCK 616 case EWOULDBLOCK: 617#endif 618#endif 619 break; 620#ifdef ENETDOWN 621 case ENETDOWN: 622 /* 623 * When the network layer has been shut down, there 624 * is not much use in simply exiting: the parent 625 * would simply re-create us (and we'd fail again). 626 * Use the CHILDFATAL code to tear the server down. 627 * @@@ Martin's idea for possible improvement: 628 * A different approach would be to define 629 * a new APEXIT_NETDOWN exit code, the reception 630 * of which would make the parent shutdown all 631 * children, then idle-loop until it detected that 632 * the network is up again, and restart the children. 633 * Ben Hyde noted that temporary ENETDOWN situations 634 * occur in mobile IP. 635 */ 636 ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf, 637 "apr_socket_accept: giving up."); 638 return APR_EGENERAL; 639#endif /*ENETDOWN*/ 640 641#ifdef TPF 642 case EINACT: 643 ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf, 644 "offload device inactive"); 645 return APR_EGENERAL; 646 break; 647 default: 648 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, 649 "select/accept error (%d)", status); 650 return APR_EGENERAL; 651#else 652 default: 653 ap_log_error(APLOG_MARK, APLOG_ERR, status, ap_server_conf, 654 "apr_socket_accept: (client socket)"); 655 return APR_EGENERAL; 656#endif 657 } 658 return status; 659} 660 661 662#ifdef _OSD_POSIX 663 664#include "apr_lib.h" 665 666#define USER_LEN 8 667 668typedef enum 669{ 670 bs2_unknown, /* not initialized yet. */ 671 bs2_noFORK, /* no fork() because -X flag was specified */ 672 bs2_FORK, /* only fork() because uid != 0 */ 673 bs2_UFORK /* Normally, ufork() is used to switch identities. */ 674} bs2_ForkType; 675 676static bs2_ForkType forktype = bs2_unknown; 677 678 679static void ap_str_toupper(char *str) 680{ 681 while (*str) { 682 *str = apr_toupper(*str); 683 ++str; 684 } 685} 686 687/* Determine the method for forking off a child in such a way as to 688 * set both the POSIX and BS2000 user id's to the unprivileged user. 689 */ 690static bs2_ForkType os_forktype(int one_process) 691{ 692 /* have we checked the OS version before? If yes return the previous 693 * result - the OS release isn't going to change suddenly! 694 */ 695 if (forktype == bs2_unknown) { 696 /* not initialized yet */ 697 698 /* No fork if the one_process option was set */ 699 if (one_process) { 700 forktype = bs2_noFORK; 701 } 702 /* If the user is unprivileged, use the normal fork() only. */ 703 else if (getuid() != 0) { 704 forktype = bs2_FORK; 705 } 706 else 707 forktype = bs2_UFORK; 708 } 709 return forktype; 710} 711 712 713 714/* This routine complements the setuid() call: it causes the BS2000 job 715 * environment to be switched to the target user's user id. 716 * That is important if CGI scripts try to execute native BS2000 commands. 717 */ 718int os_init_job_environment(server_rec *server, const char *user_name, int one_process) 719{ 720 bs2_ForkType type = os_forktype(one_process); 721 722 /* We can be sure that no change to uid==0 is possible because of 723 * the checks in http_core.c:set_user() 724 */ 725 726 if (one_process) { 727 728 type = forktype = bs2_noFORK; 729 730 ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, 731 "The debug mode of Apache should only " 732 "be started by an unprivileged user!"); 733 return 0; 734 } 735 736 return 0; 737} 738 739/* BS2000 requires a "special" version of fork() before a setuid() call */ 740pid_t os_fork(const char *user) 741{ 742 pid_t pid; 743 char username[USER_LEN+1]; 744 745 switch (os_forktype(0)) { 746 747 case bs2_FORK: 748 pid = fork(); 749 break; 750 751 case bs2_UFORK: 752 apr_cpystrn(username, user, sizeof username); 753 754 /* Make user name all upper case - for some versions of ufork() */ 755 ap_str_toupper(username); 756 757 pid = ufork(username); 758 if (pid == -1 && errno == EPERM) { 759 ap_log_error(APLOG_MARK, APLOG_EMERG, errno, 760 NULL, "ufork: Possible mis-configuration " 761 "for user %s - Aborting.", user); 762 exit(1); 763 } 764 break; 765 766 default: 767 pid = 0; 768 break; 769 } 770 771 return pid; 772} 773 774#endif /* _OSD_POSIX */ 775 776