|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "apr_strings.h"
|
|
Packit |
90a5c9 |
#include "apr_lib.h" /* for apr_isspace */
|
|
Packit |
90a5c9 |
#include "apr_base64.h" /* for apr_base64_decode et al */
|
|
Packit |
90a5c9 |
#define APR_WANT_STRFUNC /* for strcasecmp */
|
|
Packit |
90a5c9 |
#include "apr_want.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "ap_config.h"
|
|
Packit |
90a5c9 |
#include "httpd.h"
|
|
Packit |
90a5c9 |
#include "http_config.h"
|
|
Packit |
90a5c9 |
#include "http_core.h"
|
|
Packit |
90a5c9 |
#include "http_log.h"
|
|
Packit |
90a5c9 |
#include "http_protocol.h"
|
|
Packit |
90a5c9 |
#include "http_request.h"
|
|
Packit |
90a5c9 |
#include "ap_provider.h"
|
|
Packit |
90a5c9 |
#include "util_md5.h"
|
|
Packit |
90a5c9 |
#include "ap_expr.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "mod_auth.h"
|
|
Packit |
90a5c9 |
#include "mod_session.h"
|
|
Packit |
90a5c9 |
#include "mod_request.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define FORM_LOGIN_HANDLER "form-login-handler"
|
|
Packit |
90a5c9 |
#define FORM_LOGOUT_HANDLER "form-logout-handler"
|
|
Packit |
90a5c9 |
#define FORM_REDIRECT_HANDLER "form-redirect-handler"
|
|
Packit |
90a5c9 |
#define MOD_AUTH_FORM_HASH "site"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int (*ap_session_load_fn) (request_rec * r, session_rec ** z) = NULL;
|
|
Packit |
90a5c9 |
static apr_status_t (*ap_session_get_fn)(request_rec * r, session_rec * z,
|
|
Packit |
90a5c9 |
const char *key, const char **value) = NULL;
|
|
Packit |
90a5c9 |
static apr_status_t (*ap_session_set_fn)(request_rec * r, session_rec * z,
|
|
Packit |
90a5c9 |
const char *key, const char *value) = NULL;
|
|
Packit |
90a5c9 |
static void (*ap_request_insert_filter_fn) (request_rec * r) = NULL;
|
|
Packit |
90a5c9 |
static void (*ap_request_remove_filter_fn) (request_rec * r) = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
authn_provider_list *providers;
|
|
Packit |
90a5c9 |
char *dir;
|
|
Packit |
90a5c9 |
int authoritative;
|
|
Packit |
90a5c9 |
int authoritative_set;
|
|
Packit |
90a5c9 |
const char *site;
|
|
Packit |
90a5c9 |
int site_set;
|
|
Packit |
90a5c9 |
const char *username;
|
|
Packit |
90a5c9 |
int username_set;
|
|
Packit |
90a5c9 |
const char *password;
|
|
Packit |
90a5c9 |
int password_set;
|
|
Packit |
90a5c9 |
apr_size_t form_size;
|
|
Packit |
90a5c9 |
int form_size_set;
|
|
Packit |
90a5c9 |
int fakebasicauth;
|
|
Packit |
90a5c9 |
int fakebasicauth_set;
|
|
Packit |
90a5c9 |
const char *location;
|
|
Packit |
90a5c9 |
int location_set;
|
|
Packit |
90a5c9 |
const char *method;
|
|
Packit |
90a5c9 |
int method_set;
|
|
Packit |
90a5c9 |
const char *mimetype;
|
|
Packit |
90a5c9 |
int mimetype_set;
|
|
Packit |
90a5c9 |
const char *body;
|
|
Packit |
90a5c9 |
int body_set;
|
|
Packit |
90a5c9 |
int disable_no_store;
|
|
Packit |
90a5c9 |
int disable_no_store_set;
|
|
Packit |
90a5c9 |
ap_expr_info_t *loginsuccess;
|
|
Packit |
90a5c9 |
int loginsuccess_set;
|
|
Packit |
90a5c9 |
ap_expr_info_t *loginrequired;
|
|
Packit |
90a5c9 |
int loginrequired_set;
|
|
Packit |
90a5c9 |
ap_expr_info_t *logout;
|
|
Packit |
90a5c9 |
int logout_set;
|
|
Packit |
90a5c9 |
} auth_form_config_rec;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void *create_auth_form_dir_config(apr_pool_t * p, char *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = apr_pcalloc(p, sizeof(*conf));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
conf->dir = d;
|
|
Packit |
90a5c9 |
/* Any failures are fatal. */
|
|
Packit |
90a5c9 |
conf->authoritative = 1;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* form size defaults to 8k */
|
|
Packit |
90a5c9 |
conf->form_size = HUGE_STRING_LEN;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* default form field names */
|
|
Packit |
90a5c9 |
conf->username = "httpd_username";
|
|
Packit |
90a5c9 |
conf->password = "httpd_password";
|
|
Packit |
90a5c9 |
conf->location = "httpd_location";
|
|
Packit |
90a5c9 |
conf->method = "httpd_method";
|
|
Packit |
90a5c9 |
conf->mimetype = "httpd_mimetype";
|
|
Packit |
90a5c9 |
conf->body = "httpd_body";
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return conf;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void *merge_auth_form_dir_config(apr_pool_t * p, void *basev, void *addv)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *new = (auth_form_config_rec *) apr_pcalloc(p, sizeof(auth_form_config_rec));
|
|
Packit |
90a5c9 |
auth_form_config_rec *add = (auth_form_config_rec *) addv;
|
|
Packit |
90a5c9 |
auth_form_config_rec *base = (auth_form_config_rec *) basev;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
new->providers = !add->providers ? base->providers : add->providers;
|
|
Packit |
90a5c9 |
new->authoritative = (add->authoritative_set == 0) ? base->authoritative : add->authoritative;
|
|
Packit |
90a5c9 |
new->authoritative_set = add->authoritative_set || base->authoritative_set;
|
|
Packit |
90a5c9 |
new->site = (add->site_set == 0) ? base->site : add->site;
|
|
Packit |
90a5c9 |
new->site_set = add->site_set || base->site_set;
|
|
Packit |
90a5c9 |
new->username = (add->username_set == 0) ? base->username : add->username;
|
|
Packit |
90a5c9 |
new->username_set = add->username_set || base->username_set;
|
|
Packit |
90a5c9 |
new->password = (add->password_set == 0) ? base->password : add->password;
|
|
Packit |
90a5c9 |
new->password_set = add->password_set || base->password_set;
|
|
Packit |
90a5c9 |
new->location = (add->location_set == 0) ? base->location : add->location;
|
|
Packit |
90a5c9 |
new->location_set = add->location_set || base->location_set;
|
|
Packit |
90a5c9 |
new->form_size = (add->form_size_set == 0) ? base->form_size : add->form_size;
|
|
Packit |
90a5c9 |
new->form_size_set = add->form_size_set || base->form_size_set;
|
|
Packit |
90a5c9 |
new->fakebasicauth = (add->fakebasicauth_set == 0) ? base->fakebasicauth : add->fakebasicauth;
|
|
Packit |
90a5c9 |
new->fakebasicauth_set = add->fakebasicauth_set || base->fakebasicauth_set;
|
|
Packit |
90a5c9 |
new->method = (add->method_set == 0) ? base->method : add->method;
|
|
Packit |
90a5c9 |
new->method_set = add->method_set || base->method_set;
|
|
Packit |
90a5c9 |
new->mimetype = (add->mimetype_set == 0) ? base->mimetype : add->mimetype;
|
|
Packit |
90a5c9 |
new->mimetype_set = add->mimetype_set || base->mimetype_set;
|
|
Packit |
90a5c9 |
new->body = (add->body_set == 0) ? base->body : add->body;
|
|
Packit |
90a5c9 |
new->body_set = add->body_set || base->body_set;
|
|
Packit |
90a5c9 |
new->disable_no_store = (add->disable_no_store_set == 0) ? base->disable_no_store : add->disable_no_store;
|
|
Packit |
90a5c9 |
new->disable_no_store_set = add->disable_no_store_set || base->disable_no_store_set;
|
|
Packit |
90a5c9 |
new->loginsuccess = (add->loginsuccess_set == 0) ? base->loginsuccess : add->loginsuccess;
|
|
Packit |
90a5c9 |
new->loginsuccess_set = add->loginsuccess_set || base->loginsuccess_set;
|
|
Packit |
90a5c9 |
new->loginrequired = (add->loginrequired_set == 0) ? base->loginrequired : add->loginrequired;
|
|
Packit |
90a5c9 |
new->loginrequired_set = add->loginrequired_set || base->loginrequired_set;
|
|
Packit |
90a5c9 |
new->logout = (add->logout_set == 0) ? base->logout : add->logout;
|
|
Packit |
90a5c9 |
new->logout_set = add->logout_set || base->logout_set;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return new;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *add_authn_provider(cmd_parms * cmd, void *config,
|
|
Packit |
90a5c9 |
const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
authn_provider_list *newp;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
newp = apr_pcalloc(cmd->pool, sizeof(authn_provider_list));
|
|
Packit |
90a5c9 |
newp->provider_name = arg;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* lookup and cache the actual provider now */
|
|
Packit |
90a5c9 |
newp->provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP,
|
|
Packit |
90a5c9 |
newp->provider_name,
|
|
Packit |
90a5c9 |
AUTHN_PROVIDER_VERSION);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (newp->provider == NULL) {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* by the time they use it, the provider should be loaded and
|
|
Packit |
90a5c9 |
* registered with us.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->pool,
|
|
Packit |
90a5c9 |
"Unknown Authn provider: %s",
|
|
Packit |
90a5c9 |
newp->provider_name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!newp->provider->check_password) {
|
|
Packit |
90a5c9 |
/* if it doesn't provide the appropriate function, reject it */
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->pool,
|
|
Packit |
90a5c9 |
"The '%s' Authn provider doesn't support "
|
|
Packit |
90a5c9 |
"Form Authentication", newp->provider_name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Add it to the list now. */
|
|
Packit |
90a5c9 |
if (!conf->providers) {
|
|
Packit |
90a5c9 |
conf->providers = newp;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
authn_provider_list *last = conf->providers;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (last->next) {
|
|
Packit |
90a5c9 |
last = last->next;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
last->next = newp;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Sanity check a given string that it exists, is not empty,
|
|
Packit |
90a5c9 |
* and does not contain special characters.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static const char *check_string(cmd_parms * cmd, const char *string)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (!string || !*string || ap_strchr_c(string, '=') || ap_strchr_c(string, '&')) {
|
|
Packit |
90a5c9 |
return apr_pstrcat(cmd->pool, cmd->directive->directive,
|
|
Packit |
90a5c9 |
" cannot be empty, or contain '=' or '&'.",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_cookie_form_location(cmd_parms * cmd, void *config, const char *location)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->location = location;
|
|
Packit |
90a5c9 |
conf->location_set = 1;
|
|
Packit |
90a5c9 |
return check_string(cmd, location);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_cookie_form_username(cmd_parms * cmd, void *config, const char *username)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->username = username;
|
|
Packit |
90a5c9 |
conf->username_set = 1;
|
|
Packit |
90a5c9 |
return check_string(cmd, username);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_cookie_form_password(cmd_parms * cmd, void *config, const char *password)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->password = password;
|
|
Packit |
90a5c9 |
conf->password_set = 1;
|
|
Packit |
90a5c9 |
return check_string(cmd, password);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_cookie_form_method(cmd_parms * cmd, void *config, const char *method)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->method = method;
|
|
Packit |
90a5c9 |
conf->method_set = 1;
|
|
Packit |
90a5c9 |
return check_string(cmd, method);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_cookie_form_mimetype(cmd_parms * cmd, void *config, const char *mimetype)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->mimetype = mimetype;
|
|
Packit |
90a5c9 |
conf->mimetype_set = 1;
|
|
Packit |
90a5c9 |
return check_string(cmd, mimetype);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_cookie_form_body(cmd_parms * cmd, void *config, const char *body)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->body = body;
|
|
Packit |
90a5c9 |
conf->body_set = 1;
|
|
Packit |
90a5c9 |
return check_string(cmd, body);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_cookie_form_size(cmd_parms * cmd, void *config,
|
|
Packit |
90a5c9 |
const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = config;
|
|
Packit |
90a5c9 |
apr_off_t size;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != apr_strtoff(&size, arg, NULL, 10)
|
|
Packit |
90a5c9 |
|| size < 0 || size > APR_SIZE_MAX) {
|
|
Packit |
90a5c9 |
return "AuthCookieFormSize must be a size in bytes, or zero.";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
conf->form_size = (apr_size_t)size;
|
|
Packit |
90a5c9 |
conf->form_size_set = 1;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_login_required_location(cmd_parms * cmd, void *config, const char *loginrequired)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
const char *err;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
conf->loginrequired = ap_expr_parse_cmd(cmd, loginrequired, AP_EXPR_FLAG_STRING_RESULT,
|
|
Packit |
90a5c9 |
&err, NULL);
|
|
Packit |
90a5c9 |
if (err) {
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->pool,
|
|
Packit |
90a5c9 |
"Could not parse login required expression '%s': %s",
|
|
Packit |
90a5c9 |
loginrequired, err);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
conf->loginrequired_set = 1;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_login_success_location(cmd_parms * cmd, void *config, const char *loginsuccess)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
const char *err;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
conf->loginsuccess = ap_expr_parse_cmd(cmd, loginsuccess, AP_EXPR_FLAG_STRING_RESULT,
|
|
Packit |
90a5c9 |
&err, NULL);
|
|
Packit |
90a5c9 |
if (err) {
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->pool,
|
|
Packit |
90a5c9 |
"Could not parse login success expression '%s': %s",
|
|
Packit |
90a5c9 |
loginsuccess, err);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
conf->loginsuccess_set = 1;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_logout_location(cmd_parms * cmd, void *config, const char *logout)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
const char *err;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
conf->logout = ap_expr_parse_cmd(cmd, logout, AP_EXPR_FLAG_STRING_RESULT,
|
|
Packit |
90a5c9 |
&err, NULL);
|
|
Packit |
90a5c9 |
if (err) {
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->pool,
|
|
Packit |
90a5c9 |
"Could not parse logout required expression '%s': %s",
|
|
Packit |
90a5c9 |
logout, err);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
conf->logout_set = 1;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_site_passphrase(cmd_parms * cmd, void *config, const char *site)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->site = site;
|
|
Packit |
90a5c9 |
conf->site_set = 1;
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_authoritative(cmd_parms * cmd, void *config, int flag)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->authoritative = flag;
|
|
Packit |
90a5c9 |
conf->authoritative_set = 1;
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_fake_basic_auth(cmd_parms * cmd, void *config, int flag)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->fakebasicauth = flag;
|
|
Packit |
90a5c9 |
conf->fakebasicauth_set = 1;
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_disable_no_store(cmd_parms * cmd, void *config, int flag)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = (auth_form_config_rec *) config;
|
|
Packit |
90a5c9 |
conf->disable_no_store = flag;
|
|
Packit |
90a5c9 |
conf->disable_no_store_set = 1;
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const command_rec auth_form_cmds[] =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
AP_INIT_ITERATE("AuthFormProvider", add_authn_provider, NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"specify the auth providers for a directory or location"),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormUsername", set_cookie_form_username, NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"The field of the login form carrying the username"),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormPassword", set_cookie_form_password, NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"The field of the login form carrying the password"),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormLocation", set_cookie_form_location, NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"The field of the login form carrying the URL to redirect on "
|
|
Packit |
90a5c9 |
"successful login."),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormMethod", set_cookie_form_method, NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"The field of the login form carrying the original request method."),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormMimetype", set_cookie_form_mimetype, NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"The field of the login form carrying the original request mimetype."),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormBody", set_cookie_form_body, NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"The field of the login form carrying the urlencoded original request "
|
|
Packit |
90a5c9 |
"body."),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormSize", set_cookie_form_size, NULL, ACCESS_CONF,
|
|
Packit |
90a5c9 |
"Maximum size of body parsed by the form parser"),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormLoginRequiredLocation", set_login_required_location,
|
|
Packit |
90a5c9 |
NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"If set, redirect the browser to this URL rather than "
|
|
Packit |
90a5c9 |
"return 401 Not Authorized."),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormLoginSuccessLocation", set_login_success_location,
|
|
Packit |
90a5c9 |
NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"If set, redirect the browser to this URL when a login "
|
|
Packit |
90a5c9 |
"processed by the login handler is successful."),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormLogoutLocation", set_logout_location,
|
|
Packit |
90a5c9 |
NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"The URL of the logout successful page. An attempt to access an "
|
|
Packit |
90a5c9 |
"URL handled by the handler " FORM_LOGOUT_HANDLER " will result "
|
|
Packit |
90a5c9 |
"in an redirect to this page after logout."),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthFormSitePassphrase", set_site_passphrase,
|
|
Packit |
90a5c9 |
NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"If set, use this passphrase to determine whether the user should "
|
|
Packit |
90a5c9 |
"be authenticated. Bypasses the user authentication check on "
|
|
Packit |
90a5c9 |
"every website hit, and is useful for high traffic sites."),
|
|
Packit |
90a5c9 |
AP_INIT_FLAG("AuthFormAuthoritative", set_authoritative,
|
|
Packit |
90a5c9 |
NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"Set to 'Off' to allow access control to be passed along to "
|
|
Packit |
90a5c9 |
"lower modules if the UserID is not known to this module"),
|
|
Packit |
90a5c9 |
AP_INIT_FLAG("AuthFormFakeBasicAuth", set_fake_basic_auth,
|
|
Packit |
90a5c9 |
NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"Set to 'On' to pass through authentication to the rest of the "
|
|
Packit |
90a5c9 |
"server as a basic authentication header."),
|
|
Packit |
90a5c9 |
AP_INIT_FLAG("AuthFormDisableNoStore", set_disable_no_store,
|
|
Packit |
90a5c9 |
NULL, OR_AUTHCFG,
|
|
Packit |
90a5c9 |
"Set to 'on' to stop the sending of a Cache-Control no-store header with "
|
|
Packit |
90a5c9 |
"the login screen. This allows the browser to cache the credentials, but "
|
|
Packit |
90a5c9 |
"at the risk of it being possible for the login form to be resubmitted "
|
|
Packit |
90a5c9 |
"and revealed to the backend server through XSS. Use at own risk."),
|
|
Packit |
90a5c9 |
{NULL}
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
module AP_MODULE_DECLARE_DATA auth_form_module;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void note_cookie_auth_failure(request_rec * r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
|
|
Packit |
90a5c9 |
&auth_form_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (conf->location && ap_strchr_c(conf->location, ':')) {
|
|
Packit |
90a5c9 |
apr_table_setn(r->err_headers_out, "Location", conf->location);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int hook_note_cookie_auth_failure(request_rec * r,
|
|
Packit |
90a5c9 |
const char *auth_type)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (strcasecmp(auth_type, "form"))
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
note_cookie_auth_failure(r);
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Set the auth username and password into the main request
|
|
Packit |
90a5c9 |
* notes table.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static void set_notes_auth(request_rec * r,
|
|
Packit |
90a5c9 |
const char *user, const char *pw,
|
|
Packit |
90a5c9 |
const char *method, const char *mimetype)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_table_t *notes = NULL;
|
|
Packit |
90a5c9 |
const char *authname;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* find the main request */
|
|
Packit |
90a5c9 |
while (r->main) {
|
|
Packit |
90a5c9 |
r = r->main;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* find the first redirect */
|
|
Packit |
90a5c9 |
while (r->prev) {
|
|
Packit |
90a5c9 |
r = r->prev;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
notes = r->notes;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* have we isolated the user and pw before? */
|
|
Packit |
90a5c9 |
authname = ap_auth_name(r);
|
|
Packit |
90a5c9 |
if (user) {
|
|
Packit |
90a5c9 |
apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-user", NULL), user);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (pw) {
|
|
Packit |
90a5c9 |
apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-pw", NULL), pw);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (method) {
|
|
Packit |
90a5c9 |
apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-method", NULL), method);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (mimetype) {
|
|
Packit |
90a5c9 |
apr_table_setn(notes, apr_pstrcat(r->pool, authname, "-mimetype", NULL), mimetype);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Get the auth username and password from the main request
|
|
Packit |
90a5c9 |
* notes table, if present.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static void get_notes_auth(request_rec *r,
|
|
Packit |
90a5c9 |
const char **user, const char **pw,
|
|
Packit |
90a5c9 |
const char **method, const char **mimetype)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *authname;
|
|
Packit |
90a5c9 |
request_rec *m = r;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* find the main request */
|
|
Packit |
90a5c9 |
while (m->main) {
|
|
Packit |
90a5c9 |
m = m->main;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* find the first redirect */
|
|
Packit |
90a5c9 |
while (m->prev) {
|
|
Packit |
90a5c9 |
m = m->prev;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* have we isolated the user and pw before? */
|
|
Packit |
90a5c9 |
authname = ap_auth_name(m);
|
|
Packit |
90a5c9 |
if (user) {
|
|
Packit |
90a5c9 |
*user = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-user", NULL));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (pw) {
|
|
Packit |
90a5c9 |
*pw = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-pw", NULL));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (method) {
|
|
Packit |
90a5c9 |
*method = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-method", NULL));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (mimetype) {
|
|
Packit |
90a5c9 |
*mimetype = (char *) apr_table_get(m->notes, apr_pstrcat(m->pool, authname, "-mimetype", NULL));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* set the user, even though the user is unauthenticated at this point */
|
|
Packit |
90a5c9 |
if (user && *user) {
|
|
Packit |
90a5c9 |
r->user = (char *) *user;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
|
|
Packit |
90a5c9 |
"from notes: user: %s, pw: %s, method: %s, mimetype: %s",
|
|
Packit |
90a5c9 |
user ? *user : "<null>", pw ? *pw : "<null>",
|
|
Packit |
90a5c9 |
method ? *method : "<null>", mimetype ? *mimetype : "<null>");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Set the auth username and password into the session.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If either the username, or the password are NULL, the username
|
|
Packit |
90a5c9 |
* and/or password will be removed from the session.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_status_t set_session_auth(request_rec * r,
|
|
Packit |
90a5c9 |
const char *user, const char *pw, const char *site)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *hash = NULL;
|
|
Packit |
90a5c9 |
const char *authname = ap_auth_name(r);
|
|
Packit |
90a5c9 |
session_rec *z = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (site) {
|
|
Packit |
90a5c9 |
hash = ap_md5(r->pool,
|
|
Packit |
90a5c9 |
(unsigned char *) apr_pstrcat(r->pool, user, ":", site, NULL));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_session_load_fn(r, &z);
|
|
Packit |
90a5c9 |
ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_USER, NULL), user);
|
|
Packit |
90a5c9 |
ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_PW, NULL), pw);
|
|
Packit |
90a5c9 |
ap_session_set_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_AUTH_FORM_HASH, NULL), hash);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Get the auth username and password from the main request
|
|
Packit |
90a5c9 |
* notes table, if present.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_status_t get_session_auth(request_rec * r,
|
|
Packit |
90a5c9 |
const char **user, const char **pw, const char **hash)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *authname = ap_auth_name(r);
|
|
Packit |
90a5c9 |
session_rec *z = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_session_load_fn(r, &z);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (user) {
|
|
Packit |
90a5c9 |
ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_USER, NULL), user);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (pw) {
|
|
Packit |
90a5c9 |
ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_SESSION_PW, NULL), pw);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (hash) {
|
|
Packit |
90a5c9 |
ap_session_get_fn(r, z, apr_pstrcat(r->pool, authname, "-" MOD_AUTH_FORM_HASH, NULL), hash);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* set the user, even though the user is unauthenticated at this point */
|
|
Packit |
90a5c9 |
if (user && *user) {
|
|
Packit |
90a5c9 |
r->user = (char *) *user;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
|
|
Packit |
90a5c9 |
"from session: " MOD_SESSION_USER ": %s, " MOD_SESSION_PW
|
|
Packit |
90a5c9 |
": %s, " MOD_AUTH_FORM_HASH ": %s",
|
|
Packit |
90a5c9 |
user ? *user : "<null>", pw ? *pw : "<null>",
|
|
Packit |
90a5c9 |
hash ? *hash : "<null>");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Isolate the username and password in a POSTed form with the
|
|
Packit |
90a5c9 |
* username in the "username" field, and the password in the
|
|
Packit |
90a5c9 |
* "password" field.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If either the username or the password is missing, this
|
|
Packit |
90a5c9 |
* function will return HTTP_UNAUTHORIZED.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* The location field is considered optional, and will be returned
|
|
Packit |
90a5c9 |
* if present.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int get_form_auth(request_rec * r,
|
|
Packit |
90a5c9 |
const char *username,
|
|
Packit |
90a5c9 |
const char *password,
|
|
Packit |
90a5c9 |
const char *location,
|
|
Packit |
90a5c9 |
const char *method,
|
|
Packit |
90a5c9 |
const char *mimetype,
|
|
Packit |
90a5c9 |
const char *body,
|
|
Packit |
90a5c9 |
const char **sent_user,
|
|
Packit |
90a5c9 |
const char **sent_pw,
|
|
Packit |
90a5c9 |
const char **sent_loc,
|
|
Packit |
90a5c9 |
const char **sent_method,
|
|
Packit |
90a5c9 |
const char **sent_mimetype,
|
|
Packit |
90a5c9 |
apr_bucket_brigade **sent_body,
|
|
Packit |
90a5c9 |
auth_form_config_rec * conf)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* sanity check - are we a POST request? */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* find the username and password in the form */
|
|
Packit |
90a5c9 |
apr_array_header_t *pairs = NULL;
|
|
Packit |
90a5c9 |
apr_off_t len;
|
|
Packit |
90a5c9 |
apr_size_t size;
|
|
Packit |
90a5c9 |
int res;
|
|
Packit |
90a5c9 |
char *buffer;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* have we isolated the user and pw before? */
|
|
Packit |
90a5c9 |
get_notes_auth(r, sent_user, sent_pw, sent_method, sent_mimetype);
|
|
Packit |
90a5c9 |
if (sent_user && *sent_user && sent_pw && *sent_pw) {
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
res = ap_parse_form_data(r, NULL, &pairs, -1, conf->form_size);
|
|
Packit |
90a5c9 |
if (res != OK) {
|
|
Packit |
90a5c9 |
return res;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
while (pairs && !apr_is_empty_array(pairs)) {
|
|
Packit |
90a5c9 |
ap_form_pair_t *pair = (ap_form_pair_t *) apr_array_pop(pairs);
|
|
Packit |
90a5c9 |
if (username && !strcmp(pair->name, username) && sent_user) {
|
|
Packit |
90a5c9 |
apr_brigade_length(pair->value, 1, &len;;
|
|
Packit |
90a5c9 |
size = (apr_size_t) len;
|
|
Packit |
90a5c9 |
buffer = apr_palloc(r->pool, size + 1);
|
|
Packit |
90a5c9 |
apr_brigade_flatten(pair->value, buffer, &size);
|
|
Packit |
90a5c9 |
buffer[len] = 0;
|
|
Packit |
90a5c9 |
*sent_user = buffer;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (password && !strcmp(pair->name, password) && sent_pw) {
|
|
Packit |
90a5c9 |
apr_brigade_length(pair->value, 1, &len;;
|
|
Packit |
90a5c9 |
size = (apr_size_t) len;
|
|
Packit |
90a5c9 |
buffer = apr_palloc(r->pool, size + 1);
|
|
Packit |
90a5c9 |
apr_brigade_flatten(pair->value, buffer, &size);
|
|
Packit |
90a5c9 |
buffer[len] = 0;
|
|
Packit |
90a5c9 |
*sent_pw = buffer;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (location && !strcmp(pair->name, location) && sent_loc) {
|
|
Packit |
90a5c9 |
apr_brigade_length(pair->value, 1, &len;;
|
|
Packit |
90a5c9 |
size = (apr_size_t) len;
|
|
Packit |
90a5c9 |
buffer = apr_palloc(r->pool, size + 1);
|
|
Packit |
90a5c9 |
apr_brigade_flatten(pair->value, buffer, &size);
|
|
Packit |
90a5c9 |
buffer[len] = 0;
|
|
Packit |
90a5c9 |
*sent_loc = buffer;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (method && !strcmp(pair->name, method) && sent_method) {
|
|
Packit |
90a5c9 |
apr_brigade_length(pair->value, 1, &len;;
|
|
Packit |
90a5c9 |
size = (apr_size_t) len;
|
|
Packit |
90a5c9 |
buffer = apr_palloc(r->pool, size + 1);
|
|
Packit |
90a5c9 |
apr_brigade_flatten(pair->value, buffer, &size);
|
|
Packit |
90a5c9 |
buffer[len] = 0;
|
|
Packit |
90a5c9 |
*sent_method = buffer;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (mimetype && !strcmp(pair->name, mimetype) && sent_mimetype) {
|
|
Packit |
90a5c9 |
apr_brigade_length(pair->value, 1, &len;;
|
|
Packit |
90a5c9 |
size = (apr_size_t) len;
|
|
Packit |
90a5c9 |
buffer = apr_palloc(r->pool, size + 1);
|
|
Packit |
90a5c9 |
apr_brigade_flatten(pair->value, buffer, &size);
|
|
Packit |
90a5c9 |
buffer[len] = 0;
|
|
Packit |
90a5c9 |
*sent_mimetype = buffer;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (body && !strcmp(pair->name, body) && sent_body) {
|
|
Packit |
90a5c9 |
*sent_body = pair->value;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
|
|
Packit |
90a5c9 |
"from form: user: %s, pw: %s, method: %s, mimetype: %s, location: %s",
|
|
Packit |
90a5c9 |
sent_user ? *sent_user : "<null>", sent_pw ? *sent_pw : "<null>",
|
|
Packit |
90a5c9 |
sent_method ? *sent_method : "<null>",
|
|
Packit |
90a5c9 |
sent_mimetype ? *sent_mimetype : "<null>",
|
|
Packit |
90a5c9 |
sent_loc ? *sent_loc : "<null>");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* set the user, even though the user is unauthenticated at this point */
|
|
Packit |
90a5c9 |
if (sent_user && *sent_user) {
|
|
Packit |
90a5c9 |
r->user = (char *) *sent_user;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* a missing username or missing password means auth denied */
|
|
Packit |
90a5c9 |
if (!sent_user || !*sent_user) {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02982)
|
|
Packit |
90a5c9 |
"form parsed, but username field '%s' was missing or empty, unauthorized",
|
|
Packit |
90a5c9 |
username);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return HTTP_UNAUTHORIZED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!sent_pw || !*sent_pw) {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02983)
|
|
Packit |
90a5c9 |
"form parsed, but password field '%s' was missing or empty, unauthorized",
|
|
Packit |
90a5c9 |
password);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return HTTP_UNAUTHORIZED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* save away the username, password, mimetype and method, so that they
|
|
Packit |
90a5c9 |
* are available should the auth need to be run again.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
set_notes_auth(r, *sent_user, *sent_pw, sent_method ? *sent_method : NULL,
|
|
Packit |
90a5c9 |
sent_mimetype ? *sent_mimetype : NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* These functions return 0 if client is OK, and proper error status
|
|
Packit |
90a5c9 |
* if not... either HTTP_UNAUTHORIZED, if we made a check, and it failed, or
|
|
Packit |
90a5c9 |
* HTTP_INTERNAL_SERVER_ERROR, if things are so totally confused that we
|
|
Packit |
90a5c9 |
* couldn't figure out how to tell if the client is authorized or not.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If they return DECLINED, and all other modules also decline, that's
|
|
Packit |
90a5c9 |
* treated by the server core as a configuration error, logged and
|
|
Packit |
90a5c9 |
* reported as such.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Given a username and site passphrase hash from the session, determine
|
|
Packit |
90a5c9 |
* whether the site passphrase is valid for this session.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If the site passphrase is NULL, or if the sent_hash is NULL, this
|
|
Packit |
90a5c9 |
* function returns DECLINED.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If the site passphrase hash does not match the sent hash, this function
|
|
Packit |
90a5c9 |
* returns AUTH_USER_NOT_FOUND.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* On success, returns OK.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int check_site(request_rec * r, const char *site, const char *sent_user, const char *sent_hash)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (site && sent_user && sent_hash) {
|
|
Packit |
90a5c9 |
const char *hash = ap_md5(r->pool,
|
|
Packit |
90a5c9 |
(unsigned char *) apr_pstrcat(r->pool, sent_user, ":", site, NULL));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!strcmp(sent_hash, hash)) {
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AUTH_USER_NOT_FOUND;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Given a username and password (extracted externally from a cookie), run
|
|
Packit |
90a5c9 |
* the authnz hooks to determine whether this request is authorized.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Return an HTTP code.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int check_authn(request_rec * r, const char *sent_user, const char *sent_pw)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
authn_status auth_result;
|
|
Packit |
90a5c9 |
authn_provider_list *current_provider;
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
|
|
Packit |
90a5c9 |
&auth_form_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
current_provider = conf->providers;
|
|
Packit |
90a5c9 |
do {
|
|
Packit |
90a5c9 |
const authn_provider *provider;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* For now, if a provider isn't set, we'll be nice and use the file
|
|
Packit |
90a5c9 |
* provider.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (!current_provider) {
|
|
Packit |
90a5c9 |
provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP,
|
|
Packit |
90a5c9 |
AUTHN_DEFAULT_PROVIDER,
|
|
Packit |
90a5c9 |
AUTHN_PROVIDER_VERSION);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!provider || !provider->check_password) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01806)
|
|
Packit |
90a5c9 |
"no authn provider configured");
|
|
Packit |
90a5c9 |
auth_result = AUTH_GENERAL_ERROR;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, AUTHN_DEFAULT_PROVIDER);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
provider = current_provider->provider;
|
|
Packit |
90a5c9 |
apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, current_provider->provider_name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!sent_user || !sent_pw) {
|
|
Packit |
90a5c9 |
auth_result = AUTH_USER_NOT_FOUND;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
auth_result = provider->check_password(r, sent_user, sent_pw);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Something occurred. Stop checking. */
|
|
Packit |
90a5c9 |
if (auth_result != AUTH_USER_NOT_FOUND) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If we're not really configured for providers, stop now. */
|
|
Packit |
90a5c9 |
if (!conf->providers) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
current_provider = current_provider->next;
|
|
Packit |
90a5c9 |
} while (current_provider);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (auth_result != AUTH_GRANTED) {
|
|
Packit |
90a5c9 |
int return_code;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If we're not authoritative, then any error is ignored. */
|
|
Packit |
90a5c9 |
if (!(conf->authoritative) && auth_result != AUTH_DENIED) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
switch (auth_result) {
|
|
Packit |
90a5c9 |
case AUTH_DENIED:
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01807)
|
|
Packit |
90a5c9 |
"user '%s': authentication failure for \"%s\": "
|
|
Packit |
90a5c9 |
"password Mismatch",
|
|
Packit |
90a5c9 |
sent_user, r->uri);
|
|
Packit |
90a5c9 |
return_code = HTTP_UNAUTHORIZED;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case AUTH_USER_NOT_FOUND:
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01808)
|
|
Packit |
90a5c9 |
"user '%s' not found: %s", sent_user, r->uri);
|
|
Packit |
90a5c9 |
return_code = HTTP_UNAUTHORIZED;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case AUTH_GENERAL_ERROR:
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* We'll assume that the module has already said what its error
|
|
Packit |
90a5c9 |
* was in the logs.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
return_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If we're returning 401, tell them to try again. */
|
|
Packit |
90a5c9 |
if (return_code == HTTP_UNAUTHORIZED) {
|
|
Packit |
90a5c9 |
note_cookie_auth_failure(r);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* TODO: Flag the user somehow as to the reason for the failure */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return return_code;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* fake the basic authentication header if configured to do so */
|
|
Packit |
90a5c9 |
static void fake_basic_authentication(request_rec *r, auth_form_config_rec *conf,
|
|
Packit |
90a5c9 |
const char *user, const char *pw)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (conf->fakebasicauth) {
|
|
Packit |
90a5c9 |
char *basic = apr_pstrcat(r->pool, user, ":", pw, NULL);
|
|
Packit |
90a5c9 |
apr_size_t size = (apr_size_t) strlen(basic);
|
|
Packit |
90a5c9 |
char *base64 = apr_palloc(r->pool,
|
|
Packit |
90a5c9 |
apr_base64_encode_len(size + 1) * sizeof(char));
|
|
Packit |
90a5c9 |
apr_base64_encode(base64, basic, size);
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_in, "Authorization",
|
|
Packit |
90a5c9 |
apr_pstrcat(r->pool, "Basic ", base64, NULL));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Must we use form authentication? If so, extract the cookie and run
|
|
Packit |
90a5c9 |
* the authnz hooks to determine if the login is valid.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If the login is not valid, a 401 Not Authorized will be returned. It
|
|
Packit |
90a5c9 |
* is up to the webmaster to ensure this screen displays a suitable login
|
|
Packit |
90a5c9 |
* form to give the user the opportunity to log in.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int authenticate_form_authn(request_rec * r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf = ap_get_module_config(r->per_dir_config,
|
|
Packit |
90a5c9 |
&auth_form_module);
|
|
Packit |
90a5c9 |
const char *sent_user = NULL, *sent_pw = NULL, *sent_hash = NULL;
|
|
Packit |
90a5c9 |
const char *sent_loc = NULL, *sent_method = "GET", *sent_mimetype = NULL;
|
|
Packit |
90a5c9 |
const char *current_auth = NULL;
|
|
Packit |
90a5c9 |
const char *err;
|
|
Packit |
90a5c9 |
apr_status_t res;
|
|
Packit |
90a5c9 |
int rv = HTTP_UNAUTHORIZED;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Are we configured to be Form auth? */
|
|
Packit |
90a5c9 |
current_auth = ap_auth_type(r);
|
|
Packit |
90a5c9 |
if (!current_auth || strcasecmp(current_auth, "form")) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* XSS security warning: using cookies to store private data only works
|
|
Packit |
90a5c9 |
* when the administrator has full control over the source website. When
|
|
Packit |
90a5c9 |
* in forward-proxy mode, websites are public by definition, and so can
|
|
Packit |
90a5c9 |
* never be secure. Abort the auth attempt in this case.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (PROXYREQ_PROXY == r->proxyreq) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01809)
|
|
Packit |
90a5c9 |
"form auth cannot be used for proxy "
|
|
Packit |
90a5c9 |
"requests due to XSS risk, access denied: %s", r->uri);
|
|
Packit |
90a5c9 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We need an authentication realm. */
|
|
Packit |
90a5c9 |
if (!ap_auth_name(r)) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01810)
|
|
Packit |
90a5c9 |
"need AuthName: %s", r->uri);
|
|
Packit |
90a5c9 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->ap_auth_type = (char *) current_auth;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* try get the username and password from the notes, if present */
|
|
Packit |
90a5c9 |
get_notes_auth(r, &sent_user, &sent_pw, &sent_method, &sent_mimetype);
|
|
Packit |
90a5c9 |
if (!sent_user || !sent_pw || !*sent_user || !*sent_pw) {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* otherwise try get the username and password from a session, if present */
|
|
Packit |
90a5c9 |
res = get_session_auth(r, &sent_user, &sent_pw, &sent_hash);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
res = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* first test whether the site passphrase matches */
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == res && sent_user && sent_hash && sent_pw) {
|
|
Packit |
90a5c9 |
rv = check_site(r, conf->site, sent_user, sent_hash);
|
|
Packit |
90a5c9 |
if (OK == rv) {
|
|
Packit |
90a5c9 |
fake_basic_authentication(r, conf, sent_user, sent_pw);
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* otherwise test for a normal password match */
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == res && sent_user && sent_pw) {
|
|
Packit |
90a5c9 |
rv = check_authn(r, sent_user, sent_pw);
|
|
Packit |
90a5c9 |
if (OK == rv) {
|
|
Packit |
90a5c9 |
fake_basic_authentication(r, conf, sent_user, sent_pw);
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* If we reach this point, the request should fail with access denied,
|
|
Packit |
90a5c9 |
* except for one potential scenario:
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If the request is a POST, and the posted form contains user defined fields
|
|
Packit |
90a5c9 |
* for a username and a password, and the username and password are correct,
|
|
Packit |
90a5c9 |
* then return the response obtained by a GET to this URL.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If an additional user defined location field is present in the form,
|
|
Packit |
90a5c9 |
* instead of a GET of the current URL, redirect the browser to the new
|
|
Packit |
90a5c9 |
* location.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* As a further option, if the user defined fields for the type of request,
|
|
Packit |
90a5c9 |
* the mime type of the body of the request, and the body of the request
|
|
Packit |
90a5c9 |
* itself are present, replace this request with a new request of the given
|
|
Packit |
90a5c9 |
* type and with the given body.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Otherwise access is denied.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Reading the body requires some song and dance, because the input filters
|
|
Packit |
90a5c9 |
* are not yet configured. To work around this problem, we create a
|
|
Packit |
90a5c9 |
* subrequest and use that to create a sane filter stack we can read the
|
|
Packit |
90a5c9 |
* form from.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* The main request is then capped with a kept_body input filter, which has
|
|
Packit |
90a5c9 |
* the effect of guaranteeing the input stack can be safely read a second time.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (HTTP_UNAUTHORIZED == rv && r->method_number == M_POST && ap_is_initial_req(r)) {
|
|
Packit |
90a5c9 |
request_rec *rr;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *sent_body = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* create a subrequest of our current uri */
|
|
Packit |
90a5c9 |
rr = ap_sub_req_lookup_uri(r->uri, r, r->input_filters);
|
|
Packit |
90a5c9 |
rr->headers_in = r->headers_in;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* run the insert_filters hook on the subrequest to ensure a body read can
|
|
Packit |
90a5c9 |
* be done properly.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ap_run_insert_filter(rr);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* parse the form by reading the subrequest */
|
|
Packit |
90a5c9 |
rv = get_form_auth(rr, conf->username, conf->password, conf->location,
|
|
Packit |
90a5c9 |
conf->method, conf->mimetype, conf->body,
|
|
Packit |
90a5c9 |
&sent_user, &sent_pw, &sent_loc, &sent_method,
|
|
Packit |
90a5c9 |
&sent_mimetype, &sent_body, conf);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* make sure any user detected within the subrequest is saved back to
|
|
Packit |
90a5c9 |
* the main request.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
r->user = apr_pstrdup(r->pool, rr->user);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* we cannot clean up rr at this point, as memory allocated to rr is
|
|
Packit |
90a5c9 |
* referenced from the main request. It will be cleaned up when the
|
|
Packit |
90a5c9 |
* main request is cleaned up.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* insert the kept_body filter on the main request to guarantee the
|
|
Packit |
90a5c9 |
* input filter stack cannot be read a second time, optionally inject
|
|
Packit |
90a5c9 |
* a saved body if one was specified in the login form.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (sent_body && sent_mimetype) {
|
|
Packit |
90a5c9 |
apr_table_set(r->headers_in, "Content-Type", sent_mimetype);
|
|
Packit |
90a5c9 |
r->kept_body = sent_body;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
r->kept_body = apr_brigade_create(r->pool, r->connection->bucket_alloc);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_request_insert_filter_fn(r);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* did the form ask to change the method? if so, switch in the redirect handler
|
|
Packit |
90a5c9 |
* to relaunch this request as the subrequest with the new method. If the
|
|
Packit |
90a5c9 |
* form didn't specify a method, the default value GET will force a redirect.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (sent_method && strcmp(r->method, sent_method)) {
|
|
Packit |
90a5c9 |
r->handler = FORM_REDIRECT_HANDLER;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* check the authn in the main request, based on the username found */
|
|
Packit |
90a5c9 |
if (OK == rv) {
|
|
Packit |
90a5c9 |
rv = check_authn(r, sent_user, sent_pw);
|
|
Packit |
90a5c9 |
if (OK == rv) {
|
|
Packit |
90a5c9 |
fake_basic_authentication(r, conf, sent_user, sent_pw);
|
|
Packit |
90a5c9 |
set_session_auth(r, sent_user, sent_pw, conf->site);
|
|
Packit |
90a5c9 |
if (sent_loc) {
|
|
Packit |
90a5c9 |
apr_table_set(r->headers_out, "Location", sent_loc);
|
|
Packit |
90a5c9 |
return HTTP_MOVED_TEMPORARILY;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (conf->loginsuccess) {
|
|
Packit |
90a5c9 |
const char *loginsuccess = ap_expr_str_exec(r,
|
|
Packit |
90a5c9 |
conf->loginsuccess, &err;;
|
|
Packit |
90a5c9 |
if (!err) {
|
|
Packit |
90a5c9 |
apr_table_set(r->headers_out, "Location", loginsuccess);
|
|
Packit |
90a5c9 |
return HTTP_MOVED_TEMPORARILY;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02339)
|
|
Packit |
90a5c9 |
"Can't evaluate login success expression: %s", err);
|
|
Packit |
90a5c9 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* did the admin prefer to be redirected to the login page on failure
|
|
Packit |
90a5c9 |
* instead?
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (HTTP_UNAUTHORIZED == rv && conf->loginrequired) {
|
|
Packit |
90a5c9 |
const char *loginrequired = ap_expr_str_exec(r,
|
|
Packit |
90a5c9 |
conf->loginrequired, &err;;
|
|
Packit |
90a5c9 |
if (!err) {
|
|
Packit |
90a5c9 |
apr_table_set(r->headers_out, "Location", loginrequired);
|
|
Packit |
90a5c9 |
return HTTP_MOVED_TEMPORARILY;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02340)
|
|
Packit |
90a5c9 |
"Can't evaluate login required expression: %s", err);
|
|
Packit |
90a5c9 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* did the user ask to be redirected on login success? */
|
|
Packit |
90a5c9 |
if (sent_loc) {
|
|
Packit |
90a5c9 |
apr_table_set(r->headers_out, "Location", sent_loc);
|
|
Packit |
90a5c9 |
rv = HTTP_MOVED_TEMPORARILY;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* potential security issue: if we return a login to the browser, we must
|
|
Packit |
90a5c9 |
* send a no-store to make sure a well behaved browser will not try and
|
|
Packit |
90a5c9 |
* send the login details a second time if the back button is pressed.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* if the user has full control over the backend, the
|
|
Packit |
90a5c9 |
* AuthCookieDisableNoStore can be used to turn this off.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (HTTP_UNAUTHORIZED == rv && !conf->disable_no_store) {
|
|
Packit |
90a5c9 |
apr_table_addn(r->headers_out, "Cache-Control", "no-store");
|
|
Packit |
90a5c9 |
apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Handle a login attempt.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If the login session is either missing or form authnz is unsuccessful, a
|
|
Packit |
90a5c9 |
* 401 Not Authorized will be returned to the browser. The webmaster
|
|
Packit |
90a5c9 |
* is expected to insert a login form into the 401 Not Authorized
|
|
Packit |
90a5c9 |
* error screen.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If the webmaster wishes, they can point the form submission at this
|
|
Packit |
90a5c9 |
* handler, which will redirect the user to the correct page on success.
|
|
Packit |
90a5c9 |
* On failure, the 401 Not Authorized error screen will be redisplayed,
|
|
Packit |
90a5c9 |
* where the login attempt can be repeated.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int authenticate_form_login_handler(request_rec * r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf;
|
|
Packit |
90a5c9 |
const char *err;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const char *sent_user = NULL, *sent_pw = NULL, *sent_loc = NULL;
|
|
Packit |
90a5c9 |
int rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (strcmp(r->handler, FORM_LOGIN_HANDLER)) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r->method_number != M_POST) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01811)
|
|
Packit |
90a5c9 |
"the " FORM_LOGIN_HANDLER " only supports the POST method for %s",
|
|
Packit |
90a5c9 |
r->uri);
|
|
Packit |
90a5c9 |
return HTTP_METHOD_NOT_ALLOWED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
conf = ap_get_module_config(r->per_dir_config, &auth_form_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = get_form_auth(r, conf->username, conf->password, conf->location,
|
|
Packit |
90a5c9 |
NULL, NULL, NULL,
|
|
Packit |
90a5c9 |
&sent_user, &sent_pw, &sent_loc,
|
|
Packit |
90a5c9 |
NULL, NULL, NULL, conf);
|
|
Packit |
90a5c9 |
if (OK == rv) {
|
|
Packit |
90a5c9 |
rv = check_authn(r, sent_user, sent_pw);
|
|
Packit |
90a5c9 |
if (OK == rv) {
|
|
Packit |
90a5c9 |
set_session_auth(r, sent_user, sent_pw, conf->site);
|
|
Packit |
90a5c9 |
if (sent_loc) {
|
|
Packit |
90a5c9 |
apr_table_set(r->headers_out, "Location", sent_loc);
|
|
Packit |
90a5c9 |
return HTTP_MOVED_TEMPORARILY;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (conf->loginsuccess) {
|
|
Packit |
90a5c9 |
const char *loginsuccess = ap_expr_str_exec(r,
|
|
Packit |
90a5c9 |
conf->loginsuccess, &err;;
|
|
Packit |
90a5c9 |
if (!err) {
|
|
Packit |
90a5c9 |
apr_table_set(r->headers_out, "Location", loginsuccess);
|
|
Packit |
90a5c9 |
return HTTP_MOVED_TEMPORARILY;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02341)
|
|
Packit |
90a5c9 |
"Can't evaluate login success expression: %s", err);
|
|
Packit |
90a5c9 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return HTTP_OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* did we prefer to be redirected to the login page on failure instead? */
|
|
Packit |
90a5c9 |
if (HTTP_UNAUTHORIZED == rv && conf->loginrequired) {
|
|
Packit |
90a5c9 |
const char *loginrequired = ap_expr_str_exec(r,
|
|
Packit |
90a5c9 |
conf->loginrequired, &err;;
|
|
Packit |
90a5c9 |
if (!err) {
|
|
Packit |
90a5c9 |
apr_table_set(r->headers_out, "Location", loginrequired);
|
|
Packit |
90a5c9 |
return HTTP_MOVED_TEMPORARILY;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02342)
|
|
Packit |
90a5c9 |
"Can't evaluate login required expression: %s", err);
|
|
Packit |
90a5c9 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Handle a logout attempt.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If an attempt is made to access this URL, any username and password
|
|
Packit |
90a5c9 |
* embedded in the session is deleted.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* This has the effect of logging the person out.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If a logout URI has been specified, this function will create an
|
|
Packit |
90a5c9 |
* internal redirect to this page.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int authenticate_form_logout_handler(request_rec * r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
auth_form_config_rec *conf;
|
|
Packit |
90a5c9 |
const char *err;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (strcmp(r->handler, FORM_LOGOUT_HANDLER)) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
conf = ap_get_module_config(r->per_dir_config, &auth_form_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* remove the username and password, effectively logging the user out */
|
|
Packit |
90a5c9 |
set_session_auth(r, NULL, NULL, NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* make sure the logout page is never cached - otherwise the logout won't
|
|
Packit |
90a5c9 |
* work!
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
apr_table_addn(r->headers_out, "Cache-Control", "no-store");
|
|
Packit |
90a5c9 |
apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* if set, internal redirect to the logout page */
|
|
Packit |
90a5c9 |
if (conf->logout) {
|
|
Packit |
90a5c9 |
const char *logout = ap_expr_str_exec(r,
|
|
Packit |
90a5c9 |
conf->logout, &err;;
|
|
Packit |
90a5c9 |
if (!err) {
|
|
Packit |
90a5c9 |
apr_table_addn(r->headers_out, "Location", logout);
|
|
Packit |
90a5c9 |
return HTTP_TEMPORARY_REDIRECT;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02343)
|
|
Packit |
90a5c9 |
"Can't evaluate logout expression: %s", err);
|
|
Packit |
90a5c9 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return HTTP_OK;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Handle a redirect attempt.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If during a form login, the method, mimetype and request body are
|
|
Packit |
90a5c9 |
* specified, this handler will ensure that this request is included
|
|
Packit |
90a5c9 |
* as an internal redirect.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int authenticate_form_redirect_handler(request_rec * r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
request_rec *rr = NULL;
|
|
Packit |
90a5c9 |
const char *sent_method = NULL, *sent_mimetype = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (strcmp(r->handler, FORM_REDIRECT_HANDLER)) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* get the method and mimetype from the notes */
|
|
Packit |
90a5c9 |
get_notes_auth(r, NULL, NULL, &sent_method, &sent_mimetype);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r->kept_body && sent_method && sent_mimetype) {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01812)
|
|
Packit |
90a5c9 |
"internal redirect to method '%s' and body mimetype '%s' for the "
|
|
Packit |
90a5c9 |
"uri: %s", sent_method, sent_mimetype, r->uri);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rr = ap_sub_req_method_uri(sent_method, r->uri, r, r->output_filters);
|
|
Packit |
90a5c9 |
r->status = ap_run_sub_req(rr);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01813)
|
|
Packit |
90a5c9 |
"internal redirect requested but one or all of method, mimetype or "
|
|
Packit |
90a5c9 |
"body are NULL: %s", r->uri);
|
|
Packit |
90a5c9 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* return the underlying error, or OK on success */
|
|
Packit |
90a5c9 |
return r->status == HTTP_OK || r->status == OK ? OK : r->status;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int authenticate_form_post_config(apr_pool_t *pconf, apr_pool_t *plog,
|
|
Packit |
90a5c9 |
apr_pool_t *ptemp, server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!ap_session_load_fn || !ap_session_get_fn || !ap_session_set_fn) {
|
|
Packit |
90a5c9 |
ap_session_load_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_load);
|
|
Packit |
90a5c9 |
ap_session_get_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_get);
|
|
Packit |
90a5c9 |
ap_session_set_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_set);
|
|
Packit |
90a5c9 |
if (!ap_session_load_fn || !ap_session_get_fn || !ap_session_set_fn) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, APLOGNO(02617)
|
|
Packit |
90a5c9 |
"You must load mod_session to enable the mod_auth_form "
|
|
Packit |
90a5c9 |
"functions");
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
|
|
Packit |
90a5c9 |
ap_request_insert_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_insert_filter);
|
|
Packit |
90a5c9 |
ap_request_remove_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_remove_filter);
|
|
Packit |
90a5c9 |
if (!ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, APLOGNO(02618)
|
|
Packit |
90a5c9 |
"You must load mod_request to enable the mod_auth_form "
|
|
Packit |
90a5c9 |
"functions");
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void register_hooks(apr_pool_t * p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_hook_post_config(authenticate_form_post_config,NULL,NULL,APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if AP_MODULE_MAGIC_AT_LEAST(20080403,1)
|
|
Packit |
90a5c9 |
ap_hook_check_authn(authenticate_form_authn, NULL, NULL, APR_HOOK_MIDDLE,
|
|
Packit |
90a5c9 |
AP_AUTH_INTERNAL_PER_CONF);
|
|
Packit |
90a5c9 |
#else
|
|
Packit |
90a5c9 |
ap_hook_check_user_id(authenticate_form_authn, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
ap_hook_handler(authenticate_form_login_handler, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
ap_hook_handler(authenticate_form_logout_handler, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
ap_hook_handler(authenticate_form_redirect_handler, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_hook_note_auth_failure(hook_note_cookie_auth_failure, NULL, NULL,
|
|
Packit |
90a5c9 |
APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE_MODULE(auth_form) =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
STANDARD20_MODULE_STUFF,
|
|
Packit |
90a5c9 |
create_auth_form_dir_config, /* dir config creater */
|
|
Packit |
90a5c9 |
merge_auth_form_dir_config, /* dir merger --- default is to override */
|
|
Packit |
90a5c9 |
NULL, /* server config */
|
|
Packit |
90a5c9 |
NULL, /* merge server config */
|
|
Packit |
90a5c9 |
auth_form_cmds, /* command apr_table_t */
|
|
Packit |
90a5c9 |
register_hooks /* register hooks */
|
|
Packit |
90a5c9 |
};
|