diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 06:33:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 06:33:50 +0000 |
commit | fe39ffb8b90ae4e002ed73fe98617cd590abb467 (patch) | |
tree | b80e5956907d8aeaaffe4e4f0c068c0e6157ce8b /modules/arch/unix/mod_unixd.c | |
parent | Initial commit. (diff) | |
download | apache2-fe39ffb8b90ae4e002ed73fe98617cd590abb467.tar.xz apache2-fe39ffb8b90ae4e002ed73fe98617cd590abb467.zip |
Adding upstream version 2.4.56.upstream/2.4.56upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/arch/unix/mod_unixd.c')
-rw-r--r-- | modules/arch/unix/mod_unixd.c | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/modules/arch/unix/mod_unixd.c b/modules/arch/unix/mod_unixd.c new file mode 100644 index 0000000..1baa278 --- /dev/null +++ b/modules/arch/unix/mod_unixd.c @@ -0,0 +1,433 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_main.h" +#include "http_log.h" +#include "http_core.h" +#include "mpm_common.h" +#include "os.h" +#include "ap_mpm.h" +#include "mod_unixd.h" +#include "apr_thread_proc.h" +#include "apr_strings.h" +#include "apr_portable.h" +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif +/* XXX */ +#include <sys/stat.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_GRP_H +#include <grp.h> +#endif +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#ifdef HAVE_SYS_SEM_H +#include <sys/sem.h> +#endif +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#endif + +#ifndef DEFAULT_USER +#define DEFAULT_USER "#-1" +#endif +#ifndef DEFAULT_GROUP +#define DEFAULT_GROUP "#-1" +#endif + +#if 0 +typedef struct { + const char *user_name; + uid_t user_id; + gid_t group_id; + const char *chroot_dir; +} unixd_config_t; +#else +/* + * TODO: clean up the separation between this code + * and its data structures and unixd.c, as shown + * by the fact that we include unixd.h. Create + * mod_unixd.h which does what we need and + * clean up unixd.h for what it no longer needs + */ +#include "unixd.h" +#endif + + +/* Set group privileges. + * + * Note that we use the username as set in the config files, rather than + * the lookup of to uid --- the same uid may have multiple passwd entries, + * with different sets of groups for each. + */ + +static int set_group_privs(void) +{ + if (!geteuid()) { + const char *name; + + /* Get username if passed as a uid */ + + if (ap_unixd_config.user_name[0] == '#') { + struct passwd *ent; + uid_t uid = atol(&ap_unixd_config.user_name[1]); + + if ((ent = getpwuid(uid)) == NULL) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02155) + "getpwuid: couldn't determine user name from uid %ld, " + "you probably need to modify the User directive", + (long)uid); + return -1; + } + + name = ent->pw_name; + } + else + name = ap_unixd_config.user_name; + +#if !defined(OS2) + /* OS/2 doesn't support groups. */ + /* + * Set the GID before initgroups(), since on some platforms + * setgid() is known to zap the group list. + */ + if (setgid(ap_unixd_config.group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02156) + "setgid: unable to set group id to Group %ld", + (long)ap_unixd_config.group_id); + return -1; + } + + /* Reset `groups' attributes. */ + + if (initgroups(name, ap_unixd_config.group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02157) + "initgroups: unable to set groups for User %s " + "and Group %ld", name, (long)ap_unixd_config.group_id); + return -1; + } +#endif /* !defined(OS2) */ + } + return 0; +} + + +static int +unixd_drop_privileges(apr_pool_t *pool, server_rec *s) +{ + int rv = set_group_privs(); + + if (rv) { + return rv; + } + + if (NULL != ap_unixd_config.chroot_dir) { + if (geteuid()) { + rv = errno; + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02158) + "Cannot chroot when not started as root"); + return rv; + } + + if (chdir(ap_unixd_config.chroot_dir) != 0) { + rv = errno; + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02159) + "Can't chdir to %s", ap_unixd_config.chroot_dir); + return rv; + } + + if (chroot(ap_unixd_config.chroot_dir) != 0) { + rv = errno; + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02160) + "Can't chroot to %s", ap_unixd_config.chroot_dir); + return rv; + } + + if (chdir("/") != 0) { + rv = errno; + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02161) + "Can't chdir to new root"); + return rv; + } + } + + /* Only try to switch if we're running as root */ + if (!geteuid() && ( +#ifdef _OSD_POSIX + os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 || +#endif + setuid(ap_unixd_config.user_id) == -1)) { + rv = errno; + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02162) + "setuid: unable to change to uid: %ld", + (long) ap_unixd_config.user_id); + return rv; + } +#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) + /* this applies to Linux 2.4+ */ + if (ap_coredumpdir_configured) { + if (prctl(PR_SET_DUMPABLE, 1)) { + rv = errno; + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02163) + "set dumpable failed - this child will not coredump" + " after software errors"); + return rv; + } + } +#endif + + return OK; +} + + +static const char * +unixd_set_user(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_unixd_config.user_name = arg; + ap_unixd_config.user_id = ap_uname2id(arg); +#if !defined (BIG_SECURITY_HOLE) && !defined (OS2) + if (ap_unixd_config.user_id == 0) { + return "Error:\tApache has not been designed to serve pages while\n" + "\trunning as root. There are known race conditions that\n" + "\twill allow any local user to read any file on the system.\n" + "\tIf you still desire to serve pages as root then\n" + "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n" + "\tand then rebuild the server.\n" + "\tIt is strongly suggested that you instead modify the User\n" + "\tdirective in your httpd.conf file to list a non-root\n" + "\tuser.\n"; + } +#endif + + return NULL; +} + +static const char* +unixd_set_group(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_unixd_config.group_name = arg; + ap_unixd_config.group_id = ap_gname2id(arg); + + return NULL; +} + +static const char* +unixd_set_chroot_dir(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + if (!ap_is_directory(cmd->pool, arg)) { + return "ChrootDir must be a valid directory"; + } + + ap_unixd_config.chroot_dir = arg; + return NULL; +} + +static const char * +unixd_set_suexec(cmd_parms *cmd, void *dummy, int arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + if (!ap_unixd_config.suexec_enabled && arg) { + return apr_pstrcat(cmd->pool, "suEXEC isn't supported: ", + ap_unixd_config.suexec_disabled_reason, NULL); + } + + if (!arg) { + ap_unixd_config.suexec_disabled_reason = "Suexec directive is Off"; + } + + ap_unixd_config.suexec_enabled = arg; + return NULL; +} + +#ifdef AP_SUEXEC_CAPABILITIES +/* If suexec is using capabilities, don't test for the setuid bit. */ +#define SETUID_TEST(finfo) (1) +#else +#define SETUID_TEST(finfo) (finfo.protection & APR_USETID) +#endif + +static int +unixd_pre_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp) +{ + apr_finfo_t wrapper; + ap_unixd_config.user_name = DEFAULT_USER; + ap_unixd_config.user_id = ap_uname2id(DEFAULT_USER); + ap_unixd_config.group_name = DEFAULT_GROUP; + ap_unixd_config.group_id = ap_gname2id(DEFAULT_GROUP); + + ap_unixd_config.chroot_dir = NULL; /* none */ + + /* Check for suexec */ + ap_unixd_config.suexec_enabled = 0; + if ((apr_stat(&wrapper, SUEXEC_BIN, APR_FINFO_NORM, ptemp)) + == APR_SUCCESS) { + if (SETUID_TEST(wrapper) && wrapper.user == 0 + && (access(SUEXEC_BIN, R_OK|X_OK) == 0)) { + ap_unixd_config.suexec_enabled = 1; + ap_unixd_config.suexec_disabled_reason = ""; + } + else { + ap_unixd_config.suexec_disabled_reason = + "Invalid owner or file mode for " SUEXEC_BIN; + } + } + else { + ap_unixd_config.suexec_disabled_reason = + "Missing suexec binary " SUEXEC_BIN; + } + + ap_sys_privileges_handlers(1); + return OK; +} + +AP_DECLARE(int) ap_unixd_setup_child(void) +{ + if (set_group_privs()) { + return -1; + } + + if (NULL != ap_unixd_config.chroot_dir) { + if (geteuid()) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02164) + "Cannot chroot when not started as root"); + return -1; + } + if (chdir(ap_unixd_config.chroot_dir) != 0) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02165) + "Can't chdir to %s", ap_unixd_config.chroot_dir); + return -1; + } + if (chroot(ap_unixd_config.chroot_dir) != 0) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02166) + "Can't chroot to %s", ap_unixd_config.chroot_dir); + return -1; + } + if (chdir("/") != 0) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02167) + "Can't chdir to new root"); + return -1; + } + } + + /* Only try to switch if we're running as root */ + if (!geteuid() && ( +#ifdef _OSD_POSIX + os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 || +#endif + setuid(ap_unixd_config.user_id) == -1)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02168) + "setuid: unable to change to uid: %ld", + (long) ap_unixd_config.user_id); + return -1; + } +#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) + /* this applies to Linux 2.4+ */ + if (ap_coredumpdir_configured) { + if (prctl(PR_SET_DUMPABLE, 1)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, APLOGNO(02169) + "set dumpable failed - this child will not coredump" + " after software errors"); + } + } +#endif + return 0; +} + +static void unixd_dump_config(apr_pool_t *p, server_rec *s) +{ + apr_file_t *out = NULL; + apr_uid_t uid = ap_unixd_config.user_id; + apr_gid_t gid = ap_unixd_config.group_id; + char *no_root = ""; + if (!ap_exists_config_define("DUMP_RUN_CFG")) + return; + if (geteuid() != 0) + no_root = " not_used"; + apr_file_open_stdout(&out, p); + apr_file_printf(out, "User: name=\"%s\" id=%lu%s\n", + ap_unixd_config.user_name, (unsigned long)uid, no_root); + apr_file_printf(out, "Group: name=\"%s\" id=%lu%s\n", + ap_unixd_config.group_name, (unsigned long)gid, no_root); + if (ap_unixd_config.chroot_dir) + apr_file_printf(out, "ChrootDir: \"%s\"%s\n", + ap_unixd_config.chroot_dir, no_root); +} + +static void unixd_hooks(apr_pool_t *pool) +{ + ap_hook_pre_config(unixd_pre_config, + NULL, NULL, APR_HOOK_FIRST); + ap_hook_test_config(unixd_dump_config, + NULL, NULL, APR_HOOK_FIRST); + ap_hook_drop_privileges(unixd_drop_privileges, + NULL, NULL, APR_HOOK_MIDDLE); +} + +static const command_rec unixd_cmds[] = { + AP_INIT_TAKE1("User", unixd_set_user, NULL, RSRC_CONF, + "Effective user id for this server"), + AP_INIT_TAKE1("Group", unixd_set_group, NULL, RSRC_CONF, + "Effective group id for this server"), + AP_INIT_TAKE1("ChrootDir", unixd_set_chroot_dir, NULL, RSRC_CONF, + "The directory to chroot(2) into"), + AP_INIT_FLAG("Suexec", unixd_set_suexec, NULL, RSRC_CONF, + "Enable or disable suEXEC support"), + {NULL} +}; + +AP_DECLARE_MODULE(unixd) = { + STANDARD20_MODULE_STUFF, + NULL, + NULL, + NULL, + NULL, + unixd_cmds, + unixd_hooks +}; + |