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