/[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 713961 - (hide annotations)
Fri Nov 14 09:48:39 2008 UTC (12 months, 1 week ago) by niq
File MIME type: text/plain
File size: 15508 byte(s)
Introduce mod_privileges: a platform-specific module offering enhanced
security and a (limited) solution to the "perchild" problem.
1 niq 713961 /* 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     };

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2