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
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include <priv.h>
18#include <sys/types.h>
19#include <unistd.h>
20
21#include "httpd.h"
22#include "http_config.h"
23#include "http_protocol.h"
24#include "http_log.h"
25#include "mpm_common.h"
26#include "ap_mpm.h"
27#include "apr_strings.h"
28
29/* TODO - get rid of unixd dependency */
30#include "unixd.h"
31
32#define CFG_CHECK(x) if ((x) == -1) { \
33    char msgbuf[128]; \
34    apr_strerror(errno, msgbuf, sizeof(msgbuf)); \
35    return apr_pstrdup(cmd->pool, msgbuf); \
36}
37#define CR_CHECK(x) if (x == -1) \
38    ap_log_error(APLOG_MARK, APLOG_CRIT, errno, 0, \
39                 "Failed to initialise privileges")
40
41module AP_MODULE_DECLARE_DATA privileges_module;
42
43/* #define BIG_SECURITY_HOLE 1 */
44
45typedef enum { PRIV_UNSET, PRIV_FAST, PRIV_SECURE, PRIV_SELECTIVE } priv_mode;
46
47typedef struct {
48    priv_set_t *priv;
49    priv_set_t *child_priv;
50    uid_t uid;
51    gid_t gid;
52    priv_mode mode;
53} priv_cfg;
54
55typedef struct {
56    priv_mode mode;
57} priv_dir_cfg;
58
59static priv_set_t *priv_setid;
60static priv_set_t *priv_default = NULL;
61static int dtrace_enabled = 0;
62
63static apr_status_t priv_cfg_cleanup(void *CFG)
64{
65    priv_cfg *cfg = CFG;
66    priv_freeset(cfg->priv);
67    priv_freeset(cfg->child_priv);
68    return APR_SUCCESS;
69}
70static void *privileges_merge_cfg(apr_pool_t *pool, void *BASE, void *ADD)
71{
72    /* inherit the mode if it's not set; the rest won't be inherited */
73    priv_cfg *base = BASE;
74    priv_cfg *add = ADD;
75    priv_cfg *ret = apr_pmemdup(pool, add, sizeof(priv_cfg));
76    ret->mode = (add->mode == PRIV_UNSET) ? base->mode : add->mode;
77    return ret;
78}
79static void *privileges_create_cfg(apr_pool_t *pool, server_rec *s)
80{
81    priv_cfg *cfg = apr_palloc(pool, sizeof(priv_cfg));
82
83    /* Start at basic privileges all round. */
84    cfg->priv = priv_str_to_set("basic", ",", NULL);
85    cfg->child_priv = priv_str_to_set("basic", ",", NULL);
86
87    /* By default, run in secure vhost mode.
88     * That means dropping basic privileges we don't usually need.
89     */
90    CR_CHECK(priv_delset(cfg->priv, PRIV_FILE_LINK_ANY));
91    CR_CHECK(priv_delset(cfg->priv, PRIV_PROC_INFO));
92    CR_CHECK(priv_delset(cfg->priv, PRIV_PROC_SESSION));
93
94/* Hmmm, should CGI default to secure too ? */
95/*
96    CR_CHECK(priv_delset(cfg->child_priv, PRIV_FILE_LINK_ANY));
97    CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_INFO));
98    CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_SESSION));
99    CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_FORK));
100    CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_EXEC));
101*/
102
103    /* we´ll use 0 for unset */
104    cfg->uid = 0;
105    cfg->gid = 0;
106    cfg->mode = PRIV_UNSET;
107    apr_pool_cleanup_register(pool, cfg, priv_cfg_cleanup,
108                              apr_pool_cleanup_null);
109
110    /* top-level default_priv wants the top-level cfg */
111    if (priv_default == NULL) {
112        priv_default = cfg->priv;
113    }
114    return cfg;
115}
116static void *privileges_create_dir_cfg(apr_pool_t *pool, char *dummy)
117{
118    priv_dir_cfg *cfg = apr_palloc(pool, sizeof(priv_dir_cfg));
119    cfg->mode = PRIV_UNSET;
120    return cfg;
121}
122static void *privileges_merge_dir_cfg(apr_pool_t *pool, void *BASE, void *ADD)
123{
124    priv_dir_cfg *base = BASE;
125    priv_dir_cfg *add = ADD;
126    priv_dir_cfg *ret = apr_palloc(pool, sizeof(priv_dir_cfg));
127    ret->mode = (add->mode == PRIV_UNSET) ? base->mode : add->mode;
128    return ret;
129}
130
131static apr_status_t privileges_end_req(void *data)
132{
133    request_rec *r = data;
134    priv_cfg *cfg = ap_get_module_config(r->server->module_config,
135                                         &privileges_module);
136    priv_dir_cfg *dcfg = ap_get_module_config(r->per_dir_config,
137                                              &privileges_module);
138
139    /* ugly hack: grab default uid and gid from unixd */
140    extern unixd_config_rec ap_unixd_config;
141
142    /* If we forked a child, we dropped privilege to revert, so
143     * all we can do now is exit
144     */
145    if ((cfg->mode == PRIV_SECURE) ||
146        ((cfg->mode == PRIV_SELECTIVE) && (dcfg->mode == PRIV_SECURE))) {
147        exit(0);
148    }
149
150    /* if either user or group are not the default, restore them */
151    if (cfg->uid || cfg->gid) {
152        if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) {
153            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02136)
154                          "PRIV_ON failed restoring default user/group");
155        }
156        if (cfg->uid && (setuid(ap_unixd_config.user_id) == -1)) {
157            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02137)
158                          "Error restoring default userid");
159        }
160        if (cfg->gid && (setgid(ap_unixd_config.group_id) == -1)) {
161            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02138)
162                          "Error restoring default group");
163        }
164    }
165
166    /* restore default privileges */
167    if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_default) == -1) {
168        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02139)
169                      "Error restoring default privileges");
170    }
171    return APR_SUCCESS;
172}
173static int privileges_req(request_rec *r)
174{
175    /* secure mode: fork a process to handle the request */
176    apr_proc_t proc;
177    apr_status_t rv;
178    int exitcode;
179    apr_exit_why_e exitwhy;
180    int fork_req;
181    priv_cfg *cfg = ap_get_module_config(r->server->module_config,
182                                         &privileges_module);
183
184    void *breadcrumb = ap_get_module_config(r->request_config,
185                                            &privileges_module);
186
187    if (!breadcrumb) {
188        /* first call: this is the vhost */
189        fork_req = (cfg->mode == PRIV_SECURE);
190
191        /* set breadcrumb */
192        ap_set_module_config(r->request_config, &privileges_module, &cfg->mode);
193
194        /* If we have per-dir config, defer doing anything */
195        if ((cfg->mode == PRIV_SELECTIVE)) {
196            /* Defer dropping privileges 'til we have a directory
197             * context that'll tell us whether to fork.
198             */
199            return DECLINED;
200        }
201    }
202    else {
203        /* second call is for per-directory. */
204        priv_dir_cfg *dcfg;
205        if ((cfg->mode != PRIV_SELECTIVE)) {
206            /* Our fate was already determined for the vhost -
207             * nothing to do per-directory
208             */
209            return DECLINED;
210        }
211        dcfg = ap_get_module_config(r->per_dir_config, &privileges_module);
212        fork_req = (dcfg->mode == PRIV_SECURE);
213    }
214
215    if (fork_req) {
216       rv = apr_proc_fork(&proc, r->pool);
217        switch (rv) {
218        case APR_INPARENT:
219            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02140)
220                          "parent waiting for child");
221            /* FIXME - does the child need to run synchronously?
222             * esp. if we enable mod_privileges with threaded MPMs?
223             * We do need at least to ensure r outlives the child.
224             */
225            rv = apr_proc_wait(&proc, &exitcode, &exitwhy, APR_WAIT);
226            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02141) "parent: child %s",
227                          (rv == APR_CHILD_DONE) ? "done" : "notdone");
228
229            /* The child has taken responsibility for reading all input
230             * and sending all output.  So we need to bow right out,
231             * and even abandon "normal" housekeeping.
232             */
233            r->eos_sent = 1;
234            apr_table_unset(r->headers_in, "Content-Type");
235            apr_table_unset(r->headers_in, "Content-Length");
236            /* Testing with ab and 100k requests reveals no nasties
237             * so I infer we're not leaking anything like memory
238             * or file descriptors.  That's nice!
239             */
240            return DONE;
241        case APR_INCHILD:
242            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02142) "In child!");
243            break;  /* now we'll drop privileges in the child */
244        default:
245            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02143)
246                          "Failed to fork secure child process!");
247            return HTTP_INTERNAL_SERVER_ERROR;
248        }
249    }
250
251    /* OK, now drop privileges. */
252
253    /* cleanup should happen even if something fails part-way through here */
254    apr_pool_cleanup_register(r->pool, r, privileges_end_req,
255                              apr_pool_cleanup_null);
256    /* set user and group if configured */
257    if (cfg->uid || cfg->gid) {
258        if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) {
259            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02144)
260                          "No privilege to set user/group");
261        }
262        /* if we should be able to set these but can't, it could be
263         * a serious security issue.  Bail out rather than risk it!
264         */
265        if (cfg->uid && (setuid(cfg->uid) == -1)) {
266            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02145)
267                          "Error setting userid");
268            return HTTP_INTERNAL_SERVER_ERROR;
269        }
270        if (cfg->gid && (setgid(cfg->gid) == -1)) {
271            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02146)
272                          "Error setting group");
273            return HTTP_INTERNAL_SERVER_ERROR;
274        }
275    }
276    /* set vhost's privileges */
277    if (setppriv(PRIV_SET, PRIV_EFFECTIVE, cfg->priv) == -1) {
278        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02147)
279                      "Error setting effective privileges");
280        return HTTP_INTERNAL_SERVER_ERROR;
281    }
282
283    /* ... including those of any subprocesses */
284    if (setppriv(PRIV_SET, PRIV_INHERITABLE, cfg->child_priv) == -1) {
285        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02148)
286                      "Error setting inheritable privileges");
287        return HTTP_INTERNAL_SERVER_ERROR;
288    }
289    if (setppriv(PRIV_SET, PRIV_LIMIT, cfg->child_priv) == -1) {
290        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02149)
291                      "Error setting limit privileges");
292        return HTTP_INTERNAL_SERVER_ERROR;
293    }
294
295    /* If we're in a child process, drop down PPERM too */
296    if (fork_req) {
297        if (setppriv(PRIV_SET, PRIV_PERMITTED, cfg->priv) == -1) {
298            ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02150)
299                          "Error setting permitted privileges");
300            return HTTP_INTERNAL_SERVER_ERROR;
301        }
302    }
303
304    return OK;
305}
306#define PDROP_CHECK(x) if (x == -1) { \
307        ap_log_error(APLOG_MARK, APLOG_CRIT, errno, s, APLOGNO(02151) \
308                     "Error dropping privileges"); \
309        return !OK; \
310    }
311
312static int privileges_drop_first(apr_pool_t *pool, server_rec *s)
313{
314    /* We need to set privileges before mod_unixd,
315     * 'cos otherwise setuid will wipe our privilege to do so
316     */
317    priv_cfg *spcfg;
318    server_rec *sp;
319    priv_set_t *ppriv = priv_allocset();
320
321    /* compute ppriv from the union of all the vhosts plus setid */
322    priv_copyset(priv_setid, ppriv);
323    for (sp = s; sp != NULL; sp=sp->next) {
324        spcfg = ap_get_module_config(sp->module_config, &privileges_module);
325        priv_union(spcfg->priv, ppriv);
326    }
327    PDROP_CHECK(setppriv(PRIV_SET, PRIV_PERMITTED, ppriv))
328    PDROP_CHECK(setppriv(PRIV_SET, PRIV_EFFECTIVE, ppriv))
329    priv_freeset(ppriv);
330
331    return OK;
332}
333static int privileges_drop_last(apr_pool_t *pool, server_rec *s)
334{
335    /* Our config stuff has set the privileges we need, so now
336     * we just set them to those of the parent server_rec
337     *
338     * This has to happen after mod_unixd, 'cos mod_unixd needs
339     * privileges we drop here.
340     */
341    priv_cfg *cfg = ap_get_module_config(s->module_config, &privileges_module);
342
343    /* defaults - the default vhost */
344    PDROP_CHECK(setppriv(PRIV_SET, PRIV_LIMIT, cfg->child_priv))
345    PDROP_CHECK(setppriv(PRIV_SET, PRIV_INHERITABLE, cfg->child_priv))
346    PDROP_CHECK(setppriv(PRIV_SET, PRIV_EFFECTIVE, cfg->priv))
347
348    return OK;
349}
350static apr_status_t privileges_term(void *rec)
351{
352    priv_freeset(priv_setid);
353    return APR_SUCCESS;
354}
355static int privileges_postconf(apr_pool_t *pconf, apr_pool_t *plog,
356                               apr_pool_t *ptemp, server_rec *s)
357{
358    priv_cfg *cfg;
359    server_rec *sp;
360
361    /* if we have dtrace enabled, merge it into everything */
362    if (dtrace_enabled) {
363        for (sp = s; sp != NULL; sp = sp->next) {
364            cfg = ap_get_module_config(sp->module_config, &privileges_module);
365            CR_CHECK(priv_addset(cfg->priv, PRIV_DTRACE_KERNEL));
366            CR_CHECK(priv_addset(cfg->priv, PRIV_DTRACE_PROC));
367            CR_CHECK(priv_addset(cfg->priv, PRIV_DTRACE_USER));
368            CR_CHECK(priv_addset(cfg->child_priv, PRIV_DTRACE_KERNEL));
369            CR_CHECK(priv_addset(cfg->child_priv, PRIV_DTRACE_PROC));
370            CR_CHECK(priv_addset(cfg->child_priv, PRIV_DTRACE_USER));
371        }
372        CR_CHECK(priv_addset(priv_default, PRIV_DTRACE_KERNEL));
373        CR_CHECK(priv_addset(priv_default, PRIV_DTRACE_PROC));
374        CR_CHECK(priv_addset(priv_default, PRIV_DTRACE_USER));
375    }
376
377    /* set up priv_setid for per-request use */
378    priv_setid = priv_allocset();
379    apr_pool_cleanup_register(pconf, NULL, privileges_term,
380                              apr_pool_cleanup_null);
381    priv_emptyset(priv_setid);
382    if (priv_addset(priv_setid, PRIV_PROC_SETID) == -1) {
383        ap_log_perror(APLOG_MARK, APLOG_CRIT, errno, ptemp, APLOGNO(02152)
384                      "priv_addset");
385        return !OK;
386    }
387    return OK;
388}
389static int privileges_init(apr_pool_t *pconf, apr_pool_t *plog,
390                           apr_pool_t *ptemp)
391{
392    /* refuse to work if the MPM is threaded */
393    int threaded;
394    int rv = ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded);
395    if (rv != APR_SUCCESS) {
396        ap_log_perror(APLOG_MARK, APLOG_NOTICE, rv, ptemp, APLOGNO(02153)
397                      "mod_privileges: unable to determine MPM characteristics."
398                      "  Please ensure you are using a non-threaded MPM "
399                      "with this module.");
400    }
401    if (threaded) {
402        ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, ptemp, APLOGNO(02154)
403                      "mod_privileges is not compatible with a threaded MPM.");
404        return !OK;
405    }
406    return OK;
407}
408static void privileges_hooks(apr_pool_t *pool)
409{
410    ap_hook_post_read_request(privileges_req, NULL, NULL,
411                              APR_HOOK_REALLY_FIRST);
412    ap_hook_header_parser(privileges_req, NULL, NULL, APR_HOOK_REALLY_FIRST);
413    ap_hook_drop_privileges(privileges_drop_first, NULL, NULL, APR_HOOK_FIRST);
414    ap_hook_drop_privileges(privileges_drop_last, NULL, NULL, APR_HOOK_LAST);
415    ap_hook_post_config(privileges_postconf, NULL, NULL, APR_HOOK_MIDDLE);
416    ap_hook_pre_config(privileges_init, NULL, NULL, APR_HOOK_FIRST);
417}
418
419static const char *vhost_user(cmd_parms *cmd, void *dir, const char *arg)
420{
421    priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
422                                         &privileges_module);
423    cfg->uid = ap_uname2id(arg);
424    if (cfg->uid == 0) {
425        return apr_pstrcat(cmd->pool, "Invalid userid for VHostUser: ",
426                           arg, NULL);
427    }
428    return NULL;
429}
430static const char *vhost_group(cmd_parms *cmd, void *dir, const char *arg)
431{
432    priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
433                                         &privileges_module);
434    cfg->gid = ap_gname2id(arg);
435    if (cfg->uid == 0) {
436        return apr_pstrcat(cmd->pool, "Invalid groupid for VHostGroup: ",
437                           arg, NULL);
438    }
439    return NULL;
440}
441static const char *vhost_secure(cmd_parms *cmd, void *dir, int arg)
442{
443    priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
444                                         &privileges_module);
445    if (!arg) {
446        /* add basic privileges, excluding those covered by cgimode */
447        CFG_CHECK(priv_addset(cfg->priv, PRIV_FILE_LINK_ANY));
448        CFG_CHECK(priv_addset(cfg->priv, PRIV_PROC_INFO));
449        CFG_CHECK(priv_addset(cfg->priv, PRIV_PROC_SESSION));
450    }
451    return NULL;
452}
453static const char *vhost_cgimode(cmd_parms *cmd, void *dir, const char *arg)
454{
455    priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
456                                         &privileges_module);
457    if (!strcasecmp(arg, "on")) {
458        /* default - nothing to do */
459    }
460    else if (!strcasecmp(arg, "off")) {
461        /* drop fork+exec privs */
462        CFG_CHECK(priv_delset(cfg->priv, PRIV_PROC_FORK));
463        CFG_CHECK(priv_delset(cfg->priv, PRIV_PROC_EXEC));
464    }
465    else if (!strcasecmp(arg, "secure")) {
466        /* deny privileges to CGI procs */
467        CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_FORK));
468        CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_EXEC));
469        CFG_CHECK(priv_delset(cfg->child_priv, PRIV_FILE_LINK_ANY));
470        CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_INFO));
471        CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_SESSION));
472    }
473    else {
474        return "VHostCGIMode must be On, Off or Secure";
475    }
476
477    return NULL;
478}
479static const char *dtraceenable(cmd_parms *cmd, void *dir, int arg)
480{
481    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
482    if (err != NULL) {
483        return err;
484    }
485    dtrace_enabled = arg;
486    return NULL;
487}
488
489static const char *privs_mode(cmd_parms *cmd, void *dir, const char *arg)
490{
491    priv_mode mode = PRIV_UNSET;
492    if (!strcasecmp(arg, "FAST")) {
493        mode = PRIV_FAST;
494    }
495    else if (!strcasecmp(arg, "SECURE")) {
496        mode = PRIV_SECURE;
497    }
498    else if (!strcasecmp(arg, "SELECTIVE")) {
499        mode = PRIV_SELECTIVE;
500    }
501
502    if (cmd->path) {
503        /* In a directory context, set the per_dir_config */
504        priv_dir_cfg *cfg = dir;
505        cfg->mode = mode;
506        if ((mode == PRIV_UNSET) || (mode == PRIV_SELECTIVE)) {
507            return "PrivilegesMode in a Directory context must be FAST or SECURE";
508        }
509    }
510    else {
511        /* In a global or vhost context, set the server config */
512        priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
513                                             &privileges_module);
514        cfg->mode = mode;
515        if (mode == PRIV_UNSET) {
516            return "PrivilegesMode must be FAST, SECURE or SELECTIVE";
517        }
518    }
519    return NULL;
520}
521
522#ifdef BIG_SECURITY_HOLE
523static const char *vhost_privs(cmd_parms *cmd, void *dir, const char *arg)
524{
525    priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
526                                         &privileges_module);
527    const char *priv = arg;
528
529    if (*priv == '-') {
530        CFG_CHECK(priv_delset(cfg->priv, priv+1));
531    }
532    else if (*priv == '+') {
533        CFG_CHECK(priv_addset(cfg->priv, priv+1));
534    }
535    else {
536        priv_emptyset(cfg->priv);
537        CFG_CHECK(priv_addset(cfg->priv, priv));
538    }
539    return NULL;
540}
541static const char *vhost_cgiprivs(cmd_parms *cmd, void *dir, const char *arg)
542{
543    priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
544                                         &privileges_module);
545    const char *priv = arg;
546    if (*priv == '-') {
547        CFG_CHECK(priv_delset(cfg->child_priv, priv+1));
548    }
549    else if (*priv == '+') {
550        CFG_CHECK(priv_addset(cfg->child_priv, priv+1));
551    }
552    else {
553        priv_emptyset(cfg->child_priv);
554        CFG_CHECK(priv_addset(cfg->child_priv, priv));
555    }
556    return NULL;
557}
558#endif
559static const command_rec privileges_cmds[] = {
560    AP_INIT_TAKE1("VHostUser", vhost_user, NULL, RSRC_CONF,
561                  "Userid under which the virtualhost will run"),
562    AP_INIT_TAKE1("VHostGroup", vhost_group, NULL, RSRC_CONF,
563                  "Group under which the virtualhost will run"),
564    AP_INIT_FLAG("VHostSecure", vhost_secure, NULL, RSRC_CONF,
565                 "Run in enhanced security mode (default ON)"),
566    AP_INIT_TAKE1("VHostCGIMode", vhost_cgimode, NULL, RSRC_CONF,
567                  "Enable fork+exec for this virtualhost (Off|Secure|On)"),
568    AP_INIT_FLAG("DTracePrivileges", dtraceenable, NULL, RSRC_CONF,
569                 "Enable DTrace"),
570    AP_INIT_TAKE1("PrivilegesMode", privs_mode, NULL, RSRC_CONF|ACCESS_CONF,
571                  "tradeoff performance vs security (fast or secure)"),
572#ifdef BIG_SECURITY_HOLE
573    AP_INIT_ITERATE("VHostPrivs", vhost_privs, NULL, RSRC_CONF,
574                    "Privileges available in the (virtual) server"),
575    AP_INIT_ITERATE("VHostCGIPrivs", vhost_cgiprivs, NULL, RSRC_CONF,
576                    "Privileges available to external programs"),
577#endif
578    {NULL}
579};
580AP_DECLARE_MODULE(privileges) = {
581    STANDARD20_MODULE_STUFF,
582    privileges_create_dir_cfg,
583    privileges_merge_dir_cfg,
584    privileges_create_cfg,
585    privileges_merge_cfg,
586    privileges_cmds,
587    privileges_hooks
588};
589