unixd.c revision ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dc
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari/* Copyright 2000-2004 Apache Software Foundation
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * Licensed under the Apache License, Version 2.0 (the "License");
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * you may not use this file except in compliance with the License.
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * You may obtain a copy of the License at
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * http://www.apache.org/licenses/LICENSE-2.0
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * Unless required by applicable law or agreed to in writing, software
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * distributed under the License is distributed on an "AS IS" BASIS,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * See the License for the specific language governing permissions and
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * limitations under the License.
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari/* Set group privileges.
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * Note that we use the username as set in the config files, rather than
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * the lookup of to uid --- the same uid may have multiple passwd entries,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * with different sets of groups for each.
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari /* Get username if passed as a uid */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari uid_t uid = atoi(&unixd_config.user_name[1]);
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "getpwuid: couldn't determine user name from uid %u, "
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "you probably need to modify the User directive",
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari /* OS/2 and TPF don't support groups. */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * Set the GID before initgroups(), since on some platforms
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * setgid() is known to zap the group list.
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "setgid: unable to set group id to Group %u",
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari /* Reset `groups' attributes. */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari if (initgroups(name, unixd_config.group_id) == -1) {
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "initgroups: unable to set groups for User %s "
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "and Group %u", name, (unsigned)unixd_config.group_id);
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari#endif /* !defined(OS2) && !defined(TPF) */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari /* Only try to switch if we're running as MANAGER.SYS */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari if (geteuid() == 1 && unixd_config.user_id > 1) {
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "setuid: unable to change to uid: %ld",
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari /* Only try to switch if we're running as root */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari os_init_job_environment(NULL, unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "setuid: unable to change to uid: %ld",
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari /* this applies to Linux 2.4+ */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
f3a838d9e2932da421939f348a2c3d31ded88989Daniel Calegari "set dumpable failed - this child will not coredump"
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari " after software errors");
cebd8658147937d53195664ad8b1e37de1e6f017Daniel CalegariAP_DECLARE(const char *) unixd_set_user(cmd_parms *cmd, void *dummy,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari#if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari return "Error:\tApache has not been designed to serve pages while\n"
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "\trunning as root. There are known race conditions that\n"
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "\twill allow any local user to read any file on the system.\n"
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "\tIf you still desire to serve pages as root then\n"
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "\tand then rebuild the server.\n"
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari "\tIt is strongly suggested that you instead modify the User\n"
f3a838d9e2932da421939f348a2c3d31ded88989Daniel Calegari "\tdirective in your httpd.conf file to list a non-root\n"
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel CalegariAP_DECLARE(const char *) unixd_set_group(cmd_parms *cmd, void *dummy,
b767e7055b5ef62c990a111ac0524500592f8781Daniel Calegari const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel CalegariAP_DECLARE(void) unixd_pre_config(apr_pool_t *ptemp)
b5c780c06889ceb5c241a6b5da7ef0663e5451caDaniel Calegari unixd_config.user_id = ap_uname2id(DEFAULT_USER);
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari /* Check for suexec */
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari /* XXX - apr_stat is incapable of checking suid bits (grumble) */
b5c780c06889ceb5c241a6b5da7ef0663e5451caDaniel Calegari /* if ((wrapper.filetype & S_ISUID) && wrapper.user == 0) { */
b767e7055b5ef62c990a111ac0524500592f8781Daniel CalegariAP_DECLARE(void) unixd_set_rlimit(cmd_parms *cmd, struct rlimit **plimit,
b767e7055b5ef62c990a111ac0524500592f8781Daniel Calegari const char *arg, const char * arg2, int type)
b767e7055b5ef62c990a111ac0524500592f8781Daniel Calegari#if (defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS)) && APR_HAVE_STRUCT_RLIMIT && APR_HAVE_GETRLIMIT
b767e7055b5ef62c990a111ac0524500592f8781Daniel Calegari /* If your platform doesn't define rlim_t then typedef it in ap_config.h */
b767e7055b5ef62c990a111ac0524500592f8781Daniel Calegari *plimit = (struct rlimit *)apr_pcalloc(cmd->pool, sizeof(**plimit));
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ERR, errno, cmd->server,
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari if ((str = ap_getword_conf(cmd->pool, &arg))) {
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari "Invalid parameters for %s", cmd->cmd->name);
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) {
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari /* if we aren't running as root, cannot increase max */
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari "Must be uid 0 to raise maximum %s", cmd->cmd->name);
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari "Platform does not support rlimit for %s", cmd->cmd->name);
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel CalegariAP_IMPLEMENT_HOOK_RUN_FIRST(ap_unix_identity_t *, get_suexec_identity,
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegaristatic apr_status_t ap_unix_create_privileged_process(
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari const char * const *args,
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari const char * const *env,
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari apr_procattr_t *attr, ap_unix_identity_t *ugid,
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari return apr_proc_create(newproc, progname, args, env, attr, p);
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari /* Allow suexec's "/" check to succeed */
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari execuser = apr_psprintf(p, "~%ld", (long) ugid->uid);
f3a838d9e2932da421939f348a2c3d31ded88989Daniel Calegari execuser = apr_psprintf(p, "%ld", (long) ugid->uid);
f3a838d9e2932da421939f348a2c3d31ded88989Daniel Calegari execgroup = apr_psprintf(p, "%ld", (long) ugid->gid);
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari /* allocate space for 4 new args, the input args, and a null terminator */
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari newargs = apr_palloc(p, sizeof(char *) * (i + 4));
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari ** using a shell to execute suexec makes no sense thus
7e9757ba2f41c4ac30634a4a1f1d87a042e37c45Daniel Calegari ** we force everything to be APR_PROGRAM, and never
f3a838d9e2932da421939f348a2c3d31ded88989Daniel Calegari ** APR_SHELLCMD
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari if(apr_procattr_cmdtype_set(attr, APR_PROGRAM) != APR_SUCCESS) {
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari } while (args[i++]);
cba1bca86fce751bb0dc758f4a7e43ff95640137Daniel Calegari return apr_proc_create(newproc, newprogname, newargs, env, attr, p);
cebd8658147937d53195664ad8b1e37de1e6f017Daniel CalegariAP_DECLARE(apr_status_t) ap_os_create_privileged_process(
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari const char * const *args,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari const char * const *env,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari ap_unix_identity_t *ugid = ap_run_get_suexec_identity(r);
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari return apr_proc_create(newproc, progname, args, env, attr, p);
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari return ap_unix_create_privileged_process(newproc, progname, args, env,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari/* XXX move to APR and externalize (but implement differently :) ) */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegaristatic apr_lockmech_e proc_mutex_mech(apr_proc_mutex_t *pmutex)
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari const char *mechname = apr_proc_mutex_name(pmutex);
cebd8658147937d53195664ad8b1e37de1e6f017Daniel CalegariAP_DECLARE(apr_status_t) unixd_set_proc_mutex_perms(apr_proc_mutex_t *pmutex)
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari apr_lockmech_e mech = proc_mutex_mech(pmutex);
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari unsigned short *array;
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari if (semctl(ospmutex.crossproc, 0, IPC_SET, ick) < 0) {
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari const char *lockfile = apr_proc_mutex_lockfile(pmutex);
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari /* do nothing */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel CalegariAP_DECLARE(apr_status_t) unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex)
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari return unixd_set_proc_mutex_perms(osgmutex.proc_mutex);
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari#else /* APR_PROC_MUTEX_IS_GLOBAL */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari /* In this case, apr_proc_mutex_t and apr_global_mutex_t are the same. */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari#endif /* APR_PROC_MUTEX_IS_GLOBAL */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel CalegariAP_DECLARE(apr_status_t) unixd_accept(void **accepted, ap_listen_rec *lr,
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari status = apr_socket_accept(&csd, lr->sd, ptrans);
b5c780c06889ceb5c241a6b5da7ef0663e5451caDaniel Calegari if (sockdes == 0) { /* 0 is invalid socket for TPF */
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari /* Our old behaviour here was to continue after accept()
cebd8658147937d53195664ad8b1e37de1e6f017Daniel Calegari * errors. But this leads us into lots of troubles
switch (status) {
case ENOBUFS:
#ifdef EPROTO
case EPROTO:
#ifdef ECONNABORTED
case ECONNABORTED:
#ifdef ECONNRESET
case ECONNRESET:
#ifdef ETIMEDOUT
case ETIMEDOUT:
#ifdef EHOSTUNREACH
case EHOSTUNREACH:
#ifdef ENETUNREACH
case ENETUNREACH:
#ifdef ENETDOWN
case ENETDOWN:
return APR_EGENERAL;
#ifdef TPF
case EINACT:
return APR_EGENERAL;
return APR_EGENERAL;
return APR_EGENERAL;
return status;