mod_authz_core.c revision 21da42a6b8f551ef603bd06356d3bf71d6d0c21d
5beae861ede7eba138c7140f195ae77ba3106cbffielding/* Licensed to the Apache Software Foundation (ASF) under one or more
5beae861ede7eba138c7140f195ae77ba3106cbffielding * contributor license agreements. See the NOTICE file distributed with
5beae861ede7eba138c7140f195ae77ba3106cbffielding * this work for additional information regarding copyright ownership.
fd2db14d870ff9aa9795841360f6e3d562ad69a2jerenkrantz * The ASF licenses this file to You under the Apache License, Version 2.0
5beae861ede7eba138c7140f195ae77ba3106cbffielding * (the "License"); you may not use this file except in compliance with
5beae861ede7eba138c7140f195ae77ba3106cbffielding * the License. You may obtain a copy of the License at
5beae861ede7eba138c7140f195ae77ba3106cbffielding *
5beae861ede7eba138c7140f195ae77ba3106cbffielding * http://www.apache.org/licenses/LICENSE-2.0
5beae861ede7eba138c7140f195ae77ba3106cbffielding *
5beae861ede7eba138c7140f195ae77ba3106cbffielding * Unless required by applicable law or agreed to in writing, software
5beae861ede7eba138c7140f195ae77ba3106cbffielding * distributed under the License is distributed on an "AS IS" BASIS,
5beae861ede7eba138c7140f195ae77ba3106cbffielding * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5beae861ede7eba138c7140f195ae77ba3106cbffielding * See the License for the specific language governing permissions and
5beae861ede7eba138c7140f195ae77ba3106cbffielding * limitations under the License.
5beae861ede7eba138c7140f195ae77ba3106cbffielding */
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding/*
5beae861ede7eba138c7140f195ae77ba3106cbffielding * Security options etc.
5beae861ede7eba138c7140f195ae77ba3106cbffielding *
5beae861ede7eba138c7140f195ae77ba3106cbffielding * Module derived from code originally written by Rob McCool
5beae861ede7eba138c7140f195ae77ba3106cbffielding *
5beae861ede7eba138c7140f195ae77ba3106cbffielding */
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "apr_strings.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "apr_network_io.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "apr_md5.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding#define APR_WANT_STRFUNC
5beae861ede7eba138c7140f195ae77ba3106cbffielding#define APR_WANT_BYTEFUNC
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "apr_want.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "ap_config.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "httpd.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "http_config.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "http_core.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "http_log.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "http_request.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "http_protocol.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "ap_provider.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "ap_expr.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include "mod_auth.h"
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding#if APR_HAVE_NETINET_IN_H
5beae861ede7eba138c7140f195ae77ba3106cbffielding#include <netinet/in.h>
5beae861ede7eba138c7140f195ae77ba3106cbffielding#endif
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding#undef AUTHZ_EXTRA_CONFIGS
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingtypedef struct provider_alias_rec {
5beae861ede7eba138c7140f195ae77ba3106cbffielding char *provider_name;
5beae861ede7eba138c7140f195ae77ba3106cbffielding char *provider_alias;
5beae861ede7eba138c7140f195ae77ba3106cbffielding char *provider_args;
5beae861ede7eba138c7140f195ae77ba3106cbffielding const void *provider_parsed_args;
5beae861ede7eba138c7140f195ae77ba3106cbffielding ap_conf_vector_t *sec_auth;
5beae861ede7eba138c7140f195ae77ba3106cbffielding const authz_provider *provider;
5beae861ede7eba138c7140f195ae77ba3106cbffielding} provider_alias_rec;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingtypedef enum {
5beae861ede7eba138c7140f195ae77ba3106cbffielding AUTHZ_LOGIC_AND,
5beae861ede7eba138c7140f195ae77ba3106cbffielding AUTHZ_LOGIC_OR,
5beae861ede7eba138c7140f195ae77ba3106cbffielding AUTHZ_LOGIC_OFF,
5beae861ede7eba138c7140f195ae77ba3106cbffielding AUTHZ_LOGIC_UNSET
5beae861ede7eba138c7140f195ae77ba3106cbffielding} authz_logic_op;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingtypedef struct authz_section_conf authz_section_conf;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingstruct authz_section_conf {
5beae861ede7eba138c7140f195ae77ba3106cbffielding const char *provider_name;
5beae861ede7eba138c7140f195ae77ba3106cbffielding const char *provider_args;
5beae861ede7eba138c7140f195ae77ba3106cbffielding const void *provider_parsed_args;
5beae861ede7eba138c7140f195ae77ba3106cbffielding const authz_provider *provider;
e38f3ccb1d1368339739ba98be433ec16b1967e4aaron apr_int64_t limited;
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_logic_op op;
342322367642446713ef0f6e6421a41aec242425gregames int negate;
e38f3ccb1d1368339739ba98be433ec16b1967e4aaron /** true if this is not a real container but produced by AuthMerging;
5beae861ede7eba138c7140f195ae77ba3106cbffielding * only used for logging */
5beae861ede7eba138c7140f195ae77ba3106cbffielding int is_merged;
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_section_conf *first;
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_section_conf *next;
dc333aed7402e0c09b9f9d6542fe0acb82823959lars};
9b12a8c4735e90e88bb42f2b03569c852834ef36rse
7b3ca63bf6e5fe39125b04327d321d61da0afe89jerenkrantztypedef struct authz_core_dir_conf authz_core_dir_conf;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingstruct authz_core_dir_conf {
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_section_conf *section;
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_core_dir_conf *next;
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_logic_op op;
a1676e72e3153253c50cb88cbe8007d96f6aa72eianh signed char authz_forbidden_on_fail;
5beae861ede7eba138c7140f195ae77ba3106cbffielding};
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding#define UNSET -1
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingtypedef struct authz_core_srv_conf {
5beae861ede7eba138c7140f195ae77ba3106cbffielding apr_hash_t *alias_rec;
5beae861ede7eba138c7140f195ae77ba3106cbffielding} authz_core_srv_conf;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingmodule AP_MODULE_DECLARE_DATA authz_core_module;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingstatic authz_core_dir_conf *authz_core_first_dir_conf;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingstatic void *create_authz_core_dir_config(apr_pool_t *p, char *dummy)
2342e783a2f2e1d69c46c6fa3e8d987069d53046wrowe{
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_core_dir_conf *conf = apr_pcalloc(p, sizeof(*conf));
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding conf->op = AUTHZ_LOGIC_UNSET;
e60a4dc5bad9da4e835be390de54053c9d696918slive conf->authz_forbidden_on_fail = UNSET;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding conf->next = authz_core_first_dir_conf;
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_core_first_dir_conf = conf;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
acf47691e4f2e788ea89e30f27015402f4bf9075trawick return (void *)conf;
c835d410b99e00a4c30527529aefba6fea659688jwoolley}
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingstatic void *merge_authz_core_dir_config(apr_pool_t *p,
5beae861ede7eba138c7140f195ae77ba3106cbffielding void *basev, void *newv)
5beae861ede7eba138c7140f195ae77ba3106cbffielding{
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_core_dir_conf *base = (authz_core_dir_conf *)basev;
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_core_dir_conf *new = (authz_core_dir_conf *)newv;
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_core_dir_conf *conf;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding if (new->op == AUTHZ_LOGIC_UNSET && !new->section && base->section ) {
5beae861ede7eba138c7140f195ae77ba3106cbffielding /* Only authz_forbidden_on_fail has been set in new. Don't treat
5beae861ede7eba138c7140f195ae77ba3106cbffielding * it as a new auth config w.r.t. AuthMerging */
5beae861ede7eba138c7140f195ae77ba3106cbffielding conf = apr_pmemdup(p, base, sizeof(*base));
5beae861ede7eba138c7140f195ae77ba3106cbffielding }
5beae861ede7eba138c7140f195ae77ba3106cbffielding else if (new->op == AUTHZ_LOGIC_OFF || new->op == AUTHZ_LOGIC_UNSET ||
5beae861ede7eba138c7140f195ae77ba3106cbffielding !(base->section || new->section)) {
5beae861ede7eba138c7140f195ae77ba3106cbffielding conf = apr_pmemdup(p, new, sizeof(*new));
5beae861ede7eba138c7140f195ae77ba3106cbffielding }
5beae861ede7eba138c7140f195ae77ba3106cbffielding else {
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_section_conf *section;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding if (base->section) {
5beae861ede7eba138c7140f195ae77ba3106cbffielding if (new->section) {
5beae861ede7eba138c7140f195ae77ba3106cbffielding section = apr_pcalloc(p, sizeof(*section));
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding section->limited =
fd2db14d870ff9aa9795841360f6e3d562ad69a2jerenkrantz base->section->limited | new->section->limited;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding section->op = new->op;
5beae861ede7eba138c7140f195ae77ba3106cbffielding section->is_merged = 1;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding section->first = apr_pmemdup(p, base->section,
5beae861ede7eba138c7140f195ae77ba3106cbffielding sizeof(*base->section));
5beae861ede7eba138c7140f195ae77ba3106cbffielding section->first->next = apr_pmemdup(p, new->section,
5beae861ede7eba138c7140f195ae77ba3106cbffielding sizeof(*new->section));
5beae861ede7eba138c7140f195ae77ba3106cbffielding } else {
5beae861ede7eba138c7140f195ae77ba3106cbffielding section = apr_pmemdup(p, base->section,
5beae861ede7eba138c7140f195ae77ba3106cbffielding sizeof(*base->section));
fd2db14d870ff9aa9795841360f6e3d562ad69a2jerenkrantz }
fd2db14d870ff9aa9795841360f6e3d562ad69a2jerenkrantz }
5beae861ede7eba138c7140f195ae77ba3106cbffielding else {
fd2db14d870ff9aa9795841360f6e3d562ad69a2jerenkrantz section = apr_pmemdup(p, new->section, sizeof(*new->section));
fd2db14d870ff9aa9795841360f6e3d562ad69a2jerenkrantz }
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding conf = apr_pcalloc(p, sizeof(*conf));
5beae861ede7eba138c7140f195ae77ba3106cbffielding
711d4b43c1e5c33611ac1b938cf7b944c3aa77b7jerenkrantz conf->section = section;
5beae861ede7eba138c7140f195ae77ba3106cbffielding conf->op = new->op;
0729ed19effa96566e715392dd17440bb5a107d6jwoolley }
0729ed19effa96566e715392dd17440bb5a107d6jwoolley
0729ed19effa96566e715392dd17440bb5a107d6jwoolley if (new->authz_forbidden_on_fail == UNSET)
5beae861ede7eba138c7140f195ae77ba3106cbffielding conf->authz_forbidden_on_fail = base->authz_forbidden_on_fail;
711d4b43c1e5c33611ac1b938cf7b944c3aa77b7jerenkrantz else
5beae861ede7eba138c7140f195ae77ba3106cbffielding conf->authz_forbidden_on_fail = new->authz_forbidden_on_fail;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding return (void*)conf;
c79b597fec8589dc6a2c74192235a1fbc6c9db0cwrowe}
fd2db14d870ff9aa9795841360f6e3d562ad69a2jerenkrantz
fd2db14d870ff9aa9795841360f6e3d562ad69a2jerenkrantzstatic void *create_authz_core_svr_config(apr_pool_t *p, server_rec *s)
5beae861ede7eba138c7140f195ae77ba3106cbffielding{
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_core_srv_conf *authcfg;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding authcfg = apr_pcalloc(p, sizeof(*authcfg));
5beae861ede7eba138c7140f195ae77ba3106cbffielding authcfg->alias_rec = apr_hash_make(p);
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding return (void *)authcfg;
5beae861ede7eba138c7140f195ae77ba3106cbffielding}
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding/* This is a fake authz provider that really merges various authz alias
5beae861ede7eba138c7140f195ae77ba3106cbffielding * configurations and then invokes them.
5beae861ede7eba138c7140f195ae77ba3106cbffielding */
5beae861ede7eba138c7140f195ae77ba3106cbffieldingstatic authz_status authz_alias_check_authorization(request_rec *r,
5beae861ede7eba138c7140f195ae77ba3106cbffielding const char *require_args,
5beae861ede7eba138c7140f195ae77ba3106cbffielding const void *parsed_require_args)
5beae861ede7eba138c7140f195ae77ba3106cbffielding{
5beae861ede7eba138c7140f195ae77ba3106cbffielding const char *provider_name;
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_status ret = AUTHZ_DENIED;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding /* Look up the provider alias in the alias list.
5beae861ede7eba138c7140f195ae77ba3106cbffielding * Get the the dir_config and call ap_Merge_per_dir_configs()
5beae861ede7eba138c7140f195ae77ba3106cbffielding * Call the real provider->check_authorization() function
5beae861ede7eba138c7140f195ae77ba3106cbffielding * return the result of the above function call
5beae861ede7eba138c7140f195ae77ba3106cbffielding */
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding provider_name = apr_table_get(r->notes, AUTHZ_PROVIDER_NAME_NOTE);
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding if (provider_name) {
5beae861ede7eba138c7140f195ae77ba3106cbffielding authz_core_srv_conf *authcfg;
5beae861ede7eba138c7140f195ae77ba3106cbffielding provider_alias_rec *prvdraliasrec;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding authcfg = ap_get_module_config(r->server->module_config,
5beae861ede7eba138c7140f195ae77ba3106cbffielding &authz_core_module);
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding prvdraliasrec = apr_hash_get(authcfg->alias_rec, provider_name,
5beae861ede7eba138c7140f195ae77ba3106cbffielding APR_HASH_KEY_STRING);
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding /* If we found the alias provider in the list, then merge the directory
5beae861ede7eba138c7140f195ae77ba3106cbffielding configurations and call the real provider */
5beae861ede7eba138c7140f195ae77ba3106cbffielding if (prvdraliasrec) {
5beae861ede7eba138c7140f195ae77ba3106cbffielding ap_conf_vector_t *orig_dir_config = r->per_dir_config;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding r->per_dir_config =
5beae861ede7eba138c7140f195ae77ba3106cbffielding ap_merge_per_dir_configs(r->pool, orig_dir_config,
5beae861ede7eba138c7140f195ae77ba3106cbffielding prvdraliasrec->sec_auth);
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding ret = prvdraliasrec->provider->
5beae861ede7eba138c7140f195ae77ba3106cbffielding check_authorization(r, prvdraliasrec->provider_args,
5beae861ede7eba138c7140f195ae77ba3106cbffielding prvdraliasrec->provider_parsed_args);
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding r->per_dir_config = orig_dir_config;
5beae861ede7eba138c7140f195ae77ba3106cbffielding }
5beae861ede7eba138c7140f195ae77ba3106cbffielding else {
5beae861ede7eba138c7140f195ae77ba3106cbffielding ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02305)
5beae861ede7eba138c7140f195ae77ba3106cbffielding "no alias provider found for '%s' (BUG?)",
5beae861ede7eba138c7140f195ae77ba3106cbffielding provider_name);
5beae861ede7eba138c7140f195ae77ba3106cbffielding }
5beae861ede7eba138c7140f195ae77ba3106cbffielding }
5beae861ede7eba138c7140f195ae77ba3106cbffielding else {
5beae861ede7eba138c7140f195ae77ba3106cbffielding ap_assert(provider_name != NULL);
5beae861ede7eba138c7140f195ae77ba3106cbffielding }
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding return ret;
5beae861ede7eba138c7140f195ae77ba3106cbffielding}
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingstatic const authz_provider authz_alias_provider =
5beae861ede7eba138c7140f195ae77ba3106cbffielding{
5beae861ede7eba138c7140f195ae77ba3106cbffielding &authz_alias_check_authorization,
5beae861ede7eba138c7140f195ae77ba3106cbffielding NULL,
5beae861ede7eba138c7140f195ae77ba3106cbffielding};
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffieldingstatic const char *authz_require_alias_section(cmd_parms *cmd, void *mconfig,
5beae861ede7eba138c7140f195ae77ba3106cbffielding const char *args)
5beae861ede7eba138c7140f195ae77ba3106cbffielding{
5beae861ede7eba138c7140f195ae77ba3106cbffielding const char *endp = ap_strrchr_c(args, '>');
5beae861ede7eba138c7140f195ae77ba3106cbffielding char *provider_name;
5beae861ede7eba138c7140f195ae77ba3106cbffielding char *provider_alias;
5beae861ede7eba138c7140f195ae77ba3106cbffielding char *provider_args;
5beae861ede7eba138c7140f195ae77ba3106cbffielding ap_conf_vector_t *new_authz_config;
5beae861ede7eba138c7140f195ae77ba3106cbffielding int old_overrides = cmd->override;
5beae861ede7eba138c7140f195ae77ba3106cbffielding const char *errmsg;
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5beae861ede7eba138c7140f195ae77ba3106cbffielding if (err != NULL) {
5beae861ede7eba138c7140f195ae77ba3106cbffielding return err;
5beae861ede7eba138c7140f195ae77ba3106cbffielding }
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding if (endp == NULL) {
5beae861ede7eba138c7140f195ae77ba3106cbffielding return apr_pstrcat(cmd->pool, cmd->cmd->name,
5beae861ede7eba138c7140f195ae77ba3106cbffielding "> directive missing closing '>'", NULL);
5beae861ede7eba138c7140f195ae77ba3106cbffielding }
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding args = apr_pstrndup(cmd->temp_pool, args, endp - args);
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding if (!args[0]) {
5beae861ede7eba138c7140f195ae77ba3106cbffielding return apr_pstrcat(cmd->pool, cmd->cmd->name,
5beae861ede7eba138c7140f195ae77ba3106cbffielding "> directive requires additional arguments", NULL);
5beae861ede7eba138c7140f195ae77ba3106cbffielding }
5beae861ede7eba138c7140f195ae77ba3106cbffielding
5beae861ede7eba138c7140f195ae77ba3106cbffielding /* Pull the real provider name and the alias name from the block header */
5beae861ede7eba138c7140f195ae77ba3106cbffielding provider_name = ap_getword_conf(cmd->pool, &args);
5beae861ede7eba138c7140f195ae77ba3106cbffielding provider_alias = ap_getword_conf(cmd->pool, &args);
5beae861ede7eba138c7140f195ae77ba3106cbffielding provider_args = ap_getword_conf(cmd->pool, &args);
5beae861ede7eba138c7140f195ae77ba3106cbffielding
if (!provider_name[0] || !provider_alias[0]) {
return apr_pstrcat(cmd->pool, cmd->cmd->name,
"> directive requires additional arguments", NULL);
}
new_authz_config = ap_create_per_dir_config(cmd->pool);
/* Walk the subsection configuration to get the per_dir config that we will
* merge just before the real provider is called.
*/
cmd->override = OR_AUTHCFG | ACCESS_CONF;
errmsg = ap_walk_config(cmd->directive->first_child, cmd,
new_authz_config);
cmd->override = old_overrides;
if (!errmsg) {
provider_alias_rec *prvdraliasrec;
authz_core_srv_conf *authcfg;
prvdraliasrec = apr_pcalloc(cmd->pool, sizeof(*prvdraliasrec));
/* Save off the new directory config along with the original
* provider name and function pointer data
*/
prvdraliasrec->provider_name = provider_name;
prvdraliasrec->provider_alias = provider_alias;
prvdraliasrec->provider_args = provider_args;
prvdraliasrec->sec_auth = new_authz_config;
prvdraliasrec->provider =
ap_lookup_provider(AUTHZ_PROVIDER_GROUP, provider_name,
AUTHZ_PROVIDER_VERSION);
/* by the time the config file is used, the provider should be loaded
* and registered with us.
*/
if (!prvdraliasrec->provider) {
return apr_psprintf(cmd->pool,
"Unknown Authz provider: %s",
provider_name);
}
if (prvdraliasrec->provider->parse_require_line) {
const char *err = prvdraliasrec->provider->parse_require_line(cmd,
provider_args, &prvdraliasrec->provider_parsed_args);
if (err)
return apr_psprintf(cmd->pool,
"Can't parse 'Require %s %s': %s",
provider_name, provider_args, err);
}
authcfg = ap_get_module_config(cmd->server->module_config,
&authz_core_module);
apr_hash_set(authcfg->alias_rec, provider_alias,
APR_HASH_KEY_STRING, prvdraliasrec);
/* Register the fake provider so that we get called first */
ap_register_auth_provider(cmd->pool, AUTHZ_PROVIDER_GROUP,
provider_alias, AUTHZ_PROVIDER_VERSION,
&authz_alias_provider,
AP_AUTH_INTERNAL_PER_CONF);
}
return errmsg;
}
static const char* format_authz_result(authz_status result)
{
return ((result == AUTHZ_DENIED)
? "denied"
: ((result == AUTHZ_GRANTED)
? "granted"
: ((result == AUTHZ_DENIED_NO_USER)
? "denied (no authenticated user yet)"
: "neutral")));
}
static const char* format_authz_command(apr_pool_t *p,
authz_section_conf *section)
{
return (section->provider
? apr_pstrcat(p, "Require ", (section->negate ? "not " : ""),
section->provider_name, " ",
section->provider_args, NULL)
: apr_pstrcat(p, section->is_merged ? "AuthMerging " : "<Require",
((section->op == AUTHZ_LOGIC_AND)
? (section->negate ? "NotAll" : "All")
: (section->negate ? "None" : "Any")),
section->is_merged ? "" : ">", NULL));
}
static authz_section_conf* create_default_section(apr_pool_t *p)
{
authz_section_conf *section = apr_pcalloc(p, sizeof(*section));
section->op = AUTHZ_LOGIC_OR;
return section;
}
static const char *add_authz_provider(cmd_parms *cmd, void *config,
const char *args)
{
authz_core_dir_conf *conf = (authz_core_dir_conf*)config;
authz_section_conf *section = apr_pcalloc(cmd->pool, sizeof(*section));
authz_section_conf *child;
section->provider_name = ap_getword_conf(cmd->pool, &args);
if (!strcasecmp(section->provider_name, "not")) {
section->provider_name = ap_getword_conf(cmd->pool, &args);
section->negate = 1;
}
section->provider_args = args;
/* lookup and cache the actual provider now */
section->provider = ap_lookup_provider(AUTHZ_PROVIDER_GROUP,
section->provider_name,
AUTHZ_PROVIDER_VERSION);
/* by the time the config file is used, the provider should be loaded
* and registered with us.
*/
if (!section->provider) {
return apr_psprintf(cmd->pool,
"Unknown Authz provider: %s",
section->provider_name);
}
/* if the provider doesn't provide the appropriate function, reject it */
if (!section->provider->check_authorization) {
return apr_psprintf(cmd->pool,
"The '%s' Authz provider is not supported by any "
"of the loaded authorization modules",
section->provider_name);
}
section->limited = cmd->limited;
if (section->provider->parse_require_line) {
const char *err = section->provider->parse_require_line(cmd, args,
&section->provider_parsed_args);
if (err)
return err;
}
if (!conf->section) {
conf->section = create_default_section(cmd->pool);
}
if (section->negate && conf->section->op == AUTHZ_LOGIC_OR) {
return apr_psprintf(cmd->pool, "negative %s directive has no effect "
"in %s directive",
cmd->cmd->name,
format_authz_command(cmd->pool, conf->section));
}
conf->section->limited |= section->limited;
child = conf->section->first;
if (child) {
while (child->next) {
child = child->next;
}
child->next = section;
}
else {
conf->section->first = section;
}
return NULL;
}
static const char *add_authz_section(cmd_parms *cmd, void *mconfig,
const char *args)
{
authz_core_dir_conf *conf = mconfig;
const char *endp = ap_strrchr_c(args, '>');
authz_section_conf *old_section = conf->section;
authz_section_conf *section;
int old_overrides = cmd->override;
apr_int64_t old_limited = cmd->limited;
const char *errmsg;
if (endp == NULL) {
return apr_pstrcat(cmd->pool, cmd->cmd->name,
"> directive missing closing '>'", NULL);
}
args = apr_pstrndup(cmd->temp_pool, args, endp - args);
if (args[0]) {
return apr_pstrcat(cmd->pool, cmd->cmd->name,
"> directive doesn't take additional arguments",
NULL);
}
section = apr_pcalloc(cmd->pool, sizeof(*section));
if (!strcasecmp(cmd->cmd->name, "<RequireAll")) {
section->op = AUTHZ_LOGIC_AND;
}
else if (!strcasecmp(cmd->cmd->name, "<RequireAny")) {
section->op = AUTHZ_LOGIC_OR;
}
else if (!strcasecmp(cmd->cmd->name, "<RequireNotAll")) {
section->op = AUTHZ_LOGIC_AND;
section->negate = 1;
}
else {
section->op = AUTHZ_LOGIC_OR;
section->negate = 1;
}
conf->section = section;
/* trigger NOT_IN_LIMIT errors as if this were a <Limit> directive */
cmd->limited &= ~(AP_METHOD_BIT << (METHODS - 1));
cmd->override = OR_AUTHCFG;
errmsg = ap_walk_config(cmd->directive->first_child, cmd, cmd->context);
cmd->override = old_overrides;
cmd->limited = old_limited;
conf->section = old_section;
if (errmsg) {
return errmsg;
}
if (section->first) {
authz_section_conf *child;
if (!old_section) {
old_section = conf->section = create_default_section(cmd->pool);
}
if (section->negate && old_section->op == AUTHZ_LOGIC_OR) {
return apr_psprintf(cmd->pool, "%s directive has "
"no effect in %s directive",
format_authz_command(cmd->pool, section),
format_authz_command(cmd->pool, old_section));
}
old_section->limited |= section->limited;
if (!section->negate && section->op == old_section->op) {
/* be associative */
section = section->first;
}
child = old_section->first;
if (child) {
while (child->next) {
child = child->next;
}
child->next = section;
}
else {
old_section->first = section;
}
}
else {
return apr_pstrcat(cmd->pool,
format_authz_command(cmd->pool, section),
" directive contains no authorization directives",
NULL);
}
return NULL;
}
static const char *authz_merge_sections(cmd_parms *cmd, void *mconfig,
const char *arg)
{
authz_core_dir_conf *conf = mconfig;
if (!strcasecmp(arg, "Off")) {
conf->op = AUTHZ_LOGIC_OFF;
}
else if (!strcasecmp(arg, "And")) {
conf->op = AUTHZ_LOGIC_AND;
}
else if (!strcasecmp(arg, "Or")) {
conf->op = AUTHZ_LOGIC_OR;
}
else {
return apr_pstrcat(cmd->pool, cmd->cmd->name, " must be one of: "
"Off | And | Or", NULL);
}
return NULL;
}
static int authz_core_check_section(apr_pool_t *p, server_rec *s,
authz_section_conf *section, int is_conf)
{
authz_section_conf *prev = NULL;
authz_section_conf *child = section->first;
int ret = !OK;
while (child) {
if (child->first) {
if (authz_core_check_section(p, s, child, 0) != OK) {
return !OK;
}
if (child->negate && child->op != section->op) {
authz_section_conf *next = child->next;
/* avoid one level of recursion when De Morgan permits */
child = child->first;
if (prev) {
prev->next = child;
}
else {
section->first = child;
}
do {
child->negate = !child->negate;
} while (child->next && (child = child->next));
child->next = next;
}
}
prev = child;
child = child->next;
}
child = section->first;
while (child) {
if (!child->negate) {
ret = OK;
break;
}
child = child->next;
}
if (ret != OK) {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, APR_SUCCESS, s, APLOGNO(01624)
"%s",
apr_pstrcat(p, (is_conf
? "<Directory>, <Location>, or similar"
: format_authz_command(p, section)),
" directive contains only negative "
"authorization directives", NULL));
}
return ret;
}
static int authz_core_pre_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp)
{
authz_core_first_dir_conf = NULL;
return OK;
}
static int authz_core_check_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
authz_core_dir_conf *conf = authz_core_first_dir_conf;
while (conf) {
if (conf->section) {
if (authz_core_check_section(p, s, conf->section, 1) != OK) {
return !OK;
}
}
conf = conf->next;
}
return OK;
}
static const command_rec authz_cmds[] =
{
AP_INIT_RAW_ARGS("<AuthzProviderAlias", authz_require_alias_section,
NULL, RSRC_CONF,
"container for grouping an authorization provider's "
"directives under a provider alias"),
AP_INIT_RAW_ARGS("Require", add_authz_provider, NULL, OR_AUTHCFG,
"specifies authorization directives "
"which one must pass (or not) for a request to suceeed"),
AP_INIT_RAW_ARGS("<RequireAll", add_authz_section, NULL, OR_AUTHCFG,
"container for grouping authorization directives "
"of which none must fail and at least one must pass "
"for a request to succeed"),
AP_INIT_RAW_ARGS("<RequireAny", add_authz_section, NULL, OR_AUTHCFG,
"container for grouping authorization directives "
"of which one must pass "
"for a request to succeed"),
#ifdef AUTHZ_EXTRA_CONFIGS
AP_INIT_RAW_ARGS("<RequireNotAll", add_authz_section, NULL, OR_AUTHCFG,
"container for grouping authorization directives "
"of which some must fail or none must pass "
"for a request to succeed"),
#endif
AP_INIT_RAW_ARGS("<RequireNone", add_authz_section, NULL, OR_AUTHCFG,
"container for grouping authorization directives "
"of which none must pass "
"for a request to succeed"),
AP_INIT_TAKE1("AuthMerging", authz_merge_sections, NULL, OR_AUTHCFG,
"controls how a <Directory>, <Location>, or similar "
"directive's authorization directives are combined with "
"those of its predecessor"),
AP_INIT_FLAG("AuthzSendForbiddenOnFailure", ap_set_flag_slot_char,
(void *)APR_OFFSETOF(authz_core_dir_conf, authz_forbidden_on_fail),
OR_AUTHCFG,
"Controls if an authorization failure should result in a "
"'403 FORBIDDEN' response instead of the HTTP-conforming "
"'401 UNAUTHORIZED'"),
{NULL}
};
static authz_status apply_authz_sections(request_rec *r,
authz_section_conf *section,
authz_logic_op parent_op)
{
authz_status auth_result;
/* check to make sure that the request method requires authorization */
if (!(section->limited & (AP_METHOD_BIT << r->method_number))) {
auth_result =
(parent_op == AUTHZ_LOGIC_AND) ? AUTHZ_GRANTED : AUTHZ_NEUTRAL;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01625)
"authorization result of %s: %s "
"(directive limited to other methods)",
format_authz_command(r->pool, section),
format_authz_result(auth_result));
return auth_result;
}
if (section->provider) {
apr_table_setn(r->notes, AUTHZ_PROVIDER_NAME_NOTE,
section->provider_name);
auth_result =
section->provider->check_authorization(r, section->provider_args,
section->provider_parsed_args);
apr_table_unset(r->notes, AUTHZ_PROVIDER_NAME_NOTE);
}
else {
authz_section_conf *child = section->first;
auth_result = AUTHZ_NEUTRAL;
while (child) {
authz_status child_result;
child_result = apply_authz_sections(r, child, section->op);
if (child_result == AUTHZ_GENERAL_ERROR) {
return AUTHZ_GENERAL_ERROR;
}
if (child_result != AUTHZ_NEUTRAL) {
/*
* Handling of AUTHZ_DENIED/AUTHZ_DENIED_NO_USER: Return
* AUTHZ_DENIED_NO_USER if providing a user may change the
* result, AUTHZ_DENIED otherwise.
*/
if (section->op == AUTHZ_LOGIC_AND) {
if (child_result == AUTHZ_DENIED) {
auth_result = child_result;
break;
}
if ((child_result == AUTHZ_DENIED_NO_USER
&& auth_result != AUTHZ_DENIED)
|| (auth_result == AUTHZ_NEUTRAL)) {
auth_result = child_result;
}
}
else {
/* AUTHZ_LOGIC_OR */
if (child_result == AUTHZ_GRANTED) {
auth_result = child_result;
break;
}
if ((child_result == AUTHZ_DENIED_NO_USER
&& auth_result == AUTHZ_DENIED)
|| (auth_result == AUTHZ_NEUTRAL)) {
auth_result = child_result;
}
}
}
child = child->next;
}
}
if (section->negate) {
if (auth_result == AUTHZ_GRANTED) {
auth_result = AUTHZ_DENIED;
}
else if (auth_result == AUTHZ_DENIED ||
auth_result == AUTHZ_DENIED_NO_USER) {
/* For negated directives, if the original result was denied
* then the new result is neutral since we can not grant
* access simply because authorization was not rejected.
*/
auth_result = AUTHZ_NEUTRAL;
}
}
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01626)
"authorization result of %s: %s",
format_authz_command(r->pool, section),
format_authz_result(auth_result));
return auth_result;
}
static int authorize_user_core(request_rec *r, int after_authn)
{
authz_core_dir_conf *conf;
authz_status auth_result;
conf = ap_get_module_config(r->per_dir_config, &authz_core_module);
if (!conf->section) {
if (ap_auth_type(r)) {
/* there's an AuthType configured, but no authorization
* directives applied to support it
*/
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01627)
"AuthType configured with no corresponding "
"authorization directives");
return HTTP_INTERNAL_SERVER_ERROR;
}
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01628)
"authorization result: granted (no directives)");
return OK;
}
auth_result = apply_authz_sections(r, conf->section, AUTHZ_LOGIC_AND);
if (auth_result == AUTHZ_GRANTED) {
return OK;
}
else if (auth_result == AUTHZ_DENIED_NO_USER) {
if (after_authn) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01629)
"authorization failure (no authenticated user): %s",
r->uri);
/*
* If we're returning 401 to an authenticated user, tell them to
* try again. If unauthenticated, note_auth_failure has already
* been called during auth.
*/
if (r->user)
ap_note_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
else {
/*
* We need a user before we can decide what to do.
* Get out of the way and proceed with authentication.
*/
return DECLINED;
}
}
else if (auth_result == AUTHZ_DENIED || auth_result == AUTHZ_NEUTRAL) {
if (!after_authn || ap_auth_type(r) == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01630)
"client denied by server configuration: %s%s",
r->filename ? "" : "uri ",
r->filename ? r->filename : r->uri);
return HTTP_FORBIDDEN;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r, APLOGNO(01631)
"user %s: authorization failure for \"%s\": ",
r->user, r->uri);
if (conf->authz_forbidden_on_fail > 0) {
return HTTP_FORBIDDEN;
}
else {
/*
* If we're returning 401 to an authenticated user, tell them to
* try again. If unauthenticated, note_auth_failure has already
* been called during auth.
*/
if (r->user)
ap_note_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
}
}
else {
/* We'll assume that the module has already said what its
* error was in the logs.
*/
return HTTP_INTERNAL_SERVER_ERROR;
}
}
static int authorize_userless(request_rec *r)
{
return authorize_user_core(r, 0);
}
static int authorize_user(request_rec *r)
{
return authorize_user_core(r, 1);
}
static int authz_some_auth_required(request_rec *r)
{
authz_core_dir_conf *conf;
conf = ap_get_module_config(r->per_dir_config, &authz_core_module);
if (conf->section
&& (conf->section->limited & (AP_METHOD_BIT << r->method_number))) {
return 1;
}
return 0;
}
/*
* env authz provider
*/
static authz_status env_check_authorization(request_rec *r,
const char *require_line,
const void *parsed_require_line)
{
const char *t, *w;
/* The 'env' provider will allow the configuration to specify a list of
env variables to check rather than a single variable. This is different
from the previous host based syntax. */
t = require_line;
while ((w = ap_getword_conf(r->pool, &t)) && w[0]) {
if (apr_table_get(r->subprocess_env, w)) {
return AUTHZ_GRANTED;
}
}
return AUTHZ_DENIED;
}
static const authz_provider authz_env_provider =
{
&env_check_authorization,
NULL,
};
/*
* all authz provider
*/
static authz_status all_check_authorization(request_rec *r,
const char *require_line,
const void *parsed_require_line)
{
if (parsed_require_line) {
return AUTHZ_GRANTED;
}
return AUTHZ_DENIED;
}
static const char *all_parse_config(cmd_parms *cmd, const char *require_line,
const void **parsed_require_line)
{
/*
* If the argument to the 'all' provider is 'granted' then just let
* everybody in. This would be equivalent to the previous syntax of
* 'allow from all'. If the argument is 'denied' we reject everbody,
* which is equivalent to 'deny from all'.
*/
if (strcasecmp(require_line, "granted") == 0) {
*parsed_require_line = (void *)1;
return NULL;
}
else if (strcasecmp(require_line, "denied") == 0) {
/* *parsed_require_line is already NULL */
return NULL;
}
else {
return "Argument for 'Require all' must be 'granted' or 'denied'";
}
}
static const authz_provider authz_all_provider =
{
&all_check_authorization,
&all_parse_config,
};
/*
* method authz provider
*/
static authz_status method_check_authorization(request_rec *r,
const char *require_line,
const void *parsed_require_line)
{
const apr_int64_t *allowed = parsed_require_line;
if (*allowed & (AP_METHOD_BIT << r->method_number))
return AUTHZ_GRANTED;
else
return AUTHZ_DENIED;
}
static const char *method_parse_config(cmd_parms *cmd, const char *require_line,
const void **parsed_require_line)
{
const char *w, *t;
apr_int64_t *allowed = apr_pcalloc(cmd->pool, sizeof(apr_int64_t));
t = require_line;
while ((w = ap_getword_conf(cmd->temp_pool, &t)) && w[0]) {
int m = ap_method_number_of(w);
if (m == M_INVALID) {
return apr_pstrcat(cmd->pool, "Invalid Method '", w, "'", NULL);
}
*allowed |= (AP_METHOD_BIT << m);
}
*parsed_require_line = allowed;
return NULL;
}
static const authz_provider authz_method_provider =
{
&method_check_authorization,
&method_parse_config,
};
static authz_status expr_check_authorization(request_rec *r,
const char *require_line,
const void *parsed_require_line)
{
const char *err = NULL;
const ap_expr_info_t *expr = parsed_require_line;
int rc = ap_expr_exec(r, expr, &err);
if (rc <= 0)
/* XXX: real error handling? */
return AUTHZ_DENIED;
else
return AUTHZ_GRANTED;
}
static const char *expr_parse_config(cmd_parms *cmd, const char *require_line,
const void **parsed_require_line)
{
const char *expr_err = NULL;
ap_expr_info_t *expr = ap_expr_parse_cmd(cmd, require_line, 0, &expr_err,
NULL);
if (expr_err)
return "Cannot parse expression in require line";
*parsed_require_line = expr;
return NULL;
}
static const authz_provider authz_expr_provider =
{
&expr_check_authorization,
&expr_parse_config,
};
static void register_hooks(apr_pool_t *p)
{
APR_REGISTER_OPTIONAL_FN(authz_some_auth_required);
ap_hook_pre_config(authz_core_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_check_config(authz_core_check_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_check_authz(authorize_user, NULL, NULL, APR_HOOK_LAST,
AP_AUTH_INTERNAL_PER_CONF);
ap_hook_check_access_ex(authorize_userless, NULL, NULL, APR_HOOK_LAST,
AP_AUTH_INTERNAL_PER_CONF);
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "env",
AUTHZ_PROVIDER_VERSION,
&authz_env_provider, AP_AUTH_INTERNAL_PER_CONF);
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "all",
AUTHZ_PROVIDER_VERSION,
&authz_all_provider, AP_AUTH_INTERNAL_PER_CONF);
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "method",
AUTHZ_PROVIDER_VERSION,
&authz_method_provider, AP_AUTH_INTERNAL_PER_CONF);
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "expr",
AUTHZ_PROVIDER_VERSION,
&authz_expr_provider, AP_AUTH_INTERNAL_PER_CONF);
}
AP_DECLARE_MODULE(authz_core) =
{
STANDARD20_MODULE_STUFF,
create_authz_core_dir_config, /* dir config creater */
merge_authz_core_dir_config, /* dir merger */
create_authz_core_svr_config, /* server config */
NULL, /* merge server config */
authz_cmds,
register_hooks /* register hooks */
};