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