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