diff options
author | Nick Kew <niq@apache.org> | 2008-11-14 09:48:39 +0000 |
---|---|---|
committer | Nick Kew <niq@apache.org> | 2008-11-14 09:48:39 +0000 |
commit | 466af3e421526125109075584193fe8b884cc659 (patch) | |
tree | 8eff3b1c6b7414eaa9beacda51070550ea26d5d5 | |
parent | aac5199100c59d83c9e86caa90c56a1bd39ae288 (diff) | |
download | httpd-466af3e421526125109075584193fe8b884cc659.tar.gz |
Introduce mod_privileges: a platform-specific module offering enhanced
security and a (limited) solution to the "perchild" problem.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@713961 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | docs/manual/mod/mod_privileges.xml | 280 | ||||
-rw-r--r-- | docs/manual/mod/mod_privileges.xml.meta | 12 | ||||
-rw-r--r-- | modules/arch/unix/config5.m4 | 1 | ||||
-rw-r--r-- | modules/arch/unix/mod_privileges.c | 427 |
4 files changed, 720 insertions, 0 deletions
diff --git a/docs/manual/mod/mod_privileges.xml b/docs/manual/mod/mod_privileges.xml new file mode 100644 index 0000000000..852d378225 --- /dev/null +++ b/docs/manual/mod/mod_privileges.xml @@ -0,0 +1,280 @@ +<?xml version="1.0"?> +<!DOCTYPE modulesynopsis SYSTEM "../style/modulesynopsis.dtd"> +<?xml-stylesheet type="text/xsl" href="../style/manual.en.xsl"?> +<!-- $LastChangedRevision: 703441 $ --> + +<!-- + 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. +--> + +<modulesynopsis metafile="mod_privileges.xml.meta"> + +<name>mod_privileges</name> +<description>Support for Solaris privileges and for running virtual hosts +under different user IDs.</description> +<status>Experimental</status> +<identifier>privileges_module</identifier> +<compatibility>Available in Apache 2.3 and up, on Solaris 10 and +OpenSolaris platforms</compatibility> + +<summary> +<p>This module enables different Virtual Hosts to run with different +Unix™ <var>User</var> and <var>Group</var> IDs, and with different +<a href="http://www.sun.com/bigadmin/features/articles/least_privilege.jsp" +>Solaris Privileges</a>. In particular, it offers a solution to the +problem of privilege separation between different Virtual Hosts, first +promised by the abandoned <module>perchild</module> MPM. +It also offers other security enhancements.</p> + +<p>Unlike <module>perchild</module>, <module>mod_privileges</module> +is not itself an MPM. It works <em>within</em> a processing model to +set privileges and User/Group <em>per request</em> in a running process. +It is therefore not compatible with a threaded MPM, and will refuse +to run under one.</p> + +<p><module>mod_privileges</module> raises security issues similar to +those of <a href="../suexec.html">suexec</a>. But unlike suexec, +it applies not only to CGI programs but to the entire request processing +cycle, including in-process applications and subprocesses. +It is ideally suited to running PHP applications under <strong>mod_php</strong>, +which is also incompatible with threaded MPMs. It is also well-suited +to other in-process scripting applications such as <strong>mod_perl</strong>, +<strong>mod_python</strong>, and <strong>mod_ruby</strong>, and to +applications implemented in C as apache modules where privilege +separation is an issue.</p> + +</summary> + +<directivesynopsis> +<name>VHostUser</name> +<description>Sets the User ID under which a virtual host runs.</description> +<syntax>VHostUser <var>unix-userid</var></syntax> +<default>Inherits the userid specified in +<directive module="mod_unixd">User</directive></default> +<contextlist><context>virtual host</context></contextlist> +<compatibility>Available on Solaris 10 and OpenSolaris with +non-threaded MPMs (<module>prefork</module> or custom MPM).</compatibility> + +<usage> + <p>The <directive>VHostUser</directive> directive sets the Unix userid + under which the server will process requests to a virtualhost. + The userid is set before the request is processed and reset afterwards + using <a + href="http://www.sun.com/bigadmin/features/articles/least_privilege.jsp" + >Solaris Privileges</a>. Since the setting applies to the + <em>process</em>, this is not compatible with threaded MPMs.</p> + <p><var>Unix-userid</var> is one of:</p> + <dl> + <dt>A username</dt> + <dd>Refers to the given user by name.</dd> + + <dt><code>#</code> followed by a user number.</dt> + <dd>Refers to a user by its number.</dd> + </dl> + + <note type="warning"><title>Security</title> + <p>This directive cannot be used to run apache as root! + Nevertheless, it opens potential security issues similar to + those discussed in the <a href="../suexec.html">suexec</a> + documentation.</p></note> +</usage> +<seealso><directive module="mod_unixd">User</directive></seealso> +<seealso><directive module="mod_suexec">SuexecUserGroup</directive></seealso> +</directivesynopsis> + +<directivesynopsis> +<name>VHostGroup</name> +<description>Sets the Group ID under which a virtual host runs.</description> +<syntax>VHostGroup <var>unix-groupid</var></syntax> +<default>Inherits the group id specified in +<directive module="mod_unixd">Group</directive></default> +<contextlist><context>virtual host</context></contextlist> +<compatibility>Available on Solaris 10 and OpenSolaris with +non-threaded MPMs (<module>prefork</module> or custom MPM).</compatibility> + +<usage> + <p>The <directive>VHostGroup</directive> directive sets the Unix group + under which the server will process requests to a virtualhost. + The group is set before the request is processed and reset afterwards + using <a + href="http://www.sun.com/bigadmin/features/articles/least_privilege.jsp" + >Solaris Privileges</a>. Since the setting applies to the + <em>process</em>, this is not compatible with threaded MPMs.</p> + <p><var>Unix-group</var> is one of:</p> + <dl> + <dt>A group name</dt> + <dd>Refers to the given group by name.</dd> + + <dt><code>#</code> followed by a group number.</dt> + <dd>Refers to a group by its number.</dd> + </dl> + + <note type="warning"><title>Security</title> + <p>This directive cannot be used to run apache as root! + Nevertheless, it opens potential security issues similar to + those discussed in the <a href="../suexec.html">suexec</a> + documentation.</p></note> +</usage> +<seealso><directive module="mod_unixd">Group</directive></seealso> +<seealso><directive module="mod_suexec">SuexecUserGroup</directive></seealso> +</directivesynopsis> + +<directivesynopsis> +<name>VHostSecure</name> +<description>Determines whether the server runs with enhanced security +for the virtualhost.</description> +<syntax>VHostSecure On|Off</syntax> +<default>VHostSecure On</default> +<contextlist><context>virtual host</context></contextlist> +<compatibility>Available on Solaris 10 and OpenSolaris with +non-threaded MPMs (<module>prefork</module> or custom MPM).</compatibility> + +<usage> + <p>Determines whether the virtual host processes requests with + security enhanced by removal of <a + href="http://www.sun.com/bigadmin/features/articles/least_privilege.jsp" + >Privileges</a> that are rarely needed in a webserver, but which are + available by default to a normal Unix user and may therefore + be required by modules and applications. It is recommended that + you retain the default (On) unless it prevents an application running. + Since the setting applies to the <em>process</em>, this is not + compatible with threaded MPMs.</p> + <note><title>Note</title> + <p>If <directive>VHostSecure</directive> prevents an application + running, this may be a warning sign that the application should be + reviewed for security.</p></note> +</usage> +</directivesynopsis> + +<directivesynopsis> +<name>VHostCGIMode</name> +<description>Determines whether the virtualhost can run +subprocesses, and the privileges available to subprocesses.</description> +<syntax>VHostCGIMode On|Off|Secure</syntax> +<default>VHostCGIMode On</default> +<contextlist><context>virtual host</context></contextlist> +<compatibility>Available on Solaris 10 and OpenSolaris with +non-threaded MPMs (<module>prefork</module> or custom MPM).</compatibility> + +<usage> + <p>Determines whether the virtual host is allowed to run fork and exec, + the <a + href="http://www.sun.com/bigadmin/features/articles/least_privilege.jsp" + >privileges</a> required to run subprocesses. If this is set to + <var>Off</var> the virtualhost is denied the privileges and will not + be able to run traditional CGI programs or scripts under the traditional + <module>mod_cgi</module>, nor similar external programs such as those + created by <module>mod_ext_filter</module> or + <directive module="mod_rewrite">RewriteMap</directive> <var>prog</var>. + Note that it does not prevent CGI programs running under alternative + process and security models such as <a href="http://fastcgi.coremail.cn" + >mod_fcgid</a>, which is a recommended solution in Solaris.</p> + <p>If set to <var>On</var> or <var>Secure</var>, the virtual host + is permitted to run external programs and scripts as above. + Setting <directive>VHostCGIMode</directive> <var>Secure</var> has + the effect of denying privileges to the subprocesses, as described + for <directive>VHostSecure</directive>.</p> +</usage> +</directivesynopsis> + +<directivesynopsis> +<name>DTracePrivileges</name> +<description>Determines whether the privileges required by dtrace are enabled.</description> +<syntax>DTracePrivileges On|Off</syntax> +<default>DTracePrivileges Off</default> +<contextlist><context>server config</context></contextlist> +<compatibility>Available on Solaris 10 and OpenSolaris with +non-threaded MPMs (<module>prefork</module> or custom MPM).</compatibility> + +<usage> + <p>This server-wide directive determines whether Apache will run with + the <a + href="http://www.sun.com/bigadmin/features/articles/least_privilege.jsp" + >privileges</a> required to run + <a href="http://www.sun.com/bigadmin/content/dtrace/">dtrace</a>. + Note that <var>DTracePrivileges On</var> will not in itself + activate DTrace, but <var>DTracePrivileges Off</var> will prevent + it working.</p> +</usage> +</directivesynopsis> + +<directivesynopsis> +<name>VHostPrivs</name> +<description>Assign arbitrary privileges to a virtual host.</description> +<syntax>VHostPrivs [+-]?<var>privilege-name</var> [[+-]?privilege-name] ...</syntax> +<default>None</default> +<contextlist><context>virtual host</context></contextlist> +<compatibility>Available on Solaris 10 and OpenSolaris with +non-threaded MPMs (<module>prefork</module> or custom MPM). +and when <module>mod_privileges</module> is compiled with the +<var>BIG_SECURITY_HOLE</var> compile-time option.</compatibility> + +<usage> + <p><directive>VHostPrivs</directive> can be used to assign arbitrary <a + href="http://www.sun.com/bigadmin/features/articles/least_privilege.jsp" + >privileges</a> to a virtual host. Each <var>privilege-name</var> + is the name of a Solaris privilege, such as <var>file_setid</var> + or <var>sys_nfs</var>.</p> + + <p>A <var>privilege-name</var> may optionally be prefixed by + + or -, which will respectively allow or deny a privilege. + If used with neither + nor -, all privileges otherwise assigned + to the virtualhost will be denied. You can use this to override + any of the default sets and construct your own privilege set.</p> + + <note type="warning"><title>Security</title> + <p>This directive can open huge security holes in apache, up to + and including running requests with root-level powers. Do not + use it unless you fully understand what you are doing!</p></note> +</usage> +</directivesynopsis> + +<directivesynopsis> +<name>VHostCGIPrivs</name> +<description>Assign arbitrary privileges to subprocesses created +by a virtual host.</description> +<syntax>VHostPrivs [+-]?<var>privilege-name</var> [[+-]?privilege-name] ...</syntax> +<default>None</default> +<contextlist><context>virtual host</context></contextlist> +<compatibility>Available on Solaris 10 and OpenSolaris with +non-threaded MPMs (<module>prefork</module> or custom MPM) +and when <module>mod_privileges</module> is compiled with the +<var>BIG_SECURITY_HOLE</var> compile-time option.</compatibility> + +<usage> + <p><directive>VHostCGIPrivs</directive> can be used to assign arbitrary <a + href="http://www.sun.com/bigadmin/features/articles/least_privilege.jsp" + >privileges</a> to subprocesses created by a virtual host, as discussed + under <directive>VHostCGIMode</directive>. Each <var>privilege-name</var> + is the name of a Solaris privilege, such as <var>file_setid</var> + or <var>sys_nfs</var>.</p> + + <p>A <var>privilege-name</var> may optionally be prefixed by + + or -, which will respectively allow or deny a privilege. + If used with neither + nor -, all privileges otherwise assigned + to the virtualhost will be denied. You can use this to override + any of the default sets and construct your own privilege set.</p> + + <note type="warning"><title>Security</title> + <p>This directive can open huge security holes in apache subprocesses, + up to and including running them with root-level powers. Do not + use it unless you fully understand what you are doing!</p></note> +</usage> +</directivesynopsis> + + + +</modulesynopsis> diff --git a/docs/manual/mod/mod_privileges.xml.meta b/docs/manual/mod/mod_privileges.xml.meta new file mode 100644 index 0000000000..e3fc6d93a9 --- /dev/null +++ b/docs/manual/mod/mod_privileges.xml.meta @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- GENERATED FROM XML: DO NOT EDIT --> + +<metafile> + <basename>mod_privileges</basename> + <path>/mod/</path> + <relpath>..</relpath> + + <variants> + <variant>en</variant> + </variants> +</metafile> diff --git a/modules/arch/unix/config5.m4 b/modules/arch/unix/config5.m4 index 528cf66860..fb3352e620 100644 --- a/modules/arch/unix/config5.m4 +++ b/modules/arch/unix/config5.m4 @@ -10,6 +10,7 @@ else fi APACHE_MODULE(unixd, unix specific support, , , $unixd_mods_enable) +APACHE_MODULE(privileges, Per-virtualhost Unix UserIDs and enhanced security for Solaris) APACHE_MODPATH_FINISH diff --git a/modules/arch/unix/mod_privileges.c b/modules/arch/unix/mod_privileges.c new file mode 100644 index 0000000000..b274606169 --- /dev/null +++ b/modules/arch/unix/mod_privileges.c @@ -0,0 +1,427 @@ +/* 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 <priv.h> +#include <sys/types.h> +#include <unistd.h> + +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" +#include "http_log.h" +#include "mpm_common.h" +#include "ap_mpm.h" +#include "apr_strings.h" + +/* TODO - get rid of unixd dependency */ +#include "unixd.h" + +#define CFG_CHECK(x) if (x == -1) return strerror(errno); +#define CR_CHECK(x) if (x == -1) \ + ap_log_error(APLOG_MARK, APLOG_CRIT,0,0, \ + "Failed to initialise privileges: %s", strerror(errno)) + +module AP_MODULE_DECLARE_DATA privileges_module; + +/* #define BIG_SECURITY_HOLE 1 */ + +typedef struct { + priv_set_t *priv; + priv_set_t *child_priv; + uid_t uid; + gid_t gid; +} priv_cfg; + +static priv_set_t *priv_setid; +static priv_set_t *priv_default = NULL; +static int dtrace_enabled = 0; + +static apr_status_t priv_cfg_cleanup(void *CFG) +{ + priv_cfg *cfg = CFG; + priv_freeset(cfg->priv); + priv_freeset(cfg->child_priv); + return APR_SUCCESS; +} +static void *privileges_create_cfg(apr_pool_t *pool, server_rec *s) +{ + priv_cfg *cfg = apr_palloc(pool, sizeof(priv_cfg)); + + /* Start at basic privileges all round. */ + cfg->priv = priv_str_to_set("basic", ",", NULL); + cfg->child_priv = priv_str_to_set("basic", ",", NULL); + + /* By default, run in secure mode. + * That means dropping basic privileges we don't usually need. + */ + CR_CHECK(priv_delset(cfg->priv, PRIV_FILE_LINK_ANY)); + CR_CHECK(priv_delset(cfg->priv, PRIV_PROC_INFO)); + CR_CHECK(priv_delset(cfg->priv, PRIV_PROC_SESSION)); + +/* Hmmm, should CGI default to secure too ? */ +/* + CR_CHECK(priv_delset(cfg->child_priv, PRIV_FILE_LINK_ANY)); + CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_INFO)); + CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_SESSION)); + CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_FORK)); + CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_EXEC)); +*/ + + /* we´ll use 0 for unset */ + cfg->uid = 0; + cfg->gid = 0; + apr_pool_cleanup_register(pool, cfg, priv_cfg_cleanup, + apr_pool_cleanup_null); + + /* top-level default_priv wants the top-level cfg */ + if (priv_default == NULL) { + priv_default = cfg->priv; + } + return cfg; +} + +static apr_status_t privileges_end_req(void *data) +{ + request_rec *r = data; + priv_cfg *cfg = ap_get_module_config(r->server->module_config, + &privileges_module); + + /* ugly hack: grab default uid and gid from unixd */ + extern unixd_config_rec unixd_config; + + /* if either user or group are not the default, restore them */ + if (cfg->uid || cfg->gid) { + if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "PRIV_ON failed restoring default user/group"); + } + if (cfg->uid && (setuid(unixd_config.user_id) == -1)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error restoring default userid"); + } + if (cfg->gid && (setgid(unixd_config.group_id) == -1)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error restoring default group"); + } + } + + /* restore default privileges */ + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_default) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error restoring default privileges: %s"); + } + return APR_SUCCESS; +} +static int privileges_req(request_rec *r) +{ + priv_cfg *cfg = ap_get_module_config(r->server->module_config, + &privileges_module); + + /* cleanup should happen even if something fails part-way through here */ + apr_pool_cleanup_register(r->pool, r, privileges_end_req, + apr_pool_cleanup_null); + + /* set user and group if configured */ + if (cfg->uid || cfg->gid) { + if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "No privilege to set user/group"); + } + /* if we should be able to set these but can't, it could be + * a serious security issue. Bail out rather than risk it! + */ + if (cfg->uid && (setuid(cfg->uid) == -1)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error setting userid"); + return HTTP_INTERNAL_SERVER_ERROR; + } + if (cfg->gid && (setgid(cfg->gid) == -1)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error setting group"); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + /* set vhost's privileges */ + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, cfg->priv) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error setting effective privileges: %s"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* ... including those of any subprocesses */ + if (setppriv(PRIV_SET, PRIV_INHERITABLE, cfg->child_priv) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error setting inheritable privileges: %s"); + return HTTP_INTERNAL_SERVER_ERROR; + } + if (setppriv(PRIV_SET, PRIV_LIMIT, cfg->child_priv) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error setting limit privileges: %s"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + return OK; +} +#define PDROP_CHECK(x) if (x == -1) { \ + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, \ + "Error dropping privileges: %s", strerror(errno)); \ + return !OK; \ + } + +static int privileges_drop_first(apr_pool_t *pool, server_rec *s) +{ + /* We need to set privileges before mod_unixd, + * 'cos otherwise setuid will wipe our privilege to do so + */ + priv_cfg *spcfg; + server_rec *sp; + priv_set_t *ppriv = priv_allocset(); + + /* compute ppriv from the union of all the vhosts plus setid */ + priv_copyset(priv_setid, ppriv); + for (sp = s; sp != NULL; sp=sp->next) { + spcfg = ap_get_module_config(sp->module_config, &privileges_module); + priv_union(spcfg->priv, ppriv); + } + PDROP_CHECK(setppriv(PRIV_SET, PRIV_PERMITTED, ppriv)) + PDROP_CHECK(setppriv(PRIV_SET, PRIV_EFFECTIVE, ppriv)) + priv_freeset(ppriv); + + return OK; +} +static int privileges_drop_last(apr_pool_t *pool, server_rec *s) +{ + /* Our config stuff has set the privileges we need, so now + * we just set them to those of the parent server_rec + * + * This has to happen after mod_unixd, 'cos mod_unixd needs + * privileges we drop here. + */ + priv_cfg *cfg = ap_get_module_config(s->module_config, &privileges_module); + + /* defaults - the default vhost */ + PDROP_CHECK(setppriv(PRIV_SET, PRIV_LIMIT, cfg->child_priv)) + PDROP_CHECK(setppriv(PRIV_SET, PRIV_INHERITABLE, cfg->child_priv)) + PDROP_CHECK(setppriv(PRIV_SET, PRIV_EFFECTIVE, cfg->priv)) + + return OK; +} +static apr_status_t privileges_term(void *rec) +{ + priv_freeset(priv_setid); + return APR_SUCCESS; +} +static int privileges_postconf(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + priv_cfg *cfg; + server_rec *sp; + + /* if we have dtrace enabled, merge it into everything */ + if (dtrace_enabled) { + for (sp = s; sp != NULL; sp = sp->next) { + cfg = ap_get_module_config(sp->module_config, &privileges_module); + CR_CHECK(priv_addset(cfg->priv, PRIV_DTRACE_KERNEL)); + CR_CHECK(priv_addset(cfg->priv, PRIV_DTRACE_PROC)); + CR_CHECK(priv_addset(cfg->priv, PRIV_DTRACE_USER)); + CR_CHECK(priv_addset(cfg->child_priv, PRIV_DTRACE_KERNEL)); + CR_CHECK(priv_addset(cfg->child_priv, PRIV_DTRACE_PROC)); + CR_CHECK(priv_addset(cfg->child_priv, PRIV_DTRACE_USER)); + } + CR_CHECK(priv_addset(priv_default, PRIV_DTRACE_KERNEL)); + CR_CHECK(priv_addset(priv_default, PRIV_DTRACE_PROC)); + CR_CHECK(priv_addset(priv_default, PRIV_DTRACE_USER)); + } + + /* set up priv_setid for per-request use */ + priv_setid = priv_allocset(); + apr_pool_cleanup_register(pconf, NULL, privileges_term, + apr_pool_cleanup_null); + priv_emptyset(priv_setid); + if (priv_addset(priv_setid, PRIV_PROC_SETID) == -1) { + ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, ptemp, + "priv_addset: ", strerror(errno)); + return !OK; + } + return OK; +} +static int privileges_init(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp) +{ + /* refuse to work if the MPM is threaded */ + int threaded; + int rv = ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded); + if (rv != APR_SUCCESS) { + ap_log_perror(APLOG_MARK, APLOG_NOTICE, rv, ptemp, + "mod_privileges: unable to determine MPM characteristics." + " Please ensure you are using a non-threaded MPM " + "with this module."); + } + if (threaded) { + ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, ptemp, + "mod_privileges is not compatible with a threaded MPM."); + return !OK; + } + return OK; +} +static void privileges_hooks(apr_pool_t *pool) +{ + ap_hook_post_read_request(privileges_req, NULL, NULL, + APR_HOOK_REALLY_FIRST); + ap_hook_drop_privileges(privileges_drop_first, NULL, NULL, APR_HOOK_FIRST); + ap_hook_drop_privileges(privileges_drop_last, NULL, NULL, APR_HOOK_LAST); + ap_hook_post_config(privileges_postconf, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_pre_config(privileges_init, NULL, NULL, APR_HOOK_FIRST); +} + +static const char *vhost_user(cmd_parms *cmd, void *dir, const char *arg) +{ + priv_cfg *cfg = ap_get_module_config(cmd->server->module_config, + &privileges_module); + cfg->uid = ap_uname2id(arg); + if (cfg->uid == 0) { + return apr_pstrcat(cmd->pool, "Invalid userid for VHostUser: ", + arg, NULL); + } + return NULL; +} +static const char *vhost_group(cmd_parms *cmd, void *dir, const char *arg) +{ + priv_cfg *cfg = ap_get_module_config(cmd->server->module_config, + &privileges_module); + cfg->gid = ap_gname2id(arg); + if (cfg->uid == 0) { + return apr_pstrcat(cmd->pool, "Invalid groupid for VHostGroup: ", + arg, NULL); + } + return NULL; +} +static const char *vhost_secure(cmd_parms *cmd, void *dir, int arg) +{ + priv_cfg *cfg = ap_get_module_config(cmd->server->module_config, + &privileges_module); + if (!arg) { + /* add basic privileges, excluding those covered by cgimode */ + CFG_CHECK(priv_addset(cfg->priv, PRIV_FILE_LINK_ANY)); + CFG_CHECK(priv_addset(cfg->priv, PRIV_PROC_INFO)); + CFG_CHECK(priv_addset(cfg->priv, PRIV_PROC_SESSION)); + } + return NULL; +} +static const char *vhost_cgimode(cmd_parms *cmd, void *dir, const char *arg) +{ + priv_cfg *cfg = ap_get_module_config(cmd->server->module_config, + &privileges_module); + if (!strcasecmp(arg, "on")) { + /* default - nothing to do */ + } + else if (!strcasecmp(arg, "off")) { + /* drop fork+exec privs */ + CFG_CHECK(priv_delset(cfg->priv, PRIV_PROC_FORK)); + CFG_CHECK(priv_delset(cfg->priv, PRIV_PROC_EXEC)); + } + else if (!strcasecmp(arg, "secure")) { + /* deny privileges to CGI procs */ + CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_FORK)); + CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_EXEC)); + CFG_CHECK(priv_delset(cfg->child_priv, PRIV_FILE_LINK_ANY)); + CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_INFO)); + CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_SESSION)); + } + else { + return "VHostCGIMode must be On, Off or Secure"; + } + + return NULL; +} +static const char *dtraceenable(cmd_parms *cmd, void *dir, int arg) +{ + priv_cfg *cfg = ap_get_module_config(cmd->server->module_config, + &privileges_module); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + dtrace_enabled = arg; + return NULL; +} + +#ifdef BIG_SECURITY_HOLE +static const char *vhost_privs(cmd_parms *cmd, void *dir, const char *arg) +{ + priv_cfg *cfg = ap_get_module_config(cmd->server->module_config, + &privileges_module); + const char *priv = arg; + + if (*priv == '-') { + CFG_CHECK(priv_delset(cfg->priv, priv+1)); + } + else if (*priv == '+') { + CFG_CHECK(priv_addset(cfg->priv, priv+1)); + } + else { + priv_emptyset(cfg->priv); + CFG_CHECK(priv_addset(cfg->priv, priv)); + } + return NULL; +} +static const char *vhost_cgiprivs(cmd_parms *cmd, void *dir, const char *arg) +{ + priv_cfg *cfg = ap_get_module_config(cmd->server->module_config, + &privileges_module); + const char *priv = arg; + if (*priv == '-') { + CFG_CHECK(priv_delset(cfg->child_priv, priv+1)); + } + else if (*priv == '+') { + CFG_CHECK(priv_addset(cfg->child_priv, priv+1)); + } + else { + priv_emptyset(cfg->child_priv); + CFG_CHECK(priv_addset(cfg->child_priv, priv)); + } + return NULL; +} +#endif + +static const command_rec privileges_cmds[] = { + AP_INIT_TAKE1("VHostUser", vhost_user, NULL, RSRC_CONF, + "Userid under which the virtualhost will run"), + AP_INIT_TAKE1("VHostGroup", vhost_group, NULL, RSRC_CONF, + "Group under which the virtualhost will run"), + AP_INIT_FLAG("VHostSecure", vhost_secure, NULL, RSRC_CONF, + "Run in secure mode (default ON)"), + AP_INIT_TAKE1("VHostCGIMode", vhost_cgimode, NULL, RSRC_CONF, + "Enable fork+exec for this virtualhost (Off|Secure|On)"), + AP_INIT_FLAG("DTracePrivileges", dtraceenable, NULL, RSRC_CONF, + "Enable DTrace"), +#ifdef BIG_SECURITY_HOLE + AP_INIT_ITERATE("VHostPrivs", vhost_privs, NULL, RSRC_CONF, + "Privileges available in the (virtual) server"), + AP_INIT_ITERATE("VHostCGIPrivs", vhost_cgiprivs, NULL, RSRC_CONF, + "Privileges available to external programs"), +#endif + {NULL} +}; +module AP_MODULE_DECLARE_DATA privileges_module = { + STANDARD20_MODULE_STUFF, + NULL, + NULL, + privileges_create_cfg, + NULL, + privileges_cmds, + privileges_hooks +}; |