Issac Goldstand wrote:
> Paul (or anyone else interested),
>   Any interested in visiting mod_authn_cache over the hackathon?  I
> won't be there physically but have been working on the template that was 
> started at the mod-auth project @ sourceforge. 

[snip]

I've actually gotten everything working except for the timeout thing. 
I'm probably missing something obvious in C, but this is what I'm doing...

I've got a timeout period set as a apr_short_interval_time_t, a
timestamp on each cache entry set as an apr_time_t, and I populate the
latter with apr_time_now() when I add the cache entry and compare if
(cache timestamp + the timeout < apr_time_now()) then use the cached
credentials, otherwise delete the old cache entry.

The odd thing is that I *never* get the else to run.  Ever. 

I've attached the codebase as I've modified it so far (no commit access
to mod-auth @ SF), and a sample config that I'm using is:

LogLevel debug

  AuthType Basic
  AuthName "Test"
  AuthBasicProvider file cache
  AuthnCacheTimeout 1
  AuthnCacheProvider pam
  AuthUserFile /root/test.htpasswd
  require valid-user

This results in log entries like the following:

[Mon Nov 02 08:34:29 2009] [debug] mod_authn_cache.c(184): [client
10.1.1.92] No entry for user issacg
[Mon Nov 02 08:34:29 2009] [info] [client 10.1.1.92] Creating new cache
entry for user issacg 0
[Mon Nov 02 08:34:29 2009] [debug] mod_authn_cache.c(162): [client
10.1.1.92] Found entry for user issacg
[Mon Nov 02 08:34:29 2009] [info] [client 10.1.1.92] Entry for user
issacg matches cached password. Authenticating.
[Mon Nov 02 08:34:29 2009] [debug] mod_authn_cache.c(162): [client
10.1.1.92] Found entry for user issacg

If I wait a few seconds and try again, the same repeats (it says "No
entry for..." and never shows the "Entry for user timed out"

Any ideas?

  Issac
/* ====================================================================
 *  Copyright 2003-2004 Paul Querna
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * mod_authn_cache
 *
 */

#include "apr_lib.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "apr_strings.h"
#include "apr_time.h"
#include "apr_pools.h"
#include "apr_hash.h"
#include "ap_provider.h"

#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"      

#include "mod_auth.h"

module AP_MODULE_DECLARE_DATA authn_cache_module;

static apr_hash_t* authn_cache;
static apr_pool_t* authn_cache_pool;

typedef struct authn_cache_conf_t {
    authn_provider_list *providers;
    apr_short_interval_time_t timeout;
} authn_cache_conf_t;

typedef struct authn_cache_entry_t {
        apr_pool_t *p;
        char *user;
        char *password;
        apr_time_t atime;
} authn_cache_entry_t;

static void *create_authn_cache_dconfig(apr_pool_t * p, char *d)
{
    authn_cache_conf_t *conf;
    if (d == NULL) {
        return NULL;
    }
    conf = (authn_cache_conf_t *) apr_pcalloc(p, sizeof(authn_cache_conf_t));
    if (conf) {
        conf->providers = NULL;
        conf->timeout = 0;
    }
    return conf;
}

static const char *set_cache_timeout_interval(cmd_parms *cmd, void *config,
                                              const char *arg)
{
    authn_cache_conf_t *conf = (authn_cache_conf_t*)config;
    conf->timeout = (apr_short_interval_time_t) atol(arg);
    return NULL;
}

static const char *add_authn_provider(cmd_parms *cmd, void *config,
                                      const char *arg)
{
    authn_cache_conf_t *conf = (authn_cache_conf_t*)config;
    authn_provider_list *newp;
    const char *provider_name;

    if (strcasecmp(arg, "cache") == 0) {
        /* hey, recursively cache`ing auth requests is bad.. mmmmkay? */
        return NULL;
    }

    provider_name = apr_pstrdup(cmd->pool, arg);
    newp = apr_pcalloc(cmd->pool, sizeof(authn_provider_list));
    newp->provider_name = provider_name;

   /* lookup and cache the actual provider now */
    newp->provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP,
                                        newp->provider_name, "0");
    if (newp->provider == NULL) {
        /* by the time they use it, the provider should be loaded and
           registered with us. */
        return apr_psprintf(cmd->pool,
                            "Unknown Authn provider: %s",
                            newp->provider_name);
    }


    if (!newp->provider->check_password) {
        /* if it doesn't provide the appropriate function, reject it */
        return apr_psprintf(cmd->pool,
                            "The '%s' Authn provider doesn't support "
                            "Basic Authentication", provider_name);
    }

    /* Add the provider to the list now. */
    if (!conf->providers) {
        conf->providers = newp;
    }
    else {
        authn_provider_list *last = conf->providers;

        while (last->next) {
            last = last->next;
        }
        last->next = newp;
    }

    return NULL;


}

static const command_rec authn_cache_cmds[] = {
    /* per auth section */
    AP_INIT_TAKE1("AuthnCacheProvider", add_authn_provider, NULL, OR_AUTHCFG,
                  "The name of the configuration to use for this section"),
    AP_INIT_TAKE1("AuthnCacheTimeout", set_cache_timeout_interval, NULL, 
OR_AUTHCFG,
                  "The time before cached credentials are discarded or 0 to 
cache forever"),    /* global config */

    {NULL}
};

module AP_MODULE_DECLARE_DATA authn_cache_module;

static authn_status check_cache_pw(request_rec * r, const char *user,
                                 const char *password)
{
    apr_pool_t *p;
    authn_cache_entry_t* cache;
    authn_status auth_result;
    authn_provider_list *current_provider;
    authn_cache_conf_t *conf = ap_get_module_config(r->per_dir_config,
                                                    &authn_cache_module);

    /** Lookup entry */
    cache = (authn_cache_entry_t *)apr_hash_get(authn_cache, user,
                                                APR_HASH_KEY_STRING);
    if (cache != NULL) {
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                              "Found entry for user %s", user);
            if (cache->atime + conf->timeout < apr_time_now()) {
                    /** DO NOT UPDATE cache->atime - we want it to timeout
                     *  to force revalidate */
                if (apr_strnatcmp(cache->password, password) == 0) {
                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                              "Entry for user %s matches cached password. "
                              "Authenticating.", user);
                    return AUTH_GRANTED;
                }
            } else {
                /** Force-release the memory, or we'll leak like a boat
                 *  with a hole... */
                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                              "Entry for user %s timed out.  Destroying.",
                              user);
                apr_hash_set(authn_cache, cache->user, APR_HASH_KEY_STRING,
                             NULL);
                apr_pool_destroy(cache->p);
            }
    } else {
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                              "No entry for user %s",
                              user);
    }
        
    current_provider = conf->providers;

    do {
        const authn_provider *provider;
        if (!current_provider) {
           ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                              "No Cache Authn provider configured");
           auth_result = AUTH_GENERAL_ERROR;
           break;
        }
        else {
            provider = current_provider->provider;
        }

        if(!provider->check_password) {
             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                   "The '%s' Cache Authn provider doesn't support Basic 
Authentication", current_provider->provider_name);
             auth_result = AUTH_GENERAL_ERROR;
             break;
        }

        auth_result = provider->check_password(r, user, password);

        /* Something occured. Stop checking. */
        if (auth_result != AUTH_USER_NOT_FOUND) {
            break;
        }

        /* If we're not really configured for providers, stop now. */
        if (!conf->providers) {
            break;
        }
       
        current_provider = current_provider->next;

    } while (current_provider);

    if(auth_result == AUTH_GRANTED)
    {
                /** Add cache entry */
                if (apr_pool_create(&p, authn_cache_pool) != APR_SUCCESS) {
                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                                  "Error allocating memory for authentication 
cache");
                    return AUTH_GRANTED;
                }
                cache = apr_pcalloc(p, sizeof(authn_cache_entry_t));
                cache->p = p;
                cache->user = apr_pstrdup(cache->p, user);
                cache->password = apr_pstrdup(cache->p, password);
                cache->atime = apr_time_now();
                apr_hash_set(authn_cache, cache->user, APR_HASH_KEY_STRING, 
cache);
                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
                              "Creating new cache entry for user %s %d", user, 
apr_time_now() - cache->atime);
        }
    else {

    }

    return auth_result;
}

static authn_status get_cache_realm_hash(request_rec * r, const char *user,
                                         const char *realm, char **rethash)
{

    return AUTH_USER_NOT_FOUND;
}

static int init_authn_cache(apr_pool_t *p, apr_pool_t *plog,
                                 apr_pool_t *ptemp, server_rec *s)
{
    void *data;
    const char *userdata_key = "mod_authn_cache_init";

    apr_pool_userdata_get(&data, userdata_key, s->process->pool);

    if (!data) {
        apr_pool_userdata_set((const void *) 1, userdata_key,
                              apr_pool_cleanup_null, s->process->pool);
        return OK;
    }

    ap_add_version_component(p, "mod_authn_cache/0.1");

    return 0;
}


static const authn_provider authn_cache_provider = {
    &check_cache_pw,
    &get_cache_realm_hash
};

static void register_hooks(apr_pool_t * p)
{
    /** pconf is around for long enough... right?) */
    if (apr_pool_create(&authn_cache_pool, p) != APR_SUCCESS) {
            return;
    }
    authn_cache = apr_hash_make(authn_cache_pool);
    ap_hook_post_config(init_authn_cache, NULL, NULL, APR_HOOK_MIDDLE);
    ap_register_provider(p, AUTHN_PROVIDER_GROUP, "cache", "0",
                         &authn_cache_provider);
}

module AP_MODULE_DECLARE_DATA authn_cache_module = {
    STANDARD20_MODULE_STUFF,
    create_authn_cache_dconfig, /* dir config creater */
    NULL,                       /* dir merger --- default is to override */
    NULL,                       /* server config creator */
    NULL,                       /* merge server config */
    authn_cache_cmds,           /* command apr_table_t */
    register_hooks              /* register hooks */
};


Reply via email to