/[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 738526 - (hide annotations)
Wed Jan 28 16:23:11 2009 UTC (9 months, 3 weeks ago) by niq
File MIME type: text/plain
File size: 15456 byte(s)
Match up formats with args in error messages.
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 covener 723078 extern unixd_config_rec ap_unixd_config;
104 niq 713961
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 covener 723078 if (cfg->uid && (setuid(ap_unixd_config.user_id) == -1)) {
112 niq 713961 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
113     "Error restoring default userid");
114     }
115 covener 723078 if (cfg->gid && (setgid(ap_unixd_config.group_id) == -1)) {
116 niq 713961 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 niq 738526 "Error restoring default privileges: %s", strerror(errno));
125 niq 713961 }
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 niq 738526 "Error setting effective privileges: %s", strerror(errno));
161 niq 713961 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 niq 738526 "Error setting inheritable privileges: %s", strerror(errno));
168 niq 713961 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 niq 738526 "Error setting limit privileges: %s", strerror(errno));
173 niq 713961 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 niq 738526 "priv_addset: %s", strerror(errno));
257 niq 713961 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