/[Apache-SVN]/httpd/httpd/trunk/modules/arch/unix/mod_privileges.c
ViewVC logotype

Contents of /httpd/httpd/trunk/modules/arch/unix/mod_privileges.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 731251 - (show annotations)
Sun Jan 4 13:11:04 2009 UTC (10 months, 3 weeks ago) by rjung
File MIME type: text/plain
File size: 15386 byte(s)
Silence compiler warning about unused variable cfg.

dtrace_enabled is static, so no need for using
the configuration at the moment.
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) return strerror(errno);
33 #define CR_CHECK(x) if (x == -1) \
34 ap_log_error(APLOG_MARK, APLOG_CRIT,0,0, \
35 "Failed to initialise privileges: %s", strerror(errno))
36
37 module AP_MODULE_DECLARE_DATA privileges_module;
38
39 /* #define BIG_SECURITY_HOLE 1 */
40
41 typedef struct {
42 priv_set_t *priv;
43 priv_set_t *child_priv;
44 uid_t uid;
45 gid_t gid;
46 } priv_cfg;
47
48 static priv_set_t *priv_setid;
49 static priv_set_t *priv_default = NULL;
50 static int dtrace_enabled = 0;
51
52 static apr_status_t priv_cfg_cleanup(void *CFG)
53 {
54 priv_cfg *cfg = CFG;
55 priv_freeset(cfg->priv);
56 priv_freeset(cfg->child_priv);
57 return APR_SUCCESS;
58 }
59 static void *privileges_create_cfg(apr_pool_t *pool, server_rec *s)
60 {
61 priv_cfg *cfg = apr_palloc(pool, sizeof(priv_cfg));
62
63 /* Start at basic privileges all round. */
64 cfg->priv = priv_str_to_set("basic", ",", NULL);
65 cfg->child_priv = priv_str_to_set("basic", ",", NULL);
66
67 /* By default, run in secure mode.
68 * That means dropping basic privileges we don't usually need.
69 */
70 CR_CHECK(priv_delset(cfg->priv, PRIV_FILE_LINK_ANY));
71 CR_CHECK(priv_delset(cfg->priv, PRIV_PROC_INFO));
72 CR_CHECK(priv_delset(cfg->priv, PRIV_PROC_SESSION));
73
74 /* Hmmm, should CGI default to secure too ? */
75 /*
76 CR_CHECK(priv_delset(cfg->child_priv, PRIV_FILE_LINK_ANY));
77 CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_INFO));
78 CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_SESSION));
79 CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_FORK));
80 CR_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_EXEC));
81 */
82
83 /* we´ll use 0 for unset */
84 cfg->uid = 0;
85 cfg->gid = 0;
86 apr_pool_cleanup_register(pool, cfg, priv_cfg_cleanup,
87 apr_pool_cleanup_null);
88
89 /* top-level default_priv wants the top-level cfg */
90 if (priv_default == NULL) {
91 priv_default = cfg->priv;
92 }
93 return cfg;
94 }
95
96 static apr_status_t privileges_end_req(void *data)
97 {
98 request_rec *r = data;
99 priv_cfg *cfg = ap_get_module_config(r->server->module_config,
100 &privileges_module);
101
102 /* ugly hack: grab default uid and gid from unixd */
103 extern unixd_config_rec ap_unixd_config;
104
105 /* if either user or group are not the default, restore them */
106 if (cfg->uid || cfg->gid) {
107 if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) {
108 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
109 "PRIV_ON failed restoring default user/group");
110 }
111 if (cfg->uid && (setuid(ap_unixd_config.user_id) == -1)) {
112 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
113 "Error restoring default userid");
114 }
115 if (cfg->gid && (setgid(ap_unixd_config.group_id) == -1)) {
116 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
117 "Error restoring default group");
118 }
119 }
120
121 /* restore default privileges */
122 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_default) == -1) {
123 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
124 "Error restoring default privileges: %s");
125 }
126 return APR_SUCCESS;
127 }
128 static int privileges_req(request_rec *r)
129 {
130 priv_cfg *cfg = ap_get_module_config(r->server->module_config,
131 &privileges_module);
132
133 /* cleanup should happen even if something fails part-way through here */
134 apr_pool_cleanup_register(r->pool, r, privileges_end_req,
135 apr_pool_cleanup_null);
136
137 /* set user and group if configured */
138 if (cfg->uid || cfg->gid) {
139 if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) {
140 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
141 "No privilege to set user/group");
142 }
143 /* if we should be able to set these but can't, it could be
144 * a serious security issue. Bail out rather than risk it!
145 */
146 if (cfg->uid && (setuid(cfg->uid) == -1)) {
147 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
148 "Error setting userid");
149 return HTTP_INTERNAL_SERVER_ERROR;
150 }
151 if (cfg->gid && (setgid(cfg->gid) == -1)) {
152 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
153 "Error setting group");
154 return HTTP_INTERNAL_SERVER_ERROR;
155 }
156 }
157 /* set vhost's privileges */
158 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, cfg->priv) == -1) {
159 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
160 "Error setting effective privileges: %s");
161 return HTTP_INTERNAL_SERVER_ERROR;
162 }
163
164 /* ... including those of any subprocesses */
165 if (setppriv(PRIV_SET, PRIV_INHERITABLE, cfg->child_priv) == -1) {
166 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
167 "Error setting inheritable privileges: %s");
168 return HTTP_INTERNAL_SERVER_ERROR;
169 }
170 if (setppriv(PRIV_SET, PRIV_LIMIT, cfg->child_priv) == -1) {
171 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
172 "Error setting limit privileges: %s");
173 return HTTP_INTERNAL_SERVER_ERROR;
174 }
175
176 return OK;
177 }
178 #define PDROP_CHECK(x) if (x == -1) { \
179 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, \
180 "Error dropping privileges: %s", strerror(errno)); \
181 return !OK; \
182 }
183
184 static int privileges_drop_first(apr_pool_t *pool, server_rec *s)
185 {
186 /* We need to set privileges before mod_unixd,
187 * 'cos otherwise setuid will wipe our privilege to do so
188 */
189 priv_cfg *spcfg;
190 server_rec *sp;
191 priv_set_t *ppriv = priv_allocset();
192
193 /* compute ppriv from the union of all the vhosts plus setid */
194 priv_copyset(priv_setid, ppriv);
195 for (sp = s; sp != NULL; sp=sp->next) {
196 spcfg = ap_get_module_config(sp->module_config, &privileges_module);
197 priv_union(spcfg->priv, ppriv);
198 }
199 PDROP_CHECK(setppriv(PRIV_SET, PRIV_PERMITTED, ppriv))
200 PDROP_CHECK(setppriv(PRIV_SET, PRIV_EFFECTIVE, ppriv))
201 priv_freeset(ppriv);
202
203 return OK;
204 }
205 static int privileges_drop_last(apr_pool_t *pool, server_rec *s)
206 {
207 /* Our config stuff has set the privileges we need, so now
208 * we just set them to those of the parent server_rec
209 *
210 * This has to happen after mod_unixd, 'cos mod_unixd needs
211 * privileges we drop here.
212 */
213 priv_cfg *cfg = ap_get_module_config(s->module_config, &privileges_module);
214
215 /* defaults - the default vhost */
216 PDROP_CHECK(setppriv(PRIV_SET, PRIV_LIMIT, cfg->child_priv))
217 PDROP_CHECK(setppriv(PRIV_SET, PRIV_INHERITABLE, cfg->child_priv))
218 PDROP_CHECK(setppriv(PRIV_SET, PRIV_EFFECTIVE, cfg->priv))
219
220 return OK;
221 }
222 static apr_status_t privileges_term(void *rec)
223 {
224 priv_freeset(priv_setid);
225 return APR_SUCCESS;
226 }
227 static int privileges_postconf(apr_pool_t *pconf, apr_pool_t *plog,
228 apr_pool_t *ptemp, server_rec *s)
229 {
230 priv_cfg *cfg;
231 server_rec *sp;
232
233 /* if we have dtrace enabled, merge it into everything */
234 if (dtrace_enabled) {
235 for (sp = s; sp != NULL; sp = sp->next) {
236 cfg = ap_get_module_config(sp->module_config, &privileges_module);
237 CR_CHECK(priv_addset(cfg->priv, PRIV_DTRACE_KERNEL));
238 CR_CHECK(priv_addset(cfg->priv, PRIV_DTRACE_PROC));
239 CR_CHECK(priv_addset(cfg->priv, PRIV_DTRACE_USER));
240 CR_CHECK(priv_addset(cfg->child_priv, PRIV_DTRACE_KERNEL));
241 CR_CHECK(priv_addset(cfg->child_priv, PRIV_DTRACE_PROC));
242 CR_CHECK(priv_addset(cfg->child_priv, PRIV_DTRACE_USER));
243 }
244 CR_CHECK(priv_addset(priv_default, PRIV_DTRACE_KERNEL));
245 CR_CHECK(priv_addset(priv_default, PRIV_DTRACE_PROC));
246 CR_CHECK(priv_addset(priv_default, PRIV_DTRACE_USER));
247 }
248
249 /* set up priv_setid for per-request use */
250 priv_setid = priv_allocset();
251 apr_pool_cleanup_register(pconf, NULL, privileges_term,
252 apr_pool_cleanup_null);
253 priv_emptyset(priv_setid);
254 if (priv_addset(priv_setid, PRIV_PROC_SETID) == -1) {
255 ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, ptemp,
256 "priv_addset: ", strerror(errno));
257 return !OK;
258 }
259 return OK;
260 }
261 static int privileges_init(apr_pool_t *pconf, apr_pool_t *plog,
262 apr_pool_t *ptemp)
263 {
264 /* refuse to work if the MPM is threaded */
265 int threaded;
266 int rv = ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded);
267 if (rv != APR_SUCCESS) {
268 ap_log_perror(APLOG_MARK, APLOG_NOTICE, rv, ptemp,
269 "mod_privileges: unable to determine MPM characteristics."
270 " Please ensure you are using a non-threaded MPM "
271 "with this module.");
272 }
273 if (threaded) {
274 ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, ptemp,
275 "mod_privileges is not compatible with a threaded MPM.");
276 return !OK;
277 }
278 return OK;
279 }
280 static void privileges_hooks(apr_pool_t *pool)
281 {
282 ap_hook_post_read_request(privileges_req, NULL, NULL,
283 APR_HOOK_REALLY_FIRST);
284 ap_hook_drop_privileges(privileges_drop_first, NULL, NULL, APR_HOOK_FIRST);
285 ap_hook_drop_privileges(privileges_drop_last, NULL, NULL, APR_HOOK_LAST);
286 ap_hook_post_config(privileges_postconf, NULL, NULL, APR_HOOK_MIDDLE);
287 ap_hook_pre_config(privileges_init, NULL, NULL, APR_HOOK_FIRST);
288 }
289
290 static const char *vhost_user(cmd_parms *cmd, void *dir, const char *arg)
291 {
292 priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
293 &privileges_module);
294 cfg->uid = ap_uname2id(arg);
295 if (cfg->uid == 0) {
296 return apr_pstrcat(cmd->pool, "Invalid userid for VHostUser: ",
297 arg, NULL);
298 }
299 return NULL;
300 }
301 static const char *vhost_group(cmd_parms *cmd, void *dir, const char *arg)
302 {
303 priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
304 &privileges_module);
305 cfg->gid = ap_gname2id(arg);
306 if (cfg->uid == 0) {
307 return apr_pstrcat(cmd->pool, "Invalid groupid for VHostGroup: ",
308 arg, NULL);
309 }
310 return NULL;
311 }
312 static const char *vhost_secure(cmd_parms *cmd, void *dir, int arg)
313 {
314 priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
315 &privileges_module);
316 if (!arg) {
317 /* add basic privileges, excluding those covered by cgimode */
318 CFG_CHECK(priv_addset(cfg->priv, PRIV_FILE_LINK_ANY));
319 CFG_CHECK(priv_addset(cfg->priv, PRIV_PROC_INFO));
320 CFG_CHECK(priv_addset(cfg->priv, PRIV_PROC_SESSION));
321 }
322 return NULL;
323 }
324 static const char *vhost_cgimode(cmd_parms *cmd, void *dir, const char *arg)
325 {
326 priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
327 &privileges_module);
328 if (!strcasecmp(arg, "on")) {
329 /* default - nothing to do */
330 }
331 else if (!strcasecmp(arg, "off")) {
332 /* drop fork+exec privs */
333 CFG_CHECK(priv_delset(cfg->priv, PRIV_PROC_FORK));
334 CFG_CHECK(priv_delset(cfg->priv, PRIV_PROC_EXEC));
335 }
336 else if (!strcasecmp(arg, "secure")) {
337 /* deny privileges to CGI procs */
338 CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_FORK));
339 CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_EXEC));
340 CFG_CHECK(priv_delset(cfg->child_priv, PRIV_FILE_LINK_ANY));
341 CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_INFO));
342 CFG_CHECK(priv_delset(cfg->child_priv, PRIV_PROC_SESSION));
343 }
344 else {
345 return "VHostCGIMode must be On, Off or Secure";
346 }
347
348 return NULL;
349 }
350 static const char *dtraceenable(cmd_parms *cmd, void *dir, int arg)
351 {
352 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
353 if (err != NULL) {
354 return err;
355 }
356 dtrace_enabled = arg;
357 return NULL;
358 }
359
360 #ifdef BIG_SECURITY_HOLE
361 static const char *vhost_privs(cmd_parms *cmd, void *dir, const char *arg)
362 {
363 priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
364 &privileges_module);
365 const char *priv = arg;
366
367 if (*priv == '-') {
368 CFG_CHECK(priv_delset(cfg->priv, priv+1));
369 }
370 else if (*priv == '+') {
371 CFG_CHECK(priv_addset(cfg->priv, priv+1));
372 }
373 else {
374 priv_emptyset(cfg->priv);
375 CFG_CHECK(priv_addset(cfg->priv, priv));
376 }
377 return NULL;
378 }
379 static const char *vhost_cgiprivs(cmd_parms *cmd, void *dir, const char *arg)
380 {
381 priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
382 &privileges_module);
383 const char *priv = arg;
384 if (*priv == '-') {
385 CFG_CHECK(priv_delset(cfg->child_priv, priv+1));
386 }
387 else if (*priv == '+') {
388 CFG_CHECK(priv_addset(cfg->child_priv, priv+1));
389 }
390 else {
391 priv_emptyset(cfg->child_priv);
392 CFG_CHECK(priv_addset(cfg->child_priv, priv));
393 }
394 return NULL;
395 }
396 #endif
397
398 static const command_rec privileges_cmds[] = {
399 AP_INIT_TAKE1("VHostUser", vhost_user, NULL, RSRC_CONF,
400 "Userid under which the virtualhost will run"),
401 AP_INIT_TAKE1("VHostGroup", vhost_group, NULL, RSRC_CONF,
402 "Group under which the virtualhost will run"),
403 AP_INIT_FLAG("VHostSecure", vhost_secure, NULL, RSRC_CONF,
404 "Run in secure mode (default ON)"),
405 AP_INIT_TAKE1("VHostCGIMode", vhost_cgimode, NULL, RSRC_CONF,
406 "Enable fork+exec for this virtualhost (Off|Secure|On)"),
407 AP_INIT_FLAG("DTracePrivileges", dtraceenable, NULL, RSRC_CONF,
408 "Enable DTrace"),
409 #ifdef BIG_SECURITY_HOLE
410 AP_INIT_ITERATE("VHostPrivs", vhost_privs, NULL, RSRC_CONF,
411 "Privileges available in the (virtual) server"),
412 AP_INIT_ITERATE("VHostCGIPrivs", vhost_cgiprivs, NULL, RSRC_CONF,
413 "Privileges available to external programs"),
414 #endif
415 {NULL}
416 };
417 module AP_MODULE_DECLARE_DATA privileges_module = {
418 STANDARD20_MODULE_STUFF,
419 NULL,
420 NULL,
421 privileges_create_cfg,
422 NULL,
423 privileges_cmds,
424 privileges_hooks
425 };

Properties

Name Value
svn:eol-style native

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2