]> granicus.if.org Git - apache/blob - modules/aaa/mod_authz_core.c
remove unused variables, rename some variables, move and change
[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
41 #include "mod_auth.h"
42
43 #if APR_HAVE_NETINET_IN_H
44 #include <netinet/in.h>
45 #endif
46
47 /* TODO List
48
49 X- Convert all of the authz modules to providers
50 X- Remove the ap_requires field from the core_dir_config structure
51 X- Remove the ap_requires field from authz_dir_conf
52 X- Remove the function ap_requires() and authz_ap_requires()
53    since their functionality is no longer supported
54    or necessary in the refactoring
55 X- Remove the calls to ap_some_auth_required() in the
56    core request handling to allow the hooks to be called
57    in all cases.  Is this function even necessary
58    anymore?
59 X- Determine of merge_authz_dir_config is even
60    necessary and remove if not
61 X- Split the authz type from the arguments when the
62    authz provider is registered and store the type
63    in ->provider_name and the arguments in ->requirement
64 X- Move the check for METHOD_MASK out of the authz
65    providers and into the provider vector
66 X- Change the status code to AUTHZ_DENIED, AUTHZ_GRANTED
67    and AUTHZ_GENERAL_ERROR
68 - Determine if setting the AUTHZ_PROVIDER_NAME_NOTE note
69    is even necessary.  This was used in authn to support
70    authn_alias.  Is there a need for an authz_alias?
71 X- Remove the Satisfy directive functionality and replace it with the
72    <SatisfyAll>, <SatisfyOne> directives
73 X- Remove the Satisfy directive
74 X- Implement the <SatisfyAll> <SatisfyOne> block directives
75    to handle the 'and' and 'or' logic for authorization.
76 X- Remove the AuthzXXXAuthoritative directives from all of
77    the authz providers
78 X- Implement the Reject directive that will deny authorization
79    if the argument is true
80 X- Fold the Reject directive into the <SatisfyAll> <SatisfyOne>
81    logic
82 X- Reimplement the host based authorization 'allow', 'deny'
83    and 'order' as authz providers
84 X- Remove the 'allow', 'deny' and 'order' directives
85 - Merge mod_authn_alias into mod_authn_core
86 X- Remove all of the references to the authzxxxAuthoritative
87    directives from the documentation
88 X- Remove the Satisfy directive from the documentation
89 */
90
91 typedef struct provider_alias_rec {
92     char *provider_name;
93     char *provider_alias;
94     char *provider_args;
95     ap_conf_vector_t *sec_auth;
96     const authz_provider *provider;
97 } provider_alias_rec;
98
99 typedef struct {
100     authz_provider_list *providers;
101     authz_request_state req_state;
102     int req_state_level;
103     int merge_rules;
104 } authz_core_dir_conf;
105
106 typedef struct authz_core_srv_conf {
107     apr_hash_t *alias_rec;
108 } authz_core_srv_conf;
109
110 module AP_MODULE_DECLARE_DATA authz_core_module;
111 static const char *merge_authz_provider(authz_core_dir_conf *conf, authz_provider_list *newp);
112 static void walk_merge_provider_list(apr_pool_t *a, authz_core_dir_conf *conf, authz_provider_list *providers);
113
114 #define BASE_REQ_STATE AUTHZ_REQSTATE_ALL
115 #define BASE_REQ_LEVEL 0
116
117 static void *create_authz_core_dir_config(apr_pool_t *p, char *dummy)
118 {
119     authz_core_dir_conf *conf = apr_pcalloc(p, sizeof(*conf));
120
121     conf->req_state = BASE_REQ_STATE;
122     conf->req_state_level = BASE_REQ_LEVEL;
123     conf->merge_rules = 1;
124     return (void *)conf;
125 }
126
127 static void *merge_authz_core_dir_config(apr_pool_t *p,
128                                          void *basev, void *newv)
129 {
130     authz_core_dir_conf *base = (authz_core_dir_conf *)basev;
131     authz_core_dir_conf *new = (authz_core_dir_conf *)newv;
132     authz_core_dir_conf *conf;
133
134     /* Create this conf by duplicating the base, replacing elements
135     * (or creating copies for merging) where new-> values exist.
136     */
137     conf = (authz_core_dir_conf *)apr_pmemdup(p, base, sizeof(authz_core_dir_conf));
138
139     /* Wipe out the providers and rejects lists so that
140         they can be recreated by the merge process. */
141     conf->providers = NULL;
142
143     /* Only merge the base providers in if the merge_rules
144         directive has been set. */
145     if (base->providers && new->merge_rules) {
146         walk_merge_provider_list (p, conf, base->providers);
147     }
148     if (new->providers) {
149         walk_merge_provider_list (p, conf, new->providers);
150     }
151
152     return (void*)conf;
153 }
154
155 static void *create_authz_core_svr_config(apr_pool_t *p, server_rec *s)
156 {
157
158     authz_core_srv_conf *authcfg;
159
160     authcfg = apr_pcalloc(p, sizeof(*authcfg));
161     authcfg->alias_rec = apr_hash_make(p);
162
163     return (void *)authcfg;
164 }
165
166 /* This is a fake authz provider that really merges various authz alias
167  * configurations and then invokes them.
168  */
169 static authz_status authz_alias_check_authorization(request_rec *r,
170                                                     const char *require_args)
171 {
172     const char *provider_name;
173     authz_status ret = AUTHZ_DENIED;
174
175     /* Look up the provider alias in the alias list.
176      * Get the the dir_config and call ap_Merge_per_dir_configs()
177      * Call the real provider->check_authorization() function
178      * return the result of the above function call
179      */
180
181     provider_name = apr_table_get(r->notes, AUTHZ_PROVIDER_NAME_NOTE);
182
183     if (provider_name) {
184         authz_core_srv_conf *authcfg;
185         provider_alias_rec *prvdraliasrec;
186
187         authcfg = ap_get_module_config(r->server->module_config,
188                                        &authz_core_module);
189
190         prvdraliasrec = apr_hash_get(authcfg->alias_rec, provider_name,
191                                      APR_HASH_KEY_STRING);
192
193         /* If we found the alias provider in the list, then merge the directory
194            configurations and call the real provider */
195         if (prvdraliasrec) {
196             ap_conf_vector_t *orig_dir_config = r->per_dir_config;
197
198             r->per_dir_config =
199                 ap_merge_per_dir_configs(r->pool, orig_dir_config,
200                                          prvdraliasrec->sec_auth);
201
202             ret = prvdraliasrec->provider->
203                 check_authorization(r, prvdraliasrec->provider_args);
204
205             r->per_dir_config = orig_dir_config;
206         }
207     }
208
209     return ret;
210 }
211
212 static const authz_provider authz_alias_provider =
213 {
214     &authz_alias_check_authorization
215 };
216
217 static const char *authz_require_alias_section(cmd_parms *cmd, void *mconfig,
218                                                const char *args)
219 {
220     const char *endp = ap_strrchr_c(args, '>');
221     char *provider_name;
222     char *provider_alias;
223     char *provider_args;
224     ap_conf_vector_t *new_authz_config;
225     int old_overrides = cmd->override;
226     const char *errmsg;
227
228     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
229     if (err != NULL) {
230         return err;
231     }
232
233     if (endp == NULL) {
234         return apr_pstrcat(cmd->pool, cmd->cmd->name,
235                            "> directive missing closing '>'", NULL);
236     }
237
238     args = apr_pstrndup(cmd->pool, args, endp - args);
239
240     if (!args[0]) {
241         return apr_pstrcat(cmd->pool, cmd->cmd->name,
242                            "> directive requires additional arguments", NULL);
243     }
244
245     /* Pull the real provider name and the alias name from the block header */
246     provider_name = ap_getword_conf(cmd->pool, &args);
247     provider_alias = ap_getword_conf(cmd->pool, &args);
248     provider_args = ap_getword_conf(cmd->pool, &args);
249
250     if (!provider_name[0] || !provider_alias[0]) {
251         return apr_pstrcat(cmd->pool, cmd->cmd->name,
252                            "> directive requires additional arguments", NULL);
253     }
254
255     new_authz_config = ap_create_per_dir_config(cmd->pool);
256
257     /* Walk the subsection configuration to get the per_dir config that we will
258      * merge just before the real provider is called.
259      */
260     cmd->override = OR_ALL|ACCESS_CONF;
261     errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_authz_config);
262     cmd->override = old_overrides;
263
264     if (!errmsg) {
265         provider_alias_rec *prvdraliasrec;
266         authz_core_srv_conf *authcfg;
267
268         prvdraliasrec = apr_pcalloc(cmd->pool, sizeof(*prvdraliasrec));
269
270         /* Save off the new directory config along with the original
271          * provider name and function pointer data
272          */
273         prvdraliasrec->provider_name = provider_name;
274         prvdraliasrec->provider_alias = provider_alias;
275         prvdraliasrec->provider_args = provider_args;
276         prvdraliasrec->sec_auth = new_authz_config;
277         prvdraliasrec->provider =
278             ap_lookup_provider(AUTHZ_PROVIDER_GROUP, provider_name,
279                                AUTHZ_PROVIDER_VERSION);
280
281         /* by the time the config file is used, the provider should be loaded
282          * and registered with us.
283          */
284         if (!prvdraliasrec->provider) {
285             return apr_psprintf(cmd->pool,
286                                 "Unknown Authz provider: %s",
287                                 provider_name);
288         }
289
290         authcfg = ap_get_module_config(cmd->server->module_config,
291                                        &authz_core_module);
292
293         apr_hash_set(authcfg->alias_rec, provider_alias,
294                      APR_HASH_KEY_STRING, prvdraliasrec);
295
296         /* Register the fake provider so that we get called first */
297         ap_register_auth_provider(cmd->pool, AUTHZ_PROVIDER_GROUP,
298                                   provider_alias, AUTHZ_PROVIDER_VERSION,
299                                   &authz_alias_provider,
300                                   AP_AUTH_INTERNAL_PER_CONF);
301     }
302
303     return errmsg;
304 }
305
306 static void walk_merge_provider_list(apr_pool_t *a, authz_core_dir_conf *conf, authz_provider_list *providers)
307 {
308     authz_provider_list *newp = (authz_provider_list *)apr_palloc(a, sizeof(authz_provider_list));
309     memcpy(newp, providers, sizeof(authz_provider_list));
310
311     /* Since the merge is being done at a later time rather than
312         at configuration time, we need to fake the current
313         state of the list so that the new element get merged
314         into the correct location. The current state is
315         derived from the state of the object to be merged. */
316     conf->req_state = newp->req_state;
317     conf->req_state_level = newp->req_state_level;
318     newp->one_next = NULL;
319     newp->all_next = NULL;
320
321     /* Merge it into the existing provider logic. */
322     merge_authz_provider(conf, newp);
323
324     /* Walk all of the elements recursively to allow each existing
325         element to be copied and merged into the final configuration.*/
326     if (BASE_REQ_STATE == AUTHZ_REQSTATE_ONE) {
327         if (providers->one_next) {
328             walk_merge_provider_list (a, conf, providers->one_next);
329         }
330         if (providers->all_next) {
331             walk_merge_provider_list (a, conf, providers->all_next);
332         }
333     }
334     else {
335         if (providers->all_next) {
336             walk_merge_provider_list (a, conf, providers->all_next);
337         }
338         if (providers->one_next) {
339             walk_merge_provider_list (a, conf, providers->one_next);
340         }
341     }
342
343     return;
344 }
345
346 static const char *merge_authz_provider(authz_core_dir_conf *conf, authz_provider_list *newp)
347 {
348     /* Add it to the list now. */
349     if (!conf->providers) {
350         conf->providers = newp;
351     }
352     else {
353         authz_provider_list *last = conf->providers;
354         int level = conf->req_state_level;
355
356         /* if the level is the base level then take care of the implicit
357          * operation at this level.
358          */
359         if (level == BASE_REQ_LEVEL) {
360             if (conf->req_state == AUTHZ_REQSTATE_ONE) {
361                 /* Just run through the Require_one list and add the
362                  * node
363                  */
364                 while (last->one_next) {
365                     last = last->one_next;
366                 }
367                 last->one_next = newp;
368             }
369             else {
370                 /* Just run through the Require_all list and add the
371                  * node
372                  */
373                 while (last->all_next) {
374                     last = last->all_next;
375                 }
376                 last->all_next = newp;
377             }
378         }
379
380         /* if the last nodes level is greater than the new nodes
381          *  level, then we need to insert the new node at this
382          *  point.  The req_state of the new node determine
383          *  how it is inserted into the list.
384          */
385         else if (last->req_state_level > newp->req_state_level) {
386             if (newp->req_state == AUTHZ_REQSTATE_ONE)
387                 newp->one_next = last;
388             else
389                 newp->all_next = last;
390             conf->providers = newp;
391         }
392         else {
393             /* Traverse the list to find the last entry.Each level
394              * indicates a transition in the logic.
395              */
396             for (;level;level--) {
397                 /* if we are in a Require_all block then run through
398                  * all of the Require_all nodes to the end of the list.
399                  * Stop if we run into a node whose level is greater than
400                  * the level of the node that is being inserted.
401                  */
402                 if (last->req_state == AUTHZ_REQSTATE_ALL) {
403                     while ((last->all_next) && (last->all_next->req_state_level <= conf->req_state_level)) {
404                         last = last->all_next;
405                     }
406                     /* If the end of the list contains a node state
407                      * change then run through all of the Require_one
408                      * nodes to the end of that list
409                      */
410                     if (level >= last->req_state_level) {
411                         while (last->one_next) {
412                             last = last->one_next;
413                         }
414                     }
415                     continue;
416                 }
417                 /* if we are in a Require_one block then run through
418                  * all of the Require_one nodes to the end of the list
419                  */
420                 if (last->req_state == AUTHZ_REQSTATE_ONE) {
421                     while (last->one_next) {
422                         last = last->one_next;
423                     }
424                     /* If the end of the list contains a node state
425                      * change then run through all of the Require_all
426                      * nodes to the end of that list
427                      */
428                     if (level >= last->req_state_level) {
429                         while (last->all_next) {
430                             last = last->all_next;
431                         }
432                     }
433                     continue;
434                 }
435             }
436
437             /* The current state flag indicates which way the transition should
438              * go.  If ALL then take the all_next path, otherwise one_next
439              */
440             if (last->req_state == AUTHZ_REQSTATE_ALL) {
441                 /* If we already have an all_next node, then
442                  * we must have dropped back a level so assign
443                  * the node to one_next
444                  */
445                 if (!last->all_next) {
446                     last->all_next = newp;
447                 }
448                 /* If the level of the new node is greater than the
449                  *  last node, then insert the new node at this point.
450                  *  The req_state of the new node will determine
451                  *  how the node is added to the list.
452                  */
453                 else if (last->req_state_level <= newp->req_state_level) {
454                     if (newp->req_state == AUTHZ_REQSTATE_ONE)
455                         newp->one_next = last->all_next;
456                     else
457                         newp->all_next = last->all_next;
458                     last->all_next = newp;
459                 }
460                 else {
461                     last->one_next = newp;
462                 }
463             }
464             else {
465                 /* If we already have a one_next node, then
466                  * we must have dropped back a level so assign
467                  * the node to all_next
468                  */
469                 if (!last->one_next) {
470                     last->one_next = newp;
471                 }
472                 /* If the level of the new node is greater than the
473                  *  last node, then insert the new node at this point.
474                  *  The req_state of the new node will determine
475                  *  how the node is added to the list.
476                  */
477                 else if (last->req_state_level <= newp->req_state_level) {
478                     if (newp->req_state == AUTHZ_REQSTATE_ONE)
479                         newp->one_next = last->one_next;
480                     else
481                         newp->all_next = last->one_next;
482                     last->one_next = newp;
483                 }
484                 else {
485                     last->all_next = newp;
486                 }
487             }
488         }
489     }
490
491     return NULL;
492 }
493
494 static const char *add_authz_provider(cmd_parms *cmd, void *config,
495                                       const char *args)
496 {
497     authz_core_dir_conf *conf = (authz_core_dir_conf*)config;
498     authz_provider_list *newp;
499     const char *t, *w;
500
501     newp = apr_pcalloc(cmd->pool, sizeof(authz_provider_list));
502
503     t = args;
504     w = ap_getword_white(cmd->pool, &t);
505
506     if (w)
507         newp->provider_name = apr_pstrdup(cmd->pool, w);
508     if (t)
509         newp->requirement = apr_pstrdup(cmd->pool, t);
510     newp->method_mask = cmd->limited;
511
512     /* lookup and cache the actual provider now */
513     newp->provider = ap_lookup_provider(AUTHZ_PROVIDER_GROUP,
514                                         newp->provider_name,
515                                         AUTHZ_PROVIDER_VERSION);
516     newp->req_state = conf->req_state;
517     newp->req_state_level = conf->req_state_level;
518     newp->is_reject = (cmd->info != NULL);
519
520     /* by the time the config file is used, the provider should be loaded
521      * and registered with us.
522      */
523     if (newp->provider == NULL) {
524         return apr_psprintf(cmd->pool,
525                             "Unknown Authz provider: %s",
526                             newp->provider_name);
527     }
528
529     /* if the provider doesn't provide the appropriate function, reject it */
530     if (!newp->provider->check_authorization) {
531         return apr_psprintf(cmd->pool,
532                             "The '%s' Authz provider is not supported by any "
533                             "of the loaded authorization modules ",
534                             newp->provider_name);
535     }
536
537     /* Add the element to the list. */
538     return merge_authz_provider(conf, newp);
539 }
540
541 static const char *add_authz_section(cmd_parms *cmd, void *mconfig,
542                                      const char *args)
543 {
544     authz_core_dir_conf *conf = mconfig;
545     const char *endp = ap_strrchr_c(args, '>');
546     authz_request_state old_reqstate;
547     int old_overrides = cmd->override;
548     const char *errmsg;
549
550     if (endp == NULL) {
551         return apr_pstrcat(cmd->pool, cmd->cmd->name,
552                            "> directive missing closing '>'", NULL);
553     }
554
555     args = apr_pstrndup(cmd->pool, args, endp - args);
556
557     if (args[0]) {
558         return apr_pstrcat(cmd->pool, cmd->cmd->name,
559                            "> directive doesn't take additional arguments", NULL);
560     }
561
562     /* Save off the current request state so that we can go back to it after walking
563      *  the subsection.  Indicate a transition in the logic incrementing the level.
564      *  After the subsection walk the level will be decremented to indicate the
565      *  path to follow. As the require directives are read by the configuration
566      *  the req_state and the level will allow it to traverse the list to find
567      *  the last element in the provider calling list.
568      */
569     old_reqstate = conf->req_state;
570     if (strcasecmp (cmd->directive->directive, "<SatisfyAll") == 0) {
571         conf->req_state = AUTHZ_REQSTATE_ALL;
572     }
573     else {
574         conf->req_state = AUTHZ_REQSTATE_ONE;
575     }
576     conf->req_state_level++;
577     cmd->override = OR_ALL|ACCESS_CONF;
578
579     /* Walk the subsection configuration to get the per_dir config that we will
580      * merge just before the real provider is called.
581      */
582     errmsg = ap_walk_config(cmd->directive->first_child, cmd, cmd->context);
583
584     conf->req_state_level--;
585     conf->req_state = old_reqstate;
586     cmd->override = old_overrides;
587
588     return errmsg;
589 }
590
591 static const command_rec authz_cmds[] =
592 {
593     AP_INIT_RAW_ARGS("Require", add_authz_provider, NULL, OR_AUTHCFG,
594                      "Selects which authenticated users or groups may access "
595                      "a protected space"),
596     AP_INIT_RAW_ARGS("Reject", add_authz_provider, (void*)1, OR_AUTHCFG,
597                      "Rejects the specified authenticated users groups or "
598                      "host based requests from accessing a protected space"),
599     AP_INIT_RAW_ARGS("<RequireAlias", authz_require_alias_section, NULL, RSRC_CONF,
600                      "Container for authorization directives grouped under "
601                      "an authz provider alias"),
602     AP_INIT_RAW_ARGS("<SatisfyAll", add_authz_section, NULL, OR_AUTHCFG,
603                      "Container for grouping require statements that must all "
604                      "succeed for authorization to be granted"),
605     AP_INIT_RAW_ARGS("<SatisfyOne", add_authz_section, NULL, OR_AUTHCFG,
606                      "Container for grouping require statements of which one "
607                      "must succeed for authorization to be granted"),
608     AP_INIT_FLAG("AuthzMergeRules", ap_set_flag_slot,
609                  (void *)APR_OFFSETOF(authz_core_dir_conf, merge_rules), OR_AUTHCFG,
610                  "Set to 'on' to allow the parent's <Directory> or <Location> authz rules "
611                  "to be merged into the current <Directory> or <Location>.  Set to 'off' "
612                  "to disable merging. If set to 'off', only the authz rules defined in "
613                  "the current <Directory> or <Location> block will apply. The default is 'on'."),
614     {NULL}
615 };
616
617 #define RESOLVE_NEUTRAL(orig,new)  (new == AUTHZ_NEUTRAL) ? orig : new;
618
619 static authz_status check_provider_list (request_rec *r, authz_provider_list *current_provider, int prev_level)
620 {
621     authz_status auth_result = AUTHZ_DENIED;
622
623     const authz_provider *provider;
624
625     provider = current_provider->provider;
626     apr_table_setn(r->notes, AUTHZ_PROVIDER_NAME_NOTE,
627                    current_provider->provider_name);
628
629     /* check to make sure that the request method requires
630      * authorization before calling the provider
631      */
632     if (!(current_provider->method_mask &
633         (AP_METHOD_BIT << r->method_number))) {
634         return AUTHZ_DENIED;
635     }
636
637     auth_result = provider->check_authorization(r,
638                     current_provider->requirement);
639
640     if (auth_result == AUTHZ_GENERAL_ERROR) {
641         return auth_result;
642     }
643
644     if (current_provider->is_reject) {
645         /* If the provider was called through a reject directive, then
646             alter the result accordingly.  If the original result was
647             Denied then the new result is Neutral since we can not grant
648             access simply because authorization was not rejected. */
649         auth_result = auth_result == AUTHZ_DENIED ? AUTHZ_NEUTRAL : AUTHZ_DENIED;
650     }
651
652     apr_table_unset(r->notes, AUTHZ_PROVIDER_NAME_NOTE);
653
654     /* If the current node is a Require_One type */
655     if (current_provider->req_state == AUTHZ_REQSTATE_ONE) {
656         /* If the auth_result of *this* node was GRANTED and we are
657          * embedded in a Require_all block then look to see if there
658          * is another Require_all node that needs to be satisfied
659          */
660         if ((auth_result == AUTHZ_GRANTED) || (auth_result == AUTHZ_NEUTRAL)) {
661             if ((current_provider->all_next) &&
662                 (current_provider->all_next->req_state_level < current_provider->req_state_level)) {
663                 authz_status temp_result = check_provider_list (r, current_provider->all_next,
664                                                                 current_provider->req_state_level);
665                 auth_result = RESOLVE_NEUTRAL(auth_result, temp_result);
666             }
667             return auth_result;
668         }
669         one_next:
670
671         /* Traverse forward to the next Require_one node it one exists
672          * otherwise just return the auth_result
673          */
674         if (current_provider->one_next) {
675             authz_status temp_result = check_provider_list (r, current_provider->one_next,
676                                                             current_provider->req_state_level);
677             auth_result = RESOLVE_NEUTRAL(auth_result, temp_result);
678         }
679         else
680             return auth_result;
681
682         /* If the *last* auth_result was GRANTED and we are embedded in
683          * a Require_all block then look to see if there is another
684          * Require_all node that needs to be satisfied
685          */
686         if (((auth_result == AUTHZ_GRANTED) || (auth_result == AUTHZ_NEUTRAL))
687             && (current_provider->all_next)
688             && (current_provider->all_next->req_state_level < current_provider->req_state_level)) {
689             authz_status temp_result = check_provider_list (r, current_provider->all_next,
690                                                             current_provider->req_state_level);
691             auth_result = RESOLVE_NEUTRAL(auth_result, temp_result);
692         }
693         /* If the *last* auth_result was DENIED and we are inside of a
694          * Require_one block then look to see if there is another
695          * Require_one node that can be satisfied
696          */
697         else if ((auth_result == AUTHZ_DENIED)
698                  && (current_provider->one_next)
699                  && (current_provider->one_next->req_state_level < current_provider->req_state_level)) {
700             goto one_next;
701         }
702
703         return auth_result;
704     }
705
706     /* If the current node is a Require_All type */
707     if (current_provider->req_state == AUTHZ_REQSTATE_ALL) {
708         /* if the auth_result of *this* node was DENIED and we are
709          * embedded in a Require_one block then look to see if there
710          * is another Require_one node that can be satisfied
711          */
712         if (auth_result == AUTHZ_DENIED) {
713             if ((current_provider->one_next) &&
714                 (current_provider->one_next->req_state_level < current_provider->req_state_level)) {
715                 authz_status temp_result = check_provider_list (r, current_provider->one_next,
716                                                                 current_provider->req_state_level);
717                 auth_result = RESOLVE_NEUTRAL(auth_result, temp_result);
718             }
719             return auth_result;
720         }
721         all_next:
722
723         /* Traverse forward to the next Require_all node it one exists
724          * otherwise just return the auth_result
725          */
726         if (current_provider->all_next) {
727             authz_status temp_result = check_provider_list (r, current_provider->all_next,
728                                                             current_provider->req_state_level);
729             auth_result = RESOLVE_NEUTRAL(auth_result, temp_result);
730         }
731         else
732             return auth_result;
733
734         /* if the *last* auth_result was DENIED and we are embedded
735          * in a Require_one block then look to see if there is another
736          * Require_one node that can be satisfied
737          */
738         if ((auth_result == AUTHZ_DENIED)
739             && (current_provider->one_next)
740             && (current_provider->one_next->req_state_level < current_provider->req_state_level)) {
741             authz_status temp_result = check_provider_list (r, current_provider->one_next,
742                                                             current_provider->req_state_level);
743             auth_result = RESOLVE_NEUTRAL(auth_result, temp_result);
744         }
745         /* If the *last* auth_result was GRANTED and we are inside of a
746          * Require_all block then look to see if there is another
747          * Require_all node that needs to be satisfied
748          */
749         else if (((auth_result == AUTHZ_GRANTED) || (auth_result == AUTHZ_NEUTRAL))
750                  && (current_provider->all_next)
751                  && (current_provider->all_next->req_state_level < current_provider->req_state_level)) {
752             goto all_next;
753         }
754     }
755
756     return auth_result;
757 }
758
759 static int authorize_user(request_rec *r)
760 {
761     authz_core_dir_conf *conf;
762     authz_status auth_result;
763     authz_provider_list *current_provider;
764     const char *note = apr_table_get(r->notes, AUTHZ_ACCESS_PASSED_NOTE);
765
766     conf = ap_get_module_config(r->per_dir_config, &authz_core_module);
767
768     /* If we're not really configured for providers, stop now. */
769     if (!conf->providers) {
770         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
771                       "no authorization providers configured");
772         return DECLINED;
773     }
774
775     /* run through the require logic. */
776     current_provider = conf->providers;
777     auth_result = check_provider_list (r, current_provider, 0);
778
779     if (auth_result != AUTHZ_GRANTED) {
780         int return_code;
781
782         switch (auth_result) {
783             case AUTHZ_DENIED:
784             case AUTHZ_NEUTRAL:
785                 /* XXX If the deprecated Satisfy directive is set to anything
786                    but ANY a failure in access control or authz will cause
787                    an HTTP_UNAUTHORIZED.  Just the if statement
788                    should be removed in 3.0 when the Satisfy directive
789                    goes away. */
790                 if (!note || (ap_satisfies(r) != SATISFY_ANY) || (note[0] == 'N')) {
791                     if (r->ap_auth_type == NULL) {
792                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
793                                       "client denied by server configuration: %s%s",
794                                       r->filename ? "" : "uri ",
795                                       r->filename ? r->filename : r->uri);
796                         return_code = HTTP_FORBIDDEN;
797                     }
798                     else {
799                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
800                                       "user %s: authorization failure for \"%s\": ",
801                                       r->user, r->uri);
802                         return_code = HTTP_UNAUTHORIZED;
803                     }
804                 }
805                 else {
806                     return_code = DECLINED;
807                 }
808                 break;
809             case AUTHZ_GENERAL_ERROR:
810             default:
811                 /* We'll assume that the module has already said what its
812                  * error was in the logs.
813                  */
814                 return_code = HTTP_INTERNAL_SERVER_ERROR;
815                 break;
816         }
817
818         /* If we're returning 403, tell them to try again. */
819         if (return_code == HTTP_UNAUTHORIZED) {
820             ap_note_auth_failure (r);
821         }
822         return return_code;
823     }
824
825     return OK;
826 }
827
828 static int authz_some_auth_required(request_rec *r)
829 {
830     authz_core_dir_conf *conf;
831     authz_provider_list *current_provider;
832
833     conf = ap_get_module_config(r->per_dir_config, &authz_core_module);
834
835     current_provider = conf->providers;
836
837     while (current_provider) {
838
839         /* Does this provider config apply for this method */
840         if (current_provider->method_mask &
841                 (AP_METHOD_BIT << r->method_number)) {
842             return 1;
843         }
844
845         current_provider = current_provider->one_next;
846     }
847
848     return 0;
849 }
850
851 static void register_hooks(apr_pool_t *p)
852 {
853     APR_REGISTER_OPTIONAL_FN(authz_some_auth_required);
854
855     ap_hook_check_authz(authorize_user, NULL, NULL, APR_HOOK_MIDDLE,
856                         AP_AUTH_INTERNAL_PER_CONF);
857 }
858
859 module AP_MODULE_DECLARE_DATA authz_core_module =
860 {
861     STANDARD20_MODULE_STUFF,
862     create_authz_core_dir_config,   /* dir config creater */
863     merge_authz_core_dir_config,    /* dir merger */
864     create_authz_core_svr_config,   /* server config */
865     NULL,                           /* merge server config */
866     authz_cmds,
867     register_hooks                  /* register hooks */
868 };
869