|
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_md5.h" /* for apr_password_validate */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "ap_config.h"
|
|
Packit |
90a5c9 |
#include "ap_provider.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 |
|
|
Packit |
90a5c9 |
#include "mod_auth.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "ap_socache.h"
|
|
Packit |
90a5c9 |
#include "util_mutex.h"
|
|
Packit |
90a5c9 |
#include "apr_optional.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
module AP_MODULE_DECLARE_DATA authn_socache_module;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct authn_cache_dircfg {
|
|
Packit |
90a5c9 |
apr_interval_time_t timeout;
|
|
Packit |
90a5c9 |
apr_array_header_t *providers;
|
|
Packit |
90a5c9 |
const char *context;
|
|
Packit |
90a5c9 |
} authn_cache_dircfg;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* FIXME:
|
|
Packit |
90a5c9 |
* I think the cache and mutex should be global
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_global_mutex_t *authn_cache_mutex = NULL;
|
|
Packit |
90a5c9 |
static ap_socache_provider_t *socache_provider = NULL;
|
|
Packit |
90a5c9 |
static ap_socache_instance_t *socache_instance = NULL;
|
|
Packit |
90a5c9 |
static const char *const authn_cache_id = "authn-socache";
|
|
Packit |
90a5c9 |
static int configured;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t remove_lock(void *data)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (authn_cache_mutex) {
|
|
Packit |
90a5c9 |
apr_global_mutex_destroy(authn_cache_mutex);
|
|
Packit |
90a5c9 |
authn_cache_mutex = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t destroy_cache(void *data)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (socache_instance) {
|
|
Packit |
90a5c9 |
socache_provider->destroy(socache_instance, (server_rec*)data);
|
|
Packit |
90a5c9 |
socache_instance = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int authn_cache_precfg(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptmp)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = ap_mutex_register(pconf, authn_cache_id,
|
|
Packit |
90a5c9 |
NULL, APR_LOCK_DEFAULT, 0);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01673)
|
|
Packit |
90a5c9 |
"failed to register %s mutex", authn_cache_id);
|
|
Packit |
90a5c9 |
return 500; /* An HTTP status would be a misnomer! */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
socache_provider = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
|
|
Packit |
90a5c9 |
AP_SOCACHE_DEFAULT_PROVIDER,
|
|
Packit |
90a5c9 |
AP_SOCACHE_PROVIDER_VERSION);
|
|
Packit |
90a5c9 |
configured = 0;
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int authn_cache_post_config(apr_pool_t *pconf, apr_pool_t *plog,
|
|
Packit |
90a5c9 |
apr_pool_t *ptmp, server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
static struct ap_socache_hints authn_cache_hints = {64, 32, 60000000};
|
|
Packit |
90a5c9 |
const char *errmsg;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!configured) {
|
|
Packit |
90a5c9 |
return OK; /* don't waste the overhead of creating mutex & cache */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (socache_provider == NULL) {
|
|
Packit |
90a5c9 |
ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, plog, APLOGNO(01674)
|
|
Packit |
90a5c9 |
"Please select a socache provider with AuthnCacheSOCache "
|
|
Packit |
90a5c9 |
"(no default found on this platform). Maybe you need to "
|
|
Packit |
90a5c9 |
"load mod_socache_shmcb or another socache module first");
|
|
Packit |
90a5c9 |
return 500; /* An HTTP status would be a misnomer! */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We have socache_provider, but do not have socache_instance. This should
|
|
Packit |
90a5c9 |
* happen only when using "default" socache_provider, so create default
|
|
Packit |
90a5c9 |
* socache_instance in this case. */
|
|
Packit |
90a5c9 |
if (socache_instance == NULL) {
|
|
Packit |
90a5c9 |
errmsg = socache_provider->create(&socache_instance, NULL,
|
|
Packit |
90a5c9 |
ptmp, pconf);
|
|
Packit |
90a5c9 |
if (errmsg) {
|
|
Packit |
90a5c9 |
ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, plog, APLOGNO(02612)
|
|
Packit |
90a5c9 |
"failed to create mod_socache_shmcb socache "
|
|
Packit |
90a5c9 |
"instance: %s", errmsg);
|
|
Packit |
90a5c9 |
return 500;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = ap_global_mutex_create(&authn_cache_mutex, NULL,
|
|
Packit |
90a5c9 |
authn_cache_id, NULL, s, pconf, 0);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01675)
|
|
Packit |
90a5c9 |
"failed to create %s mutex", authn_cache_id);
|
|
Packit |
90a5c9 |
return 500; /* An HTTP status would be a misnomer! */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_pool_cleanup_register(pconf, NULL, remove_lock, apr_pool_cleanup_null);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = socache_provider->init(socache_instance, authn_cache_id,
|
|
Packit |
90a5c9 |
&authn_cache_hints, s, pconf);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01677)
|
|
Packit |
90a5c9 |
"failed to initialise %s cache", authn_cache_id);
|
|
Packit |
90a5c9 |
return 500; /* An HTTP status would be a misnomer! */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_pool_cleanup_register(pconf, (void*)s, destroy_cache, apr_pool_cleanup_null);
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void authn_cache_child_init(apr_pool_t *p, server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *lock;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
if (!configured) {
|
|
Packit |
90a5c9 |
return; /* don't waste the overhead of creating mutex & cache */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
lock = apr_global_mutex_lockfile(authn_cache_mutex);
|
|
Packit |
90a5c9 |
rv = apr_global_mutex_child_init(&authn_cache_mutex, lock, p);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01678)
|
|
Packit |
90a5c9 |
"failed to initialise mutex in child_init");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *authn_cache_socache(cmd_parms *cmd, void *CFG,
|
|
Packit |
90a5c9 |
const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *errmsg = ap_check_cmd_context(cmd, GLOBAL_ONLY);
|
|
Packit |
90a5c9 |
const char *sep, *name;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (errmsg)
|
|
Packit |
90a5c9 |
return errmsg;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Argument is of form 'name:args' or just 'name'. */
|
|
Packit |
90a5c9 |
sep = ap_strchr_c(arg, ':');
|
|
Packit |
90a5c9 |
if (sep) {
|
|
Packit |
90a5c9 |
name = apr_pstrmemdup(cmd->pool, arg, sep - arg);
|
|
Packit |
90a5c9 |
sep++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
name = arg;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
socache_provider = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP, name,
|
|
Packit |
90a5c9 |
AP_SOCACHE_PROVIDER_VERSION);
|
|
Packit |
90a5c9 |
if (socache_provider == NULL) {
|
|
Packit |
90a5c9 |
errmsg = apr_psprintf(cmd->pool,
|
|
Packit |
90a5c9 |
"Unknown socache provider '%s'. Maybe you need "
|
|
Packit |
90a5c9 |
"to load the appropriate socache module "
|
|
Packit |
90a5c9 |
"(mod_socache_%s?)", arg, arg);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
errmsg = socache_provider->create(&socache_instance, sep,
|
|
Packit |
90a5c9 |
cmd->temp_pool, cmd->pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (errmsg) {
|
|
Packit |
90a5c9 |
errmsg = apr_psprintf(cmd->pool, "AuthnCacheSOCache: %s", errmsg);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return errmsg;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *authn_cache_enable(cmd_parms *cmd, void *CFG)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *errmsg = ap_check_cmd_context(cmd, GLOBAL_ONLY);
|
|
Packit |
90a5c9 |
configured = 1;
|
|
Packit |
90a5c9 |
return errmsg;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *const directory = "directory";
|
|
Packit |
90a5c9 |
static void* authn_cache_dircfg_create(apr_pool_t *pool, char *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
authn_cache_dircfg *ret = apr_palloc(pool, sizeof(authn_cache_dircfg));
|
|
Packit |
90a5c9 |
ret->timeout = apr_time_from_sec(300);
|
|
Packit |
90a5c9 |
ret->providers = NULL;
|
|
Packit |
90a5c9 |
ret->context = directory;
|
|
Packit |
90a5c9 |
return ret;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* not sure we want this. Might be safer to document use-all-or-none */
|
|
Packit |
90a5c9 |
static void* authn_cache_dircfg_merge(apr_pool_t *pool, void *BASE, void *ADD)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
authn_cache_dircfg *base = BASE;
|
|
Packit |
90a5c9 |
authn_cache_dircfg *add = ADD;
|
|
Packit |
90a5c9 |
authn_cache_dircfg *ret = apr_pmemdup(pool, add, sizeof(authn_cache_dircfg));
|
|
Packit |
90a5c9 |
/* preserve context and timeout if not defaults */
|
|
Packit |
90a5c9 |
if (add->context == directory) {
|
|
Packit |
90a5c9 |
ret->context = base->context;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (add->timeout == apr_time_from_sec(300)) {
|
|
Packit |
90a5c9 |
ret->timeout = base->timeout;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (add->providers == NULL) {
|
|
Packit |
90a5c9 |
ret->providers = base->providers;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return ret;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *authn_cache_setprovider(cmd_parms *cmd, void *CFG,
|
|
Packit |
90a5c9 |
const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
authn_cache_dircfg *cfg = CFG;
|
|
Packit |
90a5c9 |
if (cfg->providers == NULL) {
|
|
Packit |
90a5c9 |
cfg->providers = apr_array_make(cmd->pool, 4, sizeof(const char*));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(cfg->providers, const char*) = arg;
|
|
Packit |
90a5c9 |
configured = 1;
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *authn_cache_timeout(cmd_parms *cmd, void *CFG,
|
|
Packit |
90a5c9 |
const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
authn_cache_dircfg *cfg = CFG;
|
|
Packit |
90a5c9 |
int secs = atoi(arg);
|
|
Packit |
90a5c9 |
cfg->timeout = apr_time_from_sec(secs);
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const command_rec authn_cache_cmds[] =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* global stuff: cache and mutex */
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthnCacheSOCache", authn_cache_socache, NULL, RSRC_CONF,
|
|
Packit |
90a5c9 |
"socache provider for authn cache"),
|
|
Packit |
90a5c9 |
AP_INIT_NO_ARGS("AuthnCacheEnable", authn_cache_enable, NULL, RSRC_CONF,
|
|
Packit |
90a5c9 |
"enable socache configuration in htaccess even if not enabled anywhere else"),
|
|
Packit |
90a5c9 |
/* per-dir stuff */
|
|
Packit |
90a5c9 |
AP_INIT_ITERATE("AuthnCacheProvideFor", authn_cache_setprovider, NULL,
|
|
Packit |
90a5c9 |
OR_AUTHCFG, "Determine what authn providers to cache for"),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthnCacheTimeout", authn_cache_timeout, NULL,
|
|
Packit |
90a5c9 |
OR_AUTHCFG, "Timeout (secs) for cached credentials"),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("AuthnCacheContext", ap_set_string_slot,
|
|
Packit |
90a5c9 |
(void*)APR_OFFSETOF(authn_cache_dircfg, context),
|
|
Packit |
90a5c9 |
ACCESS_CONF, "Context for authn cache"),
|
|
Packit |
90a5c9 |
{NULL}
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *construct_key(request_rec *r, const char *context,
|
|
Packit |
90a5c9 |
const char *user, const char *realm)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* handle "special" context values */
|
|
Packit |
90a5c9 |
if (!strcmp(context, directory)) {
|
|
Packit |
90a5c9 |
/* FIXME: are we at risk of this blowing up? */
|
|
Packit |
90a5c9 |
char *new_context;
|
|
Packit |
90a5c9 |
char *slash = strrchr(r->uri, '/');
|
|
Packit |
90a5c9 |
new_context = apr_palloc(r->pool, slash - r->uri +
|
|
Packit |
90a5c9 |
strlen(r->server->server_hostname) + 1);
|
|
Packit |
90a5c9 |
strcpy(new_context, r->server->server_hostname);
|
|
Packit |
90a5c9 |
strncat(new_context, r->uri, slash - r->uri);
|
|
Packit |
90a5c9 |
context = new_context;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcmp(context, "server")) {
|
|
Packit |
90a5c9 |
context = r->server->server_hostname;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* any other context value is literal */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (realm == NULL) { /* basic auth */
|
|
Packit |
90a5c9 |
return apr_pstrcat(r->pool, context, ":", user, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else { /* digest auth */
|
|
Packit |
90a5c9 |
return apr_pstrcat(r->pool, context, ":", user, ":", realm, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void ap_authn_cache_store(request_rec *r, const char *module,
|
|
Packit |
90a5c9 |
const char *user, const char *realm,
|
|
Packit |
90a5c9 |
const char* data)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
authn_cache_dircfg *dcfg;
|
|
Packit |
90a5c9 |
const char *key;
|
|
Packit |
90a5c9 |
apr_time_t expiry;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* first check whether we're cacheing for this module */
|
|
Packit |
90a5c9 |
dcfg = ap_get_module_config(r->per_dir_config, &authn_socache_module);
|
|
Packit |
90a5c9 |
if (!configured || !dcfg->providers) {
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!ap_array_str_contains(dcfg->providers, module)) {
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* OK, we're on. Grab mutex to do our business */
|
|
Packit |
90a5c9 |
rv = apr_global_mutex_trylock(authn_cache_mutex);
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_EBUSY(rv)) {
|
|
Packit |
90a5c9 |
/* don't wait around; just abandon it */
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(01679)
|
|
Packit |
90a5c9 |
"authn credentials for %s not cached (mutex busy)", user);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01680)
|
|
Packit |
90a5c9 |
"Failed to cache authn credentials for %s in %s",
|
|
Packit |
90a5c9 |
module, dcfg->context);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We have the mutex, so go ahead */
|
|
Packit |
90a5c9 |
/* first build our key and determine expiry time */
|
|
Packit |
90a5c9 |
key = construct_key(r, dcfg->context, user, realm);
|
|
Packit |
90a5c9 |
expiry = apr_time_now() + dcfg->timeout;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* store it */
|
|
Packit |
90a5c9 |
rv = socache_provider->store(socache_instance, r->server,
|
|
Packit |
90a5c9 |
(unsigned char*)key, strlen(key), expiry,
|
|
Packit |
90a5c9 |
(unsigned char*)data, strlen(data), r->pool);
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01681)
|
|
Packit |
90a5c9 |
"Cached authn credentials for %s in %s",
|
|
Packit |
90a5c9 |
user, dcfg->context);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01682)
|
|
Packit |
90a5c9 |
"Failed to cache authn credentials for %s in %s",
|
|
Packit |
90a5c9 |
module, dcfg->context);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We're done with the mutex */
|
|
Packit |
90a5c9 |
rv = apr_global_mutex_unlock(authn_cache_mutex);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01683) "Failed to release mutex!");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define MAX_VAL_LEN 100
|
|
Packit |
90a5c9 |
static authn_status check_password(request_rec *r, const char *user,
|
|
Packit |
90a5c9 |
const char *password)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* construct key
|
|
Packit |
90a5c9 |
* look it up
|
|
Packit |
90a5c9 |
* if found, test password
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* mutexing here would be a big performance drag.
|
|
Packit |
90a5c9 |
* It's definitely unnecessary with some backends (like ndbm or gdbm)
|
|
Packit |
90a5c9 |
* Is there a risk in the general case? I guess the only risk we
|
|
Packit |
90a5c9 |
* care about is a race condition that gets us a dangling pointer
|
|
Packit |
90a5c9 |
* to no-longer-defined memory. Hmmm ...
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
const char *key;
|
|
Packit |
90a5c9 |
authn_cache_dircfg *dcfg;
|
|
Packit |
90a5c9 |
unsigned char val[MAX_VAL_LEN];
|
|
Packit |
90a5c9 |
unsigned int vallen = MAX_VAL_LEN - 1;
|
|
Packit |
90a5c9 |
dcfg = ap_get_module_config(r->per_dir_config, &authn_socache_module);
|
|
Packit |
90a5c9 |
if (!configured || !dcfg->providers) {
|
|
Packit |
90a5c9 |
return AUTH_USER_NOT_FOUND;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
key = construct_key(r, dcfg->context, user, NULL);
|
|
Packit |
90a5c9 |
rv = socache_provider->retrieve(socache_instance, r->server,
|
|
Packit |
90a5c9 |
(unsigned char*)key, strlen(key),
|
|
Packit |
90a5c9 |
val, &vallen, r->pool);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_NOTFOUND(rv)) {
|
|
Packit |
90a5c9 |
/* not found - just return */
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01684)
|
|
Packit |
90a5c9 |
"Authn cache: no credentials found for %s", user);
|
|
Packit |
90a5c9 |
return AUTH_USER_NOT_FOUND;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
/* OK, we got a value */
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01685)
|
|
Packit |
90a5c9 |
"Authn cache: found credentials for %s", user);
|
|
Packit |
90a5c9 |
val[vallen] = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* error: give up and pass the buck */
|
|
Packit |
90a5c9 |
/* FIXME: getting this for NOTFOUND - prolly a bug in mod_socache */
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01686)
|
|
Packit |
90a5c9 |
"Error accessing authentication cache");
|
|
Packit |
90a5c9 |
return AUTH_USER_NOT_FOUND;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = apr_password_validate(password, (char*) val);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return AUTH_DENIED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return AUTH_GRANTED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static authn_status get_realm_hash(request_rec *r, const char *user,
|
|
Packit |
90a5c9 |
const char *realm, char **rethash)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
const char *key;
|
|
Packit |
90a5c9 |
authn_cache_dircfg *dcfg;
|
|
Packit |
90a5c9 |
unsigned char val[MAX_VAL_LEN];
|
|
Packit |
90a5c9 |
unsigned int vallen = MAX_VAL_LEN - 1;
|
|
Packit |
90a5c9 |
dcfg = ap_get_module_config(r->per_dir_config, &authn_socache_module);
|
|
Packit |
90a5c9 |
if (!configured || !dcfg->providers) {
|
|
Packit |
90a5c9 |
return AUTH_USER_NOT_FOUND;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
key = construct_key(r, dcfg->context, user, realm);
|
|
Packit |
90a5c9 |
rv = socache_provider->retrieve(socache_instance, r->server,
|
|
Packit |
90a5c9 |
(unsigned char*)key, strlen(key),
|
|
Packit |
90a5c9 |
val, &vallen, r->pool);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_NOTFOUND(rv)) {
|
|
Packit |
90a5c9 |
/* not found - just return */
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01687)
|
|
Packit |
90a5c9 |
"Authn cache: no credentials found for %s", user);
|
|
Packit |
90a5c9 |
return AUTH_USER_NOT_FOUND;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
/* OK, we got a value */
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01688)
|
|
Packit |
90a5c9 |
"Authn cache: found credentials for %s", user);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* error: give up and pass the buck */
|
|
Packit |
90a5c9 |
/* FIXME: getting this for NOTFOUND - prolly a bug in mod_socache */
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01689)
|
|
Packit |
90a5c9 |
"Error accessing authentication cache");
|
|
Packit |
90a5c9 |
return AUTH_USER_NOT_FOUND;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*rethash = apr_pstrmemdup(r->pool, (char *)val, vallen);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return AUTH_USER_FOUND;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const authn_provider authn_cache_provider =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
&check_password,
|
|
Packit |
90a5c9 |
&get_realm_hash,
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void register_hooks(apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "socache",
|
|
Packit |
90a5c9 |
AUTHN_PROVIDER_VERSION,
|
|
Packit |
90a5c9 |
&authn_cache_provider, AP_AUTH_INTERNAL_PER_CONF);
|
|
Packit |
90a5c9 |
APR_REGISTER_OPTIONAL_FN(ap_authn_cache_store);
|
|
Packit |
90a5c9 |
ap_hook_pre_config(authn_cache_precfg, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
ap_hook_post_config(authn_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
ap_hook_child_init(authn_cache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE_MODULE(authn_socache) =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
STANDARD20_MODULE_STUFF,
|
|
Packit |
90a5c9 |
authn_cache_dircfg_create,
|
|
Packit |
90a5c9 |
authn_cache_dircfg_merge,
|
|
Packit |
90a5c9 |
NULL,
|
|
Packit |
90a5c9 |
NULL,
|
|
Packit |
90a5c9 |
authn_cache_cmds,
|
|
Packit |
90a5c9 |
register_hooks
|
|
Packit |
90a5c9 |
};
|