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
41 module AP_MODULE_DECLARE_DATA privileges_module;
42
43 /* #define BIG_SECURITY_HOLE 1 */
44
45 typedef enum { PRIV_UNSET, PRIV_FAST, PRIV_SECURE, PRIV_SELECTIVE } priv_mode;
46
47 typedef 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
55 typedef struct {
56 priv_mode mode;
57 } priv_dir_cfg;
58
59 static priv_set_t *priv_setid;
60 static priv_set_t *priv_default = NULL;
61 static int dtrace_enabled = 0;
62
priv_cfg_cleanup(void * CFG)63 static 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 }
privileges_merge_cfg(apr_pool_t * pool,void * BASE,void * ADD)70 static 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 }
privileges_create_cfg(apr_pool_t * pool,server_rec * s)79 static 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 }
privileges_create_dir_cfg(apr_pool_t * pool,char * dummy)116 static 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 }
privileges_merge_dir_cfg(apr_pool_t * pool,void * BASE,void * ADD)122 static 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
privileges_end_req(void * data)131 static 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 }
privileges_req(request_rec * r)173 static 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
privileges_drop_first(apr_pool_t * pool,server_rec * s)312 static 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 }
privileges_drop_last(apr_pool_t * pool,server_rec * s)333 static 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 }
privileges_term(void * rec)350 static apr_status_t privileges_term(void *rec)
351 {
352 priv_freeset(priv_setid);
353 return APR_SUCCESS;
354 }
privileges_postconf(apr_pool_t * pconf,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)355 static 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 }
privileges_init(apr_pool_t * pconf,apr_pool_t * plog,apr_pool_t * ptemp)389 static 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 }
privileges_hooks(apr_pool_t * pool)408 static 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
vhost_user(cmd_parms * cmd,void * dir,const char * arg)419 static 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 }
vhost_group(cmd_parms * cmd,void * dir,const char * arg)430 static 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 }
vhost_secure(cmd_parms * cmd,void * dir,int arg)441 static 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 }
vhost_cgimode(cmd_parms * cmd,void * dir,const char * arg)453 static 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 }
dtraceenable(cmd_parms * cmd,void * dir,int arg)479 static 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
privs_mode(cmd_parms * cmd,void * dir,const char * arg)489 static 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
vhost_privs(cmd_parms * cmd,void * dir,const char * arg)523 static 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 }
vhost_cgiprivs(cmd_parms * cmd,void * dir,const char * arg)541 static 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
559 static 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 };
580 AP_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