mod_authz_core.c revision 4c21a9f062e187b87c9cec726023f0fc086008f2
/* 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
*
*
* 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"
#endif
typedef struct provider_alias_rec {
char *provider_name;
char *provider_alias;
char *provider_args;
const void *provider_parsed_args;
const authz_provider *provider;
typedef enum {
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;
int negate;
/** true if this is not a real container but produced by AuthMerging;
* only used for logging */
int is_merged;
};
typedef struct authz_core_dir_conf authz_core_dir_conf;
struct authz_core_dir_conf {
signed char authz_forbidden_on_fail;
};
#define UNSET -1
typedef struct authz_core_srv_conf {
{
return (void *)conf;
}
static void *merge_authz_core_dir_config(apr_pool_t *p,
{
/* Only authz_forbidden_on_fail has been set in new. Don't treat
* it as a new auth config w.r.t. AuthMerging */
}
}
else {
} else {
}
}
else {
}
}
else
return (void*)conf;
}
{
return (void *)authcfg;
}
/* This is a fake authz provider that really merges various authz alias
* configurations and then invokes them.
*/
const char *require_args,
const void *parsed_require_args)
{
const char *provider_name;
/* Look up the provider alias in the alias list.
* Get the 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
*/
if (provider_name) {
/* If we found the alias provider in the list, then merge the directory
configurations and call the real provider */
if (prvdraliasrec) {
r->per_dir_config =
}
else {
"no alias provider found for '%s' (BUG?)",
}
}
else {
}
return ret;
}
static const authz_provider authz_alias_provider =
{
NULL,
};
const char *args)
{
char *provider_name;
char *provider_alias;
char *provider_args;
const char *errmsg;
return err;
}
"> directive missing closing '>'", NULL);
}
if (!args[0]) {
"> directive requires additional arguments", NULL);
}
/* Pull the real provider name and the alias name from the block header */
if (!provider_name[0] || !provider_alias[0]) {
"> directive requires additional arguments", NULL);
}
/* Walk the subsection configuration to get the per_dir config that we will
* merge just before the real provider is called.
*/
if (!errmsg) {
/* Save off the new directory config along with the original
* provider name and function pointer data
*/
/* by the time the config file is used, the provider should be loaded
* and registered with us.
*/
if (!prvdraliasrec->provider) {
"Unknown Authz provider: %s",
}
if (err)
"Can't parse 'Require %s %s': %s",
}
/* Register the fake provider so that we get called first */
}
return errmsg;
}
{
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,
{
}
{
return section;
}
const char *args)
{
}
/* lookup and cache the actual provider now */
/* by the time the config file is used, the provider should be loaded
* and registered with us.
*/
"Unknown Authz provider: %s",
}
/* if the provider doesn't provide the appropriate function, reject it */
"The '%s' Authz provider is not supported by any "
"of the loaded authorization modules",
}
const char *err;
if (err)
return err;
}
}
"in %s directive",
}
if (child) {
}
}
else {
}
return NULL;
}
const char *args)
{
const char *errmsg;
"> directive missing closing '>'", NULL);
}
if (args[0]) {
"> directive doesn't take additional arguments",
NULL);
}
}
}
}
else {
}
/* trigger NOT_IN_LIMIT errors as if this were a <Limit> directive */
if (errmsg) {
return errmsg;
}
if (!old_section) {
}
"no effect in %s directive",
}
/* be associative */
}
if (child) {
}
}
else {
}
}
else {
" directive contains no authorization directives",
NULL);
}
return NULL;
}
const char *arg)
{
}
}
}
else {
"Off | And | Or", NULL);
}
return NULL;
}
{
while (child) {
return !OK;
}
/* avoid one level of recursion when De Morgan permits */
if (prev) {
}
else {
}
do {
}
}
}
while (child) {
break;
}
}
"%s",
apr_pstrcat(p, (is_conf
? "<Directory>, <Location>, or similar"
: format_authz_command(p, section)),
" directive contains only negative "
"authorization directives", NULL));
}
return ret;
}
{
return OK;
}
{
while (conf) {
return !OK;
}
}
}
return OK;
}
static const command_rec authz_cmds[] =
{
"container for grouping an authorization provider's "
"directives under a provider alias"),
"specifies authorization directives "
"which one must pass (or not) for a request to suceeed"),
"container for grouping authorization directives "
"of which none must fail and at least one must pass "
"for a request to succeed"),
"container for grouping authorization directives "
"of which one must pass "
"for a request to succeed"),
#ifdef AUTHZ_EXTRA_CONFIGS
"container for grouping authorization directives "
"of which some must fail or none must pass "
"for a request to succeed"),
#endif
"container for grouping authorization directives "
"of which none must pass "
"for a request to succeed"),
"controls how a <Directory>, <Location>, or similar "
"directive's authorization directives are combined with "
"those of its predecessor"),
"Controls if an authorization failure should result in a "
"'403 FORBIDDEN' response instead of the HTTP-conforming "
"'401 UNAUTHORIZED'"),
{NULL}
};
{
/* check to make sure that the request method requires authorization */
"authorization result of %s: %s "
"(directive limited to other methods)",
return auth_result;
}
}
else {
while (child) {
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 (child_result == AUTHZ_DENIED) {
break;
}
if ((child_result == AUTHZ_DENIED_NO_USER
&& auth_result != AUTHZ_DENIED)
|| (auth_result == AUTHZ_NEUTRAL)) {
}
}
else {
/* AUTHZ_LOGIC_OR */
if (child_result == AUTHZ_GRANTED) {
break;
}
if ((child_result == AUTHZ_DENIED_NO_USER
&& auth_result == AUTHZ_DENIED)
|| (auth_result == AUTHZ_NEUTRAL)) {
}
}
}
}
}
if (auth_result == AUTHZ_GRANTED) {
}
else if (auth_result == AUTHZ_DENIED ||
/* 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.
*/
}
}
"authorization result of %s: %s",
return auth_result;
}
{
if (ap_auth_type(r)) {
/* there's an AuthType configured, but no authorization
* directives applied to support it
*/
"AuthType configured with no corresponding "
"authorization directives");
return HTTP_INTERNAL_SERVER_ERROR;
}
"authorization result: granted (no directives)");
return OK;
}
if (auth_result == AUTHZ_GRANTED) {
return OK;
}
else if (auth_result == AUTHZ_DENIED_NO_USER) {
if (after_authn) {
"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)
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;
}
}
"client denied by server configuration: %s%s",
return HTTP_FORBIDDEN;
}
else {
"user %s: authorization failure for \"%s\": ",
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)
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)
{
return 1;
}
return 0;
}
/*
* env authz provider
*/
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 =
{
NULL,
};
/*
* all authz provider
*/
const char *require_line,
const void *parsed_require_line)
{
if (parsed_require_line) {
return AUTHZ_GRANTED;
}
return AUTHZ_DENIED;
}
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'.
*/
*parsed_require_line = (void *)1;
return NULL;
}
/* *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 =
{
};
/*
* method authz provider
*/
const char *require_line,
const void *parsed_require_line)
{
return AUTHZ_GRANTED;
else
return AUTHZ_DENIED;
}
const void **parsed_require_line)
{
const char *w, *t;
t = require_line;
int m = ap_method_number_of(w);
if (m == M_INVALID) {
}
*allowed |= (AP_METHOD_BIT << m);
}
return NULL;
}
static const authz_provider authz_method_provider =
{
};
/*
* expr authz provider
*/
#define REQUIRE_EXPR_NOTE "Require_expr_info"
struct require_expr_info {
int want_user;
};
{
struct require_expr_info *info;
}
return ap_expr_lookup_default(parms);
}
const void **parsed_require_line)
{
if (expr_err)
return "Cannot parse expression in require line";
return NULL;
}
const char *require_line,
const void *parsed_require_line)
{
if (rc < 0) {
"Error evaluating expression in 'Require expr': %s",
err);
return AUTHZ_GENERAL_ERROR;
}
else if (rc == 0) {
return AUTHZ_DENIED_NO_USER;
else
return AUTHZ_DENIED;
}
else {
return AUTHZ_GRANTED;
}
}
static const authz_provider authz_expr_provider =
{
};
static void register_hooks(apr_pool_t *p)
{
}
{
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 */
register_hooks /* register hooks */
};