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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 738526, Wed Jan 28 16:23:11 2009 UTC revision 771144, Sun May 3 23:02:35 2009 UTC
# Line 38  module AP_MODULE_DECLARE_DATA privileges Line 38  module AP_MODULE_DECLARE_DATA privileges
38    
39  /* #define BIG_SECURITY_HOLE 1 */  /* #define BIG_SECURITY_HOLE 1 */
40    
41    typedef enum { PRIV_UNSET, PRIV_FAST, PRIV_SECURE, PRIV_SELECTIVE } priv_mode;
42    
43  typedef struct {  typedef struct {
44      priv_set_t *priv;      priv_set_t *priv;
45      priv_set_t *child_priv;      priv_set_t *child_priv;
46      uid_t uid;      uid_t uid;
47      gid_t gid;      gid_t gid;
48        priv_mode mode;
49  } priv_cfg;  } priv_cfg;
50    
51    typedef struct {
52        priv_mode mode;
53    } priv_dir_cfg;
54    
55  static priv_set_t *priv_setid;  static priv_set_t *priv_setid;
56  static priv_set_t *priv_default = NULL;  static priv_set_t *priv_default = NULL;
57  static int dtrace_enabled = 0;  static int dtrace_enabled = 0;
# Line 56  static apr_status_t priv_cfg_cleanup(voi Line 63  static apr_status_t priv_cfg_cleanup(voi
63      priv_freeset(cfg->child_priv);      priv_freeset(cfg->child_priv);
64      return APR_SUCCESS;      return APR_SUCCESS;
65  }  }
66    static void *privileges_merge_cfg(apr_pool_t *pool, void *BASE, void *ADD)
67    {
68        /* inherit the mode if it's not set; the rest won't be inherited */
69        priv_cfg *base = BASE;
70        priv_cfg *add = ADD;
71        priv_cfg *ret = apr_pmemdup(pool, add, sizeof(priv_cfg));
72        ret->mode = (add->mode == PRIV_UNSET) ? base->mode : add->mode;
73        return ret;
74    }
75  static void *privileges_create_cfg(apr_pool_t *pool, server_rec *s)  static void *privileges_create_cfg(apr_pool_t *pool, server_rec *s)
76  {  {
77      priv_cfg *cfg = apr_palloc(pool, sizeof(priv_cfg));      priv_cfg *cfg = apr_palloc(pool, sizeof(priv_cfg));
# Line 83  static void *privileges_create_cfg(apr_p Line 99  static void *privileges_create_cfg(apr_p
99      /* we´ll use 0 for unset */      /* we´ll use 0 for unset */
100      cfg->uid = 0;      cfg->uid = 0;
101      cfg->gid = 0;      cfg->gid = 0;
102        cfg->mode = PRIV_UNSET;
103      apr_pool_cleanup_register(pool, cfg, priv_cfg_cleanup,      apr_pool_cleanup_register(pool, cfg, priv_cfg_cleanup,
104                                apr_pool_cleanup_null);                                apr_pool_cleanup_null);
105    
# Line 92  static void *privileges_create_cfg(apr_p Line 109  static void *privileges_create_cfg(apr_p
109      }      }
110      return cfg;      return cfg;
111  }  }
112    static void *privileges_create_dir_cfg(apr_pool_t *pool, char *dummy)
113    {
114        priv_dir_cfg *cfg = apr_palloc(pool, sizeof(priv_dir_cfg));
115        cfg->mode = PRIV_UNSET;
116        return cfg;
117    }
118    static void *privileges_merge_dir_cfg(apr_pool_t *pool, void *BASE, void *ADD)
119    {
120        priv_dir_cfg *base = BASE;
121        priv_dir_cfg *add = ADD;
122        priv_dir_cfg *ret = apr_palloc(pool, sizeof(priv_dir_cfg));
123        ret->mode = (add->mode == PRIV_UNSET) ? base->mode : add->mode;
124        return ret;
125    }
126    
127  static apr_status_t privileges_end_req(void *data)  static apr_status_t privileges_end_req(void *data)
128  {  {
129      request_rec *r = data;      request_rec *r = data;
130      priv_cfg *cfg = ap_get_module_config(r->server->module_config,      priv_cfg *cfg = ap_get_module_config(r->server->module_config,
131                                           &privileges_module);                                           &privileges_module);
132        priv_dir_cfg *dcfg = ap_get_module_config(r->per_dir_config,
133                                                  &privileges_module);
134    
135      /* ugly hack: grab default uid and gid from unixd */      /* ugly hack: grab default uid and gid from unixd */
136      extern unixd_config_rec ap_unixd_config;      extern unixd_config_rec ap_unixd_config;
137    
138        /* If we forked a child, we dropped privilege to revert, so
139         * all we can do now is exit
140         */
141        if ((cfg->mode == PRIV_SECURE) ||
142            ((cfg->mode == PRIV_SELECTIVE) && (dcfg->mode == PRIV_SECURE))) {
143        //    return APR_SUCCESS;
144            exit(0);
145        }
146    
147      /* if either user or group are not the default, restore them */      /* if either user or group are not the default, restore them */
148      if (cfg->uid || cfg->gid) {      if (cfg->uid || cfg->gid) {
149          if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) {          if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) {
# Line 125  static apr_status_t privileges_end_req(v Line 167  static apr_status_t privileges_end_req(v
167      }      }
168      return APR_SUCCESS;      return APR_SUCCESS;
169  }  }
170    #if 0
171    static apr_status_t privileges_end_proc(void *data)
172    {
173        /* FIXME
174         * The process exists only for the request, and was created
175         * on the request pool which is now being destroyed.
176         * Need to figure out what needs doing here.
177         */
178        exit(0);
179    }
180    #endif
181  static int privileges_req(request_rec *r)  static int privileges_req(request_rec *r)
182  {  {
183        /* secure mode: fork a process to handle the request */
184        apr_proc_t proc;
185        apr_status_t rv;
186        int exitcode;
187        apr_exit_why_e exitwhy;
188        int fork_req;
189      priv_cfg *cfg = ap_get_module_config(r->server->module_config,      priv_cfg *cfg = ap_get_module_config(r->server->module_config,
190                                           &privileges_module);                                           &privileges_module);
191    
192        void *breadcrumb = ap_get_module_config(r->request_config,
193                                                &privileges_module);
194    
195        if (!breadcrumb) {
196            /* first call: this is the vhost */
197            fork_req = (cfg->mode == PRIV_SECURE);
198    
199            /* set breadcrumb */
200            ap_set_module_config(r->request_config, &privileges_module, &cfg->mode);
201    
202            /* If we have per-dir config, defer doing anything */
203            if ((cfg->mode == PRIV_SELECTIVE)) {
204                /* Defer dropping privileges 'til we have a directory
205                 * context that'll tell us whether to fork.
206                 */
207                return DECLINED;
208            }
209        }
210        else {
211            /* second call is for per-directory. */
212            priv_dir_cfg *dcfg;
213            if ((cfg->mode != PRIV_SELECTIVE)) {
214                /* Our fate was already determined for the vhost -
215                 * nothing to do per-directory
216                 */
217                return DECLINED;
218            }
219            dcfg = ap_get_module_config(r->per_dir_config, &privileges_module);
220            fork_req = (dcfg->mode == PRIV_SECURE);
221        }
222    
223        if (fork_req) {
224           rv = apr_proc_fork(&proc, r->pool);
225            switch (rv) {
226            case APR_INPARENT:
227                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
228                              "parent waiting for child");
229                /* FIXME - does the child need to run synchronously?
230                 * esp. if we enable mod_privileges with threaded MPMs?
231                 * We do need at least to ensure r outlives the child.
232                 */
233                rv = apr_proc_wait(&proc, &exitcode, &exitwhy, APR_WAIT);
234                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "parent: child %s",
235                              (rv == APR_CHILD_DONE) ? "done" : "notdone");
236    
237                /* The child has taken responsibility for reading all input
238                 * and sending all output.  So we need to bow right out,
239                 * and even abandon "normal" housekeeping.
240                 */
241                r->eos_sent = 1;
242                apr_table_unset(r->headers_in, "Content-Type");
243                apr_table_unset(r->headers_in, "Content-Length");
244                /* Testing with ab and 100k requests reveals no nasties
245                 * so I infer we're not leaking anything like memory
246                 * or file descriptors.  That's nice!
247                 */
248                return DONE;
249            case APR_INCHILD:
250                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "In child!");
251    //            apr_pool_cleanup_register(r->pool, r, privileges_end_proc,
252    //                                      apr_pool_cleanup_null);
253                break;  /* now we'll drop privileges in the child */
254            default:
255                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
256                              "Failed to fork secure child process!");
257                return HTTP_INTERNAL_SERVER_ERROR;
258            }
259        }
260    
261        /* OK, now drop privileges. */
262    
263      /* cleanup should happen even if something fails part-way through here */      /* cleanup should happen even if something fails part-way through here */
264      apr_pool_cleanup_register(r->pool, r, privileges_end_req,      apr_pool_cleanup_register(r->pool, r, privileges_end_req,
265                                apr_pool_cleanup_null);                                apr_pool_cleanup_null);
   
266      /* set user and group if configured */      /* set user and group if configured */
267      if (cfg->uid || cfg->gid) {      if (cfg->uid || cfg->gid) {
268          if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) {          if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) {
# Line 173  static int privileges_req(request_rec *r Line 302  static int privileges_req(request_rec *r
302          return HTTP_INTERNAL_SERVER_ERROR;          return HTTP_INTERNAL_SERVER_ERROR;
303      }      }
304    
305        /* If we're in a child process, drop down PPERM too */
306        if (fork_req) {
307            if (setppriv(PRIV_SET, PRIV_PERMITTED, cfg->priv) == -1) {
308                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
309                              "Error setting permitted privileges: %s",
310                              strerror(errno));
311                return HTTP_INTERNAL_SERVER_ERROR;
312            }
313        }
314    
315      return OK;      return OK;
316  }  }
317  #define PDROP_CHECK(x) if (x == -1) { \  #define PDROP_CHECK(x) if (x == -1) { \
# Line 261  static int privileges_postconf(apr_pool_ Line 400  static int privileges_postconf(apr_pool_
400  static int privileges_init(apr_pool_t *pconf, apr_pool_t *plog,  static int privileges_init(apr_pool_t *pconf, apr_pool_t *plog,
401                             apr_pool_t *ptemp)                             apr_pool_t *ptemp)
402  {  {
403    #if 0
404      /* refuse to work if the MPM is threaded */      /* refuse to work if the MPM is threaded */
405      int threaded;      int threaded;
406      int rv = ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded);      int rv = ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded);
# Line 275  static int privileges_init(apr_pool_t *p Line 415  static int privileges_init(apr_pool_t *p
415                        "mod_privileges is not compatible with a threaded MPM.");                        "mod_privileges is not compatible with a threaded MPM.");
416          return !OK;          return !OK;
417      }      }
418    #endif
419      return OK;      return OK;
420  }  }
421  static void privileges_hooks(apr_pool_t *pool)  static void privileges_hooks(apr_pool_t *pool)
422  {  {
423      ap_hook_post_read_request(privileges_req, NULL, NULL,      ap_hook_post_read_request(privileges_req, NULL, NULL,
424                                APR_HOOK_REALLY_FIRST);                                APR_HOOK_REALLY_FIRST);
425        ap_hook_header_parser(privileges_req, NULL, NULL, APR_HOOK_REALLY_FIRST);
426      ap_hook_drop_privileges(privileges_drop_first, NULL, NULL, APR_HOOK_FIRST);      ap_hook_drop_privileges(privileges_drop_first, NULL, NULL, APR_HOOK_FIRST);
427      ap_hook_drop_privileges(privileges_drop_last, NULL, NULL, APR_HOOK_LAST);      ap_hook_drop_privileges(privileges_drop_last, NULL, NULL, APR_HOOK_LAST);
428      ap_hook_post_config(privileges_postconf, NULL, NULL, APR_HOOK_MIDDLE);      ap_hook_post_config(privileges_postconf, NULL, NULL, APR_HOOK_MIDDLE);
# Line 357  static const char *dtraceenable(cmd_parm Line 499  static const char *dtraceenable(cmd_parm
499      return NULL;      return NULL;
500  }  }
501    
502    static const char *privs_mode(cmd_parms *cmd, void *dir, const char *arg)
503    {
504        priv_mode mode = PRIV_UNSET;
505        if (!strcasecmp(arg, "FAST")) {
506            mode = PRIV_FAST;
507        }
508        else if (!strcasecmp(arg, "SECURE")) {
509            mode = PRIV_SECURE;
510        }
511        else if (!strcasecmp(arg, "SELECTIVE")) {
512            mode = PRIV_SELECTIVE;
513        }
514    
515        if (cmd->path) {
516            /* In a directory context, set the per_dir_config */
517            priv_dir_cfg *cfg = dir;
518            cfg->mode = mode;
519            if ((mode == PRIV_UNSET) || (mode == PRIV_SELECTIVE)) {
520                return "PrivilegesMode in a Directory context must be FAST or SECURE";
521            }
522        }
523        else {
524            /* In a global or vhost context, set the server config */
525            priv_cfg *cfg = ap_get_module_config(cmd->server->module_config,
526                                                 &privileges_module);
527            cfg->mode = mode;
528            if (mode == PRIV_UNSET) {
529                return "PrivilegesMode must be FAST, SECURE or SELECTIVE";
530            }
531        }
532        return NULL;
533    }
534    
535  #ifdef BIG_SECURITY_HOLE  #ifdef BIG_SECURITY_HOLE
536  static const char *vhost_privs(cmd_parms *cmd, void *dir, const char *arg)  static const char *vhost_privs(cmd_parms *cmd, void *dir, const char *arg)
537  {  {
# Line 394  static const char *vhost_cgiprivs(cmd_pa Line 569  static const char *vhost_cgiprivs(cmd_pa
569      return NULL;      return NULL;
570  }  }
571  #endif  #endif
   
572  static const command_rec privileges_cmds[] = {  static const command_rec privileges_cmds[] = {
573      AP_INIT_TAKE1("VHostUser", vhost_user, NULL, RSRC_CONF,      AP_INIT_TAKE1("VHostUser", vhost_user, NULL, RSRC_CONF,
574                    "Userid under which the virtualhost will run"),                    "Userid under which the virtualhost will run"),
575      AP_INIT_TAKE1("VHostGroup", vhost_group, NULL, RSRC_CONF,      AP_INIT_TAKE1("VHostGroup", vhost_group, NULL, RSRC_CONF,
576                    "Group under which the virtualhost will run"),                    "Group under which the virtualhost will run"),
577      AP_INIT_FLAG("VHostSecure", vhost_secure, NULL, RSRC_CONF,      AP_INIT_FLAG("VHostSecure", vhost_secure, NULL, RSRC_CONF,
578                   "Run in secure mode (default ON)"),                   "Run in enhanced security mode (default ON)"),
579      AP_INIT_TAKE1("VHostCGIMode", vhost_cgimode, NULL, RSRC_CONF,      AP_INIT_TAKE1("VHostCGIMode", vhost_cgimode, NULL, RSRC_CONF,
580                    "Enable fork+exec for this virtualhost (Off|Secure|On)"),                    "Enable fork+exec for this virtualhost (Off|Secure|On)"),
581      AP_INIT_FLAG("DTracePrivileges", dtraceenable, NULL, RSRC_CONF,      AP_INIT_FLAG("DTracePrivileges", dtraceenable, NULL, RSRC_CONF,
582                   "Enable DTrace"),                   "Enable DTrace"),
583        AP_INIT_TAKE1("PrivilegesMode", privs_mode, NULL, RSRC_CONF|ACCESS_CONF,
584                      "tradeoff performance vs security (fast or secure)"),
585  #ifdef BIG_SECURITY_HOLE  #ifdef BIG_SECURITY_HOLE
586      AP_INIT_ITERATE("VHostPrivs", vhost_privs, NULL, RSRC_CONF,      AP_INIT_ITERATE("VHostPrivs", vhost_privs, NULL, RSRC_CONF,
587                      "Privileges available in the (virtual) server"),                      "Privileges available in the (virtual) server"),
# Line 416  static const command_rec privileges_cmds Line 592  static const command_rec privileges_cmds
592  };  };
593  module AP_MODULE_DECLARE_DATA privileges_module = {  module AP_MODULE_DECLARE_DATA privileges_module = {
594      STANDARD20_MODULE_STUFF,      STANDARD20_MODULE_STUFF,
595      NULL,      privileges_create_dir_cfg,
596      NULL,      privileges_merge_dir_cfg,
597      privileges_create_cfg,      privileges_create_cfg,
598      NULL,      privileges_merge_cfg,
599      privileges_cmds,      privileges_cmds,
600      privileges_hooks      privileges_hooks
601  };  };

Legend:
Removed from v.738526  
changed lines
  Added in v.771144

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2