/[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 718015 - (show annotations)
Sun Nov 16 12:43:06 2008 UTC (12 months, 1 week ago) by rpluem
File MIME type: text/plain
File size: 15508 byte(s)
* Fix properties.

Submitted by: Takashi Sato <takashi lans-tv.com>
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 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(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(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 priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
353 &privileges_module);
354 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
355 if (err != NULL) {
356 return err;
357 }
358 dtrace_enabled = arg;
359 return NULL;
360 }
361
362 #ifdef BIG_SECURITY_HOLE
363 static const char *vhost_privs(cmd_parms *cmd, void *dir, const char *arg)
364 {
365 priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
366 &privileges_module);
367 const char *priv = arg;
368
369 if (*priv == '-') {
370 CFG_CHECK(priv_delset(cfg->priv, priv+1));
371 }
372 else if (*priv == '+') {
373 CFG_CHECK(priv_addset(cfg->priv, priv+1));
374 }
375 else {
376 priv_emptyset(cfg->priv);
377 CFG_CHECK(priv_addset(cfg->priv, priv));
378 }
379 return NULL;
380 }
381 static const char *vhost_cgiprivs(cmd_parms *cmd, void *dir, const char *arg)
382 {
383 priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
384 &privileges_module);
385 const char *priv = arg;
386 if (*priv == '-') {
387 CFG_CHECK(priv_delset(cfg->child_priv, priv+1));
388 }
389 else if (*priv == '+') {
390 CFG_CHECK(priv_addset(cfg->child_priv, priv+1));
391 }
392 else {
393 priv_emptyset(cfg->child_priv);
394 CFG_CHECK(priv_addset(cfg->child_priv, priv));
395 }
396 return NULL;
397 }
398 #endif
399
400 static const command_rec privileges_cmds[] = {
401 AP_INIT_TAKE1("VHostUser", vhost_user, NULL, RSRC_CONF,
402 "Userid under which the virtualhost will run"),
403 AP_INIT_TAKE1("VHostGroup", vhost_group, NULL, RSRC_CONF,
404 "Group under which the virtualhost will run"),
405 AP_INIT_FLAG("VHostSecure", vhost_secure, NULL, RSRC_CONF,
406 "Run in secure mode (default ON)"),
407 AP_INIT_TAKE1("VHostCGIMode", vhost_cgimode, NULL, RSRC_CONF,
408 "Enable fork+exec for this virtualhost (Off|Secure|On)"),
409 AP_INIT_FLAG("DTracePrivileges", dtraceenable, NULL, RSRC_CONF,
410 "Enable DTrace"),
411 #ifdef BIG_SECURITY_HOLE
412 AP_INIT_ITERATE("VHostPrivs", vhost_privs, NULL, RSRC_CONF,
413 "Privileges available in the (virtual) server"),
414 AP_INIT_ITERATE("VHostCGIPrivs", vhost_cgiprivs, NULL, RSRC_CONF,
415 "Privileges available to external programs"),
416 #endif
417 {NULL}
418 };
419 module AP_MODULE_DECLARE_DATA privileges_module = {
420 STANDARD20_MODULE_STUFF,
421 NULL,
422 NULL,
423 privileges_create_cfg,
424 NULL,
425 privileges_cmds,
426 privileges_hooks
427 };

Properties

Name Value
svn:eol-style native

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2