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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "ap_config.h"
19 #include "http_config.h"
20 #include "http_main.h"
23 #include "mpm_common.h"
26 #include "apr_thread_proc.h"
27 #include "apr_strings.h"
28 #include "apr_portable.h"
32 #ifdef HAVE_SYS_RESOURCE_H
33 #include <sys/resource.h>
49 #ifdef HAVE_SYS_PRCTL_H
50 #include <sys/prctl.h>
53 unixd_config_rec unixd_config;
55 /* Set group privileges.
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.
62 static int set_group_privs(void)
67 /* Get username if passed as a uid */
69 if (unixd_config.user_name[0] == '#') {
71 uid_t uid = atol(&unixd_config.user_name[1]);
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",
84 name = unixd_config.user_name;
86 #if !defined(OS2) && !defined(TPF)
87 /* OS/2 and TPF don't support groups. */
90 * Set the GID before initgroups(), since on some platforms
91 * setgid() is known to zap the group list.
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);
100 /* Reset `groups' attributes. */
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);
108 #endif /* !defined(OS2) && !defined(TPF) */
114 AP_DECLARE(int) unixd_setup_child(void)
116 if (set_group_privs()) {
120 if (NULL != unixd_config.chroot_dir) {
122 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
123 "Cannot chroot when not started as root");
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);
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);
136 if (chdir("/") != 0) {
137 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
138 "Can't chdir to new root");
144 /* Only try to switch if we're running as MANAGER.SYS */
145 if (geteuid() == 1 && unixd_config.user_id > 1) {
147 if (setuid(unixd_config.user_id) == -1) {
149 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
150 "setuid: unable to change to uid: %ld",
151 (long) unixd_config.user_id);
157 /* Only try to switch if we're running as root */
160 os_init_job_environment(NULL, unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
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);
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");
185 AP_DECLARE(const char *) unixd_set_user(cmd_parms *cmd, void *dummy,
188 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
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"
212 AP_DECLARE(const char *) unixd_set_group(cmd_parms *cmd, void *dummy,
215 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
220 unixd_config.group_id = ap_gname2id(arg);
224 AP_DECLARE(const char *) unixd_set_chroot_dir(cmd_parms *cmd, void *dummy,
227 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
231 if (!ap_is_directory(cmd->pool, arg)) {
232 return "ChrootDir must be a valid directory";
235 unixd_config.chroot_dir = arg;
239 AP_DECLARE(void) unixd_pre_config(apr_pool_t *ptemp)
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);
247 unixd_config.chroot_dir = NULL; /* none */
249 /* Check for suexec */
250 unixd_config.suexec_enabled = 0;
251 if ((apr_stat(&wrapper, SUEXEC_BIN,
252 APR_FINFO_NORM, ptemp)) != APR_SUCCESS) {
256 if ((wrapper.protection & APR_USETID) && wrapper.user == 0) {
257 unixd_config.suexec_enabled = 1;
262 AP_DECLARE(void) unixd_set_rlimit(cmd_parms *cmd, struct rlimit **plimit,
263 const char *arg, const char * arg2, int type)
265 #if (defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS)) && APR_HAVE_STRUCT_RLIMIT && APR_HAVE_GETRLIMIT
267 struct rlimit *limit;
268 /* If your platform doesn't define rlim_t then typedef it in ap_config.h */
272 *plimit = (struct rlimit *)apr_pcalloc(cmd->pool, sizeof(**plimit));
274 if ((getrlimit(type, limit)) != 0) {
276 ap_log_error(APLOG_MARK, APLOG_ERR, errno, cmd->server,
277 "%s: getrlimit failed", cmd->cmd->name);
281 if ((str = ap_getword_conf(cmd->pool, &arg))) {
282 if (!strcasecmp(str, "max")) {
283 cur = limit->rlim_max;
290 ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
291 "Invalid parameters for %s", cmd->cmd->name);
295 if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) {
299 /* if we aren't running as root, cannot increase max */
301 limit->rlim_cur = cur;
303 ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
304 "Must be uid 0 to raise maximum %s", cmd->cmd->name);
309 limit->rlim_cur = cur;
312 limit->rlim_max = max;
317 ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
318 "Platform does not support rlimit for %s", cmd->cmd->name);
323 APR_HOOK_LINK(get_suexec_identity)
326 AP_IMPLEMENT_HOOK_RUN_FIRST(ap_unix_identity_t *, get_suexec_identity,
327 (const request_rec *r), (r), NULL)
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,
337 const char **newargs;
339 char *execuser, *execgroup;
342 if (!unixd_config.suexec_enabled) {
343 return apr_proc_create(newproc, progname, args, env, attr, p);
346 argv0 = ap_strrchr_c(progname, '/');
347 /* Allow suexec's "/" check to succeed */
357 execuser = apr_psprintf(p, "~%ld", (long) ugid->uid);
360 execuser = apr_psprintf(p, "%ld", (long) ugid->uid);
362 execgroup = apr_psprintf(p, "%ld", (long) ugid->gid);
364 if (!execuser || !execgroup) {
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);
383 ** using a shell to execute suexec makes no sense thus
384 ** we force everything to be APR_PROGRAM, and never
387 if(apr_procattr_cmdtype_set(attr, APR_PROGRAM) != APR_SUCCESS) {
393 newargs[i + 3] = args[i];
396 return apr_proc_create(newproc, newprogname, newargs, env, attr, p);
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)
406 ap_unix_identity_t *ugid = ap_run_get_suexec_identity(r);
409 return apr_proc_create(newproc, progname, args, env, attr, p);
412 return ap_unix_create_privileged_process(newproc, progname, args, env,
416 /* XXX move to APR and externalize (but implement differently :) ) */
417 static apr_lockmech_e proc_mutex_mech(apr_proc_mutex_t *pmutex)
419 const char *mechname = apr_proc_mutex_name(pmutex);
421 if (!strcmp(mechname, "sysvsem")) {
422 return APR_LOCK_SYSVSEM;
424 else if (!strcmp(mechname, "flock")) {
425 return APR_LOCK_FLOCK;
427 return APR_LOCK_DEFAULT;
430 AP_DECLARE(apr_status_t) unixd_set_proc_mutex_perms(apr_proc_mutex_t *pmutex)
433 apr_lockmech_e mech = proc_mutex_mech(pmutex);
436 #if APR_HAS_SYSVSEM_SERIALIZE
437 case APR_LOCK_SYSVSEM:
439 apr_os_proc_mutex_t ospmutex;
440 #if !APR_HAVE_UNION_SEMUN
443 struct semid_ds *buf;
444 unsigned short *array;
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;
455 if (semctl(ospmutex.crossproc, 0, IPC_SET, ick) < 0) {
461 #if APR_HAS_FLOCK_SERIALIZE
464 const char *lockfile = apr_proc_mutex_lockfile(pmutex);
467 if (chown(lockfile, unixd_config.user_id,
468 -1 /* no gid change */) < 0) {
483 AP_DECLARE(apr_status_t) unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex)
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 */
495 AP_DECLARE(apr_status_t) unixd_accept(void **accepted, ap_listen_rec *lr,
505 status = apr_socket_accept(&csd, lr->sd, ptrans);
506 if (status == APR_SUCCESS) {
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 "
515 sockdes, FD_SETSIZE);
516 apr_socket_close(csd);
523 if (APR_STATUS_IS_EINTR(status)) {
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.
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
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
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.
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
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)
597 #if !defined(EAGAIN) || EAGAIN != EWOULDBLOCK
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.
618 ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf,
619 "apr_socket_accept: giving up.");
625 ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf,
626 "offload device inactive");
630 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
631 "select/accept error (%d)", status);
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.
640 if (!lr->active && status == EBADF)
643 ap_log_error(APLOG_MARK, APLOG_ERR, status, ap_server_conf,
644 "apr_socket_accept: (client socket)");
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. */
666 static bs2_ForkType forktype = bs2_unknown;
669 static void ap_str_toupper(char *str)
672 *str = apr_toupper(*str);
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.
680 static bs2_ForkType os_forktype(int one_process)
682 /* have we checked the OS version before? If yes return the previous
683 * result - the OS release isn't going to change suddenly!
685 if (forktype == bs2_unknown) {
686 /* not initialized yet */
688 /* No fork if the one_process option was set */
690 forktype = bs2_noFORK;
692 /* If the user is unprivileged, use the normal fork() only. */
693 else if (getuid() != 0) {
697 forktype = bs2_UFORK;
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.
708 int os_init_job_environment(server_rec *server, const char *user_name, int one_process)
710 bs2_ForkType type = os_forktype(one_process);
712 /* We can be sure that no change to uid==0 is possible because of
713 * the checks in http_core.c:set_user()
718 type = forktype = bs2_noFORK;
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!");
729 /* BS2000 requires a "special" version of fork() before a setuid() call */
730 pid_t os_fork(const char *user)
733 char username[USER_LEN+1];
735 switch (os_forktype(0)) {
742 apr_cpystrn(username, user, sizeof username);
744 /* Make user name all upper case - for some versions of ufork() */
745 ap_str_toupper(username);
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);
764 #endif /* _OSD_POSIX */