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