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