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 "http_core.h"
23 #include "mpm_common.h"
26 #include "mod_unixd.h"
27 #include "apr_thread_proc.h"
28 #include "apr_strings.h"
29 #include "apr_portable.h"
33 #ifdef HAVE_SYS_RESOURCE_H
34 #include <sys/resource.h>
50 #ifdef HAVE_SYS_PRCTL_H
51 #include <sys/prctl.h>
55 #define DEFAULT_USER "#-1"
58 #define DEFAULT_GROUP "#-1"
63 const char *user_name;
66 const char *chroot_dir;
70 * TODO: clean up the separation between this code
71 * and its data structures and unixd.c, as shown
72 * by the fact that we include unixd.h. Create
73 * mod_unixd.h which does what we need and
74 * clean up unixd.h for what it no longer needs
80 /* Set group privileges.
82 * Note that we use the username as set in the config files, rather than
83 * the lookup of to uid --- the same uid may have multiple passwd entries,
84 * with different sets of groups for each.
87 static int set_group_privs(void)
92 /* Get username if passed as a uid */
94 if (ap_unixd_config.user_name[0] == '#') {
96 uid_t uid = atol(&ap_unixd_config.user_name[1]);
98 if ((ent = getpwuid(uid)) == NULL) {
99 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
100 "getpwuid: couldn't determine user name from uid %ld, "
101 "you probably need to modify the User directive",
109 name = ap_unixd_config.user_name;
112 /* OS/2 doesn'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 %ld",
120 (long)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 %ld", name, (long)ap_unixd_config.group_id);
132 #endif /* !defined(OS2) */
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");
177 /* Only try to switch if we're running as root */
180 os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
182 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);
189 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
190 /* this applies to Linux 2.4+ */
191 if (ap_coredumpdir_configured) {
192 if (prctl(PR_SET_DUMPABLE, 1)) {
194 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
195 "set dumpable failed - this child will not coredump"
196 " after software errors");
207 unixd_set_user(cmd_parms *cmd, void *dummy,
210 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
215 ap_unixd_config.user_name = arg;
216 ap_unixd_config.user_id = ap_uname2id(arg);
217 #if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
218 if (ap_unixd_config.user_id == 0) {
219 return "Error:\tApache has not been designed to serve pages while\n"
220 "\trunning as root. There are known race conditions that\n"
221 "\twill allow any local user to read any file on the system.\n"
222 "\tIf you still desire to serve pages as root then\n"
223 "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
224 "\tand then rebuild the server.\n"
225 "\tIt is strongly suggested that you instead modify the User\n"
226 "\tdirective in your httpd.conf file to list a non-root\n"
235 unixd_set_group(cmd_parms *cmd, void *dummy,
238 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
243 ap_unixd_config.group_name = arg;
244 ap_unixd_config.group_id = ap_gname2id(arg);
250 unixd_set_chroot_dir(cmd_parms *cmd, void *dummy,
253 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
257 if (!ap_is_directory(cmd->pool, arg)) {
258 return "ChrootDir must be a valid directory";
261 ap_unixd_config.chroot_dir = arg;
266 unixd_set_suexec(cmd_parms *cmd, void *dummy, int arg)
268 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
274 if (!ap_unixd_config.suexec_enabled && arg) {
275 return apr_pstrcat(cmd->pool, "suEXEC isn't supported: ",
276 ap_unixd_config.suexec_disabled_reason, NULL);
280 ap_unixd_config.suexec_disabled_reason = "Suexec directive is Off";
283 ap_unixd_config.suexec_enabled = arg;
288 unixd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
292 ap_unixd_config.user_name = DEFAULT_USER;
293 ap_unixd_config.user_id = ap_uname2id(DEFAULT_USER);
294 ap_unixd_config.group_name = DEFAULT_GROUP;
295 ap_unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
297 ap_unixd_config.chroot_dir = NULL; /* none */
299 /* Check for suexec */
300 ap_unixd_config.suexec_enabled = 0;
301 if ((apr_stat(&wrapper, SUEXEC_BIN, APR_FINFO_NORM, ptemp))
303 if ((wrapper.protection & APR_USETID) && wrapper.user == 0
304 && (access(SUEXEC_BIN, R_OK|X_OK) == 0)) {
305 ap_unixd_config.suexec_enabled = 1;
306 ap_unixd_config.suexec_disabled_reason = "";
309 ap_unixd_config.suexec_disabled_reason =
310 "Invalid owner or file mode for " SUEXEC_BIN;
314 ap_unixd_config.suexec_disabled_reason =
315 "Missing suexec binary " SUEXEC_BIN;
318 ap_sys_privileges_handlers(1);
322 AP_DECLARE(int) ap_unixd_setup_child(void)
324 if (set_group_privs()) {
328 if (NULL != ap_unixd_config.chroot_dir) {
330 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
331 "Cannot chroot when not started as root");
334 if (chdir(ap_unixd_config.chroot_dir) != 0) {
335 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
336 "Can't chdir to %s", ap_unixd_config.chroot_dir);
339 if (chroot(ap_unixd_config.chroot_dir) != 0) {
340 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
341 "Can't chroot to %s", ap_unixd_config.chroot_dir);
344 if (chdir("/") != 0) {
345 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
346 "Can't chdir to new root");
351 /* Only try to switch if we're running as root */
354 os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
356 setuid(ap_unixd_config.user_id) == -1)) {
357 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
358 "setuid: unable to change to uid: %ld",
359 (long) ap_unixd_config.user_id);
362 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
363 /* this applies to Linux 2.4+ */
364 if (ap_coredumpdir_configured) {
365 if (prctl(PR_SET_DUMPABLE, 1)) {
366 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
367 "set dumpable failed - this child will not coredump"
368 " after software errors");
375 static void unixd_dump_config(apr_pool_t *p, server_rec *s)
377 apr_file_t *out = NULL;
378 apr_uid_t uid = ap_unixd_config.user_id;
379 apr_gid_t gid = ap_unixd_config.group_id;
381 if (!ap_exists_config_define("DUMP_RUN_CFG"))
384 no_root = " not_used";
385 apr_file_open_stdout(&out, p);
386 apr_file_printf(out, "User: name=\"%s\" id=%lu%s\n",
387 ap_unixd_config.user_name, (unsigned long)uid, no_root);
388 apr_file_printf(out, "Group: name=\"%s\" id=%lu%s\n",
389 ap_unixd_config.group_name, (unsigned long)gid, no_root);
390 if (ap_unixd_config.chroot_dir)
391 apr_file_printf(out, "ChrootDir: \"%s\"%s\n",
392 ap_unixd_config.chroot_dir, no_root);
395 static void unixd_hooks(apr_pool_t *pool)
397 ap_hook_pre_config(unixd_pre_config,
398 NULL, NULL, APR_HOOK_FIRST);
399 ap_hook_test_config(unixd_dump_config,
400 NULL, NULL, APR_HOOK_FIRST);
401 ap_hook_drop_privileges(unixd_drop_privileges,
402 NULL, NULL, APR_HOOK_MIDDLE);
405 static const command_rec unixd_cmds[] = {
406 AP_INIT_TAKE1("User", unixd_set_user, NULL, RSRC_CONF,
407 "Effective user id for this server"),
408 AP_INIT_TAKE1("Group", unixd_set_group, NULL, RSRC_CONF,
409 "Effective group id for this server"),
410 AP_INIT_TAKE1("ChrootDir", unixd_set_chroot_dir, NULL, RSRC_CONF,
411 "The directory to chroot(2) into"),
412 AP_INIT_FLAG("Suexec", unixd_set_suexec, NULL, RSRC_CONF,
413 "Enable or disable suEXEC support"),
417 AP_DECLARE_MODULE(unixd) = {
418 STANDARD20_MODULE_STUFF,