summaryrefslogtreecommitdiffstats
path: root/modules/aaa/mod_authz_core.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:33:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:33:50 +0000
commitfe39ffb8b90ae4e002ed73fe98617cd590abb467 (patch)
treeb80e5956907d8aeaaffe4e4f0c068c0e6157ce8b /modules/aaa/mod_authz_core.c
parentInitial commit. (diff)
downloadapache2-fe39ffb8b90ae4e002ed73fe98617cd590abb467.tar.xz
apache2-fe39ffb8b90ae4e002ed73fe98617cd590abb467.zip
Adding upstream version 2.4.56.upstream/2.4.56
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/aaa/mod_authz_core.c')
-rw-r--r--modules/aaa/mod_authz_core.c1173
1 files changed, 1173 insertions, 0 deletions
diff --git a/modules/aaa/mod_authz_core.c b/modules/aaa/mod_authz_core.c
new file mode 100644
index 0000000..40e5fe1
--- /dev/null
+++ b/modules/aaa/mod_authz_core.c
@@ -0,0 +1,1173 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Security options etc.
+ *
+ * Module derived from code originally written by Rob McCool
+ *
+ */
+
+#include "apr_strings.h"
+#include "apr_network_io.h"
+#include "apr_md5.h"
+
+#define APR_WANT_STRFUNC
+#define APR_WANT_BYTEFUNC
+#include "apr_want.h"
+
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "ap_provider.h"
+#include "ap_expr.h"
+
+#include "mod_auth.h"
+
+#if APR_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#undef AUTHZ_EXTRA_CONFIGS
+
+typedef struct provider_alias_rec {
+ char *provider_name;
+ char *provider_alias;
+ char *provider_args;
+ const void *provider_parsed_args;
+ ap_conf_vector_t *sec_auth;
+ const authz_provider *provider;
+} provider_alias_rec;
+
+typedef enum {
+ AUTHZ_LOGIC_AND,
+ AUTHZ_LOGIC_OR,
+ AUTHZ_LOGIC_OFF,
+ AUTHZ_LOGIC_UNSET
+} authz_logic_op;
+
+typedef struct authz_section_conf authz_section_conf;
+
+struct authz_section_conf {
+ const char *provider_name;
+ const char *provider_args;
+ const void *provider_parsed_args;
+ const authz_provider *provider;
+ apr_int64_t limited;
+ authz_logic_op op;
+ int negate;
+ /** true if this is not a real container but produced by AuthMerging;
+ * only used for logging */
+ int is_merged;
+ authz_section_conf *first;
+ authz_section_conf *next;
+};
+
+typedef struct authz_core_dir_conf authz_core_dir_conf;
+
+struct authz_core_dir_conf {
+ authz_section_conf *section;
+ authz_core_dir_conf *next;
+ authz_logic_op op;
+ signed char authz_forbidden_on_fail;
+};
+
+#define UNSET -1
+
+typedef struct authz_core_srv_conf {
+ apr_hash_t *alias_rec;
+} authz_core_srv_conf;
+
+module AP_MODULE_DECLARE_DATA authz_core_module;
+
+static authz_core_dir_conf *authz_core_first_dir_conf;
+
+static void *create_authz_core_dir_config(apr_pool_t *p, char *dummy)
+{
+ authz_core_dir_conf *conf = apr_pcalloc(p, sizeof(*conf));
+
+ conf->op = AUTHZ_LOGIC_UNSET;
+ conf->authz_forbidden_on_fail = UNSET;
+
+ conf->next = authz_core_first_dir_conf;
+ authz_core_first_dir_conf = conf;
+
+ return (void *)conf;
+}
+
+static void *merge_authz_core_dir_config(apr_pool_t *p,
+ void *basev, void *newv)
+{
+ authz_core_dir_conf *base = (authz_core_dir_conf *)basev;
+ authz_core_dir_conf *new = (authz_core_dir_conf *)newv;
+ authz_core_dir_conf *conf;
+
+ if (new->op == AUTHZ_LOGIC_UNSET && !new->section && base->section ) {
+ /* Only authz_forbidden_on_fail has been set in new. Don't treat
+ * it as a new auth config w.r.t. AuthMerging */
+ conf = apr_pmemdup(p, base, sizeof(*base));
+ }
+ else if (new->op == AUTHZ_LOGIC_OFF || new->op == AUTHZ_LOGIC_UNSET ||
+ !(base->section || new->section)) {
+ conf = apr_pmemdup(p, new, sizeof(*new));
+ }
+ else {
+ authz_section_conf *section;
+
+ if (base->section) {
+ if (new->section) {
+ section = apr_pcalloc(p, sizeof(*section));
+
+ section->limited =
+ base->section->limited | new->section->limited;
+
+ section->op = new->op;
+ section->is_merged = 1;
+
+ section->first = apr_pmemdup(p, base->section,
+ sizeof(*base->section));
+ section->first->next = apr_pmemdup(p, new->section,
+ sizeof(*new->section));
+ } else {
+ section = apr_pmemdup(p, base->section,
+ sizeof(*base->section));
+ }
+ }
+ else {
+ section = apr_pmemdup(p, new->section, sizeof(*new->section));
+ }
+
+ conf = apr_pcalloc(p, sizeof(*conf));
+
+ conf->section = section;
+ conf->op = new->op;
+ }
+
+ if (new->authz_forbidden_on_fail == UNSET)
+ conf->authz_forbidden_on_fail = base->authz_forbidden_on_fail;
+ else
+ conf->authz_forbidden_on_fail = new->authz_forbidden_on_fail;
+
+ return (void*)conf;
+}
+
+/* Only per-server directive we have is GLOBAL_ONLY */
+static void *merge_authz_core_svr_config(apr_pool_t *p,
+ void *basev, void *newv)
+{
+ return basev;
+}
+
+static void *create_authz_core_svr_config(apr_pool_t *p, server_rec *s)
+{
+ authz_core_srv_conf *authcfg;
+
+ authcfg = apr_pcalloc(p, sizeof(*authcfg));
+ authcfg->alias_rec = apr_hash_make(p);
+
+ return (void *)authcfg;
+}
+
+/* This is a fake authz provider that really merges various authz alias
+ * configurations and then invokes them.
+ */
+static authz_status authz_alias_check_authorization(request_rec *r,
+ const char *require_args,
+ const void *parsed_require_args)
+{
+ const char *provider_name;
+
+ /* Look up the provider alias in the alias list.
+ * Get the dir_config and call ap_merge_per_dir_configs()
+ * Call the real provider->check_authorization() function
+ * Return the result of the above function call
+ */
+
+ provider_name = apr_table_get(r->notes, AUTHZ_PROVIDER_NAME_NOTE);
+
+ if (provider_name) {
+ authz_core_srv_conf *authcfg;
+ provider_alias_rec *prvdraliasrec;
+
+ authcfg = ap_get_module_config(r->server->module_config,
+ &authz_core_module);
+
+ prvdraliasrec = apr_hash_get(authcfg->alias_rec, provider_name,
+ APR_HASH_KEY_STRING);
+
+ /* If we found the alias provider in the list, then merge the directory
+ configurations and call the real provider */
+ if (prvdraliasrec) {
+ ap_conf_vector_t *orig_dir_config = r->per_dir_config;
+ authz_status ret;
+
+ r->per_dir_config =
+ ap_merge_per_dir_configs(r->pool, orig_dir_config,
+ prvdraliasrec->sec_auth);
+
+ ret = prvdraliasrec->provider->
+ check_authorization(r, prvdraliasrec->provider_args,
+ prvdraliasrec->provider_parsed_args);
+
+ r->per_dir_config = orig_dir_config;
+
+ return ret;
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02305)
+ "no alias provider found for '%s' (BUG?)",
+ provider_name ? provider_name : "n/a");
+
+ return AUTHZ_DENIED;
+}
+
+static const authz_provider authz_alias_provider =
+{
+ &authz_alias_check_authorization,
+ NULL,
+};
+
+static const char *authz_require_alias_section(cmd_parms *cmd, void *mconfig,
+ const char *args)
+{
+ const char *endp = ap_strrchr_c(args, '>');
+ char *provider_name;
+ char *provider_alias;
+ char *provider_args, *extra_args;
+ ap_conf_vector_t *new_authz_config;
+ int old_overrides = cmd->override;
+ const char *errmsg;
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ 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 requires additional arguments", NULL);
+ }
+
+ /* Pull the real provider name and the alias name from the block header */
+ provider_name = ap_getword_conf(cmd->pool, &args);
+ provider_alias = ap_getword_conf(cmd->pool, &args);
+ provider_args = ap_getword_conf(cmd->pool, &args);
+ extra_args = ap_getword_conf(cmd->pool, &args);
+
+ if (!provider_name[0] || !provider_alias[0]) {
+ return apr_pstrcat(cmd->pool, cmd->cmd->name,
+ "> directive requires additional arguments", NULL);
+ }
+
+ /* We only handle one "Require-Parameters" parameter. If several parameters
+ are needed, they must be enclosed between quotes */
+ if (extra_args && *extra_args) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(10142)
+ "When several arguments (%s %s...) are passed to a %s directive, "
+ "they must be enclosed in quotation marks. Otherwise, only the "
+ "first one is taken into account",
+ provider_args, extra_args, cmd->cmd->name);
+ }
+
+ 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) {
+ 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;
+ apr_pool_userdata_setn(section->provider_name,
+ AUTHZ_PROVIDER_NAME_NOTE,
+ apr_pool_cleanup_null,
+ cmd->temp_pool);
+ 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 directive contains only negative authorization directives",
+ is_conf ? "<Directory>, <Location>, or similar"
+ : format_authz_command(p, section));
+ }
+
+ 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 everybody,
+ * 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,
+};
+
+/*
+ * expr authz provider
+ */
+
+#define REQUIRE_EXPR_NOTE "Require_expr_info"
+struct require_expr_info {
+ ap_expr_info_t *expr;
+ int want_user;
+};
+
+static int expr_lookup_fn(ap_expr_lookup_parms *parms)
+{
+ if (parms->type == AP_EXPR_FUNC_VAR
+ && strcasecmp(parms->name, "REMOTE_USER") == 0) {
+ struct require_expr_info *info;
+ apr_pool_userdata_get((void**)&info, REQUIRE_EXPR_NOTE, parms->ptemp);
+ AP_DEBUG_ASSERT(info != NULL);
+ info->want_user = 1;
+ }
+ return ap_expr_lookup_default(parms);
+}
+
+static const char *expr_parse_config(cmd_parms *cmd, const char *require_line,
+ const void **parsed_require_line)
+{
+ const char *expr_err = NULL;
+ struct require_expr_info *info = apr_pcalloc(cmd->pool, sizeof(*info));
+
+ /* if the expression happens to be surrounded by quotes, skip them */
+ if (require_line[0] == '"') {
+ apr_size_t len = strlen(require_line);
+
+ if (require_line[len-1] == '"')
+ require_line = apr_pstrndup(cmd->temp_pool,
+ require_line + 1,
+ len - 2);
+ }
+
+ apr_pool_userdata_setn(info, REQUIRE_EXPR_NOTE, apr_pool_cleanup_null,
+ cmd->temp_pool);
+ info->expr = ap_expr_parse_cmd(cmd, require_line, 0, &expr_err,
+ expr_lookup_fn);
+
+ if (expr_err)
+ return apr_pstrcat(cmd->temp_pool,
+ "Cannot parse expression in require line: ",
+ expr_err, NULL);
+
+ *parsed_require_line = info;
+
+ return NULL;
+}
+
+static authz_status expr_check_authorization(request_rec *r,
+ const char *require_line,
+ const void *parsed_require_line)
+{
+ const char *err = NULL;
+ const struct require_expr_info *info = parsed_require_line;
+ int rc = ap_expr_exec(r, info->expr, &err);
+
+ if (rc < 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02320)
+ "Error evaluating expression in 'Require expr': %s",
+ err);
+ return AUTHZ_GENERAL_ERROR;
+ }
+ else if (rc == 0) {
+ if (info->want_user)
+ return AUTHZ_DENIED_NO_USER;
+ else
+ return AUTHZ_DENIED;
+ }
+ else {
+ return AUTHZ_GRANTED;
+ }
+}
+
+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 */
+ merge_authz_core_svr_config , /* merge server config */
+ authz_cmds,
+ register_hooks /* register hooks */
+};
+