diff --git a/mod_mellon2/Makefile.in b/mod_mellon2/Makefile.in index aa83ec2..a2f9d18 100644 --- a/mod_mellon2/Makefile.in +++ b/mod_mellon2/Makefile.in @@ -23,7 +23,7 @@ DISTFILES=$(SRC) \ all: mod_auth_mellon.la mod_auth_mellon.la: $(SRC) auth_mellon.h auth_mellon_compat.h - @APXS2@ -Wc,"@OPENSSL_CFLAGS@ @LASSO_CFLAGS@ @CURL_CFLAGS@ @GLIB_CFLAGS@" -Wl,"@OPENSSL_LIBS@ @LASSO_LIBS@ @CURL_LIBS@ @GLIB_LIBS@" -Wc,-Wall -Wc,-g -c $(SRC) + @APXS2@ -Wc,"-std=c99 @OPENSSL_CFLAGS@ @LASSO_CFLAGS@ @CURL_CFLAGS@ @GLIB_CFLAGS@" -Wl,"@OPENSSL_LIBS@ @LASSO_LIBS@ @CURL_LIBS@ @GLIB_LIBS@" -Wc,-Wall -Wc,-g -c $(SRC) # Building configure (for distribution) diff --git a/mod_mellon2/README b/mod_mellon2/README index eb48deb..2381713 100644 --- a/mod_mellon2/README +++ b/mod_mellon2/README @@ -97,6 +97,13 @@ for mod_auth_mellon. The following is an example configuration: # Default: MellonCacheSize 100 MellonCacheSize 100 +# MellonCacheEntrySize sets the maximum size for a single session entry in +# bytes. When mod_auth_mellon reaches this limit, it cannot store any more +# data in the session and will return an error. The minimum entry size is +# 65536 bytes, values lower than that will be ignored and the minimum will +# be used. +# Default: MellonCacheEntrySize 196608 + # MellonLockFile is the full path to a file used for synchronizing access # to the session data. The path should only be used by one instance of # apache at a time. The server must be restarted before any changes to this diff --git a/mod_mellon2/auth_mellon.h b/mod_mellon2/auth_mellon.h index 8347013..c6a10b3 100644 --- a/mod_mellon2/auth_mellon.h +++ b/mod_mellon2/auth_mellon.h @@ -22,6 +22,8 @@ #ifndef MOD_AUTH_MELLON_H #define MOD_AUTH_MELLON_H +#include + #include #include #include @@ -71,13 +73,10 @@ /* Size definitions for the session cache. */ #define AM_CACHE_KEYSIZE 120 -#define AM_CACHE_VARSIZE 128 -#define AM_CACHE_VALSIZE 512-AM_CACHE_VARSIZE #define AM_CACHE_ENVSIZE 128 #define AM_CACHE_USERSIZE 512 -#define AM_CACHE_MAX_LASSO_IDENTITY_SIZE 1024 -#define AM_CACHE_MAX_LASSO_SESSION_SIZE 32768 -#define AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE 65536 +#define AM_CACHE_DEFAULT_ENTRY_SIZE 196608 +#define AM_CACHE_MIN_ENTRY_SIZE 65536 /* This is the length of the id we use (for session IDs and @@ -101,12 +100,15 @@ typedef struct am_mod_cfg_rec { int post_count; apr_size_t post_size; + int entry_size; + /* These variables can't be allowed to change after the session store * has been initialized. Therefore we copy them before initializing * the session store. */ int init_cache_size; const char *init_lock_file; + apr_size_t init_entry_size; apr_shm_t *cache; apr_global_mutex_t *lock; @@ -240,10 +242,13 @@ typedef struct am_dir_cfg_rec { LassoServer *server; } am_dir_cfg_rec; +typedef struct am_cache_storage_t { + apr_uintptr_t ptr; +} am_cache_storage_t; typedef struct am_cache_env_t { - char varname[AM_CACHE_VARSIZE]; - char value[AM_CACHE_VALSIZE]; + am_cache_storage_t varname; + am_cache_storage_t value; } am_cache_env_t; typedef struct am_cache_entry_t { @@ -252,16 +257,20 @@ typedef struct am_cache_entry_t { apr_time_t expires; int logged_in; unsigned short size; - char user[AM_CACHE_USERSIZE]; + am_cache_storage_t user; /* Variables used to store lasso state between login requests *and logout requests. */ - char lasso_identity[AM_CACHE_MAX_LASSO_IDENTITY_SIZE]; - char lasso_session[AM_CACHE_MAX_LASSO_SESSION_SIZE]; - char lasso_saml_response[AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE]; + am_cache_storage_t lasso_identity; + am_cache_storage_t lasso_session; + am_cache_storage_t lasso_saml_response; am_cache_env_t env[AM_CACHE_ENVSIZE]; + + apr_size_t pool_size; + apr_size_t pool_used; + char pool[]; } am_cache_entry_t; typedef enum { @@ -322,6 +331,8 @@ void am_cookie_delete(request_rec *r); am_cache_entry_t *am_cache_lock(server_rec *s, am_cache_key_t type, const char *key); +const char *am_cache_entry_get_string(am_cache_entry_t *e, + am_cache_storage_t *slot); am_cache_entry_t *am_cache_new(server_rec *s, const char *key); void am_cache_unlock(server_rec *s, am_cache_entry_t *entry); diff --git a/mod_mellon2/auth_mellon_cache.c b/mod_mellon2/auth_mellon_cache.c index 3923569..70c4879 100644 --- a/mod_mellon2/auth_mellon_cache.c +++ b/mod_mellon2/auth_mellon_cache.c @@ -55,8 +55,6 @@ am_cache_entry_t *am_cache_lock(server_rec *s, return NULL; break; case AM_CACHE_NAMEID: - if (strlen(key) > AM_CACHE_MAX_LASSO_IDENTITY_SIZE) - return NULL; break; default: return NULL; @@ -113,6 +111,109 @@ am_cache_entry_t *am_cache_lock(server_rec *s, return NULL; } +static inline bool am_cache_entry_slot_is_empty(am_cache_storage_t *slot) +{ + return (slot->ptr == 0); +} + +static inline void am_cache_storage_null(am_cache_storage_t *slot) +{ + slot->ptr = 0; +} + +static inline void am_cache_entry_env_null(am_cache_entry_t *e) +{ + for (int i = 0; i < AM_CACHE_ENVSIZE; i++) { + am_cache_storage_null(&e->env[i].varname); + am_cache_storage_null(&e->env[i].value); + } +} + +static inline apr_size_t am_cache_entry_pool_left(am_cache_entry_t *e) +{ + return e->pool_size - e->pool_used; +} + +static inline apr_size_t am_cache_entry_pool_size(am_mod_cfg_rec *cfg) +{ + return cfg->init_entry_size - sizeof(am_cache_entry_t); +} + +/* This function sets a string into the specified storage on the entry. + * + * NOTE: The string pointer may be NULL, in that case storage is freed + * and set to NULL. + * + * Parametrs: + * am_cache_entry_t *entry Pointer to an entry + * am_cache_storage_t *slot Pointer to storage + * const char *string Pointer to a replacement string + * + * Returns: + * 0 on success, HTTP_INTERNAL_SERVER_ERROR on error. + */ +static int am_cache_entry_store_string(am_cache_entry_t *entry, + am_cache_storage_t *slot, + const char *string) +{ + char *datastr = NULL; + apr_size_t datalen = 0; + apr_size_t str_len = 0; + + if (string == NULL) return 0; + + if (slot->ptr != 0) { + datastr = &entry->pool[slot->ptr]; + datalen = strlen(datastr) + 1; + } + str_len = strlen(string) + 1; + if (str_len - datalen <= 0) { + memcpy(datastr, string, str_len); + return 0; + } + + /* recover space if slot happens to point to the last allocated space */ + if (slot->ptr + datalen == entry->pool_used) { + entry->pool_used -= datalen; + slot->ptr = 0; + } + + if (am_cache_entry_pool_left(entry) < str_len) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "apr_cache_entry_store_string() asked %zd available: %zd. " + "It may be a good idea to increase MellonCacheEntrySize.", + str_len, am_cache_entry_pool_left(entry)); + return HTTP_INTERNAL_SERVER_ERROR; + } + + slot->ptr = entry->pool_used; + datastr = &entry->pool[slot->ptr]; + memcpy(datastr, string, str_len); + entry->pool_used += str_len; + return 0; +} + +/* Returns a pointer to the string in the storage slot specified + * + * + * Parametrs: + * am_cache_entry_t *entry Pointer to an entry + * am_cache_storage_t *slot Pointer to storage slot + * + * Returns: + * A string or NULL if the slot is empty. + */ +const char *am_cache_entry_get_string(am_cache_entry_t *e, + am_cache_storage_t *slot) +{ + char *ret = NULL; + + if (slot->ptr != 0) { + ret = &e->pool[slot->ptr]; + } + + return ret; +} /* 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 @@ -222,10 +323,16 @@ am_cache_entry_t *am_cache_new(server_rec *s, const char *key) t->logged_in = 0; t->size = 0; - t->user[0] = '\0'; - t->lasso_identity[0] = '\0'; - t->lasso_session[0] = '\0'; + am_cache_storage_null(&t->user); + am_cache_storage_null(&t->lasso_identity); + am_cache_storage_null(&t->lasso_session); + am_cache_storage_null(&t->lasso_saml_response); + am_cache_entry_env_null(t); + + t->pool_size = am_cache_entry_pool_size(mod_cfg); + t->pool[0] = '\0'; + t->pool_used = 1; return t; } @@ -286,27 +393,36 @@ void am_cache_update_expires(am_cache_entry_t *t, apr_time_t expires) int am_cache_env_append(am_cache_entry_t *t, const char *var, const char *val) { + int status; + /* 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) { + if(t->size >= AM_CACHE_ENVSIZE) { 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); + "Unable to store attribute value because we have" + " reached the maximum number of name-value pairs for" + " this session. The maximum number is %d.", + AM_CACHE_ENVSIZE); return HTTP_INTERNAL_SERVER_ERROR; } - if(t->size >= AM_CACHE_ENVSIZE) { + status = am_cache_entry_store_string(t, &t->env[t->size].varname, var); + if (status != 0) { 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."); + "Unable to store session data because there is no more " + "space in the session. Attribute Name = \"%s\".", var); + return HTTP_INTERNAL_SERVER_ERROR; + } + + status = am_cache_entry_store_string(t, &t->env[t->size].value, val); + if (status != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Unable to store session data because there is no more " + "space in the session. Attribute Value = \"%s\".", val); return HTTP_INTERNAL_SERVER_ERROR; } - strcpy(t->env[t->size].varname, var); - strcpy(t->env[t->size].value, val); t->size++; return OK; @@ -325,11 +441,15 @@ int am_cache_env_append(am_cache_entry_t *t, const char *am_cache_env_fetch_first(am_cache_entry_t *t, const char *var) { + const char *str; int i; for (i = 0; t->size; i++) { - if (strcmp(t->env[i].varname, var) == 0) - return t->env[i].value; + str = am_cache_entry_get_string(t, &t->env[i].varname); + if (str == NULL) + break; + if (strcmp(str, var) == 0) + return str; } return NULL; @@ -356,15 +476,24 @@ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t) const char *varname_prefix; const char *value; int *count; + int status; 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') { + if (am_cache_entry_slot_is_empty(&t->user)) { for(i = 0; i < t->size; ++i) { - if(strcmp(t->env[i].varname, d->userattr) == 0) { - strcpy(t->user, t->env[i].value); + varname = am_cache_entry_get_string(t, &t->env[i].varname); + if (strcmp(varname, d->userattr) == 0) { + value = am_cache_entry_get_string(t, &t->env[i].value); + status = am_cache_entry_store_string(t, &t->user, value); + if (status != 0) { + ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, + "Unable to store the user name because there" + " is no more space in the session. " + "Username = \"%s\".", value); + } } } } @@ -376,7 +505,7 @@ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t) * received from the IdP. */ for(i = 0; i < t->size; ++i) { - varname = t->env[i].varname; + varname = am_cache_entry_get_string(t, &t->env[i].varname); varname_prefix = "MELLON_"; /* Check if we should map this name into another name. */ @@ -390,13 +519,21 @@ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t) } } - value = t->env[i].value; + value = am_cache_entry_get_string(t, &t->env[i].value); /* * If we find a variable remapping to MellonUser, use it. */ - if ((t->user[0] == '\0') && (strcmp(varname, d->userattr) == 0)) - strcpy(t->user, value); + if (am_cache_entry_slot_is_empty(&t->user) && + (strcmp(varname, d->userattr) == 0)) { + status = am_cache_entry_store_string(t, &t->user, value); + if (status != 0) { + ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, + "Unable to store the user name because there" + " is no more space in the session. " + "Username = \"%s\".", value); + } + } /* Find the number of times this variable has been set. */ count = apr_hash_get(counters, varname, APR_HASH_KEY_STRING); @@ -424,9 +561,9 @@ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t) ++(*count); } - if(t->user[0] != '\0') { + if (!am_cache_entry_slot_is_empty(&t->user)) { /* We have a user-"name". Set r->user and r->ap_auth_type. */ - r->user = apr_pstrdup(r->pool, t->user); + r->user = apr_pstrdup(r->pool, am_cache_entry_get_string(t, &t->user)); r->ap_auth_type = apr_pstrdup(r->pool, "Mellon"); } else { /* We don't have a user-"name". Log error. */ @@ -441,20 +578,24 @@ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t) /* Populate with the session? */ if (d->dump_session) { char *session; + const char *srcstr; int srclen, dstlen; - srclen = strlen(t->lasso_session); + srcstr = am_cache_entry_get_string(t, &t->lasso_session); + srclen = strlen(srcstr); dstlen = apr_base64_encode_len(srclen); session = apr_palloc(r->pool, dstlen); - (void)apr_base64_encode(session, t->lasso_session, srclen); + (void)apr_base64_encode(session, srcstr, srclen); apr_table_set(r->subprocess_env, "MELLON_SESSION", session); } - if (d->dump_saml_response) - apr_table_set(r->subprocess_env, - "MELLON_SAML_RESPONSE", - t->lasso_saml_response); + if (d->dump_saml_response) { + const char *sr = am_cache_entry_get_string(t, &t->lasso_saml_response); + if (sr) { + apr_table_set(r->subprocess_env, "MELLON_SAML_RESPONSE", sr); + } + } } @@ -496,56 +637,39 @@ int am_cache_set_lasso_state(am_cache_entry_t *session, const char *lasso_session, const char *lasso_saml_response) { - 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 %" APR_SIZE_T_FMT ", max size is %" - APR_SIZE_T_FMT ".", - (apr_size_t)strlen(lasso_identity), - (apr_size_t)AM_CACHE_MAX_LASSO_IDENTITY_SIZE - 1); - return HTTP_INTERNAL_SERVER_ERROR; - } - } else { - /* No identity dump to save. */ - strcpy(session->lasso_identity, ""); - } + int status; + status = am_cache_entry_store_string(session, + &session->lasso_identity, + lasso_identity); + if (status != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Lasso identity is to big for storage. Size of lasso" + " identity is %" APR_SIZE_T_FMT ".", + (apr_size_t)strlen(lasso_identity)); + return HTTP_INTERNAL_SERVER_ERROR; + } - 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 %" APR_SIZE_T_FMT ", max size is %" - APR_SIZE_T_FMT ".", - (apr_size_t)strlen(lasso_session), - (apr_size_t)AM_CACHE_MAX_LASSO_SESSION_SIZE - 1); - return HTTP_INTERNAL_SERVER_ERROR; - } - } else { - /* No session dump to save. */ - strcpy(session->lasso_session, ""); + status = am_cache_entry_store_string(session, + &session->lasso_session, + lasso_session); + if (status != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Lasso session is to big for storage. Size of lasso" + " session is %" APR_SIZE_T_FMT ".", + (apr_size_t)strlen(lasso_session)); + return HTTP_INTERNAL_SERVER_ERROR; } - if(lasso_saml_response != NULL) { - if(strlen(lasso_saml_response) < AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE) { - strcpy(session->lasso_saml_response, lasso_saml_response); - } else { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, - "Lasso SAML response is to big for storage. " - "Size of lasso session is %" APR_SIZE_T_FMT - ", max size is %" APR_SIZE_T_FMT ".", - (apr_size_t)strlen(lasso_saml_response), - (apr_size_t)AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE - 1); - return HTTP_INTERNAL_SERVER_ERROR; - } - } else { - /* No session dump to save. */ - strcpy(session->lasso_saml_response, ""); + status = am_cache_entry_store_string(session, + &session->lasso_saml_response, + lasso_saml_response); + if (status != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Lasso SAML response is to big for storage. Size of " + "lasso SAML Reponse is %" APR_SIZE_T_FMT ".", + (apr_size_t)strlen(lasso_saml_response)); + return HTTP_INTERNAL_SERVER_ERROR; } return OK; @@ -562,11 +686,7 @@ int am_cache_set_lasso_state(am_cache_entry_t *session, */ const char *am_cache_get_lasso_identity(am_cache_entry_t *session) { - if(strlen(session->lasso_identity) == 0) { - return NULL; - } - - return session->lasso_identity; + return am_cache_entry_get_string(session, &session->lasso_identity); } @@ -580,9 +700,5 @@ const char *am_cache_get_lasso_identity(am_cache_entry_t *session) */ const char *am_cache_get_lasso_session(am_cache_entry_t *session) { - if(strlen(session->lasso_session) == 0) { - return NULL; - } - - return session->lasso_session; + return am_cache_entry_get_string(session, &session->lasso_session); } diff --git a/mod_mellon2/auth_mellon_config.c b/mod_mellon2/auth_mellon_config.c index 36f6b96..dbcbfaa 100644 --- a/mod_mellon2/auth_mellon_config.c +++ b/mod_mellon2/auth_mellon_config.c @@ -19,8 +19,6 @@ * */ -#include - #include "auth_mellon.h" /* This is the default endpoint path. Remember to update the description of @@ -863,6 +861,15 @@ const command_rec auth_mellon_commands[] = { " take effect. The default value is 100." ), AP_INIT_TAKE1( + "MellonCacheEntrySize", + am_set_module_config_int_slot, + (void *)APR_OFFSETOF(am_mod_cfg_rec, entry_size), + RSRC_CONF, + "The maximum size for a single session entry. You must" + " restart the server before any changes to this directive will" + " take effect. The default value is 192KiB." + ), + AP_INIT_TAKE1( "MellonLockFile", am_set_module_config_file_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, lock_file), @@ -1571,8 +1578,11 @@ void *auth_mellon_server_config(apr_pool_t *p, server_rec *s) mod->post_count = post_count; mod->post_size = post_size; + mod->entry_size = AM_CACHE_DEFAULT_ENTRY_SIZE; + mod->init_cache_size = 0; mod->init_lock_file = NULL; + mod->init_entry_size = 0; mod->cache = NULL; mod->lock = NULL; diff --git a/mod_mellon2/auth_mellon_util.c b/mod_mellon2/auth_mellon_util.c index 6219c83..4a34acd 100644 --- a/mod_mellon2/auth_mellon_util.c +++ b/mod_mellon2/auth_mellon_util.c @@ -307,7 +307,8 @@ int am_check_permissions(request_rec *r, am_cache_entry_t *session) */ if (ce->flags & AM_COND_FLAG_MAP) varname = apr_hash_get(dir_cfg->envattr, - session->env[j].varname, + am_cache_entry_get_string(session, + &session->env[j].varname), APR_HASH_KEY_STRING); /* @@ -315,12 +316,13 @@ int am_check_permissions(request_rec *r, am_cache_entry_t *session) * sent by the IdP. */ if (varname == NULL) - varname = session->env[j].varname; + varname = am_cache_entry_get_string(session, + &session->env[j].varname); if (strcmp(varname, ce->varname) != 0) continue; - value = session->env[j].value; + value = am_cache_entry_get_string(session, &session->env[j].value); /* * Substiture backrefs if available diff --git a/mod_mellon2/configure.ac b/mod_mellon2/configure.ac index 0c1a602..be74cbf 100644 --- a/mod_mellon2/configure.ac +++ b/mod_mellon2/configure.ac @@ -1,5 +1,8 @@ AC_INIT([mod_auth_mellon],[0.7.0],[olav.morken@uninett.no]) +# We require support for C99. +AC_PROG_CC_C99 + AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION()) diff --git a/mod_mellon2/mod_auth_mellon.c b/mod_mellon2/mod_auth_mellon.c index 86949a4..fc34962 100644 --- a/mod_mellon2/mod_auth_mellon.c +++ b/mod_mellon2/mod_auth_mellon.c @@ -88,9 +88,13 @@ static int am_global_init(apr_pool_t *conf, apr_pool_t *log, */ mod->init_cache_size = mod->cache_size; mod->init_lock_file = apr_pstrdup(conf, mod->lock_file); + mod->init_entry_size = mod->entry_size; + if (mod->init_entry_size < AM_CACHE_MIN_ENTRY_SIZE) { + mod->init_entry_size = AM_CACHE_MIN_ENTRY_SIZE; + } /* find out the memory size of the cache */ - mem_size = sizeof(am_cache_entry_t) * mod->init_cache_size; + mem_size = mod->init_entry_size * mod->init_cache_size; /* Create the shared memory, exit if it fails. */