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"
22 #include "mpm_common.h"
25 #include "mod_unixd.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>
54 #define DEFAULT_USER "#-1"
57 #define DEFAULT_GROUP "#-1"
62 const char *user_name;
65 const char *chroot_dir;
69 * TODO: clean up the separation between this code
70 * and its data structures and unixd.c, as shown
71 * by the fact that we include unixd.h. Create
72 * mod_unixd.h which does what we need and
73 * clean up unixd.h for what it no longer needs
79 /* Set group privileges.
81 * Note that we use the username as set in the config files, rather than
82 * the lookup of to uid --- the same uid may have multiple passwd entries,
83 * with different sets of groups for each.
86 static int set_group_privs(void)
91 /* Get username if passed as a uid */
93 if (ap_unixd_config.user_name[0] == '#') {
95 uid_t uid = atol(&ap_unixd_config.user_name[1]);
97 if ((ent = getpwuid(uid)) == NULL) {
98 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
99 "getpwuid: couldn't determine user name from uid %ld, "
100 "you probably need to modify the User directive",
108 name = ap_unixd_config.user_name;
110 #if !defined(OS2) && !defined(TPF)
111 /* OS/2 and TPF don't support groups. */
114 * Set the GID before initgroups(), since on some platforms
115 * setgid() is known to zap the group list.
117 if (setgid(ap_unixd_config.group_id) == -1) {
118 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
119 "setgid: unable to set group id to Group %u",
120 (unsigned)ap_unixd_config.group_id);
124 /* Reset `groups' attributes. */
126 if (initgroups(name, ap_unixd_config.group_id) == -1) {
127 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
128 "initgroups: unable to set groups for User %s "
129 "and Group %u", name, (unsigned)ap_unixd_config.group_id);
132 #endif /* !defined(OS2) && !defined(TPF) */
139 unixd_drop_privileges(apr_pool_t *pool, server_rec *s)
141 int rv = set_group_privs();
147 if (NULL != ap_unixd_config.chroot_dir) {
150 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
151 "Cannot chroot when not started as root");
155 if (chdir(ap_unixd_config.chroot_dir) != 0) {
157 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
158 "Can't chdir to %s", ap_unixd_config.chroot_dir);
162 if (chroot(ap_unixd_config.chroot_dir) != 0) {
164 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
165 "Can't chroot to %s", ap_unixd_config.chroot_dir);
169 if (chdir("/") != 0) {
171 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
172 "Can't chdir to new root");
178 /* Only try to switch if we're running as MANAGER.SYS */
179 if (geteuid() == 1 && ap_unixd_config.user_id > 1) {
181 if (setuid(ap_unixd_config.user_id) == -1) {
184 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
185 "setuid: unable to change to uid: %ld",
186 (long) ap_unixd_config.user_id);
192 /* Only try to switch if we're running as root */
195 os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
197 setuid(ap_unixd_config.user_id) == -1)) {
199 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
200 "setuid: unable to change to uid: %ld",
201 (long) ap_unixd_config.user_id);
204 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
205 /* this applies to Linux 2.4+ */
206 if (ap_coredumpdir_configured) {
207 if (prctl(PR_SET_DUMPABLE, 1)) {
209 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
210 "set dumpable failed - this child will not coredump"
211 " after software errors");
223 unixd_set_user(cmd_parms *cmd, void *dummy,
226 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
231 ap_unixd_config.user_name = arg;
232 ap_unixd_config.user_id = ap_uname2id(arg);
233 #if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
234 if (ap_unixd_config.user_id == 0) {
235 return "Error:\tApache has not been designed to serve pages while\n"
236 "\trunning as root. There are known race conditions that\n"
237 "\twill allow any local user to read any file on the system.\n"
238 "\tIf you still desire to serve pages as root then\n"
239 "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
240 "\tand then rebuild the server.\n"
241 "\tIt is strongly suggested that you instead modify the User\n"
242 "\tdirective in your httpd.conf file to list a non-root\n"
251 unixd_set_group(cmd_parms *cmd, void *dummy,
254 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
259 ap_unixd_config.group_id = ap_gname2id(arg);
265 unixd_set_chroot_dir(cmd_parms *cmd, void *dummy,
268 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
272 if (!ap_is_directory(cmd->pool, arg)) {
273 return "ChrootDir must be a valid directory";
276 ap_unixd_config.chroot_dir = arg;
281 unixd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
285 ap_unixd_config.user_name = DEFAULT_USER;
286 ap_unixd_config.user_id = ap_uname2id(DEFAULT_USER);
287 ap_unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
289 ap_unixd_config.chroot_dir = NULL; /* none */
291 /* Check for suexec */
292 ap_unixd_config.suexec_enabled = 0;
293 if ((apr_stat(&wrapper, SUEXEC_BIN, APR_FINFO_NORM, ptemp))
295 if ((wrapper.protection & APR_USETID) && wrapper.user == 0) {
296 ap_unixd_config.suexec_enabled = 1;
300 ap_sys_privileges_handlers(1);
304 AP_DECLARE(int) ap_unixd_setup_child(void)
306 if (set_group_privs()) {
310 if (NULL != ap_unixd_config.chroot_dir) {
312 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
313 "Cannot chroot when not started as root");
316 if (chdir(ap_unixd_config.chroot_dir) != 0) {
317 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
318 "Can't chdir to %s", ap_unixd_config.chroot_dir);
321 if (chroot(ap_unixd_config.chroot_dir) != 0) {
322 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
323 "Can't chroot to %s", ap_unixd_config.chroot_dir);
326 if (chdir("/") != 0) {
327 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
328 "Can't chdir to new root");
334 /* Only try to switch if we're running as MANAGER.SYS */
335 if (geteuid() == 1 && ap_unixd_config.user_id > 1) {
337 if (setuid(ap_unixd_config.user_id) == -1) {
339 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
340 "setuid: unable to change to uid: %ld",
341 (long) ap_unixd_config.user_id);
347 /* Only try to switch if we're running as root */
350 os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
352 setuid(ap_unixd_config.user_id) == -1)) {
353 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
354 "setuid: unable to change to uid: %ld",
355 (long) ap_unixd_config.user_id);
358 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
359 /* this applies to Linux 2.4+ */
360 if (ap_coredumpdir_configured) {
361 if (prctl(PR_SET_DUMPABLE, 1)) {
362 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
363 "set dumpable failed - this child will not coredump"
364 " after software errors");
372 static void unixd_hooks(apr_pool_t *pool)
374 ap_hook_pre_config(unixd_pre_config,
375 NULL, NULL, APR_HOOK_FIRST);
377 ap_hook_drop_privileges(unixd_drop_privileges,
378 NULL, NULL, APR_HOOK_MIDDLE);
381 static const command_rec unixd_cmds[] = {
382 AP_INIT_TAKE1("User", unixd_set_user, NULL, RSRC_CONF,
383 "Effective user id for this server"),
384 AP_INIT_TAKE1("Group", unixd_set_group, NULL, RSRC_CONF,
385 "Effective group id for this server"),
386 AP_INIT_TAKE1("ChrootDir", unixd_set_chroot_dir, NULL, RSRC_CONF,
387 "The directory to chroot(2) into"),
391 module AP_MODULE_DECLARE_DATA unixd_module = {
392 STANDARD20_MODULE_STUFF,