1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * ====================================================================
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
54 * Portions of this software are based upon public domain software
55 * originally written at the National Center for Supercomputing Applications,
56 * University of Illinois, Urbana-Champaign.
59 #include "ap_config.h"
62 #include "http_config.h"
63 #include "http_main.h"
66 #include "mpm_common.h"
69 #include "apr_thread_proc.h"
70 #include "apr_strings.h"
74 #ifdef HAVE_SYS_RESOURCE_H
75 #include <sys/resource.h>
89 unixd_config_rec unixd_config;
91 /* Set group privileges.
93 * Note that we use the username as set in the config files, rather than
94 * the lookup of to uid --- the same uid may have multiple passwd entries,
95 * with different sets of groups for each.
98 static int set_group_privs(void)
103 /* Get username if passed as a uid */
105 if (unixd_config.user_name[0] == '#') {
107 uid_t uid = atoi(&unixd_config.user_name[1]);
109 if ((ent = getpwuid(uid)) == NULL) {
110 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
111 "getpwuid: couldn't determine user name from uid %u, "
112 "you probably need to modify the User directive",
120 name = unixd_config.user_name;
122 #if !defined(OS2) && !defined(TPF)
123 /* OS/2 and TPF don't support groups. */
126 * Set the GID before initgroups(), since on some platforms
127 * setgid() is known to zap the group list.
129 if (setgid(unixd_config.group_id) == -1) {
130 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
131 "setgid: unable to set group id to Group %u",
132 (unsigned)unixd_config.group_id);
136 /* Reset `groups' attributes. */
138 if (initgroups(name, unixd_config.group_id) == -1) {
139 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
140 "initgroups: unable to set groups for User %s "
141 "and Group %u", name, (unsigned)unixd_config.group_id);
144 #endif /* !defined(OS2) && !defined(TPF) */
150 int unixd_setup_child(void)
152 if (set_group_privs()) {
156 /* Only try to switch if we're running as MANAGER.SYS */
157 if (geteuid() == 1 && unixd_config.user_id > 1) {
159 if (setuid(unixd_config.user_id) == -1) {
161 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
162 "setuid: unable to change to uid: %ld",
163 (long) unixd_config.user_id);
169 /* Only try to switch if we're running as root */
172 os_init_job_environment(server_conf, unixd_config.user_name, one_process) != 0 ||
174 setuid(unixd_config.user_id) == -1)) {
175 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
176 "setuid: unable to change to uid: %ld",
177 (long) unixd_config.user_id);
185 const char *unixd_set_user(cmd_parms *cmd, void *dummy, const char *arg)
187 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
192 unixd_config.user_name = arg;
193 unixd_config.user_id = ap_uname2id(arg);
194 #if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
195 if (unixd_config.user_id == 0) {
196 return "Error:\tApache has not been designed to serve pages while\n"
197 "\trunning as root. There are known race conditions that\n"
198 "\twill allow any local user to read any file on the system.\n"
199 "\tIf you still desire to serve pages as root then\n"
200 "\tadd -DBIG_SECURITY_HOLE to the EXTRA_CFLAGS line in your\n"
201 "\tsrc/Configuration file and rebuild the server. It is\n"
202 "\tstrongly suggested that you instead modify the User\n"
203 "\tdirective in your httpd.conf file to list a non-root\n"
211 const char *unixd_set_group(cmd_parms *cmd, void *dummy, const char *arg)
213 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
218 unixd_config.group_id = ap_gname2id(arg);
223 void unixd_pre_config(apr_pool_t *ptemp)
227 unixd_config.user_name = DEFAULT_USER;
228 unixd_config.user_id = ap_uname2id(DEFAULT_USER);
229 unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
231 /* Check for suexec */
232 unixd_config.suexec_enabled = 0;
233 if ((apr_stat(&wrapper, SUEXEC_BIN,
234 APR_FINFO_NORM, ptemp)) != APR_SUCCESS) {
238 /* XXX - apr_stat is incapable of checking suid bits (grumble) */
239 /* if ((wrapper.filetype & S_ISUID) && wrapper.user == 0) { */
240 unixd_config.suexec_enabled = 1;
244 #ifdef NEED_AP_SYS_SIGLIST
246 const char *ap_sys_siglist[NumSIG];
248 #define store_str(array,index,string) \
249 (ap_assert(index < (sizeof(array)/sizeof(array[0]))),array[index]=string)
251 void unixd_siglist_init(void)
255 ap_sys_siglist[0] = "Signal 0";
257 store_str(ap_sys_siglist,SIGHUP,"Hangup");
260 store_str(ap_sys_siglist,SIGINT,"Interrupt");
263 store_str(ap_sys_siglist,SIGQUIT,"Quit");
266 store_str(ap_sys_siglist,SIGILL,"Illegal instruction");
269 store_str(ap_sys_siglist,SIGTRAP,"Trace/BPT trap");
272 store_str(ap_sys_siglist,SIGIOT,"IOT instruction");
275 store_str(ap_sys_siglist,SIGABRT,"Abort");
278 store_str(ap_sys_siglist,SIGEMT,"Emulator trap");
281 store_str(ap_sys_siglist,SIGFPE,"Arithmetic exception");
284 store_str(ap_sys_siglist,SIGKILL,"Killed");
287 store_str(ap_sys_siglist,SIGBUS,"Bus error");
290 store_str(ap_sys_siglist,SIGSEGV,"Segmentation fault");
293 store_str(ap_sys_siglist,SIGSYS,"Bad system call");
296 store_str(ap_sys_siglist,SIGPIPE,"Broken pipe");
299 store_str(ap_sys_siglist,SIGALRM,"Alarm clock");
302 store_str(ap_sys_siglist,SIGTERM,"Terminated");
305 store_str(ap_sys_siglist,SIGUSR1,"User defined signal 1");
308 store_str(ap_sys_siglist,SIGUSR2,"User defined signal 2");
311 store_str(ap_sys_siglist,SIGCLD,"Child status change");
314 store_str(ap_sys_siglist,SIGCHLD,"Child status change");
317 store_str(ap_sys_siglist,SIGPWR,"Power-fail restart");
320 store_str(ap_sys_siglist,SIGWINCH,"Window changed");
323 store_str(ap_sys_siglist,SIGURG,"urgent socket condition");
326 store_str(ap_sys_siglist,SIGPOLL,"Pollable event occurred");
329 store_str(ap_sys_siglist,SIGIO,"socket I/O possible");
332 store_str(ap_sys_siglist,SIGSTOP,"Stopped (signal)");
335 store_str(ap_sys_siglist,SIGTSTP,"Stopped");
338 store_str(ap_sys_siglist,SIGCONT,"Continued");
341 store_str(ap_sys_siglist,SIGTTIN,"Stopped (tty input)");
344 store_str(ap_sys_siglist,SIGTTOU,"Stopped (tty output)");
347 store_str(ap_sys_siglist,SIGVTALRM,"virtual timer expired");
350 store_str(ap_sys_siglist,SIGPROF,"profiling timer expired");
353 store_str(ap_sys_siglist,SIGXCPU,"exceeded cpu limit");
356 store_str(ap_sys_siglist,SIGXFSZ,"exceeded file size limit");
358 for (sig=0; sig < sizeof(ap_sys_siglist)/sizeof(ap_sys_siglist[0]); ++sig)
359 if (ap_sys_siglist[sig] == NULL)
360 ap_sys_siglist[sig] = "";
362 #endif /* NEED_AP_SYS_SIGLIST */
364 AP_DECLARE(void) unixd_set_rlimit(cmd_parms *cmd, struct rlimit **plimit,
365 const char *arg, const char * arg2, int type)
367 #if (defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS)) && APR_HAVE_STRUCT_RLIMIT && APR_HAVE_GETRLIMIT
369 struct rlimit *limit;
370 /* If your platform doesn't define rlim_t then typedef it in ap_config.h */
374 *plimit = (struct rlimit *)apr_pcalloc(cmd->pool, sizeof(**plimit));
376 if ((getrlimit(type, limit)) != 0) {
378 ap_log_error(APLOG_MARK, APLOG_ERR, errno, cmd->server,
379 "%s: getrlimit failed", cmd->cmd->name);
383 if ((str = ap_getword_conf(cmd->pool, &arg))) {
384 if (!strcasecmp(str, "max")) {
385 cur = limit->rlim_max;
392 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, cmd->server,
393 "Invalid parameters for %s", cmd->cmd->name);
397 if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) {
401 /* if we aren't running as root, cannot increase max */
403 limit->rlim_cur = cur;
405 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, cmd->server,
406 "Must be uid 0 to raise maximum %s", cmd->cmd->name);
411 limit->rlim_cur = cur;
414 limit->rlim_max = max;
419 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, cmd->server,
420 "Platform does not support rlimit for %s", cmd->cmd->name);
425 APR_HOOK_LINK(get_suexec_identity)
428 AP_IMPLEMENT_HOOK_RUN_FIRST(ap_unix_identity_t *, get_suexec_identity,
429 (const request_rec *r), (r), NULL)
431 static apr_status_t ap_unix_create_privileged_process(
432 apr_proc_t *newproc, const char *progname,
433 const char * const *args,
434 const char * const *env,
435 apr_procattr_t *attr, ap_unix_identity_t *ugid,
439 const char **newargs;
441 char *execuser, *execgroup;
443 if (!unixd_config.suexec_enabled) {
444 return apr_create_process(newproc, progname, args, env, attr, p);
447 execuser = apr_psprintf(p, "%ld", (long) ugid->uid);
448 execgroup = apr_psprintf(p, "%ld", (long) ugid->gid);
450 if (!execuser || !execgroup) {
460 newargs = apr_palloc(p, sizeof(char *) * (i + 4));
461 newprogname = SUEXEC_BIN;
462 newargs[0] = SUEXEC_BIN;
463 newargs[1] = execuser;
464 newargs[2] = execgroup;
465 newargs[3] = apr_pstrdup(p, progname);
469 newargs[i + 4] = args[i];
472 return apr_create_process(newproc, newprogname, newargs, env, attr, p);
475 AP_DECLARE(apr_status_t) ap_os_create_privileged_process(
476 const request_rec *r,
477 apr_proc_t *newproc, const char *progname,
478 const char * const *args,
479 const char * const *env,
480 apr_procattr_t *attr, apr_pool_t *p)
482 ap_unix_identity_t *ugid = ap_run_get_suexec_identity(r);
485 return apr_create_process(newproc, progname, args, env, attr, p);
488 return ap_unix_create_privileged_process(newproc, progname, args, env,