Apache HTTPD
mod_privileges.c
Go to the documentation of this file.
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, y) if (x == -1) \
38 ap_log_error(APLOG_MARK, APLOG_CRIT, errno, 0, y \
39 "Failed to initialise privileges")
40
41module AP_MODULE_DECLARE_DATA privileges_module;
42
43/* #define BIG_SECURITY_HOLE 1 */
44
46
54
55typedef struct {
58
61static int dtrace_enabled = 0;
62
64{
65 priv_cfg *cfg = CFG;
66 priv_freeset(cfg->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 */
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}
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 */
93
94/* Hmmm, should CGI default to secure too ? */
95/*
96 CR_CHECK(priv_delset(cfg->child_priv, PRIV_FILE_LINK_ANY), APLOGNO(03163));
97 CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_INFO), APLOGNO(03164));
98 CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_SESSION), APLOGNO(03165));
99 CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_FORK), APLOGNO(03166));
100 CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_EXEC), APLOGNO(03167));
101*/
102
103 /* we´ll use 0 for unset */
104 cfg->uid = 0;
105 cfg->gid = 0;
106 cfg->mode = PRIV_UNSET;
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}
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{
125 priv_dir_cfg *add = ADD;
127 ret->mode = (add->mode == PRIV_UNSET) ? base->mode : add->mode;
128 return ret;
129}
130
132{
133 request_rec *r = data;
135 &privileges_module);
137 &privileges_module);
138
139 /* ugly hack: grab default uid and gid from unixd */
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) {
154 "PRIV_ON failed restoring default user/group");
155 }
156 if (cfg->uid && (setuid(ap_unixd_config.user_id) == -1)) {
158 "Error restoring default userid");
159 }
160 if (cfg->gid && (setgid(ap_unixd_config.group_id) == -1)) {
162 "Error restoring default group");
163 }
164 }
165
166 /* restore default privileges */
169 "Error restoring default privileges");
170 }
171 return APR_SUCCESS;
172}
174{
175 /* secure mode: fork a process to handle the request */
177 apr_status_t rv;
178 int exitcode;
180 int fork_req;
182 &privileges_module);
183
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. */
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:
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 */
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:
246 "Failed to fork secure child process!");
248 }
249 }
250
251 /* OK, now drop privileges. */
252
253 /* cleanup should happen even if something fails part-way through here */
256 /* set user and group if configured */
257 if (cfg->uid || cfg->gid) {
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)) {
267 "Error setting userid");
269 }
270 if (cfg->gid && (setgid(cfg->gid) == -1)) {
272 "Error setting group");
274 }
275 }
276 /* set vhost's privileges */
277 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, cfg->priv) == -1) {
279 "Error setting effective privileges");
281 }
282
283 /* ... including those of any subprocesses */
284 if (setppriv(PRIV_SET, PRIV_INHERITABLE, cfg->child_priv) == -1) {
286 "Error setting inheritable privileges");
288 }
289 if (setppriv(PRIV_SET, PRIV_LIMIT, cfg->child_priv) == -1) {
291 "Error setting limit privileges");
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) {
299 "Error setting permitted privileges");
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
313{
314 /* We need to set privileges before mod_unixd,
315 * 'cos otherwise setuid will wipe our privilege to do so
316 */
318 server_rec *sp;
320
321 /* compute ppriv from the union of all the vhosts plus setid */
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 }
330
331 return OK;
332}
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 */
347
348 return OK;
349}
351{
353 return APR_SUCCESS;
354}
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);
371 }
375 }
376
377 /* set up priv_setid for per-request use */
384 "priv_addset");
385 return !OK;
386 }
387 return OK;
388}
390 apr_pool_t *ptemp)
391{
392 /* refuse to work if the MPM is threaded */
393 int 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}
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 */
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 */
464 }
465 else if (!strcasecmp(arg, "secure")) {
466 /* deny privileges to CGI procs */
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 }
486 return NULL;
487}
488
489static const char *privs_mode(cmd_parms *cmd, void *dir, const char *arg)
490{
492 if (!strcasecmp(arg, "FAST")) {
493 mode = PRIV_FAST;
494 }
495 else if (!strcasecmp(arg, "SECURE")) {
497 }
498 else if (!strcasecmp(arg, "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 {
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"),
571 "tradeoff performance vs security (fast or secure)"),
572#ifdef BIG_SECURITY_HOLE
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};
588};
Apache Multi-Processing Module library.
APR Strings library.
static apr_pool_t * pconf
Definition event.c:441
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
void ap_hook_header_parser(ap_HOOK_header_parser_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:86
void ap_hook_post_config(ap_HOOK_post_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:105
#define AP_DECLARE_MODULE(foo)
#define AP_INIT_FLAG(directive, func, mconfig, where, help)
ap_conf_vector_t * base
#define AP_INIT_ITERATE(directive, func, mconfig, where, help)
#define ap_set_module_config(v, m, val)
void ap_hook_pre_config(ap_HOOK_pre_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:91
request_rec * r
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
#define DONE
Definition httpd.h:458
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_NOTICE
Definition http_log.h:69
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_MARK
Definition http_log.h:283
#define ap_log_perror
Definition http_log.h:412
#define APLOG_CRIT
Definition http_log.h:66
#define APLOG_DEBUG
Definition http_log.h:71
void ap_hook_post_read_request(ap_HOOK_post_read_request_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition protocol.c:2585
void * dummy
Definition http_vhost.h:62
void const char * arg
Definition http_vhost.h:63
void ap_hook_drop_privileges(ap_HOOK_drop_privileges_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mpm_common.c:94
#define APR_CHILD_DONE
Definition apr_errno.h:446
#define APR_INCHILD
Definition apr_errno.h:438
#define APR_INPARENT
Definition apr_errno.h:440
apr_dbd_transaction_t int mode
Definition apr_dbd.h:261
#define APR_HOOK_FIRST
Definition apr_hooks.h:301
#define APR_HOOK_LAST
Definition apr_hooks.h:305
#define APR_HOOK_REALLY_FIRST
Definition apr_hooks.h:299
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
#define ACCESS_CONF
#define RSRC_CONF
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define STANDARD20_MODULE_STUFF
#define GLOBAL_ONLY
const char * ap_check_cmd_context(cmd_parms *cmd, unsigned forbidden)
Definition core.c:1301
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
void * data
int strcasecmp(const char *a, const char *b)
void * rec
Definition apr_hash.h:270
apr_dir_t * dir
const char * s
Definition apr_strings.h:95
apr_proc_t * proc
apr_int32_t apr_int32_t apr_int32_t err
int apr_exit_why_e * exitwhy
apr_cmdtype_e cmd
apr_exit_why_e
int * exitcode
@ APR_WAIT
apr_status_t ap_mpm_query(int query_code, int *result)
Definition mpm_common.c:421
#define AP_MPMQ_IS_THREADED
Definition ap_mpm.h:152
Apache Configuration.
Apache Logging library.
HTTP protocol handling.
HTTP Daemon routines.
#define CR_CHECK(x, y)
static int dtrace_enabled
static int privileges_init(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
static priv_set_t * priv_default
static int privileges_req(request_rec *r)
static const command_rec privileges_cmds[]
static const char * privs_mode(cmd_parms *cmd, void *dir, const char *arg)
static int privileges_drop_first(apr_pool_t *pool, server_rec *s)
static const char * vhost_secure(cmd_parms *cmd, void *dir, int arg)
static int privileges_postconf(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
#define CFG_CHECK(x)
static apr_status_t privileges_term(void *rec)
#define PDROP_CHECK(x)
static priv_set_t * priv_setid
priv_mode
@ PRIV_SELECTIVE
@ PRIV_FAST
@ PRIV_UNSET
@ PRIV_SECURE
static const char * vhost_group(cmd_parms *cmd, void *dir, const char *arg)
static const char * vhost_cgimode(cmd_parms *cmd, void *dir, const char *arg)
static void * privileges_merge_cfg(apr_pool_t *pool, void *BASE, void *ADD)
static void * privileges_create_dir_cfg(apr_pool_t *pool, char *dummy)
static const char * dtraceenable(cmd_parms *cmd, void *dir, int arg)
static apr_status_t priv_cfg_cleanup(void *CFG)
static const char * vhost_user(cmd_parms *cmd, void *dir, const char *arg)
static void * privileges_create_cfg(apr_pool_t *pool, server_rec *s)
static void privileges_hooks(apr_pool_t *pool)
static int privileges_drop_last(apr_pool_t *pool, server_rec *s)
static apr_status_t privileges_end_req(void *data)
static void * privileges_merge_dir_cfg(apr_pool_t *pool, void *BASE, void *ADD)
return NULL
Definition mod_so.c:359
Multi-Processing Modules functions.
priv_mode mode
priv_set_t * priv
priv_set_t * child_priv
priv_mode mode
A structure that represents the current request.
Definition httpd.h:845
int eos_sent
Definition httpd.h:1039
apr_pool_t * pool
Definition httpd.h:847
struct ap_conf_vector_t * request_config
Definition httpd.h:1049
apr_table_t * headers_in
Definition httpd.h:976
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
A structure to store information for each virtual server.
Definition httpd.h:1322
struct ap_conf_vector_t * module_config
Definition httpd.h:1341