]> granicus.if.org Git - apache/blob - modules/aaa/mod_authz_core.c
mod_authn_socache.c: fix creation of default socache_instance.
[apache] / modules / aaa / mod_authz_core.c
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 /*
18  * Security options etc.
19  *
20  * Module derived from code originally written by Rob McCool
21  *
22  */
23
24 #include "apr_strings.h"
25 #include "apr_network_io.h"
26 #include "apr_md5.h"
27
28 #define APR_WANT_STRFUNC
29 #define APR_WANT_BYTEFUNC
30 #include "apr_want.h"
31
32 #include "ap_config.h"
33 #include "httpd.h"
34 #include "http_config.h"
35 #include "http_core.h"
36 #include "http_log.h"
37 #include "http_request.h"
38 #include "http_protocol.h"
39 #include "ap_provider.h"
40 #include "ap_expr.h"
41
42 #include "mod_auth.h"
43
44 #if APR_HAVE_NETINET_IN_H
45 #include <netinet/in.h>
46 #endif
47
48 #undef AUTHZ_EXTRA_CONFIGS
49
50 typedef struct provider_alias_rec {
51     char *provider_name;
52     char *provider_alias;
53     char *provider_args;
54     const void *provider_parsed_args;
55     ap_conf_vector_t *sec_auth;
56     const authz_provider *provider;
57 } provider_alias_rec;
58
59 typedef enum {
60     AUTHZ_LOGIC_AND,
61     AUTHZ_LOGIC_OR,
62     AUTHZ_LOGIC_OFF,
63     AUTHZ_LOGIC_UNSET
64 } authz_logic_op;
65
66 typedef struct authz_section_conf authz_section_conf;
67
68 struct authz_section_conf {
69     const char *provider_name;
70     const char *provider_args;
71     const void *provider_parsed_args;
72     const authz_provider *provider;
73     apr_int64_t limited;
74     authz_logic_op op;
75     int negate;
76     /** true if this is not a real container but produced by AuthMerging;
77      *  only used for logging */
78     int is_merged;
79     authz_section_conf *first;
80     authz_section_conf *next;
81 };
82
83 typedef struct authz_core_dir_conf authz_core_dir_conf;
84
85 struct authz_core_dir_conf {
86     authz_section_conf *section;
87     authz_core_dir_conf *next;
88     authz_logic_op op;
89     signed char authz_forbidden_on_fail;
90 };
91
92 #define UNSET -1
93
94 typedef struct authz_core_srv_conf {
95     apr_hash_t *alias_rec;
96 } authz_core_srv_conf;
97
98 module AP_MODULE_DECLARE_DATA authz_core_module;
99
100 static authz_core_dir_conf *authz_core_first_dir_conf;
101
102 static void *create_authz_core_dir_config(apr_pool_t *p, char *dummy)
103 {
104     authz_core_dir_conf *conf = apr_pcalloc(p, sizeof(*conf));
105
106     conf->op = AUTHZ_LOGIC_UNSET;
107     conf->authz_forbidden_on_fail = UNSET;
108
109     conf->next = authz_core_first_dir_conf;
110     authz_core_first_dir_conf = conf;
111
112     return (void *)conf;
113 }
114
115 static void *merge_authz_core_dir_config(apr_pool_t *p,
116                                          void *basev, void *newv)
117 {
118     authz_core_dir_conf *base = (authz_core_dir_conf *)basev;
119     authz_core_dir_conf *new = (authz_core_dir_conf *)newv;
120     authz_core_dir_conf *conf;
121
122     if (new->op == AUTHZ_LOGIC_UNSET && !new->section && base->section ) {
123         /* Only authz_forbidden_on_fail has been set in new. Don't treat
124          * it as a new auth config w.r.t. AuthMerging */
125         conf = apr_pmemdup(p, base, sizeof(*base));
126     }
127     else if (new->op == AUTHZ_LOGIC_OFF || new->op == AUTHZ_LOGIC_UNSET ||
128              !(base->section || new->section)) {
129         conf = apr_pmemdup(p, new, sizeof(*new));
130     }
131     else {
132         authz_section_conf *section;
133
134         if (base->section) {
135             if (new->section) {
136                 section = apr_pcalloc(p, sizeof(*section));
137
138                 section->limited =
139                     base->section->limited | new->section->limited;
140
141                 section->op = new->op;
142                 section->is_merged = 1;
143
144                 section->first = apr_pmemdup(p, base->section,
145                                              sizeof(*base->section));
146                 section->first->next = apr_pmemdup(p, new->section,
147                                                    sizeof(*new->section));
148             } else {
149                 section = apr_pmemdup(p, base->section,
150                                       sizeof(*base->section));
151             }
152         }
153         else {
154             section = apr_pmemdup(p, new->section, sizeof(*new->section));
155         }
156
157         conf = apr_pcalloc(p, sizeof(*conf));
158
159         conf->section = section;
160         conf->op = new->op;
161     }
162
163     if (new->authz_forbidden_on_fail == UNSET)
164         conf->authz_forbidden_on_fail = base->authz_forbidden_on_fail;
165     else
166         conf->authz_forbidden_on_fail = new->authz_forbidden_on_fail;
167
168     return (void*)conf;
169 }
170
171 static void *create_authz_core_svr_config(apr_pool_t *p, server_rec *s)
172 {
173     authz_core_srv_conf *authcfg;
174
175     authcfg = apr_pcalloc(p, sizeof(*authcfg));
176     authcfg->alias_rec = apr_hash_make(p);
177
178     return (void *)authcfg;
179 }
180
181 /* This is a fake authz provider that really merges various authz alias
182  * configurations and then invokes them.
183  */
184 static authz_status authz_alias_check_authorization(request_rec *r,
185                                                     const char *require_args,
186                                                     const void *parsed_require_args)
187 {
188     const char *provider_name;
189     authz_status ret = AUTHZ_DENIED;
190
191     /* Look up the provider alias in the alias list.
192      * Get the the dir_config and call ap_Merge_per_dir_configs()
193      * Call the real provider->check_authorization() function
194      * return the result of the above function call
195      */
196
197     provider_name = apr_table_get(r->notes, AUTHZ_PROVIDER_NAME_NOTE);
198
199     if (provider_name) {
200         authz_core_srv_conf *authcfg;
201         provider_alias_rec *prvdraliasrec;
202
203         authcfg = ap_get_module_config(r->server->module_config,
204                                        &authz_core_module);
205
206         prvdraliasrec = apr_hash_get(authcfg->alias_rec, provider_name,
207                                      APR_HASH_KEY_STRING);
208
209         /* If we found the alias provider in the list, then merge the directory
210            configurations and call the real provider */
211         if (prvdraliasrec) {
212             ap_conf_vector_t *orig_dir_config = r->per_dir_config;
213
214             r->per_dir_config =
215                 ap_merge_per_dir_configs(r->pool, orig_dir_config,
216                                          prvdraliasrec->sec_auth);
217
218             ret = prvdraliasrec->provider->
219                 check_authorization(r, prvdraliasrec->provider_args,
220                                     prvdraliasrec->provider_parsed_args);
221
222             r->per_dir_config = orig_dir_config;
223         }
224         else {
225             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02305)
226                           "no alias provider found for '%s' (BUG?)",
227                           provider_name);
228         }
229     }
230     else {
231         ap_assert(provider_name != NULL);
232     }
233
234     return ret;
235 }
236
237 static const authz_provider authz_alias_provider =
238 {
239     &authz_alias_check_authorization,
240     NULL,
241 };
242
243 static const char *authz_require_alias_section(cmd_parms *cmd, void *mconfig,
244                                                const char *args)
245 {
246     const char *endp = ap_strrchr_c(args, '>');
247     char *provider_name;
248     char *provider_alias;
249     char *provider_args;
250     ap_conf_vector_t *new_authz_config;
251     int old_overrides = cmd->override;
252     const char *errmsg;
253
254     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
255     if (err != NULL) {
256         return err;
257     }
258
259     if (endp == NULL) {
260         return apr_pstrcat(cmd->pool, cmd->cmd->name,
261                            "> directive missing closing '>'", NULL);
262     }
263
264     args = apr_pstrndup(cmd->temp_pool, args, endp - args);
265
266     if (!args[0]) {
267         return apr_pstrcat(cmd->pool, cmd->cmd->name,
268                            "> directive requires additional arguments", NULL);
269     }
270
271     /* Pull the real provider name and the alias name from the block header */
272     provider_name = ap_getword_conf(cmd->pool, &args);
273     provider_alias = ap_getword_conf(cmd->pool, &args);
274     provider_args = ap_getword_conf(cmd->pool, &args);
275
276     if (!provider_name[0] || !provider_alias[0]) {
277         return apr_pstrcat(cmd->pool, cmd->cmd->name,
278                            "> directive requires additional arguments", NULL);
279     }
280
281     new_authz_config = ap_create_per_dir_config(cmd->pool);
282
283     /* Walk the subsection configuration to get the per_dir config that we will
284      * merge just before the real provider is called.
285      */
286     cmd->override = OR_AUTHCFG | ACCESS_CONF;
287     errmsg = ap_walk_config(cmd->directive->first_child, cmd,
288                             new_authz_config);
289     cmd->override = old_overrides;
290
291     if (!errmsg) {
292         provider_alias_rec *prvdraliasrec;
293         authz_core_srv_conf *authcfg;
294
295         prvdraliasrec = apr_pcalloc(cmd->pool, sizeof(*prvdraliasrec));
296
297         /* Save off the new directory config along with the original
298          * provider name and function pointer data
299          */
300         prvdraliasrec->provider_name = provider_name;
301         prvdraliasrec->provider_alias = provider_alias;
302         prvdraliasrec->provider_args = provider_args;
303         prvdraliasrec->sec_auth = new_authz_config;
304         prvdraliasrec->provider =
305             ap_lookup_provider(AUTHZ_PROVIDER_GROUP, provider_name,
306                                AUTHZ_PROVIDER_VERSION);
307
308         /* by the time the config file is used, the provider should be loaded
309          * and registered with us.
310          */
311         if (!prvdraliasrec->provider) {
312             return apr_psprintf(cmd->pool,
313                                 "Unknown Authz provider: %s",
314                                 provider_name);
315         }
316         if (prvdraliasrec->provider->parse_require_line) {
317             err = prvdraliasrec->provider->parse_require_line(cmd,
318                          provider_args, &prvdraliasrec->provider_parsed_args);
319             if (err)
320                 return apr_psprintf(cmd->pool,
321                                     "Can't parse 'Require %s %s': %s",
322                                     provider_name, provider_args, err);
323         }
324
325         authcfg = ap_get_module_config(cmd->server->module_config,
326                                        &authz_core_module);
327
328         apr_hash_set(authcfg->alias_rec, provider_alias,
329                      APR_HASH_KEY_STRING, prvdraliasrec);
330
331         /* Register the fake provider so that we get called first */
332         ap_register_auth_provider(cmd->pool, AUTHZ_PROVIDER_GROUP,
333                                   provider_alias, AUTHZ_PROVIDER_VERSION,
334                                   &authz_alias_provider,
335                                   AP_AUTH_INTERNAL_PER_CONF);
336     }
337
338     return errmsg;
339 }
340
341 static const char* format_authz_result(authz_status result)
342 {
343     return ((result == AUTHZ_DENIED)
344             ? "denied"
345             : ((result == AUTHZ_GRANTED)
346                ? "granted"
347                : ((result == AUTHZ_DENIED_NO_USER)
348                   ? "denied (no authenticated user yet)"
349                   : "neutral")));
350 }
351
352 static const char* format_authz_command(apr_pool_t *p,
353                                         authz_section_conf *section)
354 {
355     return (section->provider
356             ? apr_pstrcat(p, "Require ", (section->negate ? "not " : ""),
357                           section->provider_name, " ",
358                           section->provider_args, NULL)
359             : apr_pstrcat(p, section->is_merged ? "AuthMerging " : "<Require",
360                           ((section->op == AUTHZ_LOGIC_AND)
361                            ? (section->negate ? "NotAll" : "All")
362                            : (section->negate ? "None" : "Any")),
363                           section->is_merged ? "" : ">", NULL));
364 }
365
366 static authz_section_conf* create_default_section(apr_pool_t *p)
367 {
368     authz_section_conf *section = apr_pcalloc(p, sizeof(*section));
369
370     section->op = AUTHZ_LOGIC_OR;
371
372     return section;
373 }
374
375 static const char *add_authz_provider(cmd_parms *cmd, void *config,
376                                       const char *args)
377 {
378     authz_core_dir_conf *conf = (authz_core_dir_conf*)config;
379     authz_section_conf *section = apr_pcalloc(cmd->pool, sizeof(*section));
380     authz_section_conf *child;
381
382     section->provider_name = ap_getword_conf(cmd->pool, &args);
383
384     if (!strcasecmp(section->provider_name, "not")) {
385         section->provider_name = ap_getword_conf(cmd->pool, &args);
386         section->negate = 1;
387     }
388
389     section->provider_args = args;
390
391     /* lookup and cache the actual provider now */
392     section->provider = ap_lookup_provider(AUTHZ_PROVIDER_GROUP,
393                                            section->provider_name,
394                                            AUTHZ_PROVIDER_VERSION);
395
396     /* by the time the config file is used, the provider should be loaded
397      * and registered with us.
398      */
399     if (!section->provider) {
400         return apr_psprintf(cmd->pool,
401                             "Unknown Authz provider: %s",
402                             section->provider_name);
403     }
404
405     /* if the provider doesn't provide the appropriate function, reject it */
406     if (!section->provider->check_authorization) {
407         return apr_psprintf(cmd->pool,
408                             "The '%s' Authz provider is not supported by any "
409                             "of the loaded authorization modules",
410                             section->provider_name);
411     }
412
413     section->limited = cmd->limited;
414
415     if (section->provider->parse_require_line) {
416         const char *err;
417         apr_pool_userdata_setn(section->provider_name,
418                                AUTHZ_PROVIDER_NAME_NOTE,
419                                apr_pool_cleanup_null,
420                                cmd->temp_pool);
421         err = section->provider->parse_require_line(cmd, args,
422                                               &section->provider_parsed_args);
423
424         if (err)
425             return err;
426     }
427
428     if (!conf->section) {
429         conf->section = create_default_section(cmd->pool);
430     }
431
432     if (section->negate && conf->section->op == AUTHZ_LOGIC_OR) {
433         return apr_psprintf(cmd->pool, "negative %s directive has no effect "
434                             "in %s directive",
435                             cmd->cmd->name,
436                             format_authz_command(cmd->pool, conf->section));
437     }
438
439     conf->section->limited |= section->limited;
440
441     child = conf->section->first;
442
443     if (child) {
444         while (child->next) {
445             child = child->next;
446         }
447
448         child->next = section;
449     }
450     else {
451         conf->section->first = section;
452     }
453
454     return NULL;
455 }
456
457 static const char *add_authz_section(cmd_parms *cmd, void *mconfig,
458                                      const char *args)
459 {
460     authz_core_dir_conf *conf = mconfig;
461     const char *endp = ap_strrchr_c(args, '>');
462     authz_section_conf *old_section = conf->section;
463     authz_section_conf *section;
464     int old_overrides = cmd->override;
465     apr_int64_t old_limited = cmd->limited;
466     const char *errmsg;
467
468     if (endp == NULL) {
469         return apr_pstrcat(cmd->pool, cmd->cmd->name,
470                            "> directive missing closing '>'", NULL);
471     }
472
473     args = apr_pstrndup(cmd->temp_pool, args, endp - args);
474
475     if (args[0]) {
476         return apr_pstrcat(cmd->pool, cmd->cmd->name,
477                            "> directive doesn't take additional arguments",
478                            NULL);
479     }
480
481     section = apr_pcalloc(cmd->pool, sizeof(*section));
482
483     if (!strcasecmp(cmd->cmd->name, "<RequireAll")) {
484         section->op = AUTHZ_LOGIC_AND;
485     }
486     else if (!strcasecmp(cmd->cmd->name, "<RequireAny")) {
487         section->op = AUTHZ_LOGIC_OR;
488     }
489     else if (!strcasecmp(cmd->cmd->name, "<RequireNotAll")) {
490         section->op = AUTHZ_LOGIC_AND;
491         section->negate = 1;
492     }
493     else {
494         section->op = AUTHZ_LOGIC_OR;
495         section->negate = 1;
496     }
497
498     conf->section = section;
499
500     /* trigger NOT_IN_LIMIT errors as if this were a <Limit> directive */
501     cmd->limited &= ~(AP_METHOD_BIT << (METHODS - 1));
502
503     cmd->override = OR_AUTHCFG;
504     errmsg = ap_walk_config(cmd->directive->first_child, cmd, cmd->context);
505     cmd->override = old_overrides;
506
507     cmd->limited = old_limited;
508
509     conf->section = old_section;
510
511     if (errmsg) {
512         return errmsg;
513     }
514
515     if (section->first) {
516         authz_section_conf *child;
517
518         if (!old_section) {
519             old_section = conf->section = create_default_section(cmd->pool);
520         }
521
522         if (section->negate && old_section->op == AUTHZ_LOGIC_OR) {
523             return apr_psprintf(cmd->pool, "%s directive has "
524                                 "no effect in %s directive",
525                                 format_authz_command(cmd->pool, section),
526                                 format_authz_command(cmd->pool, old_section));
527         }
528
529         old_section->limited |= section->limited;
530
531         if (!section->negate && section->op == old_section->op) {
532             /* be associative */
533             section = section->first;
534         }
535
536         child = old_section->first;
537
538         if (child) {
539             while (child->next) {
540                 child = child->next;
541             }
542
543             child->next = section;
544         }
545         else {
546             old_section->first = section;
547         }
548     }
549     else {
550         return apr_pstrcat(cmd->pool,
551                            format_authz_command(cmd->pool, section),
552                            " directive contains no authorization directives",
553                            NULL);
554     }
555
556     return NULL;
557 }
558
559 static const char *authz_merge_sections(cmd_parms *cmd, void *mconfig,
560                                         const char *arg)
561 {
562     authz_core_dir_conf *conf = mconfig;
563
564     if (!strcasecmp(arg, "Off")) {
565         conf->op = AUTHZ_LOGIC_OFF;
566     }
567     else if (!strcasecmp(arg, "And")) {
568         conf->op = AUTHZ_LOGIC_AND;
569     }
570     else if (!strcasecmp(arg, "Or")) {
571         conf->op = AUTHZ_LOGIC_OR;
572     }
573     else {
574         return apr_pstrcat(cmd->pool, cmd->cmd->name, " must be one of: "
575                            "Off | And | Or", NULL);
576     }
577
578     return NULL;
579 }
580
581 static int authz_core_check_section(apr_pool_t *p, server_rec *s,
582                                     authz_section_conf *section, int is_conf)
583 {
584     authz_section_conf *prev = NULL;
585     authz_section_conf *child = section->first;
586     int ret = !OK;
587
588     while (child) {
589         if (child->first) {
590             if (authz_core_check_section(p, s, child, 0) != OK) {
591                 return !OK;
592             }
593
594             if (child->negate && child->op != section->op) {
595                 authz_section_conf *next = child->next;
596
597                 /* avoid one level of recursion when De Morgan permits */
598                 child = child->first;
599
600                 if (prev) {
601                     prev->next = child;
602                 }
603                 else {
604                     section->first = child;
605                 }
606
607                 do {
608                     child->negate = !child->negate;
609                 } while (child->next && (child = child->next));
610
611                 child->next = next;
612             }
613         }
614
615         prev = child;
616         child = child->next;
617     }
618
619     child = section->first;
620
621     while (child) {
622         if (!child->negate) {
623             ret = OK;
624             break;
625         }
626
627         child = child->next;
628     }
629
630     if (ret != OK) {
631         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, APR_SUCCESS, s, APLOGNO(01624)
632                      "%s directive contains only negative authorization directives",
633                      is_conf ? "<Directory>, <Location>, or similar"
634                              : format_authz_command(p, section));
635     }
636
637     return ret;
638 }
639
640 static int authz_core_pre_config(apr_pool_t *p, apr_pool_t *plog,
641                                  apr_pool_t *ptemp)
642 {
643     authz_core_first_dir_conf = NULL;
644
645     return OK;
646 }
647
648 static int authz_core_check_config(apr_pool_t *p, apr_pool_t *plog,
649                                    apr_pool_t *ptemp, server_rec *s)
650 {
651     authz_core_dir_conf *conf = authz_core_first_dir_conf;
652
653     while (conf) {
654         if (conf->section) {
655             if (authz_core_check_section(p, s, conf->section, 1) != OK) {
656                 return !OK;
657             }
658         }
659
660         conf = conf->next;
661     }
662
663     return OK;
664 }
665
666 static const command_rec authz_cmds[] =
667 {
668     AP_INIT_RAW_ARGS("<AuthzProviderAlias", authz_require_alias_section,
669                      NULL, RSRC_CONF,
670                      "container for grouping an authorization provider's "
671                      "directives under a provider alias"),
672     AP_INIT_RAW_ARGS("Require", add_authz_provider, NULL, OR_AUTHCFG,
673                      "specifies authorization directives "
674                      "which one must pass (or not) for a request to suceeed"),
675     AP_INIT_RAW_ARGS("<RequireAll", add_authz_section, NULL, OR_AUTHCFG,
676                      "container for grouping authorization directives "
677                      "of which none must fail and at least one must pass "
678                      "for a request to succeed"),
679     AP_INIT_RAW_ARGS("<RequireAny", add_authz_section, NULL, OR_AUTHCFG,
680                      "container for grouping authorization directives "
681                      "of which one must pass "
682                      "for a request to succeed"),
683 #ifdef AUTHZ_EXTRA_CONFIGS
684     AP_INIT_RAW_ARGS("<RequireNotAll", add_authz_section, NULL, OR_AUTHCFG,
685                      "container for grouping authorization directives "
686                      "of which some must fail or none must pass "
687                      "for a request to succeed"),
688 #endif
689     AP_INIT_RAW_ARGS("<RequireNone", add_authz_section, NULL, OR_AUTHCFG,
690                      "container for grouping authorization directives "
691                      "of which none must pass "
692                      "for a request to succeed"),
693     AP_INIT_TAKE1("AuthMerging", authz_merge_sections, NULL, OR_AUTHCFG,
694                   "controls how a <Directory>, <Location>, or similar "
695                   "directive's authorization directives are combined with "
696                   "those of its predecessor"),
697     AP_INIT_FLAG("AuthzSendForbiddenOnFailure", ap_set_flag_slot_char,
698                  (void *)APR_OFFSETOF(authz_core_dir_conf, authz_forbidden_on_fail),
699                  OR_AUTHCFG,
700                  "Controls if an authorization failure should result in a "
701                  "'403 FORBIDDEN' response instead of the HTTP-conforming "
702                  "'401 UNAUTHORIZED'"),
703     {NULL}
704 };
705
706 static authz_status apply_authz_sections(request_rec *r,
707                                          authz_section_conf *section,
708                                          authz_logic_op parent_op)
709 {
710     authz_status auth_result;
711
712     /* check to make sure that the request method requires authorization */
713     if (!(section->limited & (AP_METHOD_BIT << r->method_number))) {
714         auth_result =
715             (parent_op == AUTHZ_LOGIC_AND) ? AUTHZ_GRANTED : AUTHZ_NEUTRAL;
716
717         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01625)
718                       "authorization result of %s: %s "
719                       "(directive limited to other methods)",
720                       format_authz_command(r->pool, section),
721                       format_authz_result(auth_result));
722
723         return auth_result;
724     }
725
726     if (section->provider) {
727         apr_table_setn(r->notes, AUTHZ_PROVIDER_NAME_NOTE,
728                        section->provider_name);
729
730         auth_result =
731             section->provider->check_authorization(r, section->provider_args,
732                                                    section->provider_parsed_args);
733
734         apr_table_unset(r->notes, AUTHZ_PROVIDER_NAME_NOTE);
735     }
736     else {
737         authz_section_conf *child = section->first;
738
739         auth_result = AUTHZ_NEUTRAL;
740
741         while (child) {
742             authz_status child_result;
743
744             child_result = apply_authz_sections(r, child, section->op);
745
746             if (child_result == AUTHZ_GENERAL_ERROR) {
747                 return AUTHZ_GENERAL_ERROR;
748             }
749
750             if (child_result != AUTHZ_NEUTRAL) {
751                 /*
752                  * Handling of AUTHZ_DENIED/AUTHZ_DENIED_NO_USER: Return
753                  * AUTHZ_DENIED_NO_USER if providing a user may change the
754                  * result, AUTHZ_DENIED otherwise.
755                  */
756                 if (section->op == AUTHZ_LOGIC_AND) {
757                     if (child_result == AUTHZ_DENIED) {
758                         auth_result = child_result;
759                         break;
760                     }
761                     if ((child_result == AUTHZ_DENIED_NO_USER
762                          && auth_result != AUTHZ_DENIED)
763                         || (auth_result == AUTHZ_NEUTRAL)) {
764                         auth_result = child_result;
765                     }
766                 }
767                 else {
768                     /* AUTHZ_LOGIC_OR */
769                     if (child_result == AUTHZ_GRANTED) {
770                         auth_result = child_result;
771                         break;
772                     }
773                     if ((child_result == AUTHZ_DENIED_NO_USER
774                          && auth_result == AUTHZ_DENIED)
775                         || (auth_result == AUTHZ_NEUTRAL)) {
776                         auth_result = child_result;
777                     }
778                 }
779             }
780
781             child = child->next;
782         }
783     }
784
785     if (section->negate) {
786         if (auth_result == AUTHZ_GRANTED) {
787             auth_result = AUTHZ_DENIED;
788         }
789         else if (auth_result == AUTHZ_DENIED ||
790                  auth_result == AUTHZ_DENIED_NO_USER) {
791             /* For negated directives, if the original result was denied
792              * then the new result is neutral since we can not grant
793              * access simply because authorization was not rejected.
794              */
795             auth_result = AUTHZ_NEUTRAL;
796         }
797     }
798
799     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01626)
800                   "authorization result of %s: %s",
801                   format_authz_command(r->pool, section),
802                   format_authz_result(auth_result));
803
804     return auth_result;
805 }
806
807 static int authorize_user_core(request_rec *r, int after_authn)
808 {
809     authz_core_dir_conf *conf;
810     authz_status auth_result;
811
812     conf = ap_get_module_config(r->per_dir_config, &authz_core_module);
813
814     if (!conf->section) {
815         if (ap_auth_type(r)) {
816             /* there's an AuthType configured, but no authorization
817              * directives applied to support it
818              */
819
820             ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01627)
821                           "AuthType configured with no corresponding "
822                           "authorization directives");
823
824             return HTTP_INTERNAL_SERVER_ERROR;
825         }
826
827         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01628)
828                       "authorization result: granted (no directives)");
829
830         return OK;
831     }
832
833     auth_result = apply_authz_sections(r, conf->section, AUTHZ_LOGIC_AND);
834
835     if (auth_result == AUTHZ_GRANTED) {
836         return OK;
837     }
838     else if (auth_result == AUTHZ_DENIED_NO_USER) {
839         if (after_authn) {
840             ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01629)
841                           "authorization failure (no authenticated user): %s",
842                           r->uri);
843             /*
844              * If we're returning 401 to an authenticated user, tell them to
845              * try again. If unauthenticated, note_auth_failure has already
846              * been called during auth.
847              */
848             if (r->user)
849                 ap_note_auth_failure(r);
850
851             return HTTP_UNAUTHORIZED;
852         }
853         else {
854             /*
855              * We need a user before we can decide what to do.
856              * Get out of the way and proceed with authentication.
857              */
858             return DECLINED;
859         }
860     }
861     else if (auth_result == AUTHZ_DENIED || auth_result == AUTHZ_NEUTRAL) {
862         if (!after_authn || ap_auth_type(r) == NULL) {
863             ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01630)
864                           "client denied by server configuration: %s%s",
865                           r->filename ? "" : "uri ",
866                           r->filename ? r->filename : r->uri);
867
868             return HTTP_FORBIDDEN;
869         }
870         else {
871             ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01631)
872                           "user %s: authorization failure for \"%s\": ",
873                           r->user, r->uri);
874
875             if (conf->authz_forbidden_on_fail > 0) {
876                 return HTTP_FORBIDDEN;
877             }
878             else {
879                 /*
880                  * If we're returning 401 to an authenticated user, tell them to
881                  * try again. If unauthenticated, note_auth_failure has already
882                  * been called during auth.
883                  */
884                 if (r->user)
885                     ap_note_auth_failure(r);
886                 return HTTP_UNAUTHORIZED;
887             }
888         }
889     }
890     else {
891         /* We'll assume that the module has already said what its
892          * error was in the logs.
893          */
894         return HTTP_INTERNAL_SERVER_ERROR;
895     }
896 }
897
898 static int authorize_userless(request_rec *r)
899 {
900     return authorize_user_core(r, 0);
901 }
902
903 static int authorize_user(request_rec *r)
904 {
905     return authorize_user_core(r, 1);
906 }
907
908 static int authz_some_auth_required(request_rec *r)
909 {
910     authz_core_dir_conf *conf;
911
912     conf = ap_get_module_config(r->per_dir_config, &authz_core_module);
913
914     if (conf->section
915         && (conf->section->limited & (AP_METHOD_BIT << r->method_number))) {
916         return 1;
917     }
918
919     return 0;
920 }
921
922 /*
923  * env authz provider
924  */
925
926 static authz_status env_check_authorization(request_rec *r,
927                                             const char *require_line,
928                                             const void *parsed_require_line)
929 {
930     const char *t, *w;
931
932     /* The 'env' provider will allow the configuration to specify a list of
933         env variables to check rather than a single variable.  This is different
934         from the previous host based syntax. */
935     t = require_line;
936     while ((w = ap_getword_conf(r->pool, &t)) && w[0]) {
937         if (apr_table_get(r->subprocess_env, w)) {
938             return AUTHZ_GRANTED;
939         }
940     }
941
942     return AUTHZ_DENIED;
943 }
944
945 static const authz_provider authz_env_provider =
946 {
947     &env_check_authorization,
948     NULL,
949 };
950
951
952 /*
953  * all authz provider
954  */
955
956 static authz_status all_check_authorization(request_rec *r,
957                                             const char *require_line,
958                                             const void *parsed_require_line)
959 {
960     if (parsed_require_line) {
961         return AUTHZ_GRANTED;
962     }
963     return AUTHZ_DENIED;
964 }
965
966 static const char *all_parse_config(cmd_parms *cmd, const char *require_line,
967                                     const void **parsed_require_line)
968 {
969     /*
970      * If the argument to the 'all' provider is 'granted' then just let
971      * everybody in. This would be equivalent to the previous syntax of
972      * 'allow from all'. If the argument is 'denied' we reject everbody,
973      * which is equivalent to 'deny from all'.
974      */
975     if (strcasecmp(require_line, "granted") == 0) {
976         *parsed_require_line = (void *)1;
977         return NULL;
978     }
979     else if (strcasecmp(require_line, "denied") == 0) {
980         /* *parsed_require_line is already NULL */
981         return NULL;
982     }
983     else {
984         return "Argument for 'Require all' must be 'granted' or 'denied'";
985     }
986 }
987
988 static const authz_provider authz_all_provider =
989 {
990     &all_check_authorization,
991     &all_parse_config,
992 };
993
994
995 /*
996  * method authz provider
997  */
998
999 static authz_status method_check_authorization(request_rec *r,
1000                                                const char *require_line,
1001                                                const void *parsed_require_line)
1002 {
1003     const apr_int64_t *allowed = parsed_require_line;
1004     if (*allowed & (AP_METHOD_BIT << r->method_number))
1005         return AUTHZ_GRANTED;
1006     else
1007         return AUTHZ_DENIED;
1008 }
1009
1010 static const char *method_parse_config(cmd_parms *cmd, const char *require_line,
1011                                        const void **parsed_require_line)
1012 {
1013     const char *w, *t;
1014     apr_int64_t *allowed = apr_pcalloc(cmd->pool, sizeof(apr_int64_t));
1015
1016     t = require_line;
1017
1018     while ((w = ap_getword_conf(cmd->temp_pool, &t)) && w[0]) {
1019         int m = ap_method_number_of(w);
1020         if (m == M_INVALID) {
1021             return apr_pstrcat(cmd->pool, "Invalid Method '", w, "'", NULL);
1022         }
1023
1024         *allowed |= (AP_METHOD_BIT << m);
1025     }
1026
1027     *parsed_require_line = allowed;
1028     return NULL;
1029 }
1030
1031 static const authz_provider authz_method_provider =
1032 {
1033     &method_check_authorization,
1034     &method_parse_config,
1035 };
1036
1037 /*
1038  * expr authz provider
1039  */
1040
1041 #define REQUIRE_EXPR_NOTE "Require_expr_info"
1042 struct require_expr_info {
1043     ap_expr_info_t *expr;
1044     int want_user;
1045 };
1046
1047 static int expr_lookup_fn(ap_expr_lookup_parms *parms)
1048 {
1049     if (parms->type == AP_EXPR_FUNC_VAR
1050         && strcasecmp(parms->name, "REMOTE_USER") == 0) {
1051         struct require_expr_info *info;
1052         apr_pool_userdata_get((void**)&info, REQUIRE_EXPR_NOTE, parms->ptemp);
1053         AP_DEBUG_ASSERT(info != NULL);
1054         info->want_user = 1;
1055     }
1056     return ap_expr_lookup_default(parms);
1057 }
1058
1059 static const char *expr_parse_config(cmd_parms *cmd, const char *require_line,
1060                                      const void **parsed_require_line)
1061 {
1062     const char *expr_err = NULL;
1063     struct require_expr_info *info = apr_pcalloc(cmd->pool, sizeof(*info));
1064
1065     apr_pool_userdata_setn(info, REQUIRE_EXPR_NOTE, apr_pool_cleanup_null,
1066                           cmd->temp_pool);
1067     info->expr = ap_expr_parse_cmd(cmd, require_line, 0, &expr_err,
1068                                    expr_lookup_fn);
1069
1070     if (expr_err)
1071         return apr_pstrcat(cmd->temp_pool,
1072                            "Cannot parse expression in require line: ",
1073                            expr_err, NULL);
1074
1075     *parsed_require_line = info;
1076
1077     return NULL;
1078 }
1079
1080 static authz_status expr_check_authorization(request_rec *r,
1081                                              const char *require_line,
1082                                              const void *parsed_require_line)
1083 {
1084     const char *err = NULL;
1085     const struct require_expr_info *info = parsed_require_line;
1086     int rc = ap_expr_exec(r, info->expr, &err);
1087
1088     if (rc < 0) {
1089         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02320)
1090                       "Error evaluating expression in 'Require expr': %s",
1091                       err);
1092         return AUTHZ_GENERAL_ERROR;
1093     }
1094     else if (rc == 0) {
1095         if (info->want_user)
1096             return AUTHZ_DENIED_NO_USER;
1097         else
1098             return AUTHZ_DENIED;
1099     }
1100     else {
1101         return AUTHZ_GRANTED;
1102     }
1103 }
1104
1105 static const authz_provider authz_expr_provider =
1106 {
1107     &expr_check_authorization,
1108     &expr_parse_config,
1109 };
1110
1111
1112 static void register_hooks(apr_pool_t *p)
1113 {
1114     APR_REGISTER_OPTIONAL_FN(authz_some_auth_required);
1115
1116     ap_hook_pre_config(authz_core_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
1117     ap_hook_check_config(authz_core_check_config, NULL, NULL, APR_HOOK_MIDDLE);
1118     ap_hook_check_authz(authorize_user, NULL, NULL, APR_HOOK_LAST,
1119                         AP_AUTH_INTERNAL_PER_CONF);
1120     ap_hook_check_access_ex(authorize_userless, NULL, NULL, APR_HOOK_LAST,
1121                             AP_AUTH_INTERNAL_PER_CONF);
1122
1123     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "env",
1124                               AUTHZ_PROVIDER_VERSION,
1125                               &authz_env_provider, AP_AUTH_INTERNAL_PER_CONF);
1126     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "all",
1127                               AUTHZ_PROVIDER_VERSION,
1128                               &authz_all_provider, AP_AUTH_INTERNAL_PER_CONF);
1129     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "method",
1130                               AUTHZ_PROVIDER_VERSION,
1131                               &authz_method_provider, AP_AUTH_INTERNAL_PER_CONF);
1132     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "expr",
1133                               AUTHZ_PROVIDER_VERSION,
1134                               &authz_expr_provider, AP_AUTH_INTERNAL_PER_CONF);
1135 }
1136
1137 AP_DECLARE_MODULE(authz_core) =
1138 {
1139     STANDARD20_MODULE_STUFF,
1140     create_authz_core_dir_config,   /* dir config creater */
1141     merge_authz_core_dir_config,    /* dir merger */
1142     create_authz_core_svr_config,   /* server config */
1143     NULL,                           /* merge server config */
1144     authz_cmds,
1145     register_hooks                  /* register hooks */
1146 };
1147