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