diff options
author | olavmrk <olavmrk@a716ebb1-153a-0410-b759-cfb97c6a1b53> | 2007-09-24 09:56:34 +0000 |
---|---|---|
committer | olavmrk <olavmrk@a716ebb1-153a-0410-b759-cfb97c6a1b53> | 2007-09-24 09:56:34 +0000 |
commit | 1fa6146abe8ee1b8f224646866a855d969bbb0b6 (patch) | |
tree | 30a81b462b338316625daf61e2527a3a4262554d /auth_mellon_cache.c | |
download | mod_auth_mellon-1fa6146abe8ee1b8f224646866a855d969bbb0b6.tar.gz mod_auth_mellon-1fa6146abe8ee1b8f224646866a855d969bbb0b6.tar.xz mod_auth_mellon-1fa6146abe8ee1b8f224646866a855d969bbb0b6.zip |
Initial import of version 0.0.6
git-svn-id: https://modmellon.googlecode.com/svn/trunk@3 a716ebb1-153a-0410-b759-cfb97c6a1b53
Diffstat (limited to 'auth_mellon_cache.c')
-rw-r--r-- | auth_mellon_cache.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/auth_mellon_cache.c b/auth_mellon_cache.c new file mode 100644 index 0000000..e1593dd --- /dev/null +++ b/auth_mellon_cache.c @@ -0,0 +1,473 @@ +/* + * + * auth_mellon_cache.c: an authentication apache module + * Copyright © 2003-2007 UNINETT (http://www.uninett.no/) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "auth_mellon.h" + +/* This function locks the session table and locates a session entry. + * Unlocks the table and returns NULL if the entry wasn't found. + * If a entry was found, then you _must_ unlock it with am_cache_unlock + * after you are done with it. + * + * Parameters: + * server_rec *s The current server. + * const char *key The session key. + * + * Returns: + * The session entry on success or NULL on failure. + */ +am_cache_entry_t *am_cache_lock(server_rec *s, const char *key) +{ + am_mod_cfg_rec *mod_cfg; + am_cache_entry_t *table; + int i; + + + /* Check if we have a valid session key. We abort if we don't. */ + if(key == NULL || strlen(key) != AM_SESSION_ID_LENGTH) { + return NULL; + } + + + mod_cfg = am_get_mod_cfg(s); + + + /* Lock the table. */ + apr_global_mutex_lock(mod_cfg->lock); + table = apr_shm_baseaddr_get(mod_cfg->cache); + + + for(i = 0; i < mod_cfg->init_cache_size; i++) { + if(strcmp(table[i].key, key) == 0) { + /* We found the entry. */ + if(table[i].expires > apr_time_now()) { + /* And it hasn't expired. */ + return &table[i]; + } + } + } + + + /* We didn't find a entry matching the key. Unlock the table and + * return NULL; + */ + apr_global_mutex_unlock(mod_cfg->lock); + return NULL; +} + + +/* This function locks the session table and creates a new session entry. + * It will first attempt to locate a free session. If it doesn't find a + * free session, then it will take the least recentry used session. + * + * Remember to unlock the table with am_cache_unlock(...) afterwards. + * + * Parameters: + * server_rec *s The current server. + * const char *key The key of the session to allocate. + * + * Returns: + * The new session entry on success. NULL if key is a invalid session + * key. + */ +am_cache_entry_t *am_cache_new(server_rec *s, const char *key) +{ + am_cache_entry_t *t; + am_mod_cfg_rec *mod_cfg; + am_cache_entry_t *table; + apr_time_t current_time; + int i; + apr_time_t age; + + /* Check if we have a valid session key. We abort if we don't. */ + if(key == NULL || strlen(key) != AM_SESSION_ID_LENGTH) { + return NULL; + } + + + /* First we try to find another session with the given key. */ + t = am_cache_lock(s, key); + + + if(t == NULL) { + /* We didn't find a previous session with the key. We will search + * for the least recently used entry or a free entry in stead. + */ + + mod_cfg = am_get_mod_cfg(s); + + + /* Lock the table. */ + apr_global_mutex_lock(mod_cfg->lock); + table = apr_shm_baseaddr_get(mod_cfg->cache); + + /* Get current time. If we find a entry with expires <= the current + * time, then we can use it. + */ + current_time = apr_time_now(); + + /* We will use 't' to remember the best/oldest entry. We + * initalize it to the first entry in the table to simplify the + * following code (saves test for t == NULL). + */ + t = &table[0]; + + /* Iterate over the session table. Update 't' to match the "best" + * entry (the least recently used). 't' will point a free entry + * if we find one. Otherwise, 't' will point to the least recently + * used entry. + */ + for(i = 0; i < mod_cfg->init_cache_size; i++) { + if(table[i].key[0] == '\0') { + /* This entry is free. Update 't' to this entry + * and exit loop. + */ + t = &table[i]; + break; + } + + if(table[i].expires <= current_time) { + /* This entry is expired, and is therefore free. + * Update 't' and exit loop. + */ + t = &table[i]; + break; + } + + if(table[i].access < t->access) { + /* This entry is older than 't' - update 't'. */ + t = &table[i]; + } + } + } + + if(t->key[0] != '\0' && t->expires > current_time) { + /* We dropped a LRU entry. Calculate the age in seconds. */ + age = (current_time - t->access) / 1000000; + + if(age < 3600) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, + "Dropping LRU entry entry with age = %llis," + " which is less than one hour. It may be a good" + " idea to increase MellonCacheSize.", + age); + } + } + + /* Now 't' points to the entry we are going to use. We initialize + * it and returns it. + */ + + strcpy(t->key, key); + + /* Far far into the future. */ + t->expires = 0x7fffffffffffffffLL; + + t->logged_in = 0; + t->size = 0; + t->user[0] = '\0'; + + t->lasso_identity[0] = '\0'; + t->lasso_session[0] = '\0'; + + return t; +} + + +/* This function unlocks a session entry. + * + * Parameters: + * server_rec *s The current server. + * am_cache_entry_t *entry The session entry. + * + * Returns: + * Nothing. + */ +void am_cache_unlock(server_rec *s, am_cache_entry_t *entry) +{ + am_mod_cfg_rec *mod_cfg; + + /* Update access time. */ + entry->access = apr_time_now(); + + mod_cfg = am_get_mod_cfg(s); + apr_global_mutex_unlock(mod_cfg->lock); +} + + +/* This function updates the expire-timestamp of a session, if the new + * timestamp is earlier than the previous. + * + * Parameters: + * am_cache_entry_t *t The current session. + * apr_time_t expires The new timestamp. + * + * Returns: + * Nothing. + */ +void am_cache_update_expires(am_cache_entry_t *t, apr_time_t expires) +{ + /* Check if we should update the expires timestamp. */ + if(t->expires == 0 || t->expires > expires) { + t->expires = expires; + } +} + + +/* This function appends a name-value pair to a session. It is possible to + * store several values with the same name. This is the method used to store + * multivalued fields. + * + * Parameters: + * am_cache_entry_t *t The current session. + * const char *var The name of the value to be stored. + * const char *val The value which should be stored in the session. + * + * Returns: + * OK on success or HTTP_INTERNAL_SERVER_ERROR on failure. + */ +int am_cache_env_append(am_cache_entry_t *t, + const char *var, const char *val) +{ + /* Make sure that the name and value will fit inside the + * fixed size buffer. + */ + if(strlen(val) >= AM_CACHE_VALSIZE || + strlen(var) >= AM_CACHE_VARSIZE) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Unable to store session data because it is to big. " + "Name = \"%s\"; Value = \"%s\".", var, val); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if(t->size >= AM_CACHE_ENVSIZE) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Unable to store attribute value because we have" + " reached the maximum number of name-value pairs for" + " this session."); + return HTTP_INTERNAL_SERVER_ERROR; + } + + strcpy(t->env[t->size].varname, var); + strcpy(t->env[t->size].value, val); + t->size++; + + return OK; +} + + +/* This function populates the subprocess environment with data received + * from the IdP. + * + * Parameters: + * request_rec *r The request we should add the data to. + * am_cache_entry_t *t The session data. + * + * Returns: + * Nothing. + */ +void am_cache_env_populate(request_rec *r, am_cache_entry_t *t) +{ + am_dir_cfg_rec *d; + int i; + apr_hash_t *counters; + const char *varname; + const char *env_varname; + const char *value; + int *count; + + d = am_get_dir_cfg(r); + + /* Check if the user attribute has been set, and set it if it + * hasn't been set. */ + if(t->user[0] == '\0') { + for(i = 0; i < t->size; ++i) { + if(strcmp(t->env[i].varname, d->userattr) == 0) { + strcpy(t->user, t->env[i].value); + } + } + } + + if(t->user[0] != '\0') { + /* We have a user-"name". Set r->user and r->ap_auth_type. */ + r->user = apr_pstrdup(r->pool, t->user); + r->ap_auth_type = apr_pstrdup(r->pool, "Mellon"); + } else { + /* We don't have a user-"name". Log error. */ + ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, + "Didn't find the attribute \"%s\" in the attributes" + " which were received from the IdP. Cannot set a user" + " for this request without a valid user attribute.", + d->userattr); + } + + /* Allocate a set of counters for duplicate variables in the list. */ + counters = apr_hash_make(r->pool); + + /* Populate the subprocess environment with the attributes we + * received from the IdP. + */ + for(i = 0; i < t->size; ++i) { + varname = t->env[i].varname; + + /* Check if we should map this name into another name. */ + env_varname = (const char*)apr_hash_get( + d->envattr, varname, APR_HASH_KEY_STRING); + if(env_varname != NULL) { + varname = env_varname; + } + + value = t->env[i].value; + + + /* Find the number of times this variable has been set. */ + count = apr_hash_get(counters, varname, APR_HASH_KEY_STRING); + if(count == NULL) { + + /* This is the first time. Create a counter for this variable. */ + count = apr_palloc(r->pool, sizeof(int)); + *count = 0; + apr_hash_set(counters, varname, APR_HASH_KEY_STRING, count); + + /* Add the variable without a suffix. */ + apr_table_set(r->subprocess_env, + apr_pstrcat(r->pool, "MELLON_", varname, NULL), + value); + } + + /* Add the variable with a suffix indicating how many times it has + * been added before. + */ + apr_table_set(r->subprocess_env, + apr_psprintf(r->pool, "MELLON_%s_%d", varname, *count), + value); + + /* Increase the count. */ + ++(*count); + } +} + + +/* This function deletes a given key from the session store. + * + * Parameters: + * server_rec *s The current server. + * am_cache_entry_t *cache The entry we are deleting. + * + * Returns: + * Nothing. + */ +void am_cache_delete(server_rec *s, am_cache_entry_t *cache) +{ + /* We write a null-byte at the beginning of the key to + * mark this slot as unused. + */ + cache->key[0] = '\0'; + + /* Unlock the entry. */ + am_cache_unlock(s, cache); +} + + +/* This function stores a lasso identity dump and a lasso session dump in + * the given session object. + * + * Parameters: + * am_cache_entry_t *session The session object. + * const char *lasso_identity The identity dump. + * const char *lasso_session The session dump. + * + * Returns: + * OK on success or HTTP_INTERNAL_SERVER_ERROR if the lasso state information + * is to big to fit in our session. + */ +int am_cache_set_lasso_state(am_cache_entry_t *session, + const char *lasso_identity, + const char *lasso_session) +{ + if(lasso_identity != NULL) { + if(strlen(lasso_identity) < AM_CACHE_MAX_LASSO_IDENTITY_SIZE) { + strcpy(session->lasso_identity, lasso_identity); + } else { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Lasso identity is to big for storage. Size of lasso" + " identity is %u, max size is %u.", strlen(lasso_identity), + AM_CACHE_MAX_LASSO_IDENTITY_SIZE - 1); + return HTTP_INTERNAL_SERVER_ERROR; + } + } else { + /* No identity dump to save. */ + strcpy(session->lasso_identity, ""); + } + + + if(lasso_session != NULL) { + if(strlen(lasso_session) < AM_CACHE_MAX_LASSO_SESSION_SIZE) { + strcpy(session->lasso_session, lasso_session); + } else { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Lasso session is to big for storage. Size of lasso" + " session is %u, max size is %u.", strlen(lasso_session), + AM_CACHE_MAX_LASSO_SESSION_SIZE - 1); + return HTTP_INTERNAL_SERVER_ERROR; + } + } else { + /* No session dump to save. */ + strcpy(session->lasso_session, ""); + } + + return OK; +} + + +/* This function retrieves a lasso identity dump from the session object. + * + * Parameters: + * am_cache_entry_t *session The session object. + * + * Returns: + * The identity dump, or NULL if we don't have a session dump. + */ +const char *am_cache_get_lasso_identity(am_cache_entry_t *session) +{ + if(strlen(session->lasso_identity) == 0) { + return NULL; + } + + return session->lasso_identity; +} + + +/* This function retrieves a lasso session dump from the session object. + * + * Parameters: + * am_cache_entry_t *session The session object. + * + * Returns: + * The session dump, or NULL if we don't have a session dump. + */ +const char *am_cache_get_lasso_session(am_cache_entry_t *session) +{ + if(strlen(session->lasso_session) == 0) { + return NULL; + } + + return session->lasso_session; +} |