mod_auth_mellon-0.12.0/mod_auth_mellon.c0000644000175100017510000001755312576273035017655 0ustar olavmoolavmo/* * * mod_auth_mellon.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" #include /* This function is called after the configuration of the server is parsed * (it's a post-config hook). * * It initializes the shared memory and the mutex which is used to protect * the shared memory. * * Parameters: * apr_pool_t *conf The configuration pool. Valid as long as this * configuration is valid. * apr_pool_t *log A pool for memory which is cleared after each read * through the config files. * apr_pool_t *tmp A pool for memory which will be destroyed after * all the post_config hooks are run. * server_rec *s The current server record. * * Returns: * OK on successful initialization, or !OK on failure. */ static int am_global_init(apr_pool_t *conf, apr_pool_t *log, apr_pool_t *tmp, server_rec *s) { apr_size_t mem_size; am_mod_cfg_rec *mod; int rv; const char userdata_key[] = "auth_mellon_init"; char buffer[512]; void *data; /* Apache tests loadable modules by loading them (as is the only way). * This has the effect that all modules are loaded and initialised twice, * and we just want to initialise shared memory and mutexes when the * module loads for real! * * To accomplish this, we store a piece of data as userdata in the * process pool the first time the function is run. This data can be * detected on all subsequent runs, and then we know that this isn't the * first time this function runs. */ apr_pool_userdata_get(&data, userdata_key, s->process->pool); if (!data) { /* This is the first time this function is run. */ apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool); return OK; } mod = am_get_mod_cfg(s); /* If the session store is initialized then we can't change it. */ if(mod->cache != NULL) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "auth_mellon session store already initialized -" " reinitialization skipped."); return OK; } /* Copy from the variables set by the configuration file into variables * which will be set only once. We do this to avoid confusion if the user * tries to change the parameters of the session store after it is * initialized. */ 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 = mod->init_entry_size * mod->init_cache_size; /* Create the shared memory, exit if it fails. */ rv = apr_shm_create(&(mod->cache), mem_size, NULL, conf); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "shm_create: Error [%d] \"%s\"", rv, apr_strerror(rv, buffer, sizeof(buffer))); return !OK; } /* Initialize the session table. */ am_cache_init(mod); /* Now create the mutex that we need for locking the shared memory, then * test for success. we really need this, so we exit on failure. */ rv = apr_global_mutex_create(&(mod->lock), mod->init_lock_file, APR_LOCK_DEFAULT, conf); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "mutex_create: Error [%d] \"%s\"", rv, apr_strerror(rv, buffer, sizeof(buffer))); return !OK; } #ifdef AP_NEED_SET_MUTEX_PERMS /* On some platforms the mutex is implemented as a file. To allow child * processes running as a different user to open it, it is necessary to * change the permissions on it. */ rv = ap_unixd_set_global_mutex_perms(mod->lock); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, "Failed to set permissions on session table lock;" " check User and Group directives"); return rv; } #endif return OK; } /* This function is run when each child process of apache starts. * apr_global_mutex_child_init must be run on the session data mutex for * every child process of apache. * * Parameters: * apr_pool_t *p This pool is for data associated with this * child process. * server_rec *s The server record for the current server. * * Returns: * Nothing. */ static void am_child_init(apr_pool_t *p, server_rec *s) { am_mod_cfg_rec *m = am_get_mod_cfg(s); apr_status_t rv; CURLcode curl_res; /* Reinitialize the mutex for the child process. */ rv = apr_global_mutex_child_init(&(m->lock), m->init_lock_file, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "Child process could not connect to mutex"); } /* lasso_init() must be run before any other lasso-functions. */ lasso_init(); /* curl_global_init() should be called before any other curl * function. Relying on curl_easy_init() to call curl_global_init() * isn't thread safe. */ curl_res = curl_global_init(CURL_GLOBAL_SSL); if(curl_res != CURLE_OK) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Failed to initialize curl library: %u", curl_res); } return; } static int am_create_request(request_rec *r) { am_req_cfg_rec *req_cfg; req_cfg = apr_pcalloc(r->pool, sizeof(am_req_cfg_rec)); req_cfg->cookie_value = NULL; #ifdef HAVE_ECP req_cfg->ecp_authn_req = false; #endif /* HAVE_ECP */ ap_set_module_config(r->request_config, &auth_mellon_module, req_cfg); return OK; } static void register_hooks(apr_pool_t *p) { ap_hook_access_checker(am_auth_mellon_user, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_check_user_id(am_check_uid, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_config(am_global_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(am_child_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_create_request(am_create_request, NULL, NULL, APR_HOOK_MIDDLE); /* Add the hook to handle requests to the mod_auth_mellon endpoint. * * This is APR_HOOK_FIRST because we do not expect nor require users * to add a SetHandler option for the endpoint. Instead, simply * setting MellonEndpointPath should be enough. * * Therefore this hook must run before any handler that may check * r->handler and decide that it is the only handler for this URL. */ ap_hook_handler(am_handler, NULL, NULL, APR_HOOK_FIRST); return; } module AP_MODULE_DECLARE_DATA auth_mellon_module = { STANDARD20_MODULE_STUFF, auth_mellon_dir_config, auth_mellon_dir_merge, auth_mellon_server_config, NULL, auth_mellon_commands, register_hooks }; mod_auth_mellon-0.12.0/auth_mellon_cache.c0000644000175100017510000005735512522573372020143 0ustar olavmoolavmo/* * * 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" /* Calculate the pointer to a cache entry. * * Parameters: * am_mod_cfg_rec *mod_cfg The module configuration. * void *table The base pointer for the table. * apr_size_t index The index we are looking for. * * Returns: * The session entry with the given index. */ static inline am_cache_entry_t *am_cache_entry_ptr(am_mod_cfg_rec *mod_cfg, void *table, apr_size_t index) { uint8_t *table_calc; table_calc = table; return (am_cache_entry_t *)&table_calc[mod_cfg->init_entry_size * index]; } /* Initialize the session table. * * Parameters: * am_mod_cfg_rec *mod_cfg The module configuration. * * Returns: * Nothing. */ void am_cache_init(am_mod_cfg_rec *mod_cfg) { void *table; apr_size_t i; /* Initialize the session table. */ table = apr_shm_baseaddr_get(mod_cfg->cache); for (i = 0; i < mod_cfg->init_cache_size; i++) { am_cache_entry_t *e = am_cache_entry_ptr(mod_cfg, table, i); e->key[0] = '\0'; e->access = 0; } } /* 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 or user * am_cache_key_t type AM_CACHE_SESSION or AM_CACHE_NAMEID * * Returns: * The session entry on success or NULL on failure. */ am_cache_entry_t *am_cache_lock(server_rec *s, am_cache_key_t type, const char *key) { am_mod_cfg_rec *mod_cfg; void *table; apr_size_t i; int rv; char buffer[512]; /* Check if we have a valid session key. We abort if we don't. */ if (key == NULL) return NULL; switch (type) { case AM_CACHE_SESSION: if (strlen(key) != AM_ID_LENGTH) return NULL; break; case AM_CACHE_NAMEID: break; default: return NULL; break; } mod_cfg = am_get_mod_cfg(s); /* Lock the table. */ if((rv = apr_global_mutex_lock(mod_cfg->lock)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "apr_global_mutex_lock() failed [%d]: %s", rv, apr_strerror(rv, buffer, sizeof(buffer))); return NULL; } table = apr_shm_baseaddr_get(mod_cfg->cache); for(i = 0; i < mod_cfg->init_cache_size; i++) { am_cache_entry_t *e = am_cache_entry_ptr(mod_cfg, table, i); const char *tablekey; if (e->key[0] == '\0') { /* This entry is empty. Skip it. */ continue; } switch (type) { case AM_CACHE_SESSION: tablekey = e->key; break; case AM_CACHE_NAMEID: /* tablekey may be NULL */ tablekey = am_cache_env_fetch_first(e, "NAME_ID"); break; default: tablekey = NULL; break; } if (tablekey == NULL) continue; if(strcmp(tablekey, key) == 0) { /* We found the entry. */ if(e->expires > apr_time_now()) { /* And it hasn't expired. */ return e; } } } /* We didn't find a entry matching the key. Unlock the table and * return NULL; */ apr_global_mutex_unlock(mod_cfg->lock); 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 * 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; void *table; apr_time_t current_time; int i; apr_time_t age; int rv; char buffer[512]; /* Check if we have a valid session key. We abort if we don't. */ if(key == NULL || strlen(key) != AM_ID_LENGTH) { return NULL; } mod_cfg = am_get_mod_cfg(s); /* Lock the table. */ if((rv = apr_global_mutex_lock(mod_cfg->lock)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "apr_global_mutex_lock() failed [%d]: %s", rv, apr_strerror(rv, buffer, sizeof(buffer))); return NULL; } 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 = am_cache_entry_ptr(mod_cfg, 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++) { am_cache_entry_t *e = am_cache_entry_ptr(mod_cfg, table, i); if (e->key[0] == '\0') { /* This entry is free. Update 't' to this entry * and exit loop. */ t = e; break; } if (e->expires <= current_time) { /* This entry is expired, and is therefore free. * Update 't' and exit loop. */ t = e; break; } if (e->access < t->access) { /* This entry is older than 't' - update 't'. */ t = e; } } 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 = %" APR_TIME_T_FMT "s, 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; 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; } /* 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) { int status; /* Make sure that the name and value will fit inside the * fixed size buffer. */ 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. The maximum number is %d.", AM_CACHE_ENVSIZE); return HTTP_INTERNAL_SERVER_ERROR; } 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 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; } t->size++; return OK; } /* This function fetches a value from a session. * If multiple values are available, the first one is returned. * * Parameters: * am_cache_entry_t *t The current session. * const char *var The name of the value to be stored. * * Returns: * The first value, NULL if it does not exist. */ const char *am_cache_env_fetch_first(am_cache_entry_t *t, const char *var) { const char *str; int i; for (i = 0; i < t->size; i++) { str = am_cache_entry_get_string(t, &t->env[i].varname); if (str == NULL) break; if (strcmp(str, var) == 0) return am_cache_entry_get_string(t, &t->env[i].value); } return NULL; } /* 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; am_envattr_conf_t *env_varname_conf; const char *varname; const char *varname_prefix; const char *value; const char *prefixed_varname; 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 (am_cache_entry_slot_is_empty(&t->user)) { for(i = 0; i < t->size; ++i) { 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); } } } } /* 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 = am_cache_entry_get_string(t, &t->env[i].varname); varname_prefix = "MELLON_"; /* Check if we should map this name into another name. */ env_varname_conf = (am_envattr_conf_t *)apr_hash_get( d->envattr, varname, APR_HASH_KEY_STRING); if(env_varname_conf != NULL) { varname = env_varname_conf->name; if (!env_varname_conf->prefixed) { varname_prefix = ""; } } value = am_cache_entry_get_string(t, &t->env[i].value); /* * If we find a variable remapping to MellonUser, use it. */ 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); } } prefixed_varname = apr_pstrcat(r->pool, varname_prefix, varname, NULL); /* 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,prefixed_varname,value); } /* Check if merging of environment variables is disabled. * This is either if it is NULL (default value if not configured * by user) or an empty string (if specifically disabled by the user). */ if (d->merge_env_vars == NULL || *d->merge_env_vars == '\0') { /* 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, "%s_%d", prefixed_varname, (d->env_vars_index_start > -1 ? *count + d->env_vars_index_start : *count)), value); } else if (*count > 0) { /* * Merge multiple values, separating by default with ";" * this makes auth_mellon work same way mod_shib is: * https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPAttributeAccess */ apr_table_set(r->subprocess_env, prefixed_varname, apr_pstrcat(r->pool, apr_table_get(r->subprocess_env,prefixed_varname), d->merge_env_vars, value, NULL)); } /* Increase the count. */ ++(*count); if (d->env_vars_count_in_n > 0) { apr_table_set(r->subprocess_env, apr_pstrcat(r->pool, prefixed_varname, "_N", NULL), apr_itoa(r->pool, *count)); } } 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, 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. */ 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); } /* Populate with the session? */ if (d->dump_session) { char *session; const char *srcstr; int srclen, dstlen; 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, srcstr, srclen); apr_table_set(r->subprocess_env, "MELLON_SESSION", session); } 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); } } } /* 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, const char *lasso_saml_response) { 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; } 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; } 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; } /* 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) { return am_cache_entry_get_string(session, &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) { return am_cache_entry_get_string(session, &session->lasso_session); } mod_auth_mellon-0.12.0/auth_mellon_config.c0000644000175100017510000016755112667763074020357 0ustar olavmoolavmo/* * * auth_mellon_config.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 is the default endpoint path. Remember to update the description of * the MellonEndpointPath configuration directive if you change this. */ static const char *default_endpoint_path = "/mellon/"; /* This is the default name of the attribute we use as a username. Remember * to update the description of the MellonUser configuration directive if * you change this. */ static const char *default_user_attribute = "NAME_ID"; /* This is the default name of the cookie which mod_auth_mellon will set. * If you change this, then you should also update the description of the * MellonVar configuration directive. */ static const char *default_cookie_name = "cookie"; /* The default setting for cookie flags is to not enforce HttpOnly and secure */ static const int default_secure_cookie = 0; /* The default setting for setting MELLON_SESSION */ static const int default_dump_session = 0; /* The default setting for setting MELLON_SAML_RESPONSE */ static const int default_dump_saml_response = 0; /* This is the default IdP initiated login location * the MellonDefaultLoginPath configuration directive if you change this. */ static const char *default_login_path = "/"; /* saved POST session time to live * the MellonPostTTL configuration directive if you change this. */ static const apr_time_t post_ttl = 15 * 60; /* saved POST session maximum size * the MellonPostSize configuration directive if you change this. */ static const apr_size_t post_size = 1024 * 1024 * 1024; /* maximum saved POST sessions * the MellonPostCount configuration directive if you change this. */ static const int post_count = 100; /* whether to merge env. vars or not * the MellonMergeEnvVars configuration directive if you change this. */ static const char *default_merge_env_vars = NULL; /* for env. vars with multiple values, the index start * the MellonEnvVarsIndexStart configuration directive if you change this. */ static const int default_env_vars_index_start = -1; /* whether to also populate env. var _N with number of values * the MellonEnvVarsSetCount configuration directive if you change this. */ static const int default_env_vars_count_in_n = -1; /* The default list of trusted redirect domains. */ static const char * const default_redirect_domains[] = { "[self]", NULL }; /* This function handles configuration directives which set a * multivalued string slot in the module configuration (the destination * strucure is a hash). * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *key The string argument following this configuration * directive in the configuraion file. * const char *value Optional value to be stored in the hash. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_hash_string_slot(cmd_parms *cmd, void *struct_ptr, const char *key, const char *value) { server_rec *s = cmd->server; apr_pool_t *pconf = s->process->pconf; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; int offset; apr_hash_t **hash; /* * If no value is given, we just store the key in the hash. */ if (value == NULL || *value == '\0') value = key; offset = (int)(long)cmd->info; hash = (apr_hash_t **)((char *)cfg + offset); apr_hash_set(*hash, apr_pstrdup(pconf, key), APR_HASH_KEY_STRING, value); return NULL; } /* This function handles configuration directives which set a * multivalued string slot in the module configuration (the destination * strucure is a table). * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *key The string argument following this configuration * directive in the configuraion file. * const char *value Optional value to be stored in the hash. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_table_string_slot(cmd_parms *cmd, void *struct_ptr, const char *key, const char *value) { server_rec *s = cmd->server; apr_pool_t *pconf = s->process->pconf; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; int offset; apr_table_t **table; /* * If no value is given, we just store the key in the hash. */ if (value == NULL || *value == '\0') value = key; offset = (int)(long)cmd->info; table = (apr_table_t **)((char *)cfg + offset); apr_table_set(*table, apr_pstrdup(pconf, key), value); return NULL; } /* This function handles configuration directives which set a file * slot in the module configuration. If lasso is recent enough, it * attempts to read the file immediatly. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * This value isn't used by this function. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_filestring_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { const char *data; const char *path; path = ap_server_root_relative(cmd->pool, arg); if (!path) { return apr_pstrcat(cmd->pool, cmd->cmd->name, ": Invalid file path ", arg, NULL); } #ifdef HAVE_lasso_server_new_from_buffers data = am_getfile(cmd->pool, cmd->server, path); if (!data) { return apr_pstrcat(cmd->pool, cmd->cmd->name, ": Cannot read file ", path, NULL); } #else apr_finfo_t finfo; apr_status_t rv; char error[64]; rv = apr_stat(&finfo, path, APR_FINFO_SIZE, cmd->pool); if(rv != 0) { apr_strerror(rv, error, sizeof(error)); return apr_psprintf(cmd->pool, "%s - Cannot read file \"%s\" [%d] \"%s\"", cmd->cmd->name, path, rv, error); } data = path; #endif return ap_set_string_slot(cmd, struct_ptr, data); } /* This function handles configuration directives which use * a glob pattern, with a second optional argument * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *glob_pat glob(3) pattern * const char *option Optional argument * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_glob_fn12(cmd_parms *cmd, void *struct_ptr, const char *glob_pat, const char *option) { const char *(*take_argv)(cmd_parms *, void *, const char *, const char *); apr_array_header_t *files; const char *error; const char *directory; int i; take_argv = cmd->info; directory = am_filepath_dirname(cmd->pool, glob_pat); if (glob_pat == NULL || *glob_pat == '\0') return apr_psprintf(cmd->pool, "%s takes one or two arguments", cmd->cmd->name); if (apr_match_glob(glob_pat, &files, cmd->pool) != 0) return take_argv(cmd, struct_ptr, glob_pat, option); for (i = 0; i < files->nelts; i++) { const char *path; path = apr_pstrcat(cmd->pool, directory, "/", ((const char **)(files->elts))[i], NULL); error = take_argv(cmd, struct_ptr, path, option); if (error != NULL) return error; } return NULL; } /* This function handles configuration directives which set an * idp related slot in the module configuration. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *metadata Path to metadata file for one or multiple IdP * const char *chain Optional path to validating chain * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_idp_string_slot(cmd_parms *cmd, void *struct_ptr, const char *metadata, const char *chain) { server_rec *s = cmd->server; apr_pool_t *pconf = s->process->pconf; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; #ifndef HAVE_lasso_server_load_metadata if (chain != NULL) return apr_psprintf(cmd->pool, "Cannot specify validating chain " "for %s since lasso library lacks " "lasso_server_load_metadata()", cmd->cmd->name); #endif /* HAVE_lasso_server_load_metadata */ am_metadata_t *idp_metadata = apr_array_push(cfg->idp_metadata); idp_metadata->file = apr_pstrdup(pconf, metadata); idp_metadata->chain = apr_pstrdup(pconf, chain); return NULL; } /* This function handles configuration directives which set an * idp federation blacklist slot in the module configuration. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * int argc Number of blacklisted providerId. * char *const argv[] List of blacklisted providerId. * * Returns: * NULL on success, or errror string */ static const char *am_set_idp_ignore_slot(cmd_parms *cmd, void *struct_ptr, int argc, char *const argv[]) { #ifdef HAVE_lasso_server_load_metadata server_rec *s = cmd->server; apr_pool_t *pconf = s->process->pconf; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; GList *new_idp_ignore; int i; if (argc < 1) return apr_psprintf(cmd->pool, "%s takes at least one arguments", cmd->cmd->name); for (i = 0; i < argc; i++) { new_idp_ignore = apr_palloc(pconf, sizeof(GList)); new_idp_ignore->data = apr_pstrdup(pconf, argv[i]); /* Prepend it to the list. */ new_idp_ignore->next = cfg->idp_ignore; if (cfg->idp_ignore != NULL) cfg->idp_ignore->prev = new_idp_ignore; cfg->idp_ignore = new_idp_ignore; } return NULL; #else /* HAVE_lasso_server_load_metadata */ return apr_psprintf(cmd->pool, "Cannot use %s since lasso library lacks " "lasso_server_load_metadata()", cmd->cmd->name); #endif /* HAVE_lasso_server_load_metadata */ } /* This function handles configuration directives which set a file path * slot in the module configuration. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * This value isn't used by this function. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_module_config_file_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { return ap_set_file_slot(cmd, am_get_mod_cfg(cmd->server), arg); } /* This function handles configuration directives which set an int * slot in the module configuration. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * This value isn't used by this function. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_module_config_int_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { return ap_set_int_slot(cmd, am_get_mod_cfg(cmd->server), arg); } /* This function handles the MellonEnable configuration directive. * This directive can be set to "off", "info" or "auth". * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string if the argument is wrong. */ static const char *am_set_enable_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if(!strcasecmp(arg, "auth")) { d->enable_mellon = am_enable_auth; } else if(!strcasecmp(arg, "info")) { d->enable_mellon = am_enable_info; } else if(!strcasecmp(arg, "off")) { d->enable_mellon = am_enable_off; } else { return "parameter must be 'off', 'info' or 'auth'"; } return NULL; } /* This function handles the obsolete MellonDecoder configuration directive. * It is a no-op. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL */ static const char *am_set_decoder_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { return NULL; } /* This function handles the MellonEndpointPath configuration directive. * If the path doesn't end with a '/', then we will append one. * * Parameters: * cmd_parms *cmd The command structure for the MellonEndpointPath * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *arg The string argument containing the path of the * endpoint directory. * * Returns: * This function will always return NULL. */ static const char *am_set_endpoint_path(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; /* Make sure that the path ends with '/'. */ if(strlen(arg) == 0 || arg[strlen(arg) - 1] != '/') { d->endpoint_path = apr_pstrcat(cmd->pool, arg, "/", NULL); } else { d->endpoint_path = arg; } return NULL; } /* This function handles the MellonSetEnv configuration directive. * This directive allows the user to change the name of attributes. * * Parameters: * cmd_parms *cmd The command structure for the MellonSetEnv * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *newName The new name of the attribute. * const char *oldName The old name of the attribute. * * Returns: * This function will always return NULL. */ static const char *am_set_setenv_slot(cmd_parms *cmd, void *struct_ptr, const char *newName, const char *oldName) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; /* Configure as prefixed attribute name */ am_envattr_conf_t *envattr_conf = (am_envattr_conf_t *)apr_palloc(cmd->pool, sizeof(am_envattr_conf_t)); envattr_conf->name = newName; envattr_conf->prefixed = 1; apr_hash_set(d->envattr, oldName, APR_HASH_KEY_STRING, envattr_conf); return NULL; } /* This function handles the MellonSetEnvNoPrefix configuration directive. * This directive allows the user to change the name of attributes without prefixing them with MELLON_. * * Parameters: * cmd_parms *cmd The command structure for the MellonSetEnv * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *newName The new name of the attribute. * const char *oldName The old name of the attribute. * * Returns: * This function will always return NULL. */ static const char *am_set_setenv_no_prefix_slot(cmd_parms *cmd, void *struct_ptr, const char *newName, const char *oldName) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; /* Configure as not prefixed attribute name */ am_envattr_conf_t *envattr_conf = (am_envattr_conf_t *)apr_palloc(cmd->pool, sizeof(am_envattr_conf_t)); envattr_conf->name = newName; envattr_conf->prefixed = 0; apr_hash_set(d->envattr, oldName, APR_HASH_KEY_STRING, envattr_conf); return NULL; } /* This function decodes MellonCond flags, such as [NOT,REG] * * Parameters: * const char *arg Pointer to the flags string * * Returns: * flags, or -1 on error */ static int am_cond_flags(const char *arg) { int flags = AM_COND_FLAG_NULL; static const char const *options[] = { "OR", /* AM_EXPIRE_FLAG_OR */ "NOT", /* AM_EXPIRE_FLAG_NOT */ "REG", /* AM_EXPIRE_FLAG_REG */ "NC", /* AM_EXPIRE_FLAG_NC */ "MAP", /* AM_EXPIRE_FLAG_MAP */ "REF", /* AM_EXPIRE_FLAG_REF */ "SUB", /* AM_EXPIRE_FLAG_SUB */ /* The other options (IGN, REQ, FSTR, ...) are only internally used */ }; apr_size_t options_count = sizeof(options) / sizeof(*options); /* Skip inital [ */ if (arg[0] == '[') arg++; else return -1; do { apr_size_t i; for (i = 0; i < options_count; i++) { apr_size_t optlen = strlen(options[i]); if (strncmp(arg, options[i], optlen) == 0) { /* Make sure we have a separator next */ if (arg[optlen] && !strchr("]\t ,", (int)arg[optlen])) return -1; flags |= (1 << i); arg += optlen; break; } /* no match */ if (i == options_count) return -1; /* skip spaces, tabs and commas */ arg += strspn(arg, " \t,"); /* * End of option, but we fire an error if * there is trailing garbage */ if (*arg == ']') { arg++; return (*arg == '\0') ? flags : -1; } } } while (*arg); /* Missing trailing ] */ return -1; } /* This function handles the MellonCond configuration directive, which * allows the user to restrict access based on attributes received from * the IdP. * * Parameters: * cmd_parms *cmd The command structure for the MellonCond * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *attribute Pointer to the attribute name * const char *value Pointer to the attribute value or regex * const char *options Pointer to options * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_cond_slot(cmd_parms *cmd, void *struct_ptr, const char *attribute, const char *value, const char *options) { am_dir_cfg_rec *d = struct_ptr; int flags = AM_COND_FLAG_NULL; am_cond_t *element; if (attribute == NULL || *attribute == '\0' || value == NULL || *value == '\0') return apr_pstrcat(cmd->pool, cmd->cmd->name, " takes at least two arguments", NULL); if (options != NULL && *options != '\0') flags = am_cond_flags(options); if (flags == -1) return apr_psprintf(cmd->pool, "%s - invalid flags %s", cmd->cmd->name, options); element = (am_cond_t *)apr_array_push(d->cond); element->varname = attribute; element->flags = flags; element->str = NULL; element->regex = NULL; element->directive = apr_pstrcat(cmd->pool, cmd->directive->directive, " ", cmd->directive->args, NULL); if (element->flags & AM_COND_FLAG_REG) { int regex_flags = AP_REG_EXTENDED|AP_REG_NOSUB; if (element->flags & AM_COND_FLAG_NC) regex_flags |= AP_REG_ICASE; element->regex = ap_pregcomp(cmd->pool, value, regex_flags); if (element->regex == NULL) return apr_psprintf(cmd->pool, "%s - invalid regex %s", cmd->cmd->name, value); } /* * Flag values containing format strings to that we do * not have to process the others at runtime. */ if (strchr(value, '%') != NULL) element->flags |= AM_COND_FLAG_FSTR; /* * We keep the string also for regex, so that we can * print it for debug purpose and perform substitutions on it. */ element->str = value; return NULL; } /* This function handles the MellonRequire configuration directive, which * allows the user to restrict access based on attributes received from * the IdP. * * Parameters: * cmd_parms *cmd The command structure for the MellonRequire * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg Pointer to the configuration string. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_require_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = struct_ptr; char *attribute, *value; int i; am_cond_t *element; am_cond_t *first_element; attribute = ap_getword_conf(cmd->pool, &arg); value = ap_getword_conf(cmd->pool, &arg); if (*attribute == '\0' || *value == '\0') { return apr_pstrcat(cmd->pool, cmd->cmd->name, " takes at least two arguments", NULL); } /* * MellonRequire overwrites previous conditions on this attribute * We just tag the am_cond_t with the ignore flag, as it is * easier (and probably faster) than to really remove it. */ for (i = 0; i < d->cond->nelts; i++) { am_cond_t *ce = &((am_cond_t *)(d->cond->elts))[i]; if ((strcmp(ce->varname, attribute) == 0) && (ce->flags & AM_COND_FLAG_REQ)) ce->flags |= AM_COND_FLAG_IGN; } first_element = NULL; do { element = (am_cond_t *)apr_array_push(d->cond); element->varname = attribute; element->flags = AM_COND_FLAG_OR|AM_COND_FLAG_REQ; element->str = value; element->regex = NULL; /* * When multiple values are given, we track the first one * in order to retreive the directive */ if (first_element == NULL) { element->directive = apr_pstrcat(cmd->pool, cmd->directive->directive, " ", cmd->directive->args, NULL); first_element = element; } else { element->directive = first_element->directive; } } while (*(value = ap_getword_conf(cmd->pool, &arg)) != '\0'); /* * Remove OR flag on last element */ element->flags &= ~AM_COND_FLAG_OR; return NULL; } /* This function handles the MellonOrganization* directives, which * which specify language-qualified strings * * Parameters: * cmd_parms *cmd The command structure for the MellonOrganization* * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *lang Pointer to the language string (optional) * const char *value Pointer to the data * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_langstring_slot(cmd_parms *cmd, void *struct_ptr, const char *lang, const char *value) { apr_hash_t *h = *(apr_hash_t **)(struct_ptr + (apr_size_t)cmd->info); if (value == NULL || *value == '\0') { value = lang; lang = ""; } apr_hash_set(h, lang, APR_HASH_KEY_STRING, apr_pstrdup(cmd->server->process->pconf, value)); return NULL; } /* This function handles the MellonAuthnContextClassRef directive. * * Parameters: * cmd_parms *cmd The command structure for the MellonAuthnContextClassRef * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *arg An URI for an SAMLv2 AuthnContextClassRef * * Returns: * This function will always return NULL. */ static const char *am_set_authn_context_class_ref(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; apr_pool_t *p= cmd->pool; char **context_class_ref_p; if(strlen(arg) == 0) { return NULL; } context_class_ref_p = apr_array_push(d->authn_context_class_ref); *context_class_ref_p = apr_pstrdup(p, arg); return NULL; } /* This function handles the MellonDoNotVerifyLogoutSignature configuration directive, * it is identical to the am_set_hash_string_slot function. You can refer to it. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *key The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_do_not_verify_logout_signature(cmd_parms *cmd, void *struct_ptr, const char *key) { #ifdef HAVE_lasso_profile_set_signature_verify_hint return am_set_hash_string_slot(cmd, struct_ptr, key, NULL); #else return apr_pstrcat(cmd->pool, cmd->cmd->name, " is not usable as modmellon was compiled against " "a version of the lasso library which miss the " "function lasso_profile_set_signature_verify_hint.", NULL); #endif } /* This function handles the MellonMergeEnvVars configuration directive, * it sets merge_env_vars to nonempty separator (default semicolon), * or empty string to denote no merging. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *flag On/Off flag * const char *sep Optional separator, should be only present with On * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_merge_env_vars(cmd_parms *cmd, void *struct_ptr, const char *flag, const char *sep) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; apr_pool_t *p= cmd->pool; if (strcasecmp(flag, "on") == 0) { if (sep && *sep) { /* * TAKE12 will not give us the second argument if it is * empty string so we cannot complain about it, we will just * silently use semicolon */ d->merge_env_vars = apr_pstrdup(p, sep); } else { d->merge_env_vars = ";"; } } else if (strcasecmp(flag, "off") == 0) { if (sep) { return apr_pstrcat(cmd->pool, cmd->cmd->name, " separator should not be used with Off", NULL); } d->merge_env_vars = ""; } else { return apr_pstrcat(cmd->pool, cmd->cmd->name, " first parameer must be On or Off", NULL); } return NULL; } /* Handle MellonRedirectDomains option. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * int argc Number of redirect domains. * char *const argv[] List of redirect domains. * * Returns: * NULL on success, or errror string on failure. */ static const char *am_set_redirect_domains(cmd_parms *cmd, void *struct_ptr, int argc, char *const argv[]) { am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; const char **redirect_domains; int i; if (argc < 1) return apr_psprintf(cmd->pool, "%s takes at least one arguments", cmd->cmd->name); redirect_domains = apr_palloc(cmd->pool, sizeof(const char *) * (argc + 1)); for (i = 0; i < argc; i++) { redirect_domains[i] = argv[i]; } redirect_domains[argc] = NULL; cfg->redirect_domains = redirect_domains; return NULL; } /* This array contains all the configuration directive which are handled * by auth_mellon. */ const command_rec auth_mellon_commands[] = { /* Global configuration directives. */ AP_INIT_TAKE1( "MellonCacheSize", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, cache_size), RSRC_CONF, "The number of sessions we can keep track of at once. You must" " restart the server before any changes to this directive will" " 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), RSRC_CONF, "The lock file for session synchronization." " Default value is \"/tmp/mellonLock\"." ), AP_INIT_TAKE1( "MellonPostDirectory", am_set_module_config_file_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, post_dir), RSRC_CONF, "The directory for saving POST requests." " Default value is \"/var/tmp/mellonpost\"." ), AP_INIT_TAKE1( "MellonPostTTL", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, post_ttl), RSRC_CONF, "The time to live for saved POST requests in seconds." " Default value is 15 mn." ), AP_INIT_TAKE1( "MellonPostCount", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, post_count), RSRC_CONF, "The maximum saved POST sessions at once." " Default value is 100." ), AP_INIT_TAKE1( "MellonPostSize", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, post_size), RSRC_CONF, "The maximum size of a saved POST, in bytes." " Default value is 1 MB." ), /* Per-location configuration directives. */ AP_INIT_TAKE1( "MellonEnable", am_set_enable_slot, NULL, OR_AUTHCFG, "Enable auth_mellon on a location. This can be set to 'off', 'info'" " and 'auth'. 'off' disables auth_mellon for a location, 'info'" " will only populate the environment with attributes if the user" " has logged in already. 'auth' will redirect the user to the IdP" " if he hasn't logged in yet, but otherwise behaves like 'info'." ), AP_INIT_TAKE1( "MellonDecoder", am_set_decoder_slot, NULL, OR_AUTHCFG, "Obsolete option, now a no-op for backwards compatibility." ), AP_INIT_TAKE1( "MellonVariable", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, varname), OR_AUTHCFG, "The name of the cookie which auth_mellon will set. Defaults to" " 'cookie'. This string is appended to 'mellon-' to create the" " cookie name, and the default name of the cookie will therefore" " be 'mellon-cookie'." ), AP_INIT_FLAG( "MellonSecureCookie", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, secure), OR_AUTHCFG, "Whether the cookie set by auth_mellon should have HttpOnly and" " secure flags set. Default is off." ), AP_INIT_TAKE1( "MellonCookieDomain", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, cookie_domain), OR_AUTHCFG, "The domain of the cookie which auth_mellon will set. Defaults to" " the domain of the current request." ), AP_INIT_TAKE1( "MellonCookiePath", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, cookie_path), OR_AUTHCFG, "The path of the cookie which auth_mellon will set. Defaults to" " '/'." ), AP_INIT_TAKE1( "MellonUser", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, userattr), OR_AUTHCFG, "Attribute to set as r->user. Defaults to NAME_ID, which is the" " attribute we set to the identifier we receive from the IdP." ), AP_INIT_TAKE1( "MellonIdP", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, idpattr), OR_AUTHCFG, "Attribute we set to the IdP ProviderId." ), AP_INIT_TAKE2( "MellonSetEnv", am_set_setenv_slot, NULL, OR_AUTHCFG, "Renames attributes received from the server while retaining prefix MELLON_. The format is" " MellonSetEnv ." ), AP_INIT_TAKE2( "MellonSetEnvNoPrefix", am_set_setenv_no_prefix_slot, NULL, OR_AUTHCFG, "Renames attributes received from the server without adding prefix. The format is" " MellonSetEnvNoPrefix ." ), AP_INIT_FLAG( "MellonSessionDump", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, dump_session), OR_AUTHCFG, "Dump session in environment. Default is off" ), AP_INIT_FLAG( "MellonSamlResponseDump", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, dump_saml_response), OR_AUTHCFG, "Dump SAML authentication response in environment. Default is off" ), AP_INIT_RAW_ARGS( "MellonRequire", am_set_require_slot, NULL, OR_AUTHCFG, "Attribute requirements for authorization. Allows you to restrict" " access based on attributes received from the IdP. If you list" " several MellonRequire configuration directives, then all of them" " must match. Every MellonRequire can list several allowed values" " for the attribute. The syntax is:" " MellonRequire [value2....]." ), AP_INIT_TAKE23( "MellonCond", am_set_cond_slot, NULL, OR_AUTHCFG, "Attribute requirements for authorization. Allows you to restrict" " access based on attributes received from the IdP. The syntax is:" " MellonRequire []." ), AP_INIT_TAKE1( "MellonSessionLength", ap_set_int_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, session_length), OR_AUTHCFG, "Maximum number of seconds a session will be valid for. Defaults" " to 86400 seconds (1 day)." ), AP_INIT_TAKE1( "MellonNoCookieErrorPage", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, no_cookie_error_page), OR_AUTHCFG, "Web page to display if the user has disabled cookies. We will" " return a 400 Bad Request error if this is unset and the user" " ha disabled cookies." ), AP_INIT_TAKE1( "MellonNoSuccessErrorPage", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, no_success_error_page), OR_AUTHCFG, "Web page to display if the idp posts with a failed" " authentication error. We will return a 401 Unauthorized error" " if this is unset and the idp posts such assertion." ), AP_INIT_TAKE1( "MellonSPMetadataFile", am_set_filestring_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_metadata_file), OR_AUTHCFG, "Full path to xml file with metadata for the SP." ), AP_INIT_TAKE1( "MellonSPPrivateKeyFile", am_set_filestring_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_private_key_file), OR_AUTHCFG, "Full path to pem file with the private key for the SP." ), AP_INIT_TAKE1( "MellonSPCertFile", am_set_filestring_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_cert_file), OR_AUTHCFG, "Full path to pem file with certificate for the SP." ), AP_INIT_TAKE12( "MellonIdPMetadataFile", am_set_idp_string_slot, NULL, OR_AUTHCFG, "Full path to xml metadata file for IdP, " "with optional validating chain." ), AP_INIT_TAKE12( "MellonIdPMetadataGlob", am_set_glob_fn12, am_set_idp_string_slot, OR_AUTHCFG, "Full path to xml metadata files for IdP, with glob(3) patterns. " "An optional validating chain can be supplied." ), AP_INIT_TAKE1( "MellonIdPPublicKeyFile", ap_set_file_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, idp_public_key_file), OR_AUTHCFG, "Full path to pem file with the public key for the IdP." ), AP_INIT_TAKE1( "MellonIdPCAFile", ap_set_file_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, idp_ca_file), OR_AUTHCFG, "Full path to pem file with CA chain for the IdP." ), AP_INIT_TAKE_ARGV( "MellonIdPIgnore", am_set_idp_ignore_slot, NULL, OR_AUTHCFG, "List of IdP entityId to ignore." ), AP_INIT_TAKE1( "MellonSPentityId", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_entity_id), OR_AUTHCFG, "SP entity Id to be used for metadata auto generation." ), AP_INIT_TAKE12( "MellonOrganizationName", am_set_langstring_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_org_name), OR_AUTHCFG, "Language-qualified oranization name." ), AP_INIT_TAKE12( "MellonOrganizationDisplayName", am_set_langstring_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_org_display_name), OR_AUTHCFG, "Language-qualified oranization name, human redable." ), AP_INIT_TAKE12( "MellonOrganizationURL", am_set_langstring_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_org_url), OR_AUTHCFG, "Language-qualified oranization URL." ), AP_INIT_TAKE1( "MellonDefaultLoginPath", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, login_path), OR_AUTHCFG, "The location where to redirect after IdP initiated login." " Default value is \"/\"." ), AP_INIT_TAKE1( "MellonDiscoveryURL", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, discovery_url), OR_AUTHCFG, "The URL of IdP discovery service. Default is unset." ), AP_INIT_TAKE1( "MellonProbeDiscoveryTimeout", ap_set_int_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, probe_discovery_timeout), OR_AUTHCFG, "The timeout of IdP probe discovery service. " "Default is 1s" ), AP_INIT_TAKE12( "MellonProbeDiscoveryIdP", am_set_table_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, probe_discovery_idp), OR_AUTHCFG, "An IdP that can be used for IdP probe discovery." ), AP_INIT_TAKE1( "MellonEndpointPath", am_set_endpoint_path, NULL, OR_AUTHCFG, "The root directory of the SAML2 endpoints, relative to the root" " of the web server. Default value is \"/mellon/\", which will" " make mod_mellon to the handler for every request to" " \"http:///mellon/*\". The path you specify must" " be contained within the current Location directive." ), AP_INIT_TAKE1( "MellonAuthnContextClassRef", am_set_authn_context_class_ref, NULL, OR_AUTHCFG, "A list of AuthnContextClassRef to request in the AuthnRequest and " "to validate upon reception of an Assertion" ), AP_INIT_FLAG( "MellonSubjectConfirmationDataAddressCheck", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, subject_confirmation_data_address_check), OR_AUTHCFG, "Check address given in SubjectConfirmationData Address attribute. Default is on." ), AP_INIT_TAKE1( "MellonDoNotVerifyLogoutSignature", am_set_do_not_verify_logout_signature, (void *)APR_OFFSETOF(am_dir_cfg_rec, do_not_verify_logout_signature), OR_AUTHCFG, "A list of entity of IdP whose logout requests signatures will not " "be valided" ), AP_INIT_FLAG( "MellonPostReplay", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, post_replay), OR_AUTHCFG, "Whether we should replay POST requests that trigger authentication. Default is off." ), AP_INIT_TAKE12( "MellonMergeEnvVars", am_set_merge_env_vars, NULL, OR_AUTHCFG, "Whether to merge environment variables multi-values or not. Default is off." "When first parameter is on, optional second parameter is the separator, " "defaulting to semicolon." ), AP_INIT_TAKE1( "MellonEnvVarsIndexStart", ap_set_int_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, env_vars_index_start), OR_AUTHCFG, "Start indexing environment variables for multivalues with 0 or 1. Default is 0." ), AP_INIT_FLAG( "MellonEnvVarsSetCount", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, env_vars_count_in_n), OR_AUTHCFG, "Whether to also populate environment variable suffixed _N with number of values. Default is off." ), AP_INIT_FLAG( "MellonECPSendIDPList", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, ecp_send_idplist), OR_AUTHCFG, "Whether to send an ECP client a list of IdP's. Default is off." ), AP_INIT_TAKE_ARGV( "MellonRedirectDomains", am_set_redirect_domains, NULL, OR_AUTHCFG, "List of domains we can redirect to." ), {NULL} }; const am_error_map_t auth_mellon_errormap[] = { { LASSO_PROFILE_ERROR_STATUS_NOT_SUCCESS, HTTP_UNAUTHORIZED }, #ifdef LASSO_PROFILE_ERROR_REQUEST_DENIED { LASSO_PROFILE_ERROR_REQUEST_DENIED, HTTP_UNAUTHORIZED }, #endif { 0, 0 } }; /* Release a lasso_server object associated with this configuration. * * Parameters: * void *data The pointer to the configuration data. * * Returns: * Always APR_SUCCESS. */ static apr_status_t auth_mellon_free_server(void *data) { am_dir_cfg_rec *dir = data; if (dir->server != NULL) { lasso_server_destroy(dir->server); dir->server = NULL; } return APR_SUCCESS; } /* This function creates and initializes a directory configuration * object for auth_mellon. * * Parameters: * apr_pool_t *p The pool we should allocate memory from. * char *d Unused, always NULL. * * Returns: * The new directory configuration object. */ void *auth_mellon_dir_config(apr_pool_t *p, char *d) { am_dir_cfg_rec *dir = apr_palloc(p, sizeof(*dir)); apr_pool_cleanup_register(p, dir, auth_mellon_free_server, auth_mellon_free_server); dir->enable_mellon = am_enable_default; dir->varname = default_cookie_name; dir->secure = default_secure_cookie; dir->merge_env_vars = default_merge_env_vars; dir->env_vars_index_start = default_env_vars_index_start; dir->env_vars_count_in_n = default_env_vars_count_in_n; dir->cond = apr_array_make(p, 0, sizeof(am_cond_t)); dir->cookie_domain = NULL; dir->cookie_path = NULL; dir->envattr = apr_hash_make(p); dir->userattr = default_user_attribute; dir->idpattr = NULL; dir->dump_session = default_dump_session; dir->dump_saml_response = default_dump_saml_response; dir->endpoint_path = default_endpoint_path; dir->session_length = -1; /* -1 means use default. */ dir->no_cookie_error_page = NULL; dir->no_success_error_page = NULL; dir->sp_metadata_file = NULL; dir->sp_private_key_file = NULL; dir->sp_cert_file = NULL; dir->idp_metadata = apr_array_make(p, 0, sizeof(am_metadata_t)); dir->idp_public_key_file = NULL; dir->idp_ca_file = NULL; dir->idp_ignore = NULL; dir->login_path = default_login_path; dir->discovery_url = NULL; dir->probe_discovery_timeout = -1; /* -1 means no probe discovery */ dir->probe_discovery_idp = apr_table_make(p, 0); dir->sp_entity_id = NULL; dir->sp_org_name = apr_hash_make(p); dir->sp_org_display_name = apr_hash_make(p); dir->sp_org_url = apr_hash_make(p); apr_thread_mutex_create(&dir->server_mutex, APR_THREAD_MUTEX_DEFAULT, p); dir->inherit_server_from = dir; dir->server = NULL; dir->authn_context_class_ref = apr_array_make(p, 0, sizeof(char *)); dir->subject_confirmation_data_address_check = inherit_subject_confirmation_data_address_check; dir->do_not_verify_logout_signature = apr_hash_make(p); dir->post_replay = inherit_post_replay; dir->redirect_domains = default_redirect_domains; dir->ecp_send_idplist = inherit_ecp_send_idplist; return dir; } /* Determine whether this configuration changes anything relevant to the * lasso_server configuration. * * Parameters: * am_dir_cfg_rec *add_cfg The new configuration. * * Returns: * true if we can inherit the lasso_server object, false if not. */ static bool cfg_can_inherit_lasso_server(const am_dir_cfg_rec *add_cfg) { if (add_cfg->endpoint_path != default_endpoint_path) return false; if (add_cfg->sp_metadata_file != NULL || add_cfg->sp_private_key_file != NULL || add_cfg->sp_cert_file != NULL) return false; if (add_cfg->idp_metadata->nelts > 0 || add_cfg->idp_public_key_file != NULL || add_cfg->idp_ca_file != NULL || add_cfg->idp_ignore != NULL) return false; if (apr_hash_count(add_cfg->sp_org_name) > 0 || apr_hash_count(add_cfg->sp_org_display_name) > 0 || apr_hash_count(add_cfg->sp_org_url) > 0) return false; return true; } /* This function merges two am_dir_cfg_rec structures. * It will try to inherit from the base where possible. * * Parameters: * apr_pool_t *p The pool we should allocate memory from. * void *base The original structure. * void *add The structure we should add to base. * * Returns: * The merged structure. */ void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add) { am_dir_cfg_rec *base_cfg = (am_dir_cfg_rec *)base; am_dir_cfg_rec *add_cfg = (am_dir_cfg_rec *)add; am_dir_cfg_rec *new_cfg; new_cfg = (am_dir_cfg_rec *)apr_palloc(p, sizeof(*new_cfg)); apr_pool_cleanup_register(p, new_cfg, auth_mellon_free_server, auth_mellon_free_server); new_cfg->enable_mellon = (add_cfg->enable_mellon != am_enable_default ? add_cfg->enable_mellon : base_cfg->enable_mellon); new_cfg->varname = (add_cfg->varname != default_cookie_name ? add_cfg->varname : base_cfg->varname); new_cfg->secure = (add_cfg->secure != default_secure_cookie ? add_cfg->secure : base_cfg->secure); new_cfg->merge_env_vars = (add_cfg->merge_env_vars != default_merge_env_vars ? add_cfg->merge_env_vars : base_cfg->merge_env_vars); new_cfg->env_vars_index_start = (add_cfg->env_vars_index_start != default_env_vars_index_start ? add_cfg->env_vars_index_start : base_cfg->env_vars_index_start); new_cfg->env_vars_count_in_n = (add_cfg->env_vars_count_in_n != default_env_vars_count_in_n ? add_cfg->env_vars_count_in_n : base_cfg->env_vars_count_in_n); new_cfg->cookie_domain = (add_cfg->cookie_domain != NULL ? add_cfg->cookie_domain : base_cfg->cookie_domain); new_cfg->cookie_path = (add_cfg->cookie_path != NULL ? add_cfg->cookie_path : base_cfg->cookie_path); new_cfg->cond = apr_array_copy(p, (!apr_is_empty_array(add_cfg->cond)) ? add_cfg->cond : base_cfg->cond); new_cfg->envattr = apr_hash_copy(p, (apr_hash_count(add_cfg->envattr) > 0) ? add_cfg->envattr : base_cfg->envattr); new_cfg->userattr = (add_cfg->userattr != default_user_attribute ? add_cfg->userattr : base_cfg->userattr); new_cfg->idpattr = (add_cfg->idpattr != NULL ? add_cfg->idpattr : base_cfg->idpattr); new_cfg->dump_session = (add_cfg->dump_session != default_dump_session ? add_cfg->dump_session : base_cfg->dump_session); new_cfg->dump_saml_response = (add_cfg->dump_saml_response != default_dump_saml_response ? add_cfg->dump_saml_response : base_cfg->dump_saml_response); new_cfg->endpoint_path = ( add_cfg->endpoint_path != default_endpoint_path ? add_cfg->endpoint_path : base_cfg->endpoint_path ); new_cfg->session_length = (add_cfg->session_length != -1 ? add_cfg->session_length : base_cfg->session_length); new_cfg->no_cookie_error_page = (add_cfg->no_cookie_error_page != NULL ? add_cfg->no_cookie_error_page : base_cfg->no_cookie_error_page); new_cfg->no_success_error_page = (add_cfg->no_success_error_page != NULL ? add_cfg->no_success_error_page : base_cfg->no_success_error_page); new_cfg->sp_metadata_file = (add_cfg->sp_metadata_file ? add_cfg->sp_metadata_file : base_cfg->sp_metadata_file); new_cfg->sp_private_key_file = (add_cfg->sp_private_key_file ? add_cfg->sp_private_key_file : base_cfg->sp_private_key_file); new_cfg->sp_cert_file = (add_cfg->sp_cert_file ? add_cfg->sp_cert_file : base_cfg->sp_cert_file); new_cfg->idp_metadata = (add_cfg->idp_metadata->nelts ? add_cfg->idp_metadata : base_cfg->idp_metadata); new_cfg->idp_public_key_file = (add_cfg->idp_public_key_file ? add_cfg->idp_public_key_file : base_cfg->idp_public_key_file); new_cfg->idp_ca_file = (add_cfg->idp_ca_file ? add_cfg->idp_ca_file : base_cfg->idp_ca_file); new_cfg->idp_ignore = add_cfg->idp_ignore != NULL ? add_cfg->idp_ignore : base_cfg->idp_ignore; new_cfg->sp_entity_id = (add_cfg->sp_entity_id ? add_cfg->sp_entity_id : base_cfg->sp_entity_id); new_cfg->sp_org_name = apr_hash_copy(p, (apr_hash_count(add_cfg->sp_org_name) > 0) ? add_cfg->sp_org_name : base_cfg->sp_org_name); new_cfg->sp_org_display_name = apr_hash_copy(p, (apr_hash_count(add_cfg->sp_org_display_name) > 0) ? add_cfg->sp_org_display_name : base_cfg->sp_org_display_name); new_cfg->sp_org_url = apr_hash_copy(p, (apr_hash_count(add_cfg->sp_org_url) > 0) ? add_cfg->sp_org_url : base_cfg->sp_org_url); new_cfg->login_path = (add_cfg->login_path != default_login_path ? add_cfg->login_path : base_cfg->login_path); new_cfg->discovery_url = (add_cfg->discovery_url ? add_cfg->discovery_url : base_cfg->discovery_url); new_cfg->probe_discovery_timeout = (add_cfg->probe_discovery_timeout != -1 ? add_cfg->probe_discovery_timeout : base_cfg->probe_discovery_timeout); new_cfg->probe_discovery_idp = apr_table_copy(p, (!apr_is_empty_table(add_cfg->probe_discovery_idp)) ? add_cfg->probe_discovery_idp : base_cfg->probe_discovery_idp); if (cfg_can_inherit_lasso_server(add_cfg)) { new_cfg->inherit_server_from = base_cfg->inherit_server_from; } else { apr_thread_mutex_create(&new_cfg->server_mutex, APR_THREAD_MUTEX_DEFAULT, p); new_cfg->inherit_server_from = new_cfg; } new_cfg->server = NULL; new_cfg->authn_context_class_ref = (add_cfg->authn_context_class_ref->nelts ? add_cfg->authn_context_class_ref : base_cfg->authn_context_class_ref); new_cfg->do_not_verify_logout_signature = apr_hash_copy(p, (apr_hash_count(add_cfg->do_not_verify_logout_signature) > 0) ? add_cfg->do_not_verify_logout_signature : base_cfg->do_not_verify_logout_signature); new_cfg->subject_confirmation_data_address_check = CFG_MERGE(add_cfg, base_cfg, subject_confirmation_data_address_check); new_cfg->post_replay = CFG_MERGE(add_cfg, base_cfg, post_replay); new_cfg->ecp_send_idplist = CFG_MERGE(add_cfg, base_cfg, ecp_send_idplist); new_cfg->redirect_domains = (add_cfg->redirect_domains != default_redirect_domains ? add_cfg->redirect_domains : base_cfg->redirect_domains); return new_cfg; } /* This function creates a new per-server configuration. * auth_mellon uses the server configuration to store a pointer * to the global module configuration. * * Parameters: * apr_pool_t *p The pool we should allocate memory from. * server_rec *s The server we should add our configuration to. * * Returns: * The new per-server configuration. */ void *auth_mellon_server_config(apr_pool_t *p, server_rec *s) { am_srv_cfg_rec *srv; am_mod_cfg_rec *mod; const char key[] = "auth_mellon_server_config"; srv = apr_palloc(p, sizeof(*srv)); /* we want to keeep our global configuration of shared memory and * mutexes, so we try to find it in the userdata before doing anything * else */ apr_pool_userdata_get((void **)&mod, key, p); if (mod) { srv->mc = mod; return srv; } /* the module has not been initiated at all */ mod = apr_palloc(p, sizeof(*mod)); mod->cache_size = 100; /* ought to be enough for everybody */ mod->lock_file = "/var/run/mod_auth_mellon.lock"; mod->post_dir = NULL; mod->post_ttl = post_ttl; 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; apr_pool_userdata_set(mod, key, apr_pool_cleanup_null, p); srv->mc = mod; return srv; } mod_auth_mellon-0.12.0/auth_mellon_cookie.c0000644000175100017510000001506512576273035020343 0ustar olavmoolavmo/* * * auth_mellon_cookie.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 retrieves the name of our cookie. * * Parameters: * request_rec *r The current request. Used to find the identifier of * the cookie. We also allocate memory from r->pool. * * Returns: * The name of the cookie. */ static const char *am_cookie_name(request_rec *r) { am_dir_cfg_rec *dir_cfg; dir_cfg = am_get_dir_cfg(r); return apr_pstrcat(r->pool, "mellon-", dir_cfg->varname, NULL); } /* Calculate the cookie parameters. * * Parameters: * request_rec *r The request we should set the cookie in. * * Returns: * The cookie parameters as a string. */ static const char *am_cookie_params(request_rec *r) { int secure_cookie; const char *cookie_domain = ap_get_server_name(r); const char *cookie_path = "/"; am_dir_cfg_rec *cfg = am_get_dir_cfg(r); if (cfg->cookie_domain) { cookie_domain = cfg->cookie_domain; } if (cfg->cookie_path) { cookie_path = cfg->cookie_path; } secure_cookie = cfg->secure; return apr_psprintf(r->pool, "Version=1; Path=%s; Domain=%s%s;", cookie_path, cookie_domain, secure_cookie ? "; HttpOnly; secure" : ""); } /* This functions finds the value of our cookie. * * Parameters: * request_rec *r The request we should find the cookie in. * * Returns: * The value of the cookie, or NULL if we don't find the cookie. */ const char *am_cookie_get(request_rec *r) { am_req_cfg_rec *req_cfg; const char *name; const char *value; const char *cookie; char *buffer, *end; /* don't run for subrequests */ if (r->main) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "cookie_get: Subrequest, so return NULL"); return NULL; } /* Check if we have added a note on the current request. */ req_cfg = am_get_req_cfg(r); value = req_cfg->cookie_value; if(value != NULL) { return value; } name = am_cookie_name(r); cookie = apr_table_get(r->headers_in, "Cookie"); if(cookie == NULL) { return NULL; } for(value = ap_strstr_c(cookie, name); value != NULL; value = ap_strstr_c(value + 1, name)) { if(value != cookie) { /* value isn't pointing to the start of the string. */ switch(value[-1]) { /* We allow the name in the cookie-string to be * preceeded by [\t; ]. Note that only ' ' should be used * by browsers. We test against the others just to be sure. */ case '\t': case ';': case ' ': break; default: /* value isn't preceeded by one of the listed characters, and * therefore we assume that it is part of another cookie. */ continue; /* Search for the next instance of the name. */ } } if(value[strlen(name)] != '=') { /* We don't have an equal-sign right after the name. Therefore we * assume that what we have matched is only part of a longer name. * We continue searching. */ continue; } /* Now we have something that matches /[^ ,\t]=/. The value * (following the equal-sign) can be found at value + strlen(name) + 1. */ value += strlen(name) + 1; /* The cookie value may be double-quoted. */ if(*value == '"') { value += 1; } buffer = apr_pstrdup(r->pool, value); end = strchr(buffer, '"'); if(end) { /* Double-quoted string. */ *end = '\0'; } end = strchr(buffer, ';'); if(end) { *end = '\0'; } return buffer; } /* We didn't find the cookie. */ return NULL; } /* This function sets the value of our cookie. * * Parameters: * request_rec *r The request we should set the cookie in. * const char *id The value ve should store in the cookie. * * Returns: * Nothing. */ void am_cookie_set(request_rec *r, const char *id) { am_req_cfg_rec *req_cfg; const char *name; const char *cookie_params; char *cookie; if (id == NULL) return; name = am_cookie_name(r); cookie_params = am_cookie_params(r); cookie = apr_psprintf(r->pool, "%s=%s; %s", name, id, cookie_params); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "cookie_set: %s", cookie); /* Setting the headers inn err_headers_out ensures that they will be * sent for all responses. */ apr_table_addn(r->err_headers_out, "Set-Cookie", cookie); /* Add a note on the current request, to allow us to retrieve this * cookie in the current request. */ req_cfg = am_get_req_cfg(r); req_cfg->cookie_value = apr_pstrdup(r->pool, id); } /* This function deletes the cookie. * * Parameters: * request_rec *r The request we should clear the cookie in. We will * allocate any neccesary memory from r->pool. * * Returns: * Nothing. */ void am_cookie_delete(request_rec *r) { const char *name; const char *cookie_params; char *cookie; name = am_cookie_name(r); cookie_params = am_cookie_params(r); /* Format a cookie. To delete a cookie we set the expires-timestamp * to the past. */ cookie = apr_psprintf(r->pool, "%s=NULL;" " expires=Thu, 01-Jan-1970 00:00:00 GMT;" " %s", name, cookie_params); apr_table_addn(r->err_headers_out, "Set-Cookie", cookie); } mod_auth_mellon-0.12.0/auth_mellon_handler.c0000644000175100017510000035770312667763074020527 0ustar olavmoolavmo/* * * auth_mellon_handler.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" /* * Note: * * Information on PAOS ECP vs. Web SSO flow processing can be found in * the ECP.rst file. */ #ifdef HAVE_lasso_server_new_from_buffers # define SERVER_NEW lasso_server_new_from_buffers #else /* HAVE_lasso_server_new_from_buffers */ # define SERVER_NEW lasso_server_new #endif /* HAVE_lasso_server_new_from_buffers */ #ifdef HAVE_lasso_server_new_from_buffers /* This function generates optional metadata for a given element * * Parameters: * apr_pool_t *p Pool to allocate memory from * apr_hash_t *t Hash of lang -> strings * const char *e Name of the element * * Returns: * the metadata, or NULL if an error occured */ static char *am_optional_metadata_element(apr_pool_t *p, apr_hash_t *h, const char *e) { apr_hash_index_t *index; char *data = ""; for (index = apr_hash_first(p, h); index; index = apr_hash_next(index)) { char *lang; char *value; apr_ssize_t slen; char *xmllang = ""; apr_hash_this(index, (const void **)&lang, &slen, (void *)&value); if (*lang != '\0') xmllang = apr_psprintf(p, " xml:lang=\"%s\"", lang); data = apr_psprintf(p, "%s<%s%s>%s", data, e, xmllang, value, e); } return data; } /* This function generates optinal metadata * * Parameters: * request_rec *r The request we received. * * Returns: * the metadata, or NULL if an error occured */ static char *am_optional_metadata(apr_pool_t *p, request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); int count = 0; char *org_data = NULL; char *org_name = NULL; char *org_display_name = NULL; char *org_url = NULL; count += apr_hash_count(cfg->sp_org_name); count += apr_hash_count(cfg->sp_org_display_name); count += apr_hash_count(cfg->sp_org_url); if (count == 0) return ""; org_name = am_optional_metadata_element(p, cfg->sp_org_name, "OrganizationName"); org_display_name = am_optional_metadata_element(p, cfg->sp_org_display_name, "OrganizationDisplayName"); org_url = am_optional_metadata_element(p, cfg->sp_org_url, "OrganizationURL"); org_data = apr_psprintf(p, "%s%s%s", org_name, org_display_name, org_url); return org_data; } /* This function generates metadata * * Parameters: * request_rec *r The request we received. * * Returns: * the metadata, or NULL if an error occured */ static char *am_generate_metadata(apr_pool_t *p, request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); char *url = am_get_endpoint_url(r); char *cert = ""; const char *sp_entity_id; sp_entity_id = cfg->sp_entity_id ? cfg->sp_entity_id : url; if (cfg->sp_cert_file) { char *sp_cert_file; char *cp; char *bp; const char *begin = "-----BEGIN CERTIFICATE-----"; const char *end = "-----END CERTIFICATE-----"; /* * Try to remove leading and trailing garbage, as it can * wreak havoc XML parser if it contains [<>&] */ sp_cert_file = apr_pstrdup(p, cfg->sp_cert_file); cp = strstr(sp_cert_file, begin); if (cp != NULL) sp_cert_file = cp + strlen(begin); cp = strstr(sp_cert_file, end); if (cp != NULL) *cp = '\0'; /* * And remove any non printing char (CR, spaces...) */ bp = sp_cert_file; for (cp = sp_cert_file; *cp; cp++) { if (apr_isgraph(*cp)) *bp++ = *cp; } *bp = '\0'; cert = apr_psprintf(p, "" "" "" "%s" "" "" "" "" "" "" "%s" "" "" "", sp_cert_file, sp_cert_file); } return apr_psprintf(p, "\n\ \n\ \n\ %s\ \n\ \n\ urn:oasis:names:tc:SAML:2.0:nameid-format:transient\n\ \n\ \n\ \n\ \n\ %s\n\ ", sp_entity_id, cfg->sp_entity_id ? "" : "metadata", cert, url, url, url, url, url, am_optional_metadata(p, r)); } #endif /* HAVE_lasso_server_new_from_buffers */ /* * This function loads all IdP metadata in a lasso server * * Parameters: * am_dir_cfg_rec *cfg The server configuration. * request_rec *r The request we received. * * Returns: * number of loaded providers */ static guint am_server_add_providers(am_dir_cfg_rec *cfg, request_rec *r) { apr_size_t index; #ifndef HAVE_lasso_server_load_metadata const char *idp_public_key_file; if (cfg->idp_metadata->nelts == 1) idp_public_key_file = cfg->idp_public_key_file; else idp_public_key_file = NULL; #endif /* ! HAVE_lasso_server_load_metadata */ for (index = 0; index < cfg->idp_metadata->nelts; index++) { const am_metadata_t *idp_metadata; int error; #ifdef HAVE_lasso_server_load_metadata GList *loaded_idp = NULL; #endif /* HAVE_lasso_server_load_metadata */ idp_metadata = &( ((const am_metadata_t*)cfg->idp_metadata->elts) [index] ); #ifdef HAVE_lasso_server_load_metadata error = lasso_server_load_metadata(cfg->server, LASSO_PROVIDER_ROLE_IDP, idp_metadata->file, idp_metadata->chain, cfg->idp_ignore, &loaded_idp, LASSO_SERVER_LOAD_METADATA_FLAG_DEFAULT); if (error == 0) { GList *idx; for (idx = loaded_idp; idx != NULL; idx = idx->next) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "loaded IdP \"%s\" from \"%s\".", (char *)idx->data, idp_metadata->file); } } if (loaded_idp != NULL) { for (GList *idx = loaded_idp; idx != NULL; idx = idx->next) { g_free(idx->data); } g_list_free(loaded_idp); } #else /* HAVE_lasso_server_load_metadata */ error = lasso_server_add_provider(cfg->server, LASSO_PROVIDER_ROLE_IDP, idp_metadata->file, idp_public_key_file, cfg->idp_ca_file); #endif /* HAVE_lasso_server_load_metadata */ if (error != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error adding metadata \"%s\" to " "lasso server objects: %s.", idp_metadata->file, lasso_strerror(error)); } } return g_hash_table_size(cfg->server->providers); } static LassoServer *am_get_lasso_server(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); cfg = cfg->inherit_server_from; apr_thread_mutex_lock(cfg->server_mutex); if(cfg->server == NULL) { if(cfg->sp_metadata_file == NULL) { #ifdef HAVE_lasso_server_new_from_buffers /* * Try to generate missing metadata */ apr_pool_t *pool = r->server->process->pconf; cfg->sp_metadata_file = am_generate_metadata(pool, r); #else ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing MellonSPMetadataFile option."); apr_thread_mutex_unlock(cfg->server_mutex); return NULL; #endif /* HAVE_lasso_server_new_from_buffers */ } cfg->server = SERVER_NEW(cfg->sp_metadata_file, cfg->sp_private_key_file, NULL, cfg->sp_cert_file); if(cfg->server == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error initializing lasso server object. Please" " verify the following configuration directives:" " MellonSPMetadataFile and MellonSPPrivateKeyFile."); apr_thread_mutex_unlock(cfg->server_mutex); return NULL; } if (am_server_add_providers(cfg, r) == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error adding IdP to lasso server object. Please" " verify the following configuration directives:" " MellonIdPMetadataFile and" " MellonIdPPublicKeyFile."); lasso_server_destroy(cfg->server); cfg->server = NULL; apr_thread_mutex_unlock(cfg->server_mutex); return NULL; } } apr_thread_mutex_unlock(cfg->server_mutex); return cfg->server; } /* Redirect to discovery service. * * Parameters: * request_rec *r The request we received. * const char *return_to The URL the user should be returned to after login. * * Returns: * HTTP_SEE_OTHER on success, an error otherwise. */ static int am_start_disco(request_rec *r, const char *return_to) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); const char *endpoint = am_get_endpoint_url(r); LassoServer *server; const char *sp_entity_id; const char *sep; const char *login_url; const char *discovery_url; server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } sp_entity_id = LASSO_PROVIDER(server)->ProviderID; login_url = apr_psprintf(r->pool, "%slogin?ReturnTo=%s", endpoint, am_urlencode(r->pool, return_to)); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "login_url = %s", login_url); /* If discovery URL already has a ? we append a & */ sep = (strchr(cfg->discovery_url, '?')) ? "&" : "?"; discovery_url = apr_psprintf(r->pool, "%s%sentityID=%s&" "return=%s&returnIDParam=IdP", cfg->discovery_url, sep, am_urlencode(r->pool, sp_entity_id), am_urlencode(r->pool, login_url)); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "discovery_url = %s", discovery_url); apr_table_setn(r->headers_out, "Location", discovery_url); return HTTP_SEE_OTHER; } /* This function returns the first configured IdP * * Parameters: * request_rec *r The request we received. * * Returns: * the providerID, or NULL if an error occured */ static const char *am_first_idp(request_rec *r) { LassoServer *server; GList *idp_list; const char *idp_providerid; server = am_get_lasso_server(r); if (server == NULL) return NULL; idp_list = g_hash_table_get_keys(server->providers); if (idp_list == NULL) return NULL; idp_providerid = idp_list->data; g_list_free(idp_list); return idp_providerid; } /* This function selects an IdP and returns its provider_id * * Parameters: * request_rec *r The request we received. * * Returns: * the provider_id, or NULL if an error occured */ static const char *am_get_idp(request_rec *r) { LassoServer *server; const char *idp_provider_id; server = am_get_lasso_server(r); if (server == NULL) return NULL; /* * If we have a single IdP, return that one. */ if (g_hash_table_size(server->providers) == 1) return am_first_idp(r); /* * If IdP discovery handed us an IdP, try to use it. */ idp_provider_id = am_extract_query_parameter(r->pool, r->args, "IdP"); if (idp_provider_id != NULL) { int rc; rc = am_urldecode((char *)idp_provider_id); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not urldecode IdP discovery value."); idp_provider_id = NULL; } else { if (g_hash_table_lookup(server->providers, idp_provider_id) == NULL) idp_provider_id = NULL; } /* * If we do not know about it, fall back to default. */ if (idp_provider_id == NULL) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "IdP discovery returned unknown or inexistant IdP"); idp_provider_id = am_first_idp(r); } return idp_provider_id; } /* * No IdP answered, use default * Perhaps we should redirect to an error page instead. */ return am_first_idp(r); } /* This function stores dumps of the LassoIdentity and LassoSession objects * for the given LassoProfile object. The dumps are stored in the session * belonging to the current request. * * Parameters: * request_rec *r The current request. * am_cache_entry_t *session The session we are creating. * LassoProfile *profile The profile object. * char *saml_response The full SAML 2.0 response message. * * Returns: * OK on success or HTTP_INTERNAL_SERVER_ERROR on failure. */ static int am_save_lasso_profile_state(request_rec *r, am_cache_entry_t *session, LassoProfile *profile, char *saml_response) { LassoIdentity *lasso_identity; LassoSession *lasso_session; gchar *identity_dump; gchar *session_dump; int ret; lasso_identity = lasso_profile_get_identity(profile); if(lasso_identity == NULL) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "The current LassoProfile object doesn't contain a" " LassoIdentity object."); identity_dump = NULL; } else { identity_dump = lasso_identity_dump(lasso_identity); if(identity_dump == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Could not create a identity dump from the" " LassoIdentity object."); return HTTP_INTERNAL_SERVER_ERROR; } } lasso_session = lasso_profile_get_session(profile); if(lasso_session == NULL) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "The current LassoProfile object doesn't contain a" " LassoSession object."); session_dump = NULL; } else { session_dump = lasso_session_dump(lasso_session); if(session_dump == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Could not create a session dump from the" " LassoSession object."); if(identity_dump != NULL) { g_free(identity_dump); } return HTTP_INTERNAL_SERVER_ERROR; } } /* Save the profile state. */ ret = am_cache_set_lasso_state(session, identity_dump, session_dump, saml_response); if(identity_dump != NULL) { g_free(identity_dump); } if(session_dump != NULL) { g_free(session_dump); } return ret; } /* Returns a SAML response * * Parameters: * request_rec *r The current request. * LassoProfile *profile The profile object. * * Returns: * HTTP_INTERNAL_SERVER_ERROR if an error occurs, HTTP_SEE_OTHER for the * Redirect binding and OK for the SOAP binding. */ static int am_return_logout_response(request_rec *r, LassoProfile *profile) { if (profile->msg_url && profile->msg_body) { /* POST binding response */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error building logout response message." " POST binding is unsupported."); return HTTP_INTERNAL_SERVER_ERROR; } else if (profile->msg_url) { /* HTTP-Redirect binding response */ apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, profile->msg_url)); return HTTP_SEE_OTHER; } else if (profile->msg_body) { /* SOAP binding response */ ap_set_content_type(r, "text/xml"); ap_rputs(profile->msg_body, r); return OK; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error building logout response message." " There is no content to return."); return HTTP_INTERNAL_SERVER_ERROR; } } /* This function restores dumps of a LassoIdentity object and a LassoSession * object. The dumps are fetched from the session belonging to the current * request and restored to the given LassoProfile object. * * Parameters: * request_rec *r The current request. * LassoProfile *profile The profile object. * am_cache_entry_t *am_session The session structure. * * Returns: * OK on success or HTTP_INTERNAL_SERVER_ERROR on failure. */ static void am_restore_lasso_profile_state(request_rec *r, LassoProfile *profile, am_cache_entry_t *am_session) { const char *identity_dump; const char *session_dump; int rc; if(am_session == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Could not get auth_mellon session while attempting" " to restore the lasso profile state."); return; } identity_dump = am_cache_get_lasso_identity(am_session); if(identity_dump != NULL) { rc = lasso_profile_set_identity_from_dump(profile, identity_dump); if(rc < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Could not restore identity from dump." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); am_release_request_session(r, am_session); } } session_dump = am_cache_get_lasso_session(am_session); if(session_dump != NULL) { rc = lasso_profile_set_session_from_dump(profile, session_dump); if(rc < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Could not restore session from dump." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); am_release_request_session(r, am_session); } } } /* This function handles an IdP initiated logout request. * * Parameters: * request_rec *r The logout request. * LassoLogout *logout A LassoLogout object initiated with * the current session. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_handle_logout_request(request_rec *r, LassoLogout *logout, char *msg) { gint res = 0, rc = HTTP_OK; am_cache_entry_t *session = NULL; am_dir_cfg_rec *cfg = am_get_dir_cfg(r); /* Process the logout message. Ignore missing signature. */ res = lasso_logout_process_request_msg(logout, msg); #ifdef HAVE_lasso_profile_set_signature_verify_hint if(res != 0 && res != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) { if (apr_hash_get(cfg->do_not_verify_logout_signature, logout->parent.remote_providerID, APR_HASH_KEY_STRING)) { lasso_profile_set_signature_verify_hint(&logout->parent, LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE); res = lasso_logout_process_request_msg(logout, msg); } } #endif if(res != 0 && res != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error processing logout request message." " Lasso error: [%i] %s", res, lasso_strerror(res)); rc = HTTP_BAD_REQUEST; goto exit; } /* Search session using NameID */ if (! LASSO_IS_SAML2_NAME_ID(logout->parent.nameIdentifier)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error processing logout request message." " No NameID found"); rc = HTTP_BAD_REQUEST; goto exit; } session = am_get_request_session_by_nameid(r, ((LassoSaml2NameID*)logout->parent.nameIdentifier)->content); if (session == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error processing logout request message." " No session found for NameID %s", ((LassoSaml2NameID*)logout->parent.nameIdentifier)->content); } if (session == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error processing logout request message." " No session found."); } else { am_restore_lasso_profile_state(r, &logout->parent, session); } /* Validate the logout message. Ignore missing signature. */ res = lasso_logout_validate_request(logout); if(res != 0 && res != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND && res != LASSO_PROFILE_ERROR_SESSION_NOT_FOUND) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Error validating logout request." " Lasso error: [%i] %s", res, lasso_strerror(res)); rc = HTTP_INTERNAL_SERVER_ERROR; goto exit; } /* We continue with the logout despite those errors. They could be * caused by the IdP believing that we are logged in when we are not. */ if (session != NULL && res != LASSO_PROFILE_ERROR_SESSION_NOT_FOUND) { /* We found a matching session -- delete it. */ am_delete_request_session(r, session); session = NULL; } /* Create response message. */ res = lasso_logout_build_response_msg(logout); if(res != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error building logout response message." " Lasso error: [%i] %s", res, lasso_strerror(res)); rc = HTTP_INTERNAL_SERVER_ERROR; goto exit; } rc = am_return_logout_response(r, &logout->parent); exit: if (session != NULL) { am_release_request_session(r, session); } lasso_logout_destroy(logout); return rc; } /* This function handles a logout response message from the IdP. We get * this message after we have sent a logout request to the IdP. * * Parameters: * request_rec *r The logout response request. * LassoLogout *logout A LassoLogout object initiated with * the current session. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_handle_logout_response(request_rec *r, LassoLogout *logout) { gint res; int rc; am_cache_entry_t *session; char *return_to; am_dir_cfg_rec *cfg = am_get_dir_cfg(r); res = lasso_logout_process_response_msg(logout, r->args); #ifdef HAVE_lasso_profile_set_signature_verify_hint if(res != 0 && res != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) { if (apr_hash_get(cfg->do_not_verify_logout_signature, logout->parent.remote_providerID, APR_HASH_KEY_STRING)) { lasso_profile_set_signature_verify_hint(&logout->parent, LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE); res = lasso_logout_process_response_msg(logout, r->args); } } #endif if(res != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unable to process logout response." " Lasso error: [%i] %s", res, lasso_strerror(res)); lasso_logout_destroy(logout); return HTTP_BAD_REQUEST; } lasso_logout_destroy(logout); /* Delete the session. */ session = am_get_request_session(r); if(session != NULL) { am_delete_request_session(r, session); } return_to = am_extract_query_parameter(r->pool, r->args, "RelayState"); if(return_to == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No RelayState parameter to logout response handler." " It is possible that your IdP doesn't support the" " RelayState parameter."); return HTTP_BAD_REQUEST; } rc = am_urldecode(return_to); if(rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not urldecode RelayState value in logout" " response."); return HTTP_BAD_REQUEST; } /* Check for bad characters in RelayState. */ rc = am_check_url(r, return_to); if (rc != OK) { return rc; } /* Make sure that it is a valid redirect URL. */ rc = am_validate_redirect_url(r, return_to); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in logout response RelayState parameter."); return rc; } apr_table_setn(r->headers_out, "Location", return_to); return HTTP_SEE_OTHER; } /* This function initiates a logout request and sends it to the IdP. * * Parameters: * request_rec *r The logout response request. * LassoLogout *logout A LassoLogout object initiated with * the current session. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_init_logout_request(request_rec *r, LassoLogout *logout) { char *return_to; int rc; am_cache_entry_t *mellon_session; gint res; char *redirect_to; LassoProfile *profile; LassoSession *session; GList *assertion_list; LassoNode *assertion_n; LassoSaml2Assertion *assertion; LassoSaml2AuthnStatement *authnStatement; LassoSamlp2LogoutRequest *request; return_to = am_extract_query_parameter(r->pool, r->args, "ReturnTo"); rc = am_urldecode(return_to); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not urldecode ReturnTo value."); return HTTP_BAD_REQUEST; } rc = am_validate_redirect_url(r, return_to); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in logout request ReturnTo parameter."); return rc; } /* Disable the the local session (in case the IdP doesn't respond). */ mellon_session = am_get_request_session(r); if(mellon_session != NULL) { am_restore_lasso_profile_state(r, &logout->parent, mellon_session); mellon_session->logged_in = 0; am_release_request_session(r, mellon_session); } /* Create the logout request message. */ res = lasso_logout_init_request(logout, NULL, LASSO_HTTP_METHOD_REDIRECT); /* Early non failing return. */ if (res != 0) { if(res == LASSO_PROFILE_ERROR_SESSION_NOT_FOUND) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "User attempted to initiate logout without being" " loggged in."); } else if (res == LASSO_LOGOUT_ERROR_UNSUPPORTED_PROFILE || res == LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Current identity provider " "does not support single logout. Destroying local session only."); } else if(res != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unable to create logout request." " Lasso error: [%i] %s", res, lasso_strerror(res)); lasso_logout_destroy(logout); return HTTP_INTERNAL_SERVER_ERROR; } lasso_logout_destroy(logout); /* Check for bad characters in ReturnTo. */ rc = am_check_url(r, return_to); if (rc != OK) { return rc; } /* Redirect to the page the user should be sent to after logout. */ apr_table_setn(r->headers_out, "Location", return_to); return HTTP_SEE_OTHER; } profile = LASSO_PROFILE(logout); /* We need to set the SessionIndex in the LogoutRequest to the SessionIndex * we received during the login operation. This is not needed since release * 2.3.0. */ if (lasso_check_version(2, 3, 0, LASSO_CHECK_VERSION_NUMERIC) == 0) { session = lasso_profile_get_session(profile); assertion_list = lasso_session_get_assertions( session, profile->remote_providerID); if(! assertion_list || LASSO_IS_SAML2_ASSERTION(assertion_list->data) == FALSE) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No assertions found for the current session."); lasso_logout_destroy(logout); return HTTP_INTERNAL_SERVER_ERROR; } /* We currently only look at the first assertion in the list * lasso_session_get_assertions returns. */ assertion_n = assertion_list->data; assertion = LASSO_SAML2_ASSERTION(assertion_n); /* We assume that the first authnStatement contains the data we want. */ authnStatement = LASSO_SAML2_AUTHN_STATEMENT(assertion->AuthnStatement->data); if(!authnStatement) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No AuthnStatement found in the current assertion."); lasso_logout_destroy(logout); return HTTP_INTERNAL_SERVER_ERROR; } if(authnStatement->SessionIndex) { request = LASSO_SAMLP2_LOGOUT_REQUEST(profile->request); request->SessionIndex = g_strdup(authnStatement->SessionIndex); } } /* Set the RelayState parameter to the return url (if we have one). */ if(return_to) { profile->msg_relayState = g_strdup(return_to); } /* Serialize the request message into a url which we can redirect to. */ res = lasso_logout_build_request_msg(logout); if(res != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unable to serialize lasso logout message." " Lasso error: [%i] %s", res, lasso_strerror(res)); lasso_logout_destroy(logout); return HTTP_INTERNAL_SERVER_ERROR; } /* Set the redirect url. */ redirect_to = apr_pstrdup(r->pool, LASSO_PROFILE(logout)->msg_url); /* Check if the lasso library added the RelayState. If lasso didn't add * a RelayState parameter, then we add one ourself. This should hopefully * be removed in the future. */ if(return_to != NULL && strstr(redirect_to, "&RelayState=") == NULL && strstr(redirect_to, "?RelayState=") == NULL) { /* The url didn't contain the relaystate parameter. */ redirect_to = apr_pstrcat( r->pool, redirect_to, "&RelayState=", am_urlencode(r->pool, return_to), NULL ); } apr_table_setn(r->headers_out, "Location", redirect_to); lasso_logout_destroy(logout); /* Redirect (without including POST data if this was a POST request. */ return HTTP_SEE_OTHER; } /* This function handles requests to the logout handler. * * Parameters: * request_rec *r The request. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_handle_logout(request_rec *r) { LassoServer *server; LassoLogout *logout; server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } logout = lasso_logout_new(server); if(logout == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error creating lasso logout object."); return HTTP_INTERNAL_SERVER_ERROR; } /* Check which type of request to the logout handler this is. * We have three types: * - logout requests: The IdP sends a logout request to this service. * it can be either through HTTP-Redirect or SOAP. * - logout responses: We have sent a logout request to the IdP, and * are receiving a response. * - We want to initiate a logout request. */ /* First check for IdP-initiated SOAP logout request */ if ((r->args == NULL) && (r->method_number == M_POST)) { int rc; char *post_data; rc = am_read_post_data(r, &post_data, NULL); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Error reading POST data."); return HTTP_INTERNAL_SERVER_ERROR; } return am_handle_logout_request(r, logout, post_data); } else if(am_extract_query_parameter(r->pool, r->args, "SAMLRequest") != NULL) { /* SAMLRequest - logout request from the IdP. */ return am_handle_logout_request(r, logout, r->args); } else if(am_extract_query_parameter(r->pool, r->args, "SAMLResponse") != NULL) { /* SAMLResponse - logout response from the IdP. */ return am_handle_logout_response(r, logout); } else if(am_extract_query_parameter(r->pool, r->args, "ReturnTo") != NULL) { /* RedirectTo - SP initiated logout. */ return am_init_logout_request(r, logout); } else { /* Unknown request to the logout handler. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No known parameters passed to the logout" " handler. Query string was \"%s\". To initiate" " a logout, you need to pass a \"ReturnTo\"" " parameter with a url to the web page the user should" " be redirected to after a successful logout.", r->args); return HTTP_BAD_REQUEST; } } /* This function parses a timestamp for a SAML 2.0 condition. * * Parameters: * request_rec *r The current request. Used for logging of errors. * const char *timestamp The timestamp we should parse. Must be on * the following format: "YYYY-MM-DDThh:mm:ssZ" * * Returns: * An apr_time_t value with the timestamp, or 0 on error. */ static apr_time_t am_parse_timestamp(request_rec *r, const char *timestamp) { size_t len; int i; char c; const char *expected; apr_time_exp_t time_exp; apr_time_t res; apr_status_t rc; len = strlen(timestamp); /* Verify length of timestamp. */ if(len < 20){ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Invalid length of timestamp: \"%s\".", timestamp); } /* Verify components of timestamp. */ for(i = 0; i < len - 1; i++) { c = timestamp[i]; expected = NULL; switch(i) { case 4: case 7: /* Matches " - - " */ if(c != '-') { expected = "'-'"; } break; case 10: /* Matches " T " */ if(c != 'T') { expected = "'T'"; } break; case 13: case 16: /* Matches " : : " */ if(c != ':') { expected = "':'"; } break; case 19: /* Matches " ." */ if (c != '.') { expected = "'.'"; } break; default: /* Matches "YYYY MM DD hh mm ss uuuuuu" */ if(c < '0' || c > '9') { expected = "a digit"; } break; } if(expected != NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid character in timestamp at position %i." " Expected %s, got '%c'. Full timestamp: \"%s\"", i, expected, c, timestamp); return 0; } } if (timestamp[len - 1] != 'Z') { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Timestamp wasn't in UTC (did not end with 'Z')." " Full timestamp: \"%s\"", timestamp); return 0; } time_exp.tm_usec = 0; if (len > 20) { /* Subsecond precision. */ if (len > 27) { /* Timestamp has more than microsecond precision. Just clip it to * microseconds. */ len = 27; } len -= 1; /* Drop the 'Z' off the end. */ for (i = 20; i < len; i++) { time_exp.tm_usec = time_exp.tm_usec * 10 + timestamp[i] - '0'; } for (i = len; i < 26; i++) { time_exp.tm_usec *= 10; } } time_exp.tm_sec = (timestamp[17] - '0') * 10 + (timestamp[18] - '0'); time_exp.tm_min = (timestamp[14] - '0') * 10 + (timestamp[15] - '0'); time_exp.tm_hour = (timestamp[11] - '0') * 10 + (timestamp[12] - '0'); time_exp.tm_mday = (timestamp[8] - '0') * 10 + (timestamp[9] - '0'); time_exp.tm_mon = (timestamp[5] - '0') * 10 + (timestamp[6] - '0') - 1; time_exp.tm_year = (timestamp[0] - '0') * 1000 + (timestamp[1] - '0') * 100 + (timestamp[2] - '0') * 10 + (timestamp[3] - '0') - 1900; time_exp.tm_wday = 0; /* Unknown. */ time_exp.tm_yday = 0; /* Unknown. */ time_exp.tm_isdst = 0; /* UTC, no daylight savings time. */ time_exp.tm_gmtoff = 0; /* UTC, no offset from UTC. */ rc = apr_time_exp_gmt_get(&res, &time_exp); if(rc != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Error converting timestamp \"%s\".", timestamp); return 0; } return res; } /* Validate the subject on an Assertion. * * request_rec *r The current request. Used to log * errors. * LassoSaml2Assertion *assertion The assertion we will validate. * const char *url The current URL. * * Returns: * OK on success, HTTP_BAD_REQUEST on failure. */ static int am_validate_subject(request_rec *r, LassoSaml2Assertion *assertion, const char *url) { apr_time_t now; apr_time_t t; LassoSaml2SubjectConfirmation *sc; LassoSaml2SubjectConfirmationData *scd; am_dir_cfg_rec *cfg = am_get_dir_cfg(r); if (assertion->Subject == NULL) { /* No Subject to validate. */ return OK; } else if (!LASSO_IS_SAML2_SUBJECT(assertion->Subject)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of Subject node."); return HTTP_BAD_REQUEST; } if (assertion->Subject->SubjectConfirmation == NULL) { /* No SubjectConfirmation. */ return OK; } else if (!LASSO_IS_SAML2_SUBJECT_CONFIRMATION(assertion->Subject->SubjectConfirmation)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of SubjectConfirmation node."); return HTTP_BAD_REQUEST; } sc = assertion->Subject->SubjectConfirmation; if (sc->Method == NULL || strcmp(sc->Method, "urn:oasis:names:tc:SAML:2.0:cm:bearer")) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid Method in SubjectConfirmation."); return HTTP_BAD_REQUEST; } scd = sc->SubjectConfirmationData; if (scd == NULL) { /* Nothing to verify. */ return OK; } else if (!LASSO_IS_SAML2_SUBJECT_CONFIRMATION_DATA(scd)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of SubjectConfirmationData node."); return HTTP_BAD_REQUEST; } now = apr_time_now(); if (scd->NotBefore) { t = am_parse_timestamp(r, scd->NotBefore); if (t == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid timestamp in NotBefore in SubjectConfirmationData."); return HTTP_BAD_REQUEST; } if (t - 60000000 > now) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "NotBefore in SubjectConfirmationData was in the future."); return HTTP_BAD_REQUEST; } } if (scd->NotOnOrAfter) { t = am_parse_timestamp(r, scd->NotOnOrAfter); if (t == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid timestamp in NotOnOrAfter in SubjectConfirmationData."); return HTTP_BAD_REQUEST; } if (now >= t + 60000000) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "NotOnOrAfter in SubjectConfirmationData was in the past."); return HTTP_BAD_REQUEST; } } if (scd->Recipient) { if (strcmp(scd->Recipient, url)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong Recipient in SubjectConfirmationData. Current URL is: %s, Recipient is %s", url, scd->Recipient); return HTTP_BAD_REQUEST; } } if (scd->Address && CFG_VALUE(cfg, subject_confirmation_data_address_check)) { if (strcasecmp(scd->Address, am_compat_request_ip(r))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong Address in SubjectConfirmationData." "Current address is \"%s\", but should have been \"%s\".", am_compat_request_ip(r), scd->Address); return HTTP_BAD_REQUEST; } } return OK; } /* Validate the conditions on an Assertion. * * Parameters: * request_rec *r The current request. Used to log * errors. * LassoSaml2Assertion *assertion The assertion we will validate. * const char *providerID The providerID of the SP. * * Returns: * OK on success, HTTP_BAD_REQUEST on failure. */ static int am_validate_conditions(request_rec *r, LassoSaml2Assertion *assertion, const char *providerID) { LassoSaml2Conditions *conditions; apr_time_t now; apr_time_t t; GList *i; LassoSaml2AudienceRestriction *ar; conditions = assertion->Conditions; if (!LASSO_IS_SAML2_CONDITIONS(conditions)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of Conditions node."); return HTTP_BAD_REQUEST; } if (conditions->Condition != NULL) { /* This is a list of LassoSaml2ConditionAbstract - if it * isn't empty, we have an unsupported condition. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unsupported condition in Assertion."); return HTTP_BAD_REQUEST; } now = apr_time_now(); if (conditions->NotBefore) { t = am_parse_timestamp(r, conditions->NotBefore); if (t == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid timestamp in NotBefore in Condition."); return HTTP_BAD_REQUEST; } if (t - 60000000 > now) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "NotBefore in Condition was in the future."); return HTTP_BAD_REQUEST; } } if (conditions->NotOnOrAfter) { t = am_parse_timestamp(r, conditions->NotOnOrAfter); if (t == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid timestamp in NotOnOrAfter in Condition."); return HTTP_BAD_REQUEST; } if (now >= t + 60000000) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "NotOnOrAfter in Condition was in the past."); return HTTP_BAD_REQUEST; } } for (i = g_list_first(conditions->AudienceRestriction); i != NULL; i = g_list_next(i)) { ar = i->data; if (!LASSO_IS_SAML2_AUDIENCE_RESTRICTION(ar)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of AudienceRestriction node."); return HTTP_BAD_REQUEST; } if (ar->Audience == NULL || strcmp(ar->Audience, providerID)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid Audience in Conditions. Should be: %s", providerID); return HTTP_BAD_REQUEST; } } return OK; } /* This function sets the session expire timestamp based on NotOnOrAfter * attribute of a condition element. * * Parameters: * request_rec *r The current request. Used to log * errors. * am_cache_entry_t *session The current session. * LassoSaml2Assertion *assertion The assertion which we will extract * the conditions from. * * Returns: * Nothing. */ static void am_handle_session_expire(request_rec *r, am_cache_entry_t *session, LassoSaml2Assertion *assertion) { GList *authn_itr; LassoSaml2AuthnStatement *authn; const char *not_on_or_after; apr_time_t t; for(authn_itr = g_list_first(assertion->AuthnStatement); authn_itr != NULL; authn_itr = g_list_next(authn_itr)) { authn = authn_itr->data; if (!LASSO_IS_SAML2_AUTHN_STATEMENT(authn)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of AuthnStatement node."); continue; } /* Find timestamp. */ not_on_or_after = authn->SessionNotOnOrAfter; if(not_on_or_after == NULL) { continue; } /* Parse timestamp. */ t = am_parse_timestamp(r, not_on_or_after); if(t == 0) { continue; } /* Updates the expires timestamp if this one is earlier than the * previous timestamp. */ am_cache_update_expires(session, t); } } /* Add all the attributes from an assertion to the session data for the * current user. * * Parameters: * am_cache_entry_t *s The current session. * request_rec *r The current request. * const char *name_id The name identifier we received from * the IdP. * LassoSaml2Assertion *assertion The assertion. * * Returns: * HTTP_BAD_REQUEST if we couldn't find the session id of the user, or * OK if no error occured. */ static int add_attributes(am_cache_entry_t *session, request_rec *r, const char *name_id, LassoSaml2Assertion *assertion) { am_dir_cfg_rec *dir_cfg; GList *atr_stmt_itr; LassoSaml2AttributeStatement *atr_stmt; GList *atr_itr; LassoSaml2Attribute *attribute; GList *value_itr; LassoSaml2AttributeValue *value; GList *any_itr; char *content; char *dump; int ret; dir_cfg = am_get_dir_cfg(r); /* Set expires to whatever is set by MellonSessionLength. */ if(dir_cfg->session_length == -1) { /* -1 means "use default. The current default is 86400 seconds. */ am_cache_update_expires(session, apr_time_now() + apr_time_make(86400, 0)); } else { am_cache_update_expires(session, apr_time_now() + apr_time_make(dir_cfg->session_length, 0)); } /* Save session information. */ ret = am_cache_env_append(session, "NAME_ID", name_id); if(ret != OK) { return ret; } /* Update expires timestamp of session. */ am_handle_session_expire(r, session, assertion); /* assertion->AttributeStatement is a list of * LassoSaml2AttributeStatement objects. */ for(atr_stmt_itr = g_list_first(assertion->AttributeStatement); atr_stmt_itr != NULL; atr_stmt_itr = g_list_next(atr_stmt_itr)) { atr_stmt = atr_stmt_itr->data; if (!LASSO_IS_SAML2_ATTRIBUTE_STATEMENT(atr_stmt)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of AttributeStatement node."); continue; } /* atr_stmt->Attribute is list of LassoSaml2Attribute objects. */ for(atr_itr = g_list_first(atr_stmt->Attribute); atr_itr != NULL; atr_itr = g_list_next(atr_itr)) { attribute = atr_itr->data; if (!LASSO_IS_SAML2_ATTRIBUTE(attribute)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of Attribute node."); continue; } /* attribute->AttributeValue is a list of * LassoSaml2AttributeValue objects. */ for(value_itr = g_list_first(attribute->AttributeValue); value_itr != NULL; value_itr = g_list_next(value_itr)) { value = value_itr->data; if (!LASSO_IS_SAML2_ATTRIBUTE_VALUE(value)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of AttributeValue node."); continue; } /* value->any is a list with the child nodes of the * AttributeValue element. * * We assume that the list contains a single text node. */ if(value->any == NULL) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "AttributeValue element was empty."); continue; } content = ""; for (any_itr = g_list_first(value->any); any_itr != NULL; any_itr = g_list_next(any_itr)) { /* Verify that this is a LassoNode object. */ if(!LASSO_NODE(any_itr->data)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "AttributeValue element contained an " " element which wasn't a Node."); continue; } dump = lasso_node_dump(LASSO_NODE(any_itr->data)); if (!dump) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "AttributeValue content dump failed."); continue; } /* Use the request pool, no need to free results */ content = apr_pstrcat(r->pool, content, dump, NULL); g_free(dump); } /* Decode and save the attribute. */ ret = am_cache_env_append(session, attribute->Name, content); if(ret != OK) { return ret; } } } } return OK; } /* This function validates that the received assertion verify the security level configured by * MellonAuthnContextClassRef directives */ static int am_validate_authn_context_class_ref(request_rec *r, LassoSaml2Assertion *assertion) { int i = 0; LassoSaml2AuthnStatement *authn_statement = NULL; LassoSaml2AuthnContext *authn_context = NULL; am_dir_cfg_rec *dir_cfg; apr_array_header_t *refs; dir_cfg = am_get_dir_cfg(r); refs = dir_cfg->authn_context_class_ref; if (! refs->nelts) return OK; if (! assertion->AuthnStatement) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing AuthnStatement in assertion, returning BadRequest."); return HTTP_BAD_REQUEST; } /* we only consider the first AuthnStatement, I do not know of any idp * sending more than one. */ authn_statement = g_list_first(assertion->AuthnStatement)->data; if (! authn_statement->AuthnContext) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing AuthnContext in assertion, returning BadRequest."); return HTTP_BAD_REQUEST; } authn_context = authn_statement->AuthnContext; if (! authn_context->AuthnContextClassRef) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing AuthnContextClassRef in assertion, returning Forbidden."); return HTTP_FORBIDDEN; } for (i = 0; i < refs->nelts; i++) { const char *ref = ((char **)refs->elts)[i]; if (strcmp(ref, authn_context->AuthnContextClassRef) == 0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "AuthnContextClassRef (%s) matches the " "MellonAuthnContextClassRef directive, " "access can be granted.", authn_context->AuthnContextClassRef); return OK; } } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "AuthnContextClassRef (%s) does not match the " "MellonAuthnContextClassRef directive, returning " "Forbidden.", authn_context->AuthnContextClassRef); return HTTP_FORBIDDEN; } /* This function finishes handling of a login response after it has been parsed * by the HTTP-POST or HTTP-Artifact handler. * * Parameters: * request_rec *r The current request. * LassoLogin *login The login object which has been initialized with the * data we have received from the IdP. * char *relay_state The RelayState parameter from the POST data or from * the request url. This parameter is urlencoded, and * this function will urldecode it in-place. Therefore it * must be possible to overwrite the data. * is_paos If true then flow is PAOS ECP. * * Returns: * A HTTP status code which should be returned to the client. */ static int am_handle_reply_common(request_rec *r, LassoLogin *login, char *relay_state, char *saml_response, bool is_paos) { char *url; char *chr; const char *name_id; LassoSamlp2Response *response; LassoSaml2Assertion *assertion; const char *in_response_to; am_dir_cfg_rec *dir_cfg; am_cache_entry_t *session; int rc; const char *idp; url = am_reconstruct_url(r); chr = strchr(url, '?'); if (! chr) { chr = strchr(url, ';'); } if (chr) { *chr = '\0'; } dir_cfg = am_get_dir_cfg(r); if(LASSO_PROFILE(login)->nameIdentifier == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No acceptable name identifier found in" " SAML 2.0 response."); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } name_id = LASSO_SAML2_NAME_ID(LASSO_PROFILE(login)->nameIdentifier) ->content; response = LASSO_SAMLP2_RESPONSE(LASSO_PROFILE(login)->response); if (response->parent.Destination) { if (strcmp(response->parent.Destination, url)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid Destination on Response. Should be: %s", url); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } } if (g_list_length(response->Assertion) == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No Assertion in response."); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } if (g_list_length(response->Assertion) > 1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "More than one Assertion in response."); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } assertion = g_list_first(response->Assertion)->data; if (!LASSO_IS_SAML2_ASSERTION(assertion)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of Assertion node."); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } rc = am_validate_subject(r, assertion, url); if (rc != OK) { lasso_login_destroy(login); return rc; } rc = am_validate_conditions(r, assertion, LASSO_PROVIDER(LASSO_PROFILE(login)->server)->ProviderID); if (rc != OK) { lasso_login_destroy(login); return rc; } in_response_to = response->parent.InResponseTo; if (!is_paos) { if(in_response_to != NULL) { /* This is SP-initiated login. Check that we have a cookie. */ if(am_cookie_get(r) == NULL) { /* Missing cookie. */ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "User has disabled cookies, or has lost" " the cookie before returning from the SAML2" " login server."); if(dir_cfg->no_cookie_error_page != NULL) { apr_table_setn(r->headers_out, "Location", dir_cfg->no_cookie_error_page); lasso_login_destroy(login); return HTTP_SEE_OTHER; } else { /* Return 400 Bad Request when the user hasn't set a * no-cookie error page. */ lasso_login_destroy(login); return HTTP_BAD_REQUEST; } } } } /* Check AuthnContextClassRef */ rc = am_validate_authn_context_class_ref(r, assertion); if (rc != OK) { lasso_login_destroy(login); return rc; } /* Create a new session. */ session = am_new_request_session(r); if(session == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "am_new_request_session() failed"); return HTTP_INTERNAL_SERVER_ERROR; } rc = add_attributes(session, r, name_id, assertion); if(rc != OK) { am_release_request_session(r, session); lasso_login_destroy(login); return rc; } /* If requested, save the IdP ProviderId */ if(dir_cfg->idpattr != NULL) { idp = LASSO_PROFILE(login)->remote_providerID; if(idp != NULL) { rc = am_cache_env_append(session, dir_cfg->idpattr, idp); if(rc != OK) { am_release_request_session(r, session); lasso_login_destroy(login); return rc; } } } rc = lasso_login_accept_sso(login); if(rc < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unable to accept SSO message." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); am_release_request_session(r, session); lasso_login_destroy(login); return HTTP_INTERNAL_SERVER_ERROR; } /* Save the profile state. */ rc = am_save_lasso_profile_state(r, session, LASSO_PROFILE(login), saml_response); if(rc != OK) { am_release_request_session(r, session); lasso_login_destroy(login); return rc; } /* Mark user as logged in. */ session->logged_in = 1; am_release_request_session(r, session); lasso_login_destroy(login); /* No RelayState - we don't know what to do. Use default login path. */ if(relay_state == NULL || strlen(relay_state) == 0) { dir_cfg = am_get_dir_cfg(r); apr_table_setn(r->headers_out, "Location", dir_cfg->login_path); return HTTP_SEE_OTHER; } rc = am_urldecode(relay_state); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not urldecode RelayState value."); return HTTP_BAD_REQUEST; } /* Check for bad characters in RelayState. */ rc = am_check_url(r, relay_state); if (rc != OK) { return rc; } rc = am_validate_redirect_url(r, relay_state); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in logout response RelayState parameter."); return rc; } apr_table_setn(r->headers_out, "Location", relay_state); /* HTTP_SEE_OTHER should be a redirect where the browser doesn't repeat * the POST data to the new page. */ return HTTP_SEE_OTHER; } /* This function handles responses to login requests received with the * HTTP-POST binding. * * Parameters: * request_rec *r The request we received. * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_handle_post_reply(request_rec *r) { int rc; char *post_data; char *saml_response; LassoServer *server; LassoLogin *login; char *relay_state; am_dir_cfg_rec *dir_cfg = am_get_dir_cfg(r); int i, err; /* Make sure that this is a POST request. */ if(r->method_number != M_POST) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Expected POST request for HTTP-POST endpoint." " Got a %s request instead.", r->method); /* According to the documentation for request_rec, a handler which * doesn't handle a request method, should set r->allowed to the * methods it handles, and return DECLINED. * However, the default handler handles GET-requests, so for GET * requests the handler should return HTTP_METHOD_NOT_ALLOWED. */ r->allowed = M_POST; if(r->method_number == M_GET) { return HTTP_METHOD_NOT_ALLOWED; } else { return DECLINED; } } /* Read POST-data. */ rc = am_read_post_data(r, &post_data, NULL); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Error reading POST data."); return rc; } /* Extract the SAMLResponse-field from the data. */ saml_response = am_extract_query_parameter(r->pool, post_data, "SAMLResponse"); if (saml_response == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not find SAMLResponse field in POST data."); return HTTP_BAD_REQUEST; } rc = am_urldecode(saml_response); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not urldecode SAMLResponse value."); return rc; } server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } login = lasso_login_new(server); if (login == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to initialize LassoLogin object."); return HTTP_INTERNAL_SERVER_ERROR; } /* Process login responce. */ rc = lasso_login_process_authn_response_msg(login, saml_response); if (rc != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error processing authn response." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); lasso_login_destroy(login); err = HTTP_BAD_REQUEST; for (i = 0; auth_mellon_errormap[i].lasso_error != 0; i++) { if (auth_mellon_errormap[i].lasso_error == rc) { err = auth_mellon_errormap[i].http_error; break; } } if (err == HTTP_UNAUTHORIZED) { if (dir_cfg->no_success_error_page != NULL) { apr_table_setn(r->headers_out, "Location", dir_cfg->no_success_error_page); return HTTP_SEE_OTHER; } } return err; } /* Extract RelayState parameter. */ relay_state = am_extract_query_parameter(r->pool, post_data, "RelayState"); /* Finish handling the reply with the common handler. */ return am_handle_reply_common(r, login, relay_state, saml_response, false); } /* This function handles responses to login requests received with the * PAOS binding. * * Parameters: * request_rec *r The request we received. * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_handle_paos_reply(request_rec *r) { int rc; char *post_data; LassoServer *server; LassoLogin *login; char *relay_state = NULL; int i, err; /* Make sure that this is a POST request. */ if(r->method_number != M_POST) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Expected POST request for paosResponse endpoint." " Got a %s request instead.", r->method); /* According to the documentation for request_rec, a handler which * doesn't handle a request method, should set r->allowed to the * methods it handles, and return DECLINED. * However, the default handler handles GET-requests, so for GET * requests the handler should return HTTP_METHOD_NOT_ALLOWED. */ r->allowed = M_POST; if(r->method_number == M_GET) { return HTTP_METHOD_NOT_ALLOWED; } else { return DECLINED; } } /* Read POST-data. */ rc = am_read_post_data(r, &post_data, NULL); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Error reading POST data."); return rc; } server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } login = lasso_login_new(server); if (login == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to initialize LassoLogin object."); return HTTP_INTERNAL_SERVER_ERROR; } /* Process login response. */ rc = lasso_login_process_paos_response_msg(login, post_data); if (rc != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error processing ECP authn response." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); lasso_login_destroy(login); err = HTTP_BAD_REQUEST; for (i = 0; auth_mellon_errormap[i].lasso_error != 0; i++) { if (auth_mellon_errormap[i].lasso_error == rc) { err = auth_mellon_errormap[i].http_error; break; } } return err; } /* Extract RelayState parameter. */ if (LASSO_PROFILE(login)->msg_relayState) { relay_state = apr_pstrdup(r->pool, LASSO_PROFILE(login)->msg_relayState); } /* Finish handling the reply with the common handler. */ return am_handle_reply_common(r, login, relay_state, post_data, true); } /* This function handles responses to login requests which use the * HTTP-Artifact binding. * * Parameters: * request_rec *r The request we received. * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_handle_artifact_reply(request_rec *r) { int rc; LassoServer *server; LassoLogin *login; char *response; char *relay_state; char *saml_art; char *post_data; /* Make sure that this is a GET request. */ if(r->method_number != M_GET && r->method_number != M_POST) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Expected GET or POST request for the HTTP-Artifact endpoint." " Got a %s request instead.", r->method); /* According to the documentation for request_rec, a handler which * doesn't handle a request method, should set r->allowed to the * methods it handles, and return DECLINED. * However, the default handler handles GET-requests, so for GET * requests the handler should return HTTP_METHOD_NOT_ALLOWED. * This endpoints handles GET requests, so it isn't necessary to * check for method_number == M_GET. */ r->allowed = M_GET; return DECLINED; } server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } login = lasso_login_new(server); if (login == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to initialize LassoLogin object."); return HTTP_INTERNAL_SERVER_ERROR; } /* Parse artifact url. */ if (r->method_number == M_GET) { rc = lasso_login_init_request(login, r->args, LASSO_HTTP_METHOD_ARTIFACT_GET); if(rc < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to handle login response." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } } else { rc = am_read_post_data(r, &post_data, NULL); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Error reading POST data."); return HTTP_BAD_REQUEST; } saml_art = am_extract_query_parameter(r->pool, post_data, "SAMLart"); if (saml_art == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Error reading POST data missing SAMLart form parameter."); return HTTP_BAD_REQUEST; } ap_unescape_url(saml_art); rc = lasso_login_init_request(login, saml_art, LASSO_HTTP_METHOD_ARTIFACT_POST); if(rc < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to handle login response." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } } /* Prepare SOAP request. */ rc = lasso_login_build_request_msg(login); if(rc < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to prepare SOAP message for HTTP-Artifact" " resolution." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); lasso_login_destroy(login); return HTTP_INTERNAL_SERVER_ERROR; } /* Do the SOAP request. */ rc = am_httpclient_post_str( r, LASSO_PROFILE(login)->msg_url, LASSO_PROFILE(login)->msg_body, "text/xml", (void**)&response, NULL ); if(rc != OK) { lasso_login_destroy(login); return HTTP_INTERNAL_SERVER_ERROR; } rc = lasso_login_process_response_msg(login, response); if(rc != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to handle HTTP-Artifact response data." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); lasso_login_destroy(login); return HTTP_INTERNAL_SERVER_ERROR; } /* Extract the RelayState parameter. */ if (r->method_number == M_GET) { relay_state = am_extract_query_parameter(r->pool, r->args, "RelayState"); } else { relay_state = am_extract_query_parameter(r->pool, post_data, "RelayState"); } /* Finish handling the reply with the common handler. */ return am_handle_reply_common(r, login, relay_state, "", false); } /* This function builds web form inputs for a saved POST request, * in multipart/form-data format. * * Parameters: * request_rec *r The request * const char *post_data The savec POST request * * Returns: * The web form fragment, or NULL on failure. */ const char *am_post_mkform_multipart(request_rec *r, const char *post_data) { const char *mime_part; const char *boundary; char *l1; char *post_form = ""; /* Replace CRLF by LF */ post_data = am_strip_cr(r, post_data); if ((boundary = am_xstrtok(r, post_data, "\n", &l1)) == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Cannot figure initial boundary"); return NULL; } for (mime_part = am_xstrtok(r, post_data, boundary, &l1); mime_part; mime_part = am_xstrtok(r, NULL, boundary, &l1)) { const char *hdr; const char *name = NULL; const char *value = NULL; const char *input_item; /* End of MIME data */ if (strcmp(mime_part, "--\n") == 0) break; /* Remove leading CRLF */ if (strstr(mime_part, "\n") == mime_part) mime_part += 1; /* Empty part */ if (*mime_part == '\0') continue; /* Find Content-Disposition header * Looking for * Content-Disposition: form-data; name="the_name"\n */ hdr = am_get_mime_header(r, mime_part, "Content-Disposition"); if (hdr == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No Content-Disposition header in MIME section,"); continue; } name = am_get_header_attr(r, hdr, "form-data", "name"); if (name == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unexpected Content-Disposition header: \"%s\"", hdr); continue; } if ((value = am_get_mime_body(r, mime_part)) == NULL) value = ""; input_item = apr_psprintf(r->pool, " \n", am_htmlencode(r, name), am_htmlencode(r, value)); post_form = apr_pstrcat(r->pool, post_form, input_item, NULL); } return post_form; } /* This function builds web form inputs for a saved POST request, * in application/x-www-form-urlencoded format * * Parameters: * request_rec *r The request * const char *post_data The savec POST request * * Returns: * The web form fragment, or NULL on failure. */ const char *am_post_mkform_urlencoded(request_rec *r, const char *post_data) { const char *item; char *last; char *post_form = ""; for (item = am_xstrtok(r, post_data, "&", &last); item; item = am_xstrtok(r, NULL, "&", &last)) { char *l1; char *name; char *value; const char *input_item; name = (char *)am_xstrtok(r, item, "=", &l1); value = (char *)am_xstrtok(r, NULL, "=", &l1); if (name == NULL) continue; if (value == NULL) value = (char *)""; if (am_urldecode(name) != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "urldecode(\"%s\") failed", name); return NULL; } if (am_urldecode(value) != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "urldecode(\"%s\") failed", value); return NULL; } input_item = apr_psprintf(r->pool, " \n", am_htmlencode(r, name), am_htmlencode(r, value)); post_form = apr_pstrcat(r->pool, post_form, input_item, NULL); } return post_form; } /* This function handles responses to repost request * * Parameters: * request_rec *r The request we received. * * Returns: * OK on success, or an error on failure. */ static int am_handle_repost(request_rec *r) { am_mod_cfg_rec *mod_cfg; const char *query; const char *enctype; char *charset; char *psf_id; char *cp; char *psf_filename; char *post_data; const char *post_form; char *output; char *return_url; const char *(*post_mkform)(request_rec *, const char *); int rc; if (am_cookie_get(r) == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Repost query without a session"); return HTTP_FORBIDDEN; } mod_cfg = am_get_mod_cfg(r->server); if (!mod_cfg->post_dir) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Repost query without MellonPostDirectory."); return HTTP_NOT_FOUND; } query = r->parsed_uri.query; enctype = am_extract_query_parameter(r->pool, query, "enctype"); if (enctype == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bad repost query: missing enctype"); return HTTP_BAD_REQUEST; } if (strcmp(enctype, "urlencoded") == 0) { enctype = "application/x-www-form-urlencoded"; post_mkform = am_post_mkform_urlencoded; } else if (strcmp(enctype, "multipart") == 0) { enctype = "multipart/form-data"; post_mkform = am_post_mkform_multipart; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bad repost query: invalid enctype \"%s\".", enctype); return HTTP_BAD_REQUEST; } charset = am_extract_query_parameter(r->pool, query, "charset"); if (charset != NULL) { if (am_urldecode(charset) != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bad repost query: invalid charset \"%s\"", charset); return HTTP_BAD_REQUEST; } /* Check that charset is sane */ for (cp = charset; *cp; cp++) { if (!apr_isalnum(*cp) && (*cp != '-') && (*cp != '_')) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bad repost query: invalid charset \"%s\"", charset); return HTTP_BAD_REQUEST; } } } psf_id = am_extract_query_parameter(r->pool, query, "id"); if (psf_id == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bad repost query: missing id"); return HTTP_BAD_REQUEST; } /* Check that Id is sane */ for (cp = psf_id; *cp; cp++) { if (!apr_isalnum(*cp)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bad repost query: invalid id \"%s\"", psf_id); return HTTP_BAD_REQUEST; } } return_url = am_extract_query_parameter(r->pool, query, "ReturnTo"); if (return_url == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid or missing query ReturnTo parameter."); return HTTP_BAD_REQUEST; } if (am_urldecode(return_url) != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bad repost query: return"); return HTTP_BAD_REQUEST; } rc = am_validate_redirect_url(r, return_url); if (rc != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in repost request ReturnTo parameter."); return rc; } psf_filename = apr_psprintf(r->pool, "%s/%s", mod_cfg->post_dir, psf_id); post_data = am_getfile(r->pool, r->server, psf_filename); if (post_data == NULL) { /* Unable to load repost data. Just redirect us instead. */ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Bad repost query: cannot find \"%s\"", psf_filename); apr_table_setn(r->headers_out, "Location", return_url); return HTTP_SEE_OTHER; } if ((post_form = (*post_mkform)(r, post_data)) == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "am_post_mkform() failed"); return HTTP_INTERNAL_SERVER_ERROR; } if (charset != NULL) { ap_set_content_type(r, apr_psprintf(r->pool, "text/html; charset=\"%s\"", charset)); charset = apr_psprintf(r->pool, " accept-charset=\"%s\"", charset); } else { ap_set_content_type(r, "text/html"); charset = (char *)""; } output = apr_psprintf(r->pool, "\n" "\n" " \n" " SAML rePOST request\n" " \n" " \n" " \n" "
\n%s" " \n" "
\n" " \n" "\n", am_htmlencode(r, return_url), enctype, charset, post_form); ap_rputs(output, r); return OK; } /* This function handles responses to metadata request * * Parameters: * request_rec *r The request we received. * * Returns: * OK on success, or an error on failure. */ static int am_handle_metadata(request_rec *r) { #ifdef HAVE_lasso_server_new_from_buffers am_dir_cfg_rec *cfg = am_get_dir_cfg(r); LassoServer *server; const char *data; server = am_get_lasso_server(r); if(server == NULL) return HTTP_INTERNAL_SERVER_ERROR; cfg = cfg->inherit_server_from; data = cfg->sp_metadata_file; if (data == NULL) return HTTP_INTERNAL_SERVER_ERROR; ap_set_content_type(r, "application/samlmetadata+xml"); ap_rputs(data, r); return OK; #else /* ! HAVE_lasso_server_new_from_buffers */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "metadata publishing require lasso 2.2.2 or higher"); return HTTP_NOT_FOUND; #endif } /* Use Lasso Login to set the HTTP content & headers for HTTP-Redirect binding. * * Parameters: * request_rec *r * LassoLogin *login * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_set_authn_request_redirect_content(request_rec *r, LassoLogin *login) { char *redirect_to; /* The URL we should send the message to. */ redirect_to = apr_pstrdup(r->pool, LASSO_PROFILE(login)->msg_url); /* Check if the lasso library added the RelayState. If lasso didn't add * a RelayState parameter, then we add one ourself. This should hopefully * be removed in the future. */ if(strstr(redirect_to, "&RelayState=") == NULL && strstr(redirect_to, "?RelayState=") == NULL) { /* The url didn't contain the relaystate parameter. */ redirect_to = apr_pstrcat( r->pool, redirect_to, "&RelayState=", am_urlencode(r->pool, LASSO_PROFILE(login)->msg_relayState), NULL ); } apr_table_setn(r->headers_out, "Location", redirect_to); /* We don't want to include POST data (in case this was a POST request). */ return HTTP_SEE_OTHER; } /* Use Lasso Login to set the HTTP content & headers for HTTP-POST binding. * * Parameters: * request_rec *r The request we are processing. * LassoLogin *login The login message. * * Returns: * OK on success, or an error on failure. */ static int am_set_authn_request_post_content(request_rec *r, LassoLogin *login) { char *url; char *message; char *relay_state; char *output; url = am_htmlencode(r, LASSO_PROFILE(login)->msg_url); message = am_htmlencode(r, LASSO_PROFILE(login)->msg_body); relay_state = am_htmlencode(r, LASSO_PROFILE(login)->msg_relayState); output = apr_psprintf(r->pool, "\n" "\n" " \n" " \n" " POST data\n" " \n" " \n" " \n" "
\n" " \n" " \n" " \n" "
\n" " \n" "\n", url, message, relay_state); ap_set_content_type(r, "text/html"); ap_rputs(output, r); return OK; } /* Use Lasso Login to set the HTTP content & headers for PAOS binding. * * Parameters: * request_rec *r The request we are processing. * LassoLogin *login The login message. * * Returns: * OK on success, or an error on failure. */ static int am_set_authn_request_paos_content(request_rec *r, LassoLogin *login) { apr_table_setn(r->headers_out, "Content-Type", MEDIA_TYPE_PAOS); ap_rputs(LASSO_PROFILE(login)->msg_body, r); return OK; } /* * Create and initialize LassoLogin object * * This function creates a LassoLogin object and initializes it to the * greatest extent possible to allow it to be shared by multiple * callers. There are two return values. The function return is an * error code, the login_return parameter is a pointer in which to * receive the LassoLogin object. The caller MUST free the returned * login object using lasso_login_destroy() in all cases (even when * this function returns an error), the only execption is if the * returned LassoLogin is NULL. * * Parameters: * r The request we are processing. * login_return The returned LassoLogin object (caller must free) * idp The provider id of remote Idp * [optional, may be NULL] * http_method Specifies the SAML profile to use * destination_url If the idp parameter is non-NULL this should be * the URL of the IdP endpoint the message is being sent to * [optional, may be NULL] * assertion_consumer_service_url * The URL of this SP's endpoint which will receive the * SAML assertion * return_to_url Used to initialize the RelayState value * is_passive The SAML IsPassive flag * * Returns: * OK on success, HTTP error code otherwise * */ static int am_init_authn_request_common(request_rec *r, LassoLogin **login_return, const char *idp, LassoHttpMethod http_method, const char *destination_url, const char *assertion_consumer_service_url, const char *return_to_url, int is_passive) { gint ret; am_dir_cfg_rec *dir_cfg; LassoServer *server; LassoLogin *login; LassoSamlp2AuthnRequest *request; const char *sp_name; *login_return = NULL; dir_cfg = am_get_dir_cfg(r); server = am_get_lasso_server(r); if (server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } login = lasso_login_new(server); if(login == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error creating LassoLogin object from LassoServer."); return HTTP_INTERNAL_SERVER_ERROR; } *login_return = login; ret = lasso_login_init_authn_request(login, idp, http_method); if(ret != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error creating login request." " Lasso error: [%i] %s", ret, lasso_strerror(ret)); return HTTP_INTERNAL_SERVER_ERROR; } request = LASSO_SAMLP2_AUTHN_REQUEST(LASSO_PROFILE(login)->request); if (request->NameIDPolicy == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error creating login request. Please verify the " "MellonSPMetadataFile directive."); return HTTP_INTERNAL_SERVER_ERROR; } /* * Make sure the Destination attribute is set to the IdP * SingleSignOnService endpoint. This is required for * Shibboleth 2 interoperability, and older versions of * lasso (at least up to 2.2.91) did not do it. */ if (destination_url && LASSO_SAMLP2_REQUEST_ABSTRACT(request)->Destination == NULL) { lasso_assign_string(LASSO_SAMLP2_REQUEST_ABSTRACT(request)->Destination, destination_url); } if (assertion_consumer_service_url) { lasso_assign_string(request->AssertionConsumerServiceURL, assertion_consumer_service_url); /* Can't set request->ProtocolBinding (which is usually set along side * AssertionConsumerServiceURL) as there is no immediate function * like lasso_provider_get_assertion_consumer_service_url to get them. * So leave that empty for now, it is not strictly required */ } request->ForceAuthn = FALSE; request->IsPassive = is_passive; request->NameIDPolicy->AllowCreate = TRUE; sp_name = am_get_config_langstring(dir_cfg->sp_org_display_name, NULL); if (sp_name) { lasso_assign_string(request->ProviderName, sp_name); } LASSO_SAMLP2_REQUEST_ABSTRACT(request)->Consent = g_strdup(LASSO_SAML2_CONSENT_IMPLICIT); /* Add AuthnContextClassRef */ if (dir_cfg->authn_context_class_ref->nelts) { apr_array_header_t *refs = dir_cfg->authn_context_class_ref; int i = 0; LassoSamlp2RequestedAuthnContext *req_authn_context; req_authn_context = (LassoSamlp2RequestedAuthnContext*) lasso_samlp2_requested_authn_context_new(); request->RequestedAuthnContext = req_authn_context; for (i = 0; i < refs->nelts; i++) { const char *ref = ((char **)refs->elts)[i]; req_authn_context->AuthnContextClassRef = g_list_append(req_authn_context->AuthnContextClassRef, g_strdup(ref)); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "adding AuthnContextClassRef %s to the " "AuthnRequest", ref); } } LASSO_PROFILE(login)->msg_relayState = g_strdup(return_to_url); #ifdef HAVE_ECP { am_req_cfg_rec *req_cfg; ECPServiceOptions unsupported_ecp_options; req_cfg = am_get_req_cfg(r); /* * Currently we only support the WANT_AUTHN_SIGNED ECP option, * if a client sends us anything else let them know it's not * implemented. * * We do test for CHANNEL_BINDING below but that's because if * and when we support it we don't want to forget channel * bindings require the authn request to be signed. */ unsupported_ecp_options = req_cfg->ecp_service_options & ~ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED; if (unsupported_ecp_options) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unsupported ECP service options [%s]", am_ecp_service_options_str(r->pool, unsupported_ecp_options)); return HTTP_NOT_IMPLEMENTED; } /* * The signature hint must be set prior to calling * lasso_login_build_authn_request_msg */ if (req_cfg->ecp_service_options & (ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED | ECP_SERVICE_OPTION_CHANNEL_BINDING)) { /* * authnRequest should be signed if the client requested it * or if channel bindings are enabled. */ lasso_profile_set_signature_hint(LASSO_PROFILE(login), LASSO_PROFILE_SIGNATURE_HINT_FORCE); } } #endif ret = lasso_login_build_authn_request_msg(login); if (ret != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error building login request." " Lasso error: [%i] %s", ret, lasso_strerror(ret)); return HTTP_INTERNAL_SERVER_ERROR; } return OK; } /* Use Lasso Login to set the HTTP content & headers for selected binding. * * Parameters: * request_rec *r The request we are processing. * LassoLogin *login The login message. * * Returns: * HTTP response code */ static int am_set_authn_request_content(request_rec *r, LassoLogin *login) { switch (login->http_method) { case LASSO_HTTP_METHOD_REDIRECT: return am_set_authn_request_redirect_content(r, login); case LASSO_HTTP_METHOD_POST: return am_set_authn_request_post_content(r, login); case LASSO_HTTP_METHOD_PAOS: return am_set_authn_request_paos_content(r, login); default: /* We should never get here. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unsupported http_method."); return HTTP_INTERNAL_SERVER_ERROR; } } #ifdef HAVE_ECP /* Build an IDPList whose members have an endpoint supporing * the protocol_type and http_method. */ static LassoNode * am_get_idp_list(const LassoServer *server, LassoMdProtocolType protocol_type, LassoHttpMethod http_method) { GList *idp_entity_ids = NULL; GList *entity_id = NULL; GList *idp_entries = NULL; LassoSamlp2IDPList *idp_list; LassoSamlp2IDPEntry *idp_entry; idp_list = LASSO_SAMLP2_IDP_LIST(lasso_samlp2_idp_list_new()); idp_entity_ids = lasso_server_get_filtered_provider_list(server, LASSO_PROVIDER_ROLE_IDP, protocol_type, http_method); for (entity_id = g_list_first(idp_entity_ids); entity_id != NULL; entity_id = g_list_next(entity_id)) { idp_entry = LASSO_SAMLP2_IDP_ENTRY(lasso_samlp2_idp_entry_new()); idp_entry->ProviderID = g_strdup(entity_id->data); /* RFE: we should have a mechanism to obtain these values */ idp_entry->Name = NULL; idp_entry->Loc = NULL; idp_entries = g_list_append(idp_entries, idp_entry); } lasso_release_list_of_strings(idp_entity_ids); idp_list->IDPEntry = idp_entries; return LASSO_NODE(idp_list); } /* Send AuthnRequest using PAOS binding. * * Parameters: * request_rec *r * * Returns: * OK on success, or an error on failure. */ static int am_send_paos_authn_request(request_rec *r) { gint ret; am_dir_cfg_rec *dir_cfg; LassoServer *server; LassoLogin *login; const char *relay_state = NULL; char *assertion_consumer_service_url; int is_passive = FALSE; dir_cfg = am_get_dir_cfg(r); server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } relay_state = am_reconstruct_url(r); assertion_consumer_service_url = am_get_assertion_consumer_service_by_binding(LASSO_PROVIDER(server), "PAOS"); ret = am_init_authn_request_common(r, &login, NULL, LASSO_HTTP_METHOD_PAOS, NULL, assertion_consumer_service_url, relay_state, is_passive); g_free(assertion_consumer_service_url); if (ret != OK) { if (login) { lasso_login_destroy(login); } return ret; } if (CFG_VALUE(dir_cfg, ecp_send_idplist)) { lasso_profile_set_idp_list(LASSO_PROFILE(login), am_get_idp_list(LASSO_PROFILE(login)->server, LASSO_MD_PROTOCOL_TYPE_SINGLE_SIGN_ON, LASSO_HTTP_METHOD_SOAP)); } ret = am_set_authn_request_content(r, login); lasso_login_destroy(login); return ret; } #endif /* HAVE_ECP */ /* Create and send an authentication request. * * Parameters: * request_rec *r The request we are processing. * const char *idp The entityID of the IdP. * const char *return_to The URL we should redirect to when receiving the request. * int is_passive The value of the IsPassive flag in * * Returns: * HTTP response code indicating success or failure. */ static int am_send_login_authn_request(request_rec *r, const char *idp, const char *return_to_url, int is_passive) { int ret; LassoServer *server; LassoProvider *provider; LassoHttpMethod http_method; char *destination_url; char *assertion_consumer_service_url; LassoLogin *login; /* Add cookie for cookie test. We know that we should have * a valid cookie when we return from the IdP after SP-initiated * login. */ am_cookie_set(r, "cookietest"); server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } /* Find our IdP. */ provider = lasso_server_get_provider(server, idp); if (provider == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Could not find metadata for the IdP \"%s\".", idp); return HTTP_INTERNAL_SERVER_ERROR; } /* Determine what binding and endpoint we should use when * sending the request. */ http_method = LASSO_HTTP_METHOD_REDIRECT; destination_url = lasso_provider_get_metadata_one( provider, "SingleSignOnService HTTP-Redirect"); if (destination_url == NULL) { /* HTTP-Redirect unsupported - try HTTP-POST. */ http_method = LASSO_HTTP_METHOD_POST; destination_url = lasso_provider_get_metadata_one( provider, "SingleSignOnService HTTP-POST"); } if (destination_url == NULL) { /* Both HTTP-Redirect and HTTP-POST unsupported - give up. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Could not find a supported SingleSignOnService endpoint" " for the IdP \"%s\".", idp); return HTTP_INTERNAL_SERVER_ERROR; } assertion_consumer_service_url = lasso_provider_get_assertion_consumer_service_url( LASSO_PROVIDER(server), NULL); ret = am_init_authn_request_common(r, &login, idp, http_method, destination_url, assertion_consumer_service_url, return_to_url, is_passive); g_free(destination_url); g_free(assertion_consumer_service_url); if (ret != OK) { if (login) { lasso_login_destroy(login); } return ret; } ret = am_set_authn_request_content(r, login); lasso_login_destroy(login); return ret; } /* Handle the "auth" endpoint. * * This endpoint is included for backwards-compatibility. * * Parameters: * request_rec *r The request we received. * * Returns: * OK or HTTP_SEE_OTHER on success, an error on failure. */ static int am_handle_auth(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); const char *relay_state; relay_state = am_reconstruct_url(r); /* Check if IdP discovery is in use and no IdP was selected yet */ if ((cfg->discovery_url != NULL) && (am_extract_query_parameter(r->pool, r->args, "IdP") == NULL)) { return am_start_disco(r, relay_state); } /* If IdP discovery is in use and we have an IdP selected, * set the relay_state */ if (cfg->discovery_url != NULL) { char *return_url; return_url = am_extract_query_parameter(r->pool, r->args, "ReturnTo"); if ((return_url != NULL) && am_urldecode((char *)return_url) == 0) relay_state = return_url; } return am_send_login_authn_request(r, am_get_idp(r), relay_state, FALSE); } /* This function handles requests to the login handler. * * Parameters: * request_rec *r The request. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_handle_login(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); char *idp_param; const char *idp; char *return_to; int is_passive; int ret; return_to = am_extract_query_parameter(r->pool, r->args, "ReturnTo"); if(return_to == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing required ReturnTo parameter."); return HTTP_BAD_REQUEST; } ret = am_urldecode(return_to); if(ret != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error urldecoding ReturnTo parameter."); return ret; } ret = am_validate_redirect_url(r, return_to); if(ret != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in login request ReturnTo parameter."); return ret; } idp_param = am_extract_query_parameter(r->pool, r->args, "IdP"); if(idp_param != NULL) { ret = am_urldecode(idp_param); if(ret != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error urldecoding IdP parameter."); return ret; } } ret = am_get_boolean_query_parameter(r, "IsPassive", &is_passive, FALSE); if (ret != OK) { return ret; } if(idp_param != NULL) { idp = idp_param; } else if(cfg->discovery_url) { if(is_passive) { /* We cannot currently do discovery with passive authentication requests. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Discovery service with passive authentication request unsupported."); return HTTP_INTERNAL_SERVER_ERROR; } return am_start_disco(r, return_to); } else { /* No discovery service -- just use the default IdP. */ idp = am_get_idp(r); } return am_send_login_authn_request(r, idp, return_to, is_passive); } /* This function probes an URL (HTTP GET) * * Parameters: * request_rec *r The request. * const char *url The URL * int timeout Timeout in seconds * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_probe_url(request_rec *r, const char *url, int timeout) { void *dontcare; apr_size_t len; long status; int error; status = 0; if ((error = am_httpclient_get(r, url, &dontcare, &len, timeout, &status)) != OK) return error; if (status != HTTP_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Probe on \"%s\" returned HTTP %ld", url, status); return status; } return OK; } /* This function handles requests to the probe discovery handler * * Parameters: * request_rec *r The request. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_handle_probe_discovery(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); LassoServer *server; const char *disco_idp = NULL; int timeout; char *return_to; char *idp_param; char *redirect_url; int ret; server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } /* * If built-in IdP discovery is not configured, return error. * For now we only have the get-metadata metadata method, so this * information is not saved in configuration nor it is checked here. */ timeout = cfg->probe_discovery_timeout; if (timeout == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "probe discovery handler invoked but not " "configured. Plase set MellonProbeDiscoveryTimeout."); return HTTP_INTERNAL_SERVER_ERROR; } /* * Check for mandatory arguments early to avoid sending * probles for nothing. */ return_to = am_extract_query_parameter(r->pool, r->args, "return"); if(return_to == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing required return parameter."); return HTTP_BAD_REQUEST; } ret = am_urldecode(return_to); if (ret != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, ret, r, "Could not urldecode return value."); return HTTP_BAD_REQUEST; } ret = am_validate_redirect_url(r, return_to); if (ret != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in probe discovery return parameter."); return ret; } idp_param = am_extract_query_parameter(r->pool, r->args, "returnIDParam"); if(idp_param == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing required returnIDParam parameter."); return HTTP_BAD_REQUEST; } ret = am_urldecode(idp_param); if (ret != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, ret, r, "Could not urldecode returnIDParam value."); return HTTP_BAD_REQUEST; } /* * Proceed with built-in IdP discovery. * * First try sending probes to IdP configured for discovery. * Second send probes for all configured IdP * The first to answer is chosen. * If none answer, use the first configured IdP */ if (!apr_is_empty_table(cfg->probe_discovery_idp)) { const apr_array_header_t *header; apr_table_entry_t *elts; const char *url; const char *idp; int i; header = apr_table_elts(cfg->probe_discovery_idp); elts = (apr_table_entry_t *)header->elts; for (i = 0; i < header->nelts; i++) { idp = elts[i].key; url = elts[i].val; if (am_probe_url(r, url, timeout) == OK) { disco_idp = idp; break; } } } else { GList *iter; GList *idp_list; const char *idp; idp_list = g_hash_table_get_keys(server->providers); for (iter = idp_list; iter != NULL; iter = iter->next) { idp = iter->data; if (am_probe_url(r, idp, timeout) == OK) { disco_idp = idp; break; } } g_list_free(idp_list); } /* * On failure, try default */ if (disco_idp == NULL) { disco_idp = am_first_idp(r); if (disco_idp == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "probeDiscovery found no usable IdP."); return HTTP_INTERNAL_SERVER_ERROR; } else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "probeDiscovery " "failed, trying default IdP %s", disco_idp); } } else { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "probeDiscovery using %s", disco_idp); } redirect_url = apr_psprintf(r->pool, "%s%s%s=%s", return_to, strchr(return_to, '?') ? "&" : "?", am_urlencode(r->pool, idp_param), am_urlencode(r->pool, disco_idp)); apr_table_setn(r->headers_out, "Location", redirect_url); return HTTP_SEE_OTHER; } /* This function handles responses to request on our endpoint * * Parameters: * request_rec *r The request we received. * * Returns: * OK on success, or an error on failure. */ int am_handler(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); #ifdef HAVE_ECP am_req_cfg_rec *req_cfg = am_get_req_cfg(r); #endif /* HAVE_ECP */ const char *endpoint; /* * Normally this content handler is used to dispatch to the SAML * endpoints implmented by mod_auth_mellon. SAML endpoint dispatch * occurs when the URI begins with the SAML endpoint path. * * However, this handler is also responsible for generating ECP * authn requests, in this case the URL will be a protected * resource we're doing authtentication for. Early in the request * processing pipeline we detected we were doing ECP authn and set * a flag on the request. Here we test for that flag and if true * respond with the ECP PAOS authn request. * * If the request is neither for a SAML endpoint nor one that * requires generating an ECP authn we decline handling the request. */ #ifdef HAVE_ECP if (req_cfg->ecp_authn_req) { /* Are we doing ECP? */ return am_send_paos_authn_request(r); } #endif /* HAVE_ECP */ /* Check if this is a request for one of our endpoints. We check if * the uri starts with the path set with the MellonEndpointPath * configuration directive. */ if(strstr(r->uri, cfg->endpoint_path) != r->uri) return DECLINED; endpoint = &r->uri[strlen(cfg->endpoint_path)]; if (!strcmp(endpoint, "metadata")) { return am_handle_metadata(r); } else if (!strcmp(endpoint, "repost")) { return am_handle_repost(r); } else if(!strcmp(endpoint, "postResponse")) { return am_handle_post_reply(r); } else if(!strcmp(endpoint, "artifactResponse")) { return am_handle_artifact_reply(r); } else if(!strcmp(endpoint, "paosResponse")) { return am_handle_paos_reply(r); } else if(!strcmp(endpoint, "auth")) { return am_handle_auth(r); } else if(!strcmp(endpoint, "logout") || !strcmp(endpoint, "logoutRequest")) { /* logoutRequest is included for backwards-compatibility * with version 0.0.6 and older. */ return am_handle_logout(r); } else if(!strcmp(endpoint, "login")) { return am_handle_login(r); } else if(!strcmp(endpoint, "probeDisco")) { return am_handle_probe_discovery(r); } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Endpoint \"%s\" not handled by mod_auth_mellon.", endpoint); return HTTP_NOT_FOUND; } } /** * Trigger a login operation from a "normal" request. * * Parameters: * request_rec *r The request we received. * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_start_auth(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); const char *endpoint = am_get_endpoint_url(r); const char *return_to; const char *idp; const char *login_url; return_to = am_reconstruct_url(r); /* If this is a POST request, attempt to save it */ if (r->method_number == M_POST) { if (CFG_VALUE(cfg, post_replay)) { if (am_save_post(r, &return_to) != OK) return HTTP_INTERNAL_SERVER_ERROR; } else { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "POST data dropped because we do not have a" " MellonPostReplay is not enabled."); } } /* Check if IdP discovery is in use. */ if (cfg->discovery_url) { return am_start_disco(r, return_to); } idp = am_get_idp(r); login_url = apr_psprintf(r->pool, "%slogin?ReturnTo=%s&IdP=%s", endpoint, am_urlencode(r->pool, return_to), am_urlencode(r->pool, idp)); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Redirecting to login URL: %s", login_url); apr_table_setn(r->headers_out, "Location", login_url); return HTTP_SEE_OTHER; } int am_auth_mellon_user(request_rec *r) { am_dir_cfg_rec *dir = am_get_dir_cfg(r); int return_code = HTTP_UNAUTHORIZED; am_cache_entry_t *session; if (r->main) { /* We are a subrequest. Trust the main request to have * performed the authentication. */ return OK; } /* Check that the user has enabled authentication for this directory. */ if(dir->enable_mellon == am_enable_off || dir->enable_mellon == am_enable_default) { return DECLINED; } /* Set defaut Cache-Control headers within this location */ am_set_cache_control_headers(r); /* Check if this is a request for one of our endpoints. We check if * the uri starts with the path set with the MellonEndpointPath * configuration directive. */ if(strstr(r->uri, dir->endpoint_path) == r->uri) { /* No access control on our internal endpoints. */ return OK; } /* Get the session of this request. */ session = am_get_request_session(r); if(dir->enable_mellon == am_enable_auth) { /* This page requires the user to be authenticated and authorized. */ if(session == NULL || !session->logged_in) { /* We don't have a valid session. */ if(session) { /* Release the session. */ am_release_request_session(r, session); } #ifdef HAVE_ECP /* * If PAOS set a flag on the request indicating we're * doing ECP and allow the request to proceed through the * request handlers until we reach am_handler which then * checks the flag and if True initiates an ECP transaction. * See am_check_uid for detailed explanation. */ bool is_paos; int error_code; is_paos = am_is_paos_request(r, &error_code); if (error_code) return HTTP_BAD_REQUEST; if (is_paos) { am_req_cfg_rec *req_cfg; req_cfg = am_get_req_cfg(r); req_cfg->ecp_authn_req = true; return OK; } else { /* Send the user to the authentication page on the IdP. */ return am_start_auth(r); } #else /* HAVE_ECP */ /* Send the user to the authentication page on the IdP. */ return am_start_auth(r); #endif /* HAVE_ECP */ } /* Verify that the user has access to this resource. */ return_code = am_check_permissions(r, session); if(return_code != OK) { am_release_request_session(r, session); return return_code; } /* The user has been authenticated, and we can now populate r->user * and the r->subprocess_env with values from the session store. */ am_cache_env_populate(r, session); /* Release the session. */ am_release_request_session(r, session); return OK; } else { /* dir->enable_mellon == am_enable_info: * We should pass information about the user to the web application * if the user is authorized to access this resource. * However, we shouldn't attempt to do any access control. */ if(session != NULL && session->logged_in && am_check_permissions(r, session) == OK) { /* The user is authenticated and has access to the resource. * Now we populate the environment with information about * the user. */ am_cache_env_populate(r, session); } if(session != NULL) { /* Release the session. */ am_release_request_session(r, session); } /* We shouldn't really do any access control, so we always return * DECLINED. */ return DECLINED; } } int am_check_uid(request_rec *r) { am_dir_cfg_rec *dir = am_get_dir_cfg(r); am_cache_entry_t *session; int return_code = HTTP_UNAUTHORIZED; if (r->main) { /* We are a subrequest. Trust the main request to have * performed the authentication. */ if (r->main->user) { /* Make sure that the username from the main request is * available in the subrequest. */ r->user = apr_pstrdup(r->pool, r->main->user); } return OK; } #ifdef HAVE_ECP am_req_cfg_rec *req_cfg = am_get_req_cfg(r); if (req_cfg->ecp_authn_req) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "am_check_uid is performing ECP authn request flow"); /* * Normally when a protected resource requires authentication * the request processing pipeline is exited early by * responding with either a 401 or a redirect. But the flow * for ECP is different, there will be a successful response * (200) but instead of the response body containing the * protected resource it will contain a SAML AuthnRequest * with the Content-Type indicating it's PAOS ECP. * * In order to return a 200 Success with a PAOS body we have * to reach the handler stage of the request processing * pipeline. But this is a protected resource and we won't * reach the handler stage unless authn and authz are * satisfied. Therefore we lie and return results which * indicate authn and authz are satisfied. This is OK because * we're not actually going to respond with the protected * resource, instead we'll be responsing with a SAML request. * * Apache's internal request logic * (ap_process_request_internal) requires that after a * successful return from the check_user_id authentication * hook the r->user value be non-NULL. This makes sense * because authentication establishes who the authenticated * principal is. But with ECP flow there is no authenticated * user at this point, we're just faking successful * authentication in order to reach the handler stage. To get * around this problem we set r-user to the empty string to * keep Apache happy, otherwise it would throw an * error. mod_shibboleth does the same thing. */ r->user = ""; return OK; } #endif /* HAVE_ECP */ /* Check if this is a request for one of our endpoints. We check if * the uri starts with the path set with the MellonEndpointPath * configuration directive. */ if(strstr(r->uri, dir->endpoint_path) == r->uri) { /* No access control on our internal endpoints. */ r->user = ""; /* see above explanation */ return OK; } /* Get the session of this request. */ session = am_get_request_session(r); /* If we don't have a session, then we can't authorize the user. */ if(session == NULL) { return HTTP_UNAUTHORIZED; } /* If the user isn't logged in, then we can't authorize the user. */ if(!session->logged_in) { am_release_request_session(r, session); return HTTP_UNAUTHORIZED; } /* Verify that the user has access to this resource. */ return_code = am_check_permissions(r, session); if(return_code != OK) { am_release_request_session(r, session); return HTTP_UNAUTHORIZED; } /* The user has been authenticated, and we can now populate r->user * and the r->subprocess_env with values from the session store. */ am_cache_env_populate(r, session); /* Release the session. */ am_release_request_session(r, session); return OK; } mod_auth_mellon-0.12.0/auth_mellon_util.c0000644000175100017510000022207612667763074020061 0ustar olavmoolavmo/* * * auth_mellon_util.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 #include #include #include "auth_mellon.h" /* This function is used to get the url of the current request. * * Parameters: * request_rec *r The current request. * * Returns: * A string containing the full url of the current request. * The string is allocated from r->pool. */ char *am_reconstruct_url(request_rec *r) { char *url; /* This function will construct an full url for a given path relative to * the root of the web site. To configure what hostname and port this * function will use, see the UseCanonicalName configuration directive. */ url = ap_construct_url(r->pool, r->unparsed_uri, r); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "reconstruct_url: url==\"%s\", unparsed_uri==\"%s\"", url, r->unparsed_uri); return url; } /* Get the hostname of the current request. * * Parameters: * request_rec *r The current request. * * Returns: * The hostname of the current request. */ static const char *am_request_hostname(request_rec *r) { const char *url; apr_uri_t uri; int ret; url = am_reconstruct_url(r); ret = apr_uri_parse(r->pool, url, &uri); if (ret != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to parse request URL: %s", url); return NULL; } if (uri.hostname == NULL) { /* This shouldn't happen, since the request URL is built with a hostname, * but log a message to make any debuggin around this code easier. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No hostname in request URL: %s", url); return NULL; } return uri.hostname; } /* Validate the redirect URL. * * Checks that the redirect URL is to a trusted domain & scheme. * * Parameters: * request_rec *r The current request. * const char *url The redirect URL to validate. * * Returns: * OK if the URL is valid, HTTP_BAD_REQUEST if not. */ int am_validate_redirect_url(request_rec *r, const char *url) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); apr_uri_t uri; int ret; ret = apr_uri_parse(r->pool, url, &uri); if (ret != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid redirect URL: %s", url); return HTTP_BAD_REQUEST; } /* Sanity check of the scheme of the domain. We only allow http and https. */ if (uri.scheme) { if (strcasecmp(uri.scheme, "http") && strcasecmp(uri.scheme, "https")) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Only http or https scheme allowed in redirect URL: %s (%s)", url, uri.scheme); return HTTP_BAD_REQUEST; } } if (!uri.hostname) { return OK; /* No hostname to check. */ } for (int i = 0; cfg->redirect_domains[i] != NULL; i++) { const char *redirect_domain = cfg->redirect_domains[i]; if (!strcasecmp(redirect_domain, "[self]")) { if (!strcasecmp(uri.hostname, am_request_hostname(r))) { return OK; } } else if (apr_fnmatch(redirect_domain, uri.hostname, APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_SUCCESS) { return OK; } } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Untrusted hostname (%s) in redirect URL: %s", uri.hostname, url); return HTTP_BAD_REQUEST; } /* This function builds an array of regexp backreferences * * Parameters: * request_rec *r The current request. * const am_cond_t *ce The condition * const char *value Attribute value * const ap_regmatch_t *regmatch regmatch_t from ap_regexec() * * Returns: * An array of collected backreference strings */ const apr_array_header_t *am_cond_backrefs(request_rec *r, const am_cond_t *ce, const char *value, const ap_regmatch_t *regmatch) { apr_array_header_t *backrefs; const char **ref; int nsub; int i; nsub = ce->regex->re_nsub + 1; /* +1 for %0 */ backrefs = apr_array_make(r->pool, nsub, sizeof(const char *)); backrefs->nelts = nsub; ref = (const char **)(backrefs->elts); for (i = 0; i < nsub; i++) { if ((regmatch[i].rm_so == -1) || (regmatch[i].rm_eo == -1)) { ref[i] = ""; } else { int len = regmatch[i].rm_eo - regmatch[i].rm_so; int off = regmatch[i].rm_so; ref[i] = apr_pstrndup(r->pool, value + off, len); } } return (const apr_array_header_t *)backrefs; } /* This function clones an am_cond_t and substitute value to * match (both regexp and string) with backreferences from * a previous regex match. * * Parameters: * request_rec *r The current request. * const am_cond_t *cond The am_cond_t to clone and substiture * const apr_array_header_t *backrefs Collected backreferences * * Returns: * The cloned am_cond_t */ const am_cond_t *am_cond_substitue(request_rec *r, const am_cond_t *ce, const apr_array_header_t *backrefs) { am_cond_t *c; const char *instr = ce->str; apr_size_t inlen = strlen(instr); const char *outstr = ""; size_t last; size_t i; c = (am_cond_t *)apr_pmemdup(r->pool, ce, sizeof(*ce)); c->str = outstr; last = 0; for (i = strcspn(instr, "%"); i < inlen; i += strcspn(instr + i, "%")) { const char *fstr; const char *ns; const char *name; const char *value; apr_size_t flen; apr_size_t pad; apr_size_t nslen; /* * Make sure we got a % */ assert(instr[i] == '%'); /* * Copy the format string in fstr. It can be a single * digit (e.g.: %1) , or a curly-brace enclosed text * (e.g.: %{12}) */ fstr = instr + i + 1; if (*fstr == '{') { /* Curly-brace enclosed text */ pad = 3; /* 3 for %{} */ fstr++; flen = strcspn(fstr, "}"); /* If there is no closing }, we do not substitute */ if (fstr[flen] == '\0') { pad = 2; /* 2 for %{ */ i += flen + pad; break; } } else if (*fstr == '\0') { /* String ending by a % */ break; } else { /* Single digit */ pad = 1; /* 1 for % */ flen = 1; } /* * Try to extract a namespace (ns) and a name, e.g: %{ENV:foo} */ fstr = apr_pstrndup(r->pool, fstr, flen); if ((nslen = strcspn(fstr, ":")) != flen) { ns = apr_pstrndup(r->pool, fstr, nslen); name = fstr + nslen + 1; /* +1 for : */ } else { nslen = 0; ns = ""; name = fstr; } value = NULL; if ((*ns == '\0') && (strspn(fstr, "0123456789") == flen)) { /* * If fstr has only digits, this is a regexp backreference */ int d = (int)apr_atoi64(fstr); if ((d >= 0) && (d < backrefs->nelts)) value = ((const char **)(backrefs->elts))[d]; } else if ((*ns == '\0') && (strcmp(fstr, "%") == 0)) { /* * %-escape */ value = fstr; } else if (strcmp(ns, "ENV") == 0) { /* * ENV namespace. Get value from apache environment */ value = getenv(name); } /* * If we did not find a value, substitue the * format string with an empty string. */ if (value == NULL) value = ""; /* * Concatenate the value with leading text, and * keep track * of the last location we copied in source string */ outstr = apr_pstrcat(r->pool, outstr, apr_pstrndup(r->pool, instr + last, i - last), value, NULL); last = i + flen + pad; /* * Move index to the end of the format string */ i += flen + pad; } /* * Copy text remaining after the last format string. */ outstr = apr_pstrcat(r->pool, outstr, apr_pstrndup(r->pool, instr + last, i - last), NULL); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Directive %s, \"%s\" substituted into \"%s\"", ce->directive, instr, outstr); /* * If this was a regexp, recompile it. */ if (ce->flags & AM_COND_FLAG_REG) { int regex_flags = AP_REG_EXTENDED|AP_REG_NOSUB; if (ce->flags & AM_COND_FLAG_NC) regex_flags |= AP_REG_ICASE; c->regex = ap_pregcomp(r->pool, outstr, regex_flags); if (c->regex == NULL) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Invalid regular expression \"%s\"", outstr); return ce; } } return (const am_cond_t *)c; } /* This function checks if the user has access according * to the MellonRequire and MellonCond directives. * * Parameters: * request_rec *r The current request. * am_cache_entry_t *session The current session. * * Returns: * OK if the user has access and HTTP_FORBIDDEN if he doesn't. */ int am_check_permissions(request_rec *r, am_cache_entry_t *session) { am_dir_cfg_rec *dir_cfg; int i, j; int skip_or = 0; const apr_array_header_t *backrefs = NULL; dir_cfg = am_get_dir_cfg(r); /* Iterate over all cond-directives */ for (i = 0; i < dir_cfg->cond->nelts; i++) { const am_cond_t *ce; const char *value = NULL; int match = 0; ce = &((am_cond_t *)(dir_cfg->cond->elts))[i]; /* * Rule with ignore flog? */ if (ce->flags & AM_COND_FLAG_IGN) continue; /* * We matched a [OR] rule, skip the next rules * until we have one without [OR]. */ if (skip_or) { if (!(ce->flags & AM_COND_FLAG_OR)) skip_or = 0; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Skip %s, [OR] rule matched previously", ce->directive); continue; } /* * look for a match on each value for this attribute, * stop on first match. */ for (j = 0; (j < session->size) && !match; j++) { const char *varname = NULL; am_envattr_conf_t *envattr_conf = NULL; /* * if MAP flag is set, check for remapped * attribute name with mellonSetEnv */ if (ce->flags & AM_COND_FLAG_MAP) { envattr_conf = (am_envattr_conf_t *)apr_hash_get(dir_cfg->envattr, am_cache_entry_get_string(session,&session->env[j].varname), APR_HASH_KEY_STRING); if (envattr_conf != NULL) varname = envattr_conf->name; } /* * Otherwise or if not found, use the attribute name * sent by the IdP. */ if (varname == NULL) varname = am_cache_entry_get_string(session, &session->env[j].varname); if (strcmp(varname, ce->varname) != 0) continue; value = am_cache_entry_get_string(session, &session->env[j].value); /* * Substiture backrefs if available */ if ((ce->flags & AM_COND_FLAG_FSTR) && (backrefs != NULL)) ce = am_cond_substitue(r, ce, backrefs); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Evaluate %s vs \"%s\"", ce->directive, value); if (value == NULL) { match = 0; /* can not happen */ } else if (ce->flags & (AM_COND_FLAG_REG|AM_COND_FLAG_REF)) { int nsub = ce->regex->re_nsub + 1; ap_regmatch_t *regmatch; regmatch = (ap_regmatch_t *)apr_palloc(r->pool, nsub * sizeof(*regmatch)); match = !ap_regexec(ce->regex, value, nsub, regmatch, 0); if (match) backrefs = am_cond_backrefs(r, ce, value, regmatch); } else if (ce->flags & AM_COND_FLAG_REG) { match = !ap_regexec(ce->regex, value, 0, NULL, 0); } else if (ce->flags & (AM_COND_FLAG_SUB|AM_COND_FLAG_NC)) { match = (strcasestr(ce->str, value) != NULL); } else if (ce->flags & AM_COND_FLAG_SUB) { match = (strstr(ce->str, value) != NULL); } else if (ce->flags & AM_COND_FLAG_NC) { match = !strcasecmp(ce->str, value); } else { match = !strcmp(ce->str, value); } } if (ce->flags & AM_COND_FLAG_NOT) match = !match; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "%s: %smatch", ce->directive, (match == 0) ? "no ": ""); /* * If no match, we stop here, except if it is an [OR] condition */ if (!match & !(ce->flags & AM_COND_FLAG_OR)) { ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Client failed to match %s", ce->directive); return HTTP_FORBIDDEN; } /* * Match on [OR] condition means we skip until a rule * without [OR], */ if (match && (ce->flags & AM_COND_FLAG_OR)) skip_or = 1; } return OK; } /* This function sets default Cache-Control headers. * * Parameters: * request_rec *r The request we are handling. * * Returns: * Nothing. */ void am_set_cache_control_headers(request_rec *r) { /* Send Cache-Control header to ensure that: * - no proxy in the path caches content inside this location (private), * - user agent have to revalidate content on server (must-revalidate). * - content is always stale as the session login status can change at any * time synchronously (Redirect logout, session cookie is removed) or * asynchronously (SOAP logout, session cookie still exists but is * invalid), * * But never prohibit specifically any user agent to cache or store content * * Setting the headers in err_headers_out ensures that they will be * sent for all responses. */ apr_table_setn(r->err_headers_out, "Cache-Control", "private, max-age=0, must-revalidate"); } /* This function reads the post data for a request. * * The data is stored in a buffer allocated from the request pool. * After successful operation *data contains a pointer to the data and * *length contains the length of the data. * The data will always be null-terminated. * * Parameters: * request_rec *r The request we read the form data from. * char **data Pointer to where we will store the pointer * to the data we read. * apr_size_t *length Pointer to where we will store the length * of the data we read. Pass NULL if you don't * need to know the length of the data. * * Returns: * OK if we successfully read the POST data. * An error if we fail to read the data. */ int am_read_post_data(request_rec *r, char **data, apr_size_t *length) { apr_size_t bytes_read; apr_size_t bytes_left; apr_size_t len; long read_length; int rc; /* Prepare to receive data from the client. We request that apache * dechunks data if it is chunked. */ rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); if (rc != OK) { return rc; } /* This function will send a 100 Continue response if the client is * waiting for that. If the client isn't going to send data, then this * function will return 0. */ if (!ap_should_client_block(r)) { len = 0; } else { len = r->remaining; } if (len >= 1024*1024) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Too large POST data payload (%lu bytes).", (unsigned long)len); return HTTP_BAD_REQUEST; } if (length != NULL) { *length = len; } *data = (char *)apr_palloc(r->pool, len + 1); if (*data == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to allocate memory for %lu bytes of POST data.", (unsigned long)len); return HTTP_INTERNAL_SERVER_ERROR; } /* Make sure that the data is null-terminated. */ (*data)[len] = '\0'; bytes_read = 0; bytes_left = len; while (bytes_left > 0) { /* Read data from the client. Returns 0 on EOF and -1 on * error, the number of bytes otherwise. */ read_length = ap_get_client_block(r, &(*data)[bytes_read], bytes_left); if (read_length == 0) { /* got the EOF */ (*data)[bytes_read] = '\0'; if (length != NULL) { *length = bytes_read; } break; } else if (read_length < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to read POST data from client."); return HTTP_INTERNAL_SERVER_ERROR; } bytes_read += read_length; bytes_left -= read_length; } return OK; } /* extract_query_parameter is a function which extracts the value of * a given parameter in a query string. The query string can be the * query_string parameter of a GET request, or it can be the data * passed to the web server in a POST request. * * Parameters: * apr_pool_t *pool The memory pool which the memory for * the value will be allocated from. * const char *query_string Either the query_string from a GET * request, or the data from a POST * request. * const char *name The name of the parameter to extract. * Note that the search for this name is * case sensitive. * * Returns: * The value of the parameter or NULL if we don't find the parameter. */ char *am_extract_query_parameter(apr_pool_t *pool, const char *query_string, const char *name) { const char *ip; const char *value_end; apr_size_t namelen; if (query_string == NULL) { return NULL; } ip = query_string; namelen = strlen(name); /* Find parameter. Searches for /[^&][&=$]/. * Moves ip to the first character after the name (either '&', '=' * or '\0'). */ for (;;) { /* First we find the name of the parameter. */ ip = strstr(ip, name); if (ip == NULL) { /* Parameter not found. */ return NULL; } /* Then we check what is before the parameter name. */ if (ip != query_string && ip[-1] != '&') { /* Name not preceded by [^&]. */ ip++; continue; } /* And last we check what follows the parameter name. */ if (ip[namelen] != '=' && ip[namelen] != '&' && ip[namelen] != '\0') { /* Name not followed by [&=$]. */ ip++; continue; } /* We have found the pattern. */ ip += namelen; break; } /* Now ip points to the first character after the name. If this * character is '&' or '\0', then this field doesn't have a value. * If this character is '=', then this field has a value. */ if (ip[0] == '=') { ip += 1; } /* The value is from ip to '&' or to the end of the string, whichever * comes first. */ value_end = strchr(ip, '&'); if (value_end != NULL) { /* '&' comes first. */ return apr_pstrndup(pool, ip, value_end - ip); } else { /* Value continues until the end of the string. */ return apr_pstrdup(pool, ip); } } /* Convert a hexadecimal digit to an integer. * * Parameters: * char c The digit we should convert. * * Returns: * The digit as an integer, or -1 if it isn't a hex digit. */ static int am_unhex_digit(char c) { if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'a' && c <= 'f') { return c - 'a' + 0xa; } else if (c >= 'A' && c <= 'F') { return c - 'A' + 0xa; } else { return -1; } } /* This function urldecodes a string in-place. * * Parameters: * char *data The string to urldecode. * * Returns: * OK if successful or HTTP_BAD_REQUEST if any escape sequence decodes to a * null-byte ('\0'), or if an invalid escape sequence is found. */ int am_urldecode(char *data) { char *ip; char *op; int c1, c2; if (data == NULL) { return HTTP_BAD_REQUEST; } ip = data; op = data; while (*ip) { switch (*ip) { case '+': *op = ' '; ip++; op++; break; case '%': /* Decode the hex digits. Note that we need to check the * result of the first conversion before attempting the * second conversion -- otherwise we may read past the end * of the string. */ c1 = am_unhex_digit(ip[1]); if (c1 < 0) { return HTTP_BAD_REQUEST; } c2 = am_unhex_digit(ip[2]); if (c2 < 0) { return HTTP_BAD_REQUEST; } *op = (c1 << 4) | c2; if (*op == '\0') { /* null-byte. */ return HTTP_BAD_REQUEST; } ip += 3; op++; break; default: *op = *ip; ip++; op++; } } *op = '\0'; return OK; } /* This function urlencodes a string. It will escape all characters * except a-z, A-Z, 0-9, '_' and '.'. * * Parameters: * apr_pool_t *pool The pool we should allocate memory from. * const char *str The string we should urlencode. * * Returns: * The urlencoded string, or NULL if str == NULL. */ char *am_urlencode(apr_pool_t *pool, const char *str) { const char *ip; apr_size_t length; char *ret; char *op; int hi, low; /* Return NULL if str is NULL. */ if(str == NULL) { return NULL; } /* Find the length of the output string. */ length = 0; for(ip = str; *ip; ip++) { if(*ip >= 'a' && *ip <= 'z') { length++; } else if(*ip >= 'A' && *ip <= 'Z') { length++; } else if(*ip >= '0' && *ip <= '9') { length++; } else if(*ip == '_' || *ip == '.') { length++; } else { length += 3; } } /* Add space for null-terminator. */ length++; /* Allocate memory for string. */ ret = (char *)apr_palloc(pool, length); /* Encode string. */ for(ip = str, op = ret; *ip; ip++, op++) { if(*ip >= 'a' && *ip <= 'z') { *op = *ip; } else if(*ip >= 'A' && *ip <= 'Z') { *op = *ip; } else if(*ip >= '0' && *ip <= '9') { *op = *ip; } else if(*ip == '_' || *ip == '.') { *op = *ip; } else { *op = '%'; op++; hi = (*ip & 0xf0) >> 4; if(hi < 0xa) { *op = '0' + hi; } else { *op = 'A' + hi - 0xa; } op++; low = *ip & 0x0f; if(low < 0xa) { *op = '0' + low; } else { *op = 'A' + low - 0xa; } } } /* Make output string null-terminated. */ *op = '\0'; return ret; } /* * Check that a URL is safe for redirect. * * Parameters: * request_rec *r The request we are processing. * const char *url The URL we should check. * * Returns: * OK on success, HTTP_BAD_REQUEST otherwise. */ int am_check_url(request_rec *r, const char *url) { const char *i; for (i = url; *i; i++) { if (*i >= 0 && *i < ' ') { /* Deny all control-characters. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, HTTP_BAD_REQUEST, r, "Control character detected in URL."); return HTTP_BAD_REQUEST; } } return OK; } /* This function generates a given number of (pseudo)random bytes. * The current implementation uses OpenSSL's RAND_*-functions. * * Parameters: * request_rec *r The request we are generating random bytes for. * The request is used for configuration and * error/warning reporting. * void *dest The address if the buffer we should fill with data. * apr_size_t count The number of random bytes to create. * * Returns: * OK on success, or HTTP_INTERNAL_SERVER on failure. */ int am_generate_random_bytes(request_rec *r, void *dest, apr_size_t count) { int rc; rc = RAND_bytes((unsigned char *)dest, (int)count); if(rc != 1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error generating random data: %lu", ERR_get_error()); return HTTP_INTERNAL_SERVER_ERROR; } return OK; } /* This function generates an id which is AM_ID_LENGTH characters long. * The id will consist of hexadecimal characters. * * Parameters: * request_rec *r The request we associate allocated memory with. * * Returns: * The session id, made up of AM_ID_LENGTH hexadecimal characters, * terminated by a null-byte. */ char *am_generate_id(request_rec *r) { int rc; char *ret; int rand_data_len; unsigned char *rand_data; int i; unsigned char b; int hi, low; ret = (char *)apr_palloc(r->pool, AM_ID_LENGTH + 1); /* We need to round the length of the random data _up_, in case the * length of the session id isn't even. */ rand_data_len = (AM_ID_LENGTH + 1) / 2; /* Fill the last rand_data_len bytes of the string with * random bytes. This allows us to overwrite from the beginning of * the string. */ rand_data = (unsigned char *)&ret[AM_ID_LENGTH - rand_data_len]; /* Generate random numbers. */ rc = am_generate_random_bytes(r, rand_data, rand_data_len); if(rc != OK) { return NULL; } /* Convert the random bytes to hexadecimal. Note that we will write * AM_ID_LENGTH+1 characters if we have a non-even length of the * session id. This is OK - we will simply overwrite the last character * with the null-terminator afterwards. */ for(i = 0; i < AM_ID_LENGTH; i += 2) { b = rand_data[i / 2]; hi = (b >> 4) & 0xf; low = b & 0xf; if(hi >= 0xa) { ret[i] = 'a' + hi - 0xa; } else { ret[i] = '0' + hi; } if(low >= 0xa) { ret[i+1] = 'a' + low - 0xa; } else { ret[i+1] = '0' + low; } } /* Add null-terminator- */ ret[AM_ID_LENGTH] = '\0'; return ret; } /* This returns the directroy part of a path, a la dirname(3) * * Parameters: * apr_pool_t p Pool to allocate memory from * const char *path Path to extract directory from * * Returns: * The directory part of path */ const char *am_filepath_dirname(apr_pool_t *p, const char *path) { char *cp; /* * Try Unix and then Windows style. Borrowed from * apr_match_glob(), it seems it cannot be made more * portable. */ if (((cp = strrchr(path, (int)'/')) == NULL) && ((cp = strrchr(path, (int)'\\')) == NULL)) return "."; return apr_pstrndup(p, path, cp - path); } /* * malloc a buffer and fill it with a given file * * Parameters: * apr_pool_t *conf The configuration pool. Valid as long as this * server_rec *s The server record for the current server. * const char *file The file path * * Returns: * char * The file content */ char *am_getfile(apr_pool_t *conf, server_rec *s, const char *file) { apr_status_t rv; char buffer[512]; apr_finfo_t finfo; char *data; apr_file_t *fd; apr_size_t nbytes; if (file == NULL) return NULL; if ((rv = apr_file_open(&fd, file, APR_READ, 0, conf)) != 0) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "apr_file_open: Error opening \"%s\" [%d] \"%s\"", file, rv, apr_strerror(rv, buffer, sizeof(buffer))); return NULL; } if ((rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fd)) != 0) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "apr_file_info_get: Error opening \"%s\" [%d] \"%s\"", file, rv, apr_strerror(rv, buffer, sizeof(buffer))); (void)apr_file_close(fd); return NULL; } nbytes = finfo.size; data = (char *)apr_palloc(conf, nbytes + 1); rv = apr_file_read_full(fd, data, nbytes, NULL); if (rv != 0) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "apr_file_read_full: Error reading \"%s\" [%d] \"%s\"", file, rv, apr_strerror(rv, buffer, sizeof(buffer))); } data[nbytes] = '\0'; (void)apr_file_close(fd); return data; } /* * Purge outdated saved POST requests. * * Parameters: * request_rec *r The current request * * Returns: * OK on success, or HTTP_INTERNAL_SERVER on failure. */ int am_postdir_cleanup(request_rec *r) { am_mod_cfg_rec *mod_cfg; apr_dir_t *postdir; apr_status_t rv; char error_buffer[64]; apr_finfo_t afi; char *fname; int count; apr_time_t expire_before; mod_cfg = am_get_mod_cfg(r->server); /* The oldes file we should keep. Delete files that are older. */ expire_before = apr_time_now() - mod_cfg->post_ttl * APR_USEC_PER_SEC; /* * Open our POST directory or create it. */ rv = apr_dir_open(&postdir, mod_cfg->post_dir, r->pool); if (rv != 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unable to open MellonPostDirectory \"%s\": %s", mod_cfg->post_dir, apr_strerror(rv, error_buffer, sizeof(error_buffer))); return HTTP_INTERNAL_SERVER_ERROR; } /* * Purge outdated items */ count = 0; do { rv = apr_dir_read(&afi, APR_FINFO_NAME|APR_FINFO_CTIME, postdir); if (rv != OK) break; /* Skip dot_files */ if (afi.name[0] == '.') continue; if (afi.ctime < expire_before) { fname = apr_psprintf(r->pool, "%s/%s", mod_cfg->post_dir, afi.name); (void)apr_file_remove(fname , r->pool); } else { count++; } } while (1 /* CONSTCOND */); (void)apr_dir_close(postdir); if (count >= mod_cfg->post_count) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Too many saved POST sessions. " "Increase MellonPostCount directive."); return HTTP_INTERNAL_SERVER_ERROR; } return OK; } /* * HTML-encode a string * * Parameters: * request_rec *r The current request * const char *str The string to encode * * Returns: * The encoded string */ char *am_htmlencode(request_rec *r, const char *str) { const char *cp; char *output; apr_size_t outputlen; int i; outputlen = 0; for (cp = str; *cp; cp++) { switch (*cp) { case '&': outputlen += 5; break; case '"': outputlen += 6; break; default: outputlen += 1; break; } } i = 0; output = apr_palloc(r->pool, outputlen + 1); for (cp = str; *cp; cp++) { switch (*cp) { case '&': (void)strcpy(&output[i], "&"); i += 5; break; case '"': (void)strcpy(&output[i], """); i += 6; break; default: output[i] = *cp; i += 1; break; } } output[i] = '\0'; return output; } /* This function produces the endpoint URL * * Parameters: * request_rec *r The request we received. * * Returns: * the endpoint URL */ char *am_get_endpoint_url(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); return ap_construct_url(r->pool, cfg->endpoint_path, r); } /* * This function saves a POST request for later replay and updates * the return URL. * * Parameters: * request_rec *r The current request. * const char **relay_state The returl URL * * Returns: * OK on success, HTTP_INTERNAL_SERVER_ERROR otherwise */ int am_save_post(request_rec *r, const char **relay_state) { am_mod_cfg_rec *mod_cfg; const char *content_type; const char *charset; const char *psf_id; char *psf_name; char *post_data; apr_size_t post_data_len; apr_size_t written; apr_file_t *psf; if (am_postdir_cleanup(r) != OK) return HTTP_INTERNAL_SERVER_ERROR; /* Check Content-Type */ content_type = apr_table_get(r->headers_in, "Content-Type"); if (content_type == NULL) { content_type = "urlencoded"; charset = NULL; } else { if (am_has_header(r, content_type, "application/x-www-form-urlencoded")) { content_type = "urlencoded"; } else if (am_has_header(r, content_type, "multipart/form-data")) { content_type = "multipart"; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unknown POST Content-Type \"%s\"", content_type); return HTTP_INTERNAL_SERVER_ERROR; } charset = am_get_header_attr(r, content_type, NULL, "charset"); } mod_cfg = am_get_mod_cfg(r->server); if ((psf_id = am_generate_id(r)) == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "cannot generate id"); return HTTP_INTERNAL_SERVER_ERROR; } psf_name = apr_psprintf(r->pool, "%s/%s", mod_cfg->post_dir, psf_id); if (apr_file_open(&psf, psf_name, APR_WRITE|APR_CREATE|APR_BINARY, APR_FPROT_UREAD|APR_FPROT_UWRITE, r->pool) != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "cannot create POST session file"); return HTTP_INTERNAL_SERVER_ERROR; } if (am_read_post_data(r, &post_data, &post_data_len) != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "cannot read POST data"); (void)apr_file_close(psf); return HTTP_INTERNAL_SERVER_ERROR; } if (post_data_len > mod_cfg->post_size) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "POST data size %" APR_SIZE_T_FMT " exceeds maximum %" APR_SIZE_T_FMT ". " "Increase MellonPostSize directive.", post_data_len, mod_cfg->post_size); (void)apr_file_close(psf); return HTTP_INTERNAL_SERVER_ERROR; } written = post_data_len; if ((apr_file_write(psf, post_data, &written) != OK) || (written != post_data_len)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "cannot write to POST session file"); (void)apr_file_close(psf); return HTTP_INTERNAL_SERVER_ERROR; } if (apr_file_close(psf) != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "cannot close POST session file"); return HTTP_INTERNAL_SERVER_ERROR; } if (charset != NULL) charset = apr_psprintf(r->pool, "&charset=%s", am_urlencode(r->pool, charset)); else charset = ""; *relay_state = apr_psprintf(r->pool, "%srepost?id=%s&ReturnTo=%s&enctype=%s%s", am_get_endpoint_url(r), psf_id, am_urlencode(r->pool, *relay_state), content_type, charset); return OK; } /* * This function replaces CRLF by LF in a string * * Parameters: * request_rec *r The current request * const char *str The string * * Returns: * Output string */ const char *am_strip_cr(request_rec *r, const char *str) { char *output; const char *cp; apr_size_t i; output = apr_palloc(r->pool, strlen(str) + 1); i = 0; for (cp = str; *cp; cp++) { if ((*cp == '\r') && (*(cp + 1) == '\n')) continue; output[i++] = *cp; } output[i++] = '\0'; return (const char *)output; } /* * This function replaces LF by CRLF in a string * * Parameters: * request_rec *r The current request * const char *str The string * * Returns: * Output string */ const char *am_add_cr(request_rec *r, const char *str) { char *output; const char *cp; apr_size_t xlen; apr_size_t i; xlen = 0; for (cp = str; *cp; cp++) if (*cp == '\n') xlen++; output = apr_palloc(r->pool, strlen(str) + xlen + 1); i = 0; for (cp = str; *cp; cp++) { if (*cp == '\n') output[i++] = '\r'; output[i++] = *cp; } output[i++] = '\0'; return (const char *)output; } /* * This function tokenize a string, just like strtok_r, except that * the separator is a string instead of a character set. * * Parameters: * const char *str The string to tokenize * const char *sep The separator string * char **last Pointer to state (char *) * * Returns: * OK on success, HTTP_INTERNAL_SERVER_ERROR otherwise */ const char *am_xstrtok(request_rec *r, const char *str, const char *sep, char **last) { char *s; char *np; /* Resume */ if (str != NULL) s = apr_pstrdup(r->pool, str); else s = *last; /* End of string */ if (*s == '\0') return NULL; /* Next sep exists? */ if ((np = strstr(s, sep)) == NULL) { *last = s + strlen(s); } else { *last = np + strlen(sep); memset(np, 0, strlen(sep)); } return s; } /* This function strips leading spaces and tabs from a string * * Parameters: * const char **s Pointer to the string * */ void am_strip_blank(const char **s) { while ((**s == ' ') || (**s == '\t')) (*s)++; return; } /* This function extracts a MIME header from a MIME section * * Parameters: * request_rec *r The request * const char *m The MIME section * const char *h The header to extract (case insensitive) * * Returns: * The header value, or NULL on failure. */ const char *am_get_mime_header(request_rec *r, const char *m, const char *h) { const char *line; char *l1; const char *value; char *l2; for (line = am_xstrtok(r, m, "\n", &l1); line && *line; line = am_xstrtok(r, NULL, "\n", &l1)) { am_strip_blank(&line); if (((value = am_xstrtok(r, line, ":", &l2)) != NULL) && (strcasecmp(value, h) == 0)) { if ((value = am_xstrtok(r, NULL, ":", &l2)) != NULL) am_strip_blank(&value); return value; } } return NULL; } /* This function extracts an attribute from a header * * Parameters: * request_rec *r The request * const char *h The header * const char *v Optional header value to check (case insensitive) * const char *a Optional attribute to extract (case insensitive) * * Returns: * if i was provided, item value, or NULL on failure. * if i is NULL, the whole header, or NULL on failure. This is * useful for testing v. */ const char *am_get_header_attr(request_rec *r, const char *h, const char *v, const char *a) { const char *value; const char *attr; char *l1; const char *attr_value = NULL; /* Looking for * header-value; item_name="item_value"\n */ if ((value = am_xstrtok(r, h, ";", &l1)) == NULL) return NULL; am_strip_blank(&value); /* If a header value was provided, check it */ if ((v != NULL) && (strcasecmp(value, v) != 0)) return NULL; /* If no attribute name is provided, return everything */ if (a == NULL) return h; while ((attr = am_xstrtok(r, NULL, ";", &l1)) != NULL) { const char *attr_name = NULL; char *l2; am_strip_blank(&attr); attr_name = am_xstrtok(r, attr, "=", &l2); if ((attr_name != NULL) && (strcasecmp(attr_name, a) == 0)) { if ((attr_value = am_xstrtok(r, NULL, "=", &l2)) != NULL) am_strip_blank(&attr_value); break; } } /* Remove leading and trailing quotes */ if (attr_value != NULL) { apr_size_t len; len = strlen(attr_value); if ((len > 1) && (attr_value[len - 1] == '\"')) attr_value = apr_pstrndup(r->pool, attr_value, len - 1); if (attr_value[0] == '\"') attr_value++; } return attr_value; } /* This function checks for a header name/value existence * * Parameters: * request_rec *r The request * const char *h The header (case insensitive) * const char *v Optional header value to check (case insensitive) * * Returns: * 0 if header does not exists or does not has the value, 1 otherwise */ int am_has_header(request_rec *r, const char *h, const char *v) { return (am_get_header_attr(r, h, v, NULL) != NULL); } /* This function extracts the body from a MIME section * * Parameters: * request_rec *r The request * const char *mime The MIME section * * Returns: * The MIME section body, or NULL on failure. */ const char *am_get_mime_body(request_rec *r, const char *mime) { const char lflf[] = "\n\n"; const char *body; apr_size_t body_len; if ((body = strstr(mime, lflf)) == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No MIME body"); return NULL; } body += strlen(lflf); /* Strip tralling \n */ if ((body_len = strlen(body)) >= 1) { if (body[body_len - 1] == '\n') body = apr_pstrmemdup(r->pool, body, body_len - 1); } /* Turn back LF into CRLF */ return am_add_cr(r, body); } /* This function returns the URL for a given provider service (type + method) * * Parameters: * request_rec *r The request * LassoProfile *profile Login profile * char *endpoint_name Service and method as specified in metadata * e.g.: "SingleSignOnService HTTP-Redirect" * Returns: * The endpoint URL that must be freed by caller, or NULL on failure. */ char * am_get_service_url(request_rec *r, LassoProfile *profile, char *service_name) { LassoProvider *provider; gchar *url; provider = lasso_server_get_provider(profile->server, profile->remote_providerID); if (LASSO_IS_PROVIDER(provider) == FALSE) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Cannot find provider service %s, no provider.", service_name); return NULL; } url = lasso_provider_get_metadata_one(provider, service_name); if (url == NULL) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Cannot find provider service %s from metadata.", service_name); return NULL; } return url; } /*------------------------ Begin Token Parsing Code --------------------------*/ typedef enum { TOKEN_WHITESPACE = 1, TOKEN_SEMICOLON, TOKEN_COMMA, TOKEN_EQUAL, TOKEN_IDENTIFIER, TOKEN_DBL_QUOTE_STRING, } TokenType; typedef struct { TokenType type; /* The type of this token */ char *str; /* The string value of the token */ apr_size_t len; /* The number of characters in the token */ apr_size_t offset; /* The offset from the beginning of the string to the start of the token */ } Token; #ifdef DEBUG /* Return string representation of TokenType enumeration * * Parameters: * token_type A TokenType enumeration * Returns: String name of token_type */ static const char * token_type_str(TokenType token_type) { switch(token_type) { case TOKEN_WHITESPACE: return "WHITESPACE"; case TOKEN_SEMICOLON: return "SEMICOLON"; case TOKEN_COMMA: return "COMMA"; case TOKEN_EQUAL: return "EQUAL"; case TOKEN_IDENTIFIER: return "IDENTIFIER"; case TOKEN_DBL_QUOTE_STRING: return "DBL_QUOTE_STRING"; default: return "unknown"; } } static void dump_tokens(request_rec *r, apr_array_header_t *tokens) { apr_size_t i; for (i = 0; i < tokens->nelts; i++) { Token token = APR_ARRAY_IDX(tokens, i, Token); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "token[%2zd] %s \"%s\" offset=%lu len=%lu ", i, token_type_str(token.type), token.str, token.offset, token.len); } } #endif /* Initialize token and add to list of tokens * * Utility to assist tokenize function. * * A token object is created and added to the end of the list of * tokens. It is initialized with the type of token, a copy of the * string, it's length, and it's offset from the beginning of the * string where it was found. * * Tokens with special processing needs are also handled here. * * A double quoted string will: * * * Have it's delimiting quotes removed. * * Will unescape escaped characters. * * Parameters: * tokens Array of Token objects. * type The type of the token (e.g. TokenType). * str The string the token was parsed from, used to compute * the position of the token in the original string. * start The first character in the token. * end the last character in the token. */ static inline void push_token(apr_array_header_t *tokens, TokenType type, const char *str, const char *start, const char *end) { apr_size_t offset = start - str; Token *token = apr_array_push(tokens); if (type == TOKEN_DBL_QUOTE_STRING) { /* do not include quotes in token value */ start++; end--; } token->type = type; token->len = end - start; token->offset = offset; token->str = apr_pstrmemdup(tokens->pool, start, token->len); if (type == TOKEN_DBL_QUOTE_STRING) { /* * The original HTTP 1.1 spec was ambiguous with respect to * backslash quoting inside double quoted strings. This has since * been resolved in this errata: * * http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p1-messaging-16.html#rfc.section.3.2.3 * * Which states: * * Recipients that process the value of the quoted-string MUST * handle a quoted-pair as if it were replaced by the octet * following the backslash. * * Senders SHOULD NOT escape octets in quoted-strings that do not * require escaping (i.e., other than DQUOTE and the backslash * octet). */ char *p, *t; for (p = token->str; *p; p++) { if (p[0] == '\\' && p[1]) { /* * Found backslash with following character. * Move rest of string down 1 character. */ for (t = p; *t; t++) { t[0] = t[1]; } token->len--; } } } } /* Break a string into a series of tokens * * Given a string return an array of tokens. If the string cannot be * successfully parsed an error string is returned at the location * specified by the error parameter, if error is NULL then the parsing * was successful. If an error occured the returned array of tokens * will include all tokens parsed up until where the unrecognized * input occurred. The input str is never modified. * * Parameters: * pool memory allocation pool * str input string to be parsed. * ignore_whitespace if True whitespace tokens are not returned * error location where error string is returned * if NULL no error occurred * Returns: array of Token objects */ static apr_array_header_t * tokenize(apr_pool_t *pool, const char *str, bool ignore_whitespace, char **error) { apr_array_header_t *tokens = apr_array_make(pool, 10, sizeof(Token)); const char *p, *start; *error = NULL; p = start = str; while(*p) { if (apr_isspace(*p)) { /* whitespace */ p++; while(*p && apr_isspace(*p)) p++; if (!ignore_whitespace) { push_token(tokens, TOKEN_WHITESPACE, str, start, p); } start = p; } else if (apr_isalpha(*p)) { /* identifier: must begin with alpha then any alphanumeric or underscore */ p++; while(*p && (apr_isalnum(*p) || *p == '_')) p++; push_token(tokens, TOKEN_IDENTIFIER, str, start, p); start = p; } else if (*p == '"') { /* double quoted string */ p++; /* step over double quote */ while(*p) { if (*p == '\\') { /* backslash escape */ p++; /* step over backslash */ if (*p) { p++; /* step over escaped character */ } else { break; /* backslash at end of string, stop */ } } if (*p == '\"') break; /* terminating quote delimiter */ p++; /* keep scanning */ } if (*p != '\"') { *error = apr_psprintf(pool, "unterminated string begining at " "position %lu in \"%s\"", start-str, str); break; } p++; push_token(tokens, TOKEN_DBL_QUOTE_STRING, str, start, p); start = p; } else if (*p == '=') { /* equals */ p++; push_token(tokens, TOKEN_EQUAL, str, start, p); start = p; } else if (*p == ',') { /* comma */ p++; push_token(tokens, TOKEN_COMMA, str, start, p); start = p; } else if (*p == ';') { /* semicolon */ p++; push_token(tokens, TOKEN_SEMICOLON, str, start, p); start = p; } else { /* unrecognized token */ *error = apr_psprintf(pool, "unknown token at " "position %lu in string \"%s\"", p-str, str); break; } } return tokens; } /* Test if the token is what we're looking for * * Given an index into the tokens array determine if the token type * matches. If the value parameter is non-NULL then the token's value * must also match. If the array index is beyond the last array item * false is returned. * * Parameters: * tokens Array of Token objects * index Index used to select the Token object from the Tokens array. * If the index is beyond the last array item False is returned. * type The token type which must match * value If non-NULL then the token string value must be equal to this. * Returns: True if the token matches, False otherwise. */ static bool is_token(apr_array_header_t *tokens, apr_size_t index, TokenType type, const char *value) { if (index >= tokens->nelts) { return false; } Token token = APR_ARRAY_IDX(tokens, index, Token); if (token.type != type) { return false; } if (value) { if (!g_str_equal(token.str, value)) { return false; } } return true; } /*------------------------- End Token Parsing Code ---------------------------*/ /* Return message describing position an error when parsing. * * When parsing we expect tokens to appear in a certain sequence. We * report the contents of the unexpected token and it's position in * the string. However if the parsing error is due to the fact we've * exhausted all tokens but are still expecting another token then our * error message indicates we reached the end of the string. * * Parameters: * tokens Array of Token objects. * index Index in tokens array where bad token was found */ static inline const char * parse_error_msg(apr_array_header_t *tokens, apr_size_t index) { if (index >= tokens->nelts) { return "end of string"; } return apr_psprintf(tokens->pool, "\"%s\" at position %lu", APR_ARRAY_IDX(tokens, index, Token).str, APR_ARRAY_IDX(tokens, index, Token).offset); } /* This function checks if an HTTP PAOS header is valid and * returns any service options which may have been specified. * * A PAOS header is composed of a mandatory PAOS version and service * values. A semicolon separates the version from the service values. * * Service values are delimited by semicolons, and options are * comma-delimited from the service value and each other. * * The PAOS version must be in the form ver="xxx" (note the version * string must be in double quotes). * * The ECP service must be specified, it MAY be followed by optional * comma seperated options, all values must be in double quotes. * * ECP Service * "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp" * * Recognized Options: * * Support for channel bindings * urn:oasis:names:tc:SAML:protocol:ext:channel-binding * * Support for Holder-of-Key subject confirmation * urn:oasis:names:tc:SAML:2.0:cm:holder-of-key * * Request for signed SAML request * urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp:2.0:WantAuthnRequestsSigned * * Request to delegate credentials to the service provider * urn:oasis:names:tc:SAML:2.0:conditions:delegation * * * Example PAOS HTTP header:: * * PAOS: ver="urn:liberty:paos:2003-08"; * "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp", * "urn:oasis:names:tc:SAML:protocol:ext:channel-binding", * "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key" * * Parameters: * request_rec *r The request * const char *header The PAOS header value * ECPServiceOptions *options_return * Pointer to location to receive options, * may be NULL. Bitmask of option flags. * * Returns: * true if the PAOS header is valid, false otherwise. If options is non-NULL * then the set of option flags is returned there. * */ bool am_parse_paos_header(request_rec *r, const char *header, ECPServiceOptions *options_return) { bool result = false; ECPServiceOptions options = 0; apr_array_header_t *tokens; apr_size_t i; char *error; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "PAOS header: \"%s\"", header); tokens = tokenize(r->pool, header, true, &error); #ifdef DEBUG dump_tokens(r, tokens); #endif if (error) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", error); goto cleanup; } /* Header must begin with "ver=xxx" where xxx is paos version */ if (!is_token(tokens, 0, TOKEN_IDENTIFIER, "ver") || !is_token(tokens, 1, TOKEN_EQUAL, NULL) || !is_token(tokens, 2, TOKEN_DBL_QUOTE_STRING, LASSO_PAOS_HREF)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected header to begin with ver=\"%s\", " "actual header=\"%s\"", LASSO_PAOS_HREF, header); goto cleanup; } /* Next is the service value, separated from the version by a semicolon */ if (!is_token(tokens, 3, TOKEN_SEMICOLON, NULL)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected semicolon after PAOS version " "but found %s in header=\"%s\"", parse_error_msg(tokens, 3), header); goto cleanup; } if (!is_token(tokens, 4, TOKEN_DBL_QUOTE_STRING, LASSO_ECP_HREF)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected service token to be \"%s\", " "but found %s in header=\"%s\"", LASSO_ECP_HREF, parse_error_msg(tokens, 4), header); goto cleanup; } /* After the service value there may be optional flags separated by commas */ if (tokens->nelts == 5) { /* no options */ result = true; goto cleanup; } /* More tokens after the service value, must be options, iterate over them */ for (i = 5; i < tokens->nelts; i++) { if (!is_token(tokens, i, TOKEN_COMMA, NULL)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected comma after PAOS service " "but found %s in header=\"%s\"", parse_error_msg(tokens, i), header); goto cleanup; } if (++i > tokens->nelts) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected option after comma " "in header=\"%s\"", header); goto cleanup; } Token token = APR_ARRAY_IDX(tokens, i, Token); if (token.type != TOKEN_DBL_QUOTE_STRING) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected quoted string after comma " "but found %s in header=\"%s\"", parse_error_msg(tokens, i), header); goto cleanup; } /* Have an option string, convert it to a bit flag */ const char *value = token.str; if (g_str_equal(value, LASSO_SAML_EXT_CHANNEL_BINDING)) { options |= ECP_SERVICE_OPTION_CHANNEL_BINDING; } else if (g_str_equal(value, LASSO_SAML2_CONFIRMATION_METHOD_HOLDER_OF_KEY)) { options |= ECP_SERVICE_OPTION_HOLDER_OF_KEY; } else if (g_str_equal(value, LASSO_SAML2_ECP_PROFILE_WANT_AUTHN_SIGNED)) { options |= ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED; } else if (g_str_equal(value, LASSO_SAML2_CONDITIONS_DELEGATION)) { options |= ECP_SERVICE_OPTION_DELEGATION; } else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Unknown PAOS service option = \"%s\"", value); goto cleanup; } } result = true; cleanup: if (options_return) { *options_return = options; } return result; } /* This function checks if Accept header has a media type * * Given an Accept header value like this: * * "text/html,application/xhtml+xml,application/xml;q=0.9" * * Parse the string and find name of each media type, ignore any parameters * bound to the name. Test to see if the name matches the input media_type. * * Parameters: * request_rec *r The request * const char *header The header value * const char *media_type media type header value to check (case insensitive) * * Returns: * true if media type is in header, false otherwise */ bool am_header_has_media_type(request_rec *r, const char *header, const char *media_type) { bool result = false; char **comma_tokens = NULL; char **media_ranges = NULL; char *media_range = NULL; if (header == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "invalid Accept header, NULL"); goto cleanup; } /* * Split the header into a list of media_range tokens separated by * a comma and iterate over the list. */ comma_tokens = g_strsplit(header, ",", 0); for (media_ranges = comma_tokens, media_range = *media_ranges; media_range; media_range = *(++media_ranges)) { char **semicolon_tokens = NULL; char *name = NULL; /* * Split the media_range into a name and parameters, each * separated by a semicolon. The first element in the list is * the media_type name, subsequent params are optional and ignored. */ media_range = g_strstrip(media_range); semicolon_tokens = g_strsplit(media_range, ";", 0); /* * Does the media_type match our required media_type? * If so clean up and return success. */ name = g_strstrip(semicolon_tokens[0]); if (name && g_str_equal(name, media_type)) { result = true; g_strfreev(semicolon_tokens); goto cleanup; } g_strfreev(semicolon_tokens); } cleanup: g_strfreev(comma_tokens); return result; } /* * Lookup a config string in a specific language. If lang is NULL and * the config string had been defined without a language qualifier * return the unqualified value. If not found NULL is returned. */ const char *am_get_config_langstring(apr_hash_t *h, const char *lang) { char *string; if (lang == NULL) { lang = ""; } string = (char *)apr_hash_get(h, lang, APR_HASH_KEY_STRING); return string; } /* * Get the value of boolean query parameter. * * Parameters: * request_rec *r The request * const char *name The name of the query parameter * int *return_value The address of the variable to receive * the boolean value * int default_value The value returned if parameter is absent or * in event of an error * * Returns: * OK on success, HTTP error otherwise * * Looks for the named parameter in the query parameters, if found * parses the value which must be one of: * * * true * * false * * If value cannot be parsed HTTP_BAD_REQUEST is returned. * * If not found, or if there is an error, the returned value is set to * default_value. */ int am_get_boolean_query_parameter(request_rec *r, const char *name, int *return_value, int default_value) { char *value_str; int ret = OK; *return_value = default_value; value_str = am_extract_query_parameter(r->pool, r->args, name); if (value_str != NULL) { ret = am_urldecode(value_str); if (ret != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error urldecoding \"%s\" boolean query parameter, " "value=\"%s\"", name, value_str); return ret; } if(!strcmp(value_str, "true")) { *return_value = TRUE; } else if(!strcmp(value_str, "false")) { *return_value = FALSE; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid value for \"%s\" boolean query parameter, " "value=\"%s\"", name, value_str); ret = HTTP_BAD_REQUEST; } } return ret; } /* * Get the URL of the AssertionConsumerServer having specific protocol * binding. * * Parameters: * LassoProvider *provider The provider whose endpoints will be scanned. * const char *binding The required binding short name. * * Returns: * The endpoint URL or NULL if not found. Must be freed with g_free(). * * Lasso does not provide a public API to select a provider endpoint * by binding. The best we can do is iterate over a list of endpoint * descriptors and select a matching descriptor. * * Lasso does not document the format of these descriptor names but * essentially a descriptor is a space separated concatenation of the * endpoint properties. For SAML2 one can assume it is the endpoint * type, optionally followed by the protocol binding name, optionally * followd by the index (if the endpoint type is indexed). If the * endpoint is a response location then "ResponseLocation" will be * appended as the final token. For example here is a list of * descriptors returned for a service provider (note they are * unordered). * * "AssertionConsumerService HTTP-POST 0" * "AuthnRequestsSigned" * "AssertionConsumerService PAOS 2" * "SingleLogoutService HTTP-Redirect" * "SingleLogoutService SOAP" * "AssertionConsumerService HTTP-Artifact 1" * "NameIDFormat" * "SingleLogoutService HTTP-POST ResponseLocation" * * The possible binding names are: * * "SOAP" * "HTTP-Redirect" * "HTTP-POST" * "HTTP-Artifact" * "PAOS" * "URI" * * We know the AssertionConsumerService is indexed. If there is more * than one endpoint with the required binding we select the one with * the lowest index assuming it is preferred. */ char *am_get_assertion_consumer_service_by_binding(LassoProvider *provider, const char *binding) { GList *descriptors; char *url; char *selected_descriptor; char *descriptor; char **tokens; guint n_tokens; GList *i; char *endptr; long descriptor_index, min_index; url = NULL; selected_descriptor = NULL; min_index = LONG_MAX; /* The descriptor list is unordered */ descriptors = lasso_provider_get_metadata_keys_for_role(provider, LASSO_PROVIDER_ROLE_SP); for (i = g_list_first(descriptors), tokens=NULL; i; i = g_list_next(i), g_strfreev(tokens)) { descriptor = i->data; descriptor_index = LONG_MAX; /* * Split the descriptor into tokens, only consider descriptors * which have at least 3 tokens and whose first token is * AssertionConsumerService */ tokens = g_strsplit(descriptor, " ", 0); n_tokens = g_strv_length(tokens); if (n_tokens < 3) continue; if (!g_str_equal(tokens[0], "AssertionConsumerService")) continue; if (!g_str_equal(tokens[1], binding)) continue; descriptor_index = strtol(tokens[2], &endptr, 10); if (tokens[2] == endptr) continue; /* could not parse int */ if (descriptor_index < min_index) { selected_descriptor = descriptor; min_index = descriptor_index; } } if (selected_descriptor) { url = lasso_provider_get_metadata_one_for_role(provider, LASSO_PROVIDER_ROLE_SP, selected_descriptor); } lasso_release_list_of_strings(descriptors); return url; } #ifdef HAVE_ECP /* String representation of ECPServiceOptions bitmask * * ECPServiceOptions is a bitmask of flags. Return a comma separated string * of all the flags. If any bit in the bitmask is unaccounted for an * extra string will be appended of the form "(unknown bits = x)". * * Parameters: * pool memory allocation pool * options bitmask of PAOS options */ char *am_ecp_service_options_str(apr_pool_t *pool, ECPServiceOptions options) { apr_array_header_t *names = apr_array_make(pool, 4, sizeof(const char *)); if (options & ECP_SERVICE_OPTION_CHANNEL_BINDING) { APR_ARRAY_PUSH(names, const char *) = "channel-binding"; options &= ~ECP_SERVICE_OPTION_CHANNEL_BINDING; } if (options & ECP_SERVICE_OPTION_HOLDER_OF_KEY) { APR_ARRAY_PUSH(names, const char *) = "holder-of-key"; options &= ~ECP_SERVICE_OPTION_HOLDER_OF_KEY; } if (options & ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED) { APR_ARRAY_PUSH(names, const char *) = "want-authn-signed"; options &= ~ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED; } if (options & ECP_SERVICE_OPTION_DELEGATION) { APR_ARRAY_PUSH(names, const char *) = "delegation"; options &= ~ECP_SERVICE_OPTION_DELEGATION; } if (options) { APR_ARRAY_PUSH(names, const char *) = apr_psprintf(pool, "(unknown bits = %#x)", options); } return apr_array_pstrcat(pool, names, ','); } /* Determine if request is compatible with PAOS, decode headers * * To indicate support for the ECP profile, and the PAOS binding, the * request MUST include the following HTTP header fields: * * 1. An Accept header indicating acceptance of the MIME type * "application/vnd.paos+xml" * * 2. A PAOS header specifying the PAOS version with a value, at minimum, of * "urn:liberty:paos:2003-08" and a supported service value of * "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp". The service value MAY * contain option values. * * This function validates the Accept header the the PAOS header, if * all condidtions are met it returns true, false otherwise. If the * validation succeeds any ECP options specified along with the * ECP service are parsed and stored in req_cfg->ecp_service_options * * Any error discovered during processing are returned in the * error_code parameter, zero indicates success. This function never * returns true if an error occurred. * * Parameters: * request_rec *r The current request. * int * error_code Return error code here * */ bool am_is_paos_request(request_rec *r, int *error_code) { const char *accept_header = NULL; const char *paos_header = NULL; bool have_paos_media_type = false; bool valid_paos_header = false; bool is_paos = false; ECPServiceOptions ecp_service_options = 0; *error_code = 0; accept_header = apr_table_get(r->headers_in, "Accept"); paos_header = apr_table_get(r->headers_in, "PAOS"); if (accept_header) { if (am_header_has_media_type(r, accept_header, MEDIA_TYPE_PAOS)) { have_paos_media_type = true; } } if (paos_header) { if (am_parse_paos_header(r, paos_header, &ecp_service_options)) { valid_paos_header = true; } else { if (*error_code == 0) *error_code = AM_ERROR_INVALID_PAOS_HEADER; } } if (have_paos_media_type) { if (valid_paos_header) { is_paos = true; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "request supplied PAOS media type in Accept header " "but omitted valid PAOS header"); if (*error_code == 0) *error_code = AM_ERROR_MISSING_PAOS_HEADER; } } else { if (valid_paos_header) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "request supplied valid PAOS header " "but omitted PAOS media type in Accept header"); if (*error_code == 0) *error_code = AM_ERROR_MISSING_PAOS_MEDIA_TYPE; } } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "have_paos_media_type=%s valid_paos_header=%s is_paos=%s " "error_code=%d ecp options=[%s]", have_paos_media_type ? "True" : "False", valid_paos_header ? "True" : "False", is_paos ? "True" : "False", *error_code, am_ecp_service_options_str(r->pool, ecp_service_options)); if (is_paos) { am_req_cfg_rec *req_cfg; req_cfg = am_get_req_cfg(r); req_cfg->ecp_service_options = ecp_service_options; } return is_paos; } #endif /* HAVE_ECP */ mod_auth_mellon-0.12.0/auth_mellon_session.c0000644000175100017510000000673512354722415020554 0ustar olavmoolavmo/* * * auth_mellon_session.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 gets the session associated with a user, using a cookie * * Parameters: * request_rec *r The request we received from the user. * * Returns: * The session associated with the user who places the request, or * NULL if we don't have a session yet. */ am_cache_entry_t *am_get_request_session(request_rec *r) { const char *session_id; /* Get session id from cookie. */ session_id = am_cookie_get(r); if(session_id == NULL) { /* Cookie is unset - we don't have a session. */ return NULL; } return am_cache_lock(r->server, AM_CACHE_SESSION, session_id); } /* This function gets the session associated with a user, using a NameID * * Parameters: * request_rec *r The request we received from the user. * char *nameid The NameID * * Returns: * The session associated with the user who places the request, or * NULL if we don't have a session yet. */ am_cache_entry_t *am_get_request_session_by_nameid(request_rec *r, char *nameid) { return am_cache_lock(r->server, AM_CACHE_NAMEID, nameid); } /* This function creates a new session. * * Parameters: * request_rec *r The request we are processing. * * Returns: * The new session, or NULL if we have an internal error. */ am_cache_entry_t *am_new_request_session(request_rec *r) { const char *session_id; /* Generate session id. */ session_id = am_generate_id(r); if(session_id == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error creating session id."); return NULL; } /* Set session id. */ am_cookie_set(r, session_id); return am_cache_new(r->server, session_id); } /* This function releases the session which was returned from * am_get_request_session. * * Parameters: * request_rec *r The request we are processing. * am_cache_entry_t *session The session we are releasing. * * Returns: * Nothing. */ void am_release_request_session(request_rec *r, am_cache_entry_t *session) { am_cache_unlock(r->server, session); } /* This function releases and deletes the session which was returned from * am_get_request_session. * * Parameters: * request_rec *r The request we are processing. * am_cache_entry_t *session The session we are deleting. * * Returns: * Nothing. */ void am_delete_request_session(request_rec *r, am_cache_entry_t *session) { /* Delete the cookie. */ am_cookie_delete(r); if(session == NULL) { return; } /* Delete session from the session store. */ am_cache_delete(r->server, session); } mod_auth_mellon-0.12.0/auth_mellon_httpclient.c0000644000175100017510000005025412354722415021242 0ustar olavmoolavmo/* * * mod_auth_mellon.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" #include /* The size of the blocks we will allocate. */ #define AM_HC_BLOCK_SIZE 1000 /* This structure describes a single-linked list of downloaded blocks. */ typedef struct am_hc_block_s { /* The next block we have allocated. */ struct am_hc_block_s *next; /* The number of bytes written to this block. */ apr_size_t used; /* The data stored in this block. */ uint8_t data[AM_HC_BLOCK_SIZE]; } am_hc_block_t; /* This structure describes a header for the block list. */ typedef struct { /* The pool we will allocate memory for new blocks from. */ apr_pool_t *pool; /* The first block in the linked list of blocks. */ am_hc_block_t *first; /* The last block in the linked list of blocks. */ am_hc_block_t *last; } am_hc_block_header_t; /* This function allocates and initializes a block for data copying. * * Parameters: * apr_pool_t *pool The pool we should allocate the block from. * * Returns: * The new block we allocated. */ static am_hc_block_t *am_hc_block_alloc(apr_pool_t *pool) { am_hc_block_t *blk; blk = (am_hc_block_t *)apr_palloc(pool, sizeof(am_hc_block_t)); blk->next = NULL; blk->used = 0; return blk; } /* This function adds data to the end of a block, and allocates new blocks * if the data doesn't fit in one block. * * Parameters: * am_hc_block_t *block The block we should begin by appending data to. * apr_pool_t *pool The pool we should allocate memory for new blocks * from. * const uint8_t *data The data we should append to the blocks. * apr_size_t size The length of the data we should append. * * Returns: * The last block written to (i.e. the next block we should write to). */ static am_hc_block_t *am_hc_block_write( am_hc_block_t *block, apr_pool_t *pool, const uint8_t *data, apr_size_t size ) { apr_size_t num_cpy; /* Find the number of bytes we should write to this block. */ num_cpy = AM_HC_BLOCK_SIZE - block->used; if(num_cpy > size) { num_cpy = size; } /* Copy data to this block. */ memcpy(&block->data[block->used], data, num_cpy); block->used += num_cpy; if(block->used == AM_HC_BLOCK_SIZE) { /* This block is full. Allocate a new block, and continue * filling it. */ block->next = am_hc_block_alloc(pool); return am_hc_block_write(block->next, pool, &data[num_cpy], size - num_cpy); } /* The next write should be to this block. */ return block; } /* This function initializes a am_hc_block_header_t structure, which * contains information about the linked list of data blocks. * * Parameters: * am_hc_block_header_t *bh Pointer to the data header whcih we * should initialize. * apr_pool_t *pool The pool we should allocate data from. * * Returns: * Nothing. */ static void am_hc_block_header_init(am_hc_block_header_t *bh, apr_pool_t *pool) { bh->pool = pool; bh->first = am_hc_block_alloc(pool); bh->last = bh->first; } /* This function writes data to the linked list of blocks identified by * the stream-parameter. It matches the prototype required by curl. * * Parameters: * void *data The data that should be written. It is size*nmemb * bytes long. * size_t size The size of each block of data that should * be written. * size_t nmemb The number of blocks of data that should be written. * void *block_header A pointer to a am_hc_block_header_t structure which * identifies the linked list we should store data in. * * Returns: * The number of bytes that have been written. */ static size_t am_hc_data_write(void *data, size_t size, size_t nmemb, void *data_header) { am_hc_block_header_t *bh; bh = (am_hc_block_header_t *)data_header; bh->last = am_hc_block_write(bh->last, bh->pool, (const uint8_t *)data, size * nmemb); return size * nmemb; } /* This function fetches the data which was written to the databuffers * in the linked list which the am_hc_data_t structure keeps track of. * * Parameters: * am_hc_block_header_t *bh The header telling us which data buffers * we should extract data from. * apr_pool_t *pool The pool we should allocate the data * buffer from. * void **buffer A pointer to where we should store a pointer * to the data buffer we allocate. We will * always add a null-terminator to the end of * data buffer. This parameter can't be NULL. * apr_size_t *size This is a pointer to where we will store the * length of the data, not including the * null-terminator we add. This parameter can * be NULL. * * Returns: * Nothing. */ static void am_hc_data_extract(am_hc_block_header_t *bh, apr_pool_t *pool, void **buffer, apr_size_t *size) { am_hc_block_t *blk; apr_size_t length; uint8_t *buf; apr_size_t pos; /* First we find the length of the data. */ length = 0; for(blk = bh->first; blk != NULL; blk = blk->next) { length += blk->used; } /* Allocate memory for the data. Add one to the size in order to * have space for the null-terminator. */ buf = (uint8_t *)apr_palloc(pool, length + 1); /* Copy the data into the buffer. */ pos = 0; for(blk = bh->first; blk != NULL; blk = blk->next) { memcpy(&buf[pos], blk->data, blk->used); pos += blk->used; } /* Add the null-terminator. */ buf[length] = 0; /* Set up the return values. */ *buffer = (void *)buf; if(size != NULL) { *size = length; } } /* This function creates a curl object and performs generic initialization * of it. * * Parameters: * request_rec *r The request we should log errors against. * const char *uri The URI we should request. * am_hc_block_header_t *bh The buffer curl will write response data to. * char *curl_error A buffer of size CURL_ERROR_SIZE where curl * will store error messages. * * Returns: * A initialized curl object on succcess, or NULL on error. */ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri, am_hc_block_header_t *bh, char *curl_error) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); CURL *curl; CURLcode res; /* Initialize the curl object. */ curl = curl_easy_init(); if(curl == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to initialize a curl object."); return NULL; } /* Set up error reporting. */ res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set curl error buffer: [%u]\n", res); goto cleanup_fail; } /* Disable progress reporting. */ res = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to disable curl progress reporting: [%u] %s", res, curl_error); goto cleanup_fail; } /* Disable use of signals. */ res = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to disable signals in curl: [%u] %s", res, curl_error); goto cleanup_fail; } /* Set the timeout of the transfer. It is currently set to two minutes. */ res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120L); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set the timeout of the curl download:" " [%u] %s", res, curl_error); goto cleanup_fail; } /* If we have a CA configured, try to use it */ if (cfg->idp_ca_file != NULL) { res = curl_easy_setopt(curl, CURLOPT_CAINFO, cfg->idp_ca_file); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set SSL CA info %s:" " [%u] %s", cfg->idp_ca_file, res, curl_error); goto cleanup_fail; } } /* Enable fail on http error. */ res = curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to enable failure on http error: [%u] %s", res, curl_error); goto cleanup_fail; } /* Select which uri we should download. */ res = curl_easy_setopt(curl, CURLOPT_URL, uri); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set curl download uri to \"%s\": [%u] %s", uri, res, curl_error); goto cleanup_fail; } /* Set up data writing. */ /* Set curl write function. */ res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, am_hc_data_write); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set the curl write function: [%u] %s", res, curl_error); goto cleanup_fail; } /* Set the curl write function parameter. */ res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, bh); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set the curl write function data: [%u] %s", res, curl_error); goto cleanup_fail; } return curl; cleanup_fail: curl_easy_cleanup(curl); return NULL; } /* This function downloads data from a specified URI, with specified timeout * * Parameters: * request_rec *r The apache request this download is associated * with. It is used for memory allocation and logging. * const char *uri The URI we should download. * void **buffer A pointer to where we should store a pointer to the * downloaded data. We will always add a null-terminator * to the data. This parameter can't be NULL. * apr_size_t *size This is a pointer to where we will store the length * of the downloaded data, not including the * null-terminator we add. This parameter can be NULL. * apr_time_t timeout Timeout in seconds, 0 for no timeout. * long *status Pointer to HTTP status code. * * Returns: * OK on success, or HTTP_INTERNAL_SERVER_ERROR on failure. On failure we * will write a log message describing the error. */ int am_httpclient_get(request_rec *r, const char *uri, void **buffer, apr_size_t *size, apr_time_t timeout, long *status) { am_hc_block_header_t bh; CURL *curl; char curl_error[CURL_ERROR_SIZE]; CURLcode res; /* Initialize the data storage. */ am_hc_block_header_init(&bh, r->pool); /* Initialize the curl object. */ curl = am_httpclient_init_curl(r, uri, &bh, curl_error); if(curl == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to download data from the uri \"%s\", " "cannot set timeout to %ld: [%u] %s", uri, (long)timeout, res, curl_error); goto cleanup_fail; } res = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to download data from the uri \"%s\", " "cannot set connect timeout to %ld: [%u] %s", uri, (long)timeout, res, curl_error); goto cleanup_fail; } /* Do the download. */ res = curl_easy_perform(curl); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to download data from the uri \"%s\", " "transaction aborted: [%u] %s", uri, res, curl_error); goto cleanup_fail; } if (status != NULL) { res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to download data from the uri \"%s\", " "no status report: [%u] %s", uri, res, curl_error); goto cleanup_fail; } } /* Free the curl object. */ curl_easy_cleanup(curl); /* Copy the data. */ am_hc_data_extract(&bh, r->pool, buffer, size); return OK; cleanup_fail: curl_easy_cleanup(curl); return HTTP_INTERNAL_SERVER_ERROR; } /* This function downloads data from a specified URI by issuing a POST * request. * * Parameters: * request_rec *r The apache request this download is * associated with. It is used for memory * allocation and logging. * const char *uri The URI we should post data to. * const void *post_data The POST data we should send. * apr_size_t post_length The length of the POST data. * const char *content_type The content type of the POST data. This * parameter can be NULL, in which case the * content type will be * "application/x-www-form-urlencoded". * void **buffer A pointer to where we should store a pointer * to the downloaded data. We will always add a * null-terminator to the data. This parameter * can't be NULL. * apr_size_t *size This is a pointer to where we will store the * length of the downloaded data, not including * the null-terminator we add. This parameter * can be NULL. * * Returns: * OK on success. On failure we will write a log message describing the * error, and return HTTP_INTERNAL_SERVER_ERROR. */ int am_httpclient_post(request_rec *r, const char *uri, const void *post_data, apr_size_t post_length, const char *content_type, void **buffer, apr_size_t *size) { am_hc_block_header_t bh; CURL *curl; char curl_error[CURL_ERROR_SIZE]; CURLcode res; struct curl_slist *ctheader; /* Initialize the data storage. */ am_hc_block_header_init(&bh, r->pool); /* Initialize the curl object. */ curl = am_httpclient_init_curl(r, uri, &bh, curl_error); if(curl == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } /* Enable POST request. */ res = curl_easy_setopt(curl, CURLOPT_POST, 1L); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to enable POST request: [%u] %s", res, curl_error); goto cleanup_fail; } /* Set POST data size. */ res = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_length); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set the POST data length: [%u] %s", res, curl_error); goto cleanup_fail; } /* Set POST data. */ res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set the POST data: [%u] %s", res, curl_error); goto cleanup_fail; } /* Set the content-type header. */ /* Set default content type if content_type is NULL. */ if(content_type == NULL) { content_type = "application/x-www-form-urlencoded"; } /* Create header list. */ ctheader = NULL; ctheader = curl_slist_append(ctheader, apr_pstrcat( r->pool, "Content-Type: ", content_type, NULL )); /* Set headers. */ res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, ctheader); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set content-type header to \"%s\": [%u] %s", content_type, res, curl_error); goto cleanup_fail; } /* Do the download. */ res = curl_easy_perform(curl); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to download data from the uri \"%s\": [%u] %s", uri, res, curl_error); goto cleanup_fail; } /* Free the curl object. */ curl_easy_cleanup(curl); /* Free the content-type header. */ curl_slist_free_all(ctheader); /* Copy the data. */ am_hc_data_extract(&bh, r->pool, buffer, size); return OK; cleanup_fail: curl_easy_cleanup(curl); return HTTP_INTERNAL_SERVER_ERROR; } /* This function downloads data from a specified URI by issuing a POST * request. * * Parameters: * request_rec *r The apache request this download is * associated with. It is used for memory * allocation and logging. * const char *uri The URI we should post data to. * const char *post_data The POST data we should send. * const char *content_type The content type of the POST data. This * parameter can be NULL, in which case the * content type will be * "application/x-www-form-urlencoded". * void **buffer A pointer to where we should store a pointer * to the downloaded data. We will always add a * null-terminator to the data. This parameter * can't be NULL. * apr_size_t *size This is a pointer to where we will store the * length of the downloaded data, not including * the null-terminator we add. This parameter * can be NULL. * * Returns: * OK on success. On failure we will write a log message describing the * error, and return HTTP_INTERNAL_SERVER_ERROR. */ int am_httpclient_post_str(request_rec *r, const char *uri, const char *post_data, const char *content_type, void **buffer, apr_size_t *size) { return am_httpclient_post(r, uri, post_data, strlen(post_data), content_type, buffer, size); } mod_auth_mellon-0.12.0/auth_mellon.h0000644000175100017510000003613012667763074017023 0ustar olavmoolavmo/* * * auth_mellon.h: 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 * */ #ifndef MOD_AUTH_MELLON_H #define MOD_AUTH_MELLON_H #include #include #include #include #include #include #include #include #include #include #include #include #include "lasso_compat.h" /* The following are redefined in ap_config_auto.h */ #undef PACKAGE_BUGREPORT #undef PACKAGE_NAME #undef PACKAGE_STRING #undef PACKAGE_TARNAME #undef PACKAGE_VERSION #undef HAVE_TIMEGM /* is redefined again in ap_config.h */ #include "apr_base64.h" #include "apr_time.h" #include "apr_strings.h" #include "apr_shm.h" #include "apr_md5.h" #include "apr_file_info.h" #include "apr_file_io.h" #include "apr_xml.h" #include "apr_lib.h" #include "apr_fnmatch.h" #include "ap_config.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_ssl.h" /* Backwards-compatibility helpers. */ #include "auth_mellon_compat.h" /* Size definitions for the session cache. */ #define AM_CACHE_KEYSIZE 120 #define AM_CACHE_ENVSIZE 2048 #define AM_CACHE_USERSIZE 512 #define AM_CACHE_DEFAULT_ENTRY_SIZE 196608 #define AM_CACHE_MIN_ENTRY_SIZE 65536 /* Internal error codes */ #define AM_ERROR_INVALID_PAOS_HEADER 1 #define AM_ERROR_MISSING_PAOS_HEADER 2 #define AM_ERROR_MISSING_PAOS_MEDIA_TYPE 3 /* This is the length of the id we use (for session IDs and * replaying POST data). */ #define AM_ID_LENGTH 32 #define MEDIA_TYPE_PAOS "application/vnd.paos+xml" #define am_get_srv_cfg(s) (am_srv_cfg_rec *)ap_get_module_config((s)->module_config, &auth_mellon_module) #define am_get_mod_cfg(s) (am_get_srv_cfg((s)))->mc #define am_get_dir_cfg(r) (am_dir_cfg_rec *)ap_get_module_config((r)->per_dir_config, &auth_mellon_module) #define am_get_req_cfg(r) (am_req_cfg_rec *)ap_get_module_config((r)->request_config, &auth_mellon_module) typedef struct am_mod_cfg_rec { int cache_size; const char *lock_file; const char *post_dir; apr_time_t post_ttl; 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; } am_mod_cfg_rec; typedef struct am_srv_cfg_rec { am_mod_cfg_rec *mc; } am_srv_cfg_rec; typedef enum { am_enable_default, am_enable_off, am_enable_info, am_enable_auth } am_enable_t; typedef enum { AM_COND_FLAG_NULL = 0x000, /* No flags */ AM_COND_FLAG_OR = 0x001, /* Or with next condition */ AM_COND_FLAG_NOT = 0x002, /* Negate this condition */ AM_COND_FLAG_REG = 0x004, /* Condition is regex */ AM_COND_FLAG_NC = 0x008, /* Case insensitive match */ AM_COND_FLAG_MAP = 0x010, /* Try to use attribute name from MellonSetEnv */ AM_COND_FLAG_REF = 0x020, /* Set regex backreferences */ AM_COND_FLAG_SUB = 0x040, /* Substring match */ /* The other options are internally used */ AM_COND_FLAG_IGN = 0x1000, /* Condition is to be ignored */ AM_COND_FLAG_REQ = 0x2000, /* Condition was set using MellonRequire */ AM_COND_FLAG_FSTR = 0x4000, /* Value contains a format string */ } am_cond_flag_t; extern const char *am_cond_options[]; typedef struct { const char *varname; int flags; const char *str; ap_regex_t *regex; const char *directive; } am_cond_t; typedef struct am_metadata { const char *file; /* Metadata file with one or many IdP */ const char *chain; /* Validating chain */ } am_metadata_t; typedef struct am_dir_cfg_rec { /* enable_mellon is used to enable auth_mellon for a location. */ am_enable_t enable_mellon; const char *varname; int secure; const char *merge_env_vars; int env_vars_index_start; int env_vars_count_in_n; const char *cookie_domain; const char *cookie_path; apr_array_header_t *cond; apr_hash_t *envattr; const char *userattr; const char *idpattr; int dump_session; int dump_saml_response; /* The "root directory" of our SAML2 endpoints. This path is relative * to the root of the web server. * * This path will always end with '/'. */ const char *endpoint_path; /* Lasso configuration variables. */ const char *sp_metadata_file; const char *sp_private_key_file; const char *sp_cert_file; apr_array_header_t *idp_metadata; const char *idp_public_key_file; const char *idp_ca_file; GList *idp_ignore; /* metadata autogeneration helper */ char *sp_entity_id; apr_hash_t *sp_org_name; apr_hash_t *sp_org_display_name; apr_hash_t *sp_org_url; /* Maximum number of seconds a session is valid for. */ int session_length; /* No cookie error page. */ const char *no_cookie_error_page; /* Authorization error page. */ const char *no_success_error_page; /* Login path for IdP initiated logins */ const char *login_path; /* IdP discovery service */ const char *discovery_url; int probe_discovery_timeout; apr_table_t *probe_discovery_idp; /* The configuration record we "inherit" the lasso server object from. */ struct am_dir_cfg_rec *inherit_server_from; /* Mutex to prevent us from creating several lasso server objects. */ apr_thread_mutex_t *server_mutex; /* AuthnContextClassRef list */ apr_array_header_t *authn_context_class_ref; /* Controls the checking of SubjectConfirmationData.Address attribute */ int subject_confirmation_data_address_check; /* MellonDoNotVerifyLogoutSignature idp set */ apr_hash_t *do_not_verify_logout_signature; /* Whether we should replay POST data after authentication. */ int post_replay; /* Cached lasso server object. */ LassoServer *server; /* Whether to send an ECP client a list of IdP's */ int ecp_send_idplist; /* List of domains we can redirect to. */ const char * const *redirect_domains; } am_dir_cfg_rec; /* Bitmask for PAOS service options */ typedef enum { ECP_SERVICE_OPTION_CHANNEL_BINDING = 1, ECP_SERVICE_OPTION_HOLDER_OF_KEY = 2, ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED = 4, ECP_SERVICE_OPTION_DELEGATION = 8, } ECPServiceOptions; typedef struct am_req_cfg_rec { char *cookie_value; #ifdef HAVE_ECP bool ecp_authn_req; ECPServiceOptions ecp_service_options; #endif /* HAVE_ECP */ } am_req_cfg_rec; typedef struct am_cache_storage_t { apr_size_t ptr; } am_cache_storage_t; typedef struct am_cache_env_t { am_cache_storage_t varname; am_cache_storage_t value; } am_cache_env_t; typedef struct am_cache_entry_t { char key[AM_CACHE_KEYSIZE]; apr_time_t access; apr_time_t expires; int logged_in; unsigned short size; am_cache_storage_t user; /* Variables used to store lasso state between login requests *and logout requests. */ 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 { AM_CACHE_SESSION, AM_CACHE_NAMEID } am_cache_key_t; /* Type for configuring environment variable names */ typedef struct am_envattr_conf_t { // Name of the variable const char *name; // Should a prefix be added int prefixed; } am_envattr_conf_t; extern const command_rec auth_mellon_commands[]; typedef struct am_error_map_t { int lasso_error; int http_error; } am_error_map_t; extern const am_error_map_t auth_mellon_errormap[]; /* When using a value from a directory configuration structure, a special value is used * to state "inherit" from parent, when reading a value and the value is still inherit from, it * means that no value has ever been set for this directive, in this case, we use the default * value. * * This macro expects that if your variable is called "name" there is a static const variable named * "default_name" which holds the default value for this variable. */ #define CFG_VALUE(container, name) \ (container->name == inherit_##name ? default_##name : container->name) #define CFG_MERGE(add_cfg, base_cfg, name) \ (add_cfg->name == inherit_##name ? base_cfg->name : add_cfg->name) /** Default and inherit value for SubjectConfirmationData Address check setting. */ static const int default_subject_confirmation_data_address_check = 1; static const int inherit_subject_confirmation_data_address_check = -1; /* Default and inherit values for MellonPostReplay option. */ static const int default_post_replay = 0; static const int inherit_post_replay = -1; /* Whether to send an ECP client a list of IdP's */ static const int default_ecp_send_idplist = 0; static const int inherit_ecp_send_idplist = -1; void *auth_mellon_dir_config(apr_pool_t *p, char *d); void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add); void *auth_mellon_server_config(apr_pool_t *p, server_rec *s); const char *am_cookie_get(request_rec *r); void am_cookie_set(request_rec *r, const char *id); void am_cookie_delete(request_rec *r); void am_cache_init(am_mod_cfg_rec *mod_cfg); 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); void am_cache_update_expires(am_cache_entry_t *t, apr_time_t expires); void am_cache_env_populate(request_rec *r, am_cache_entry_t *session); int am_cache_env_append(am_cache_entry_t *session, const char *var, const char *val); const char *am_cache_env_fetch_first(am_cache_entry_t *t, const char *var); void am_cache_delete(server_rec *s, am_cache_entry_t *session); int am_cache_set_lasso_state(am_cache_entry_t *session, const char *lasso_identity, const char *lasso_session, const char *lasso_saml_response); const char *am_cache_get_lasso_identity(am_cache_entry_t *session); const char *am_cache_get_lasso_session(am_cache_entry_t *session); am_cache_entry_t *am_get_request_session(request_rec *r); am_cache_entry_t *am_get_request_session_by_nameid(request_rec *r, char *nameid); am_cache_entry_t *am_new_request_session(request_rec *r); void am_release_request_session(request_rec *r, am_cache_entry_t *session); void am_delete_request_session(request_rec *r, am_cache_entry_t *session); char *am_reconstruct_url(request_rec *r); int am_validate_redirect_url(request_rec *r, const char *url); int am_check_permissions(request_rec *r, am_cache_entry_t *session); void am_set_cache_control_headers(request_rec *r); int am_read_post_data(request_rec *r, char **data, apr_size_t *length); char *am_extract_query_parameter(apr_pool_t *pool, const char *query_string, const char *name); char *am_urlencode(apr_pool_t *pool, const char *str); int am_urldecode(char *data); int am_check_url(request_rec *r, const char *url); char *am_generate_id(request_rec *r); char *am_getfile(apr_pool_t *conf, server_rec *s, const char *file); char *am_get_endpoint_url(request_rec *r); int am_postdir_cleanup(request_rec *s); char *am_htmlencode(request_rec *r, const char *str); int am_save_post(request_rec *r, const char **relay_state); const char *am_filepath_dirname(apr_pool_t *p, const char *path); const char *am_strip_cr(request_rec *r, const char *str); const char *am_add_cr(request_rec *r, const char *str); const char *am_xstrtok(request_rec *r, const char *str, const char *sep, char **last); void am_strip_blank(const char **s); const char *am_get_header_attr(request_rec *r, const char *h, const char *v, const char *a); int am_has_header(request_rec *r, const char *h, const char *v); const char *am_get_mime_header(request_rec *r, const char *m, const char *h); const char *am_get_mime_body(request_rec *r, const char *mime); char *am_get_service_url(request_rec *r, LassoProfile *profile, char *service_name); bool am_parse_paos_header(request_rec *r, const char *header, ECPServiceOptions *options_return); bool am_header_has_media_type(request_rec *r, const char *header, const char *media_type); const char *am_get_config_langstring(apr_hash_t *h, const char *lang); int am_get_boolean_query_parameter(request_rec *r, const char *name, int *return_value, int default_value); char *am_get_assertion_consumer_service_by_binding(LassoProvider *provider, const char *binding); #ifdef HAVE_ECP char *am_ecp_service_options_str(apr_pool_t *pool, ECPServiceOptions options); bool am_is_paos_request(request_rec *r, int *error_code); #endif /* HAVE_ECP */ int am_auth_mellon_user(request_rec *r); int am_check_uid(request_rec *r); int am_handler(request_rec *r); int am_httpclient_get(request_rec *r, const char *uri, void **buffer, apr_size_t *size, apr_time_t timeout, long *status); int am_httpclient_post(request_rec *r, const char *uri, const void *post_data, apr_size_t post_length, const char *content_type, void **buffer, apr_size_t *size); int am_httpclient_post_str(request_rec *r, const char *uri, const char *post_data, const char *content_type, void **buffer, apr_size_t *size); extern module AP_MODULE_DECLARE_DATA auth_mellon_module; #endif /* MOD_AUTH_MELLON_H */ mod_auth_mellon-0.12.0/auth_mellon_compat.h0000644000175100017510000000303412354722415020346 0ustar olavmoolavmo#ifndef AUTH_MELLON_COMPAT_H #define AUTH_MELLON_COMPAT_H #include #include "ap_config.h" #include "ap_release.h" #ifdef AP_NEED_SET_MUTEX_PERMS #include "unixd.h" #endif /* Old glib compatibility */ #if (GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 14) static void g_hash_table_get_keys_helper(gpointer key, gpointer value, gpointer user_data) { GList **out = user_data; *out = g_list_prepend(*out, key); } static GList *g_hash_table_get_keys(GHashTable *ht) { GList *ret = NULL; g_hash_table_foreach(ht, g_hash_table_get_keys_helper, &ret); return g_list_reverse(ret); } #endif /* "remote_ip" in struct conn_rec changed name to "client_ip" in Apache 2.4. * This function retrieves the corrent member depending on the Apache version. */ static inline const char *am_compat_request_ip(request_rec *r) { #if (AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER < 4) return r->connection->remote_ip; #else return r->connection->client_ip; #endif } /* unixd_set_global_mutex_perms changed name to ap_unixd_set_global_mutex_perms * in Apache 2.4. This function provides a wrapper with the new name for old * versions. */ #ifdef AP_NEED_SET_MUTEX_PERMS #if (AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER < 4) static inline apr_status_t ap_unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex) { return unixd_set_global_mutex_perms(gmutex); } #endif #endif /* AP_NEED_SET_MUTEX_PERMS */ #endif /* AUTH_MELLON_COMPAT_H */ mod_auth_mellon-0.12.0/lasso_compat.h0000644000175100017510000000420712667763074017200 0ustar olavmoolavmo#ifdef HAVE_LASSO_UTILS_H #include #else #define lasso_assign_string(dest,src) \ { \ char *__tmp = g_strdup(src); \ lasso_release_string(dest); \ dest = __tmp; \ } #define lasso_release_string(dest) \ lasso_release_full(dest, g_free) #define lasso_release_full(dest, free_function) \ { \ if (dest) { \ free_function(dest); dest = NULL; \ } \ } #define lasso_check_type_equality(a,b) #define lasso_release_full2(dest, free_function, type) \ { \ lasso_check_type_equality(dest, type); \ if (dest) { \ free_function(dest); dest = NULL; \ } \ } #define lasso_release_list(dest) \ lasso_release_full2(dest, g_list_free, GList*) #define lasso_release_list_of_full(dest, free_function) \ { \ GList **__tmp = &(dest); \ if (*__tmp) { \ g_list_foreach(*__tmp, (GFunc)free_function, NULL); \ lasso_release_list(*__tmp); \ } \ } #define lasso_release_list_of_strings(dest) \ lasso_release_list_of_full(dest, g_free) #endif #ifndef LASSO_SAML2_ECP_PROFILE_WANT_AUTHN_SIGNED #define LASSO_SAML2_ECP_PROFILE_WANT_AUTHN_SIGNED "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp:2.0:WantAuthnRequestsSigned" #endif #ifndef LASSO_SAML2_CONDITIONS_DELEGATION #define LASSO_SAML2_CONDITIONS_DELEGATION "urn:oasis:names:tc:SAML:2.0:conditions:delegation" #endif #ifndef LASSO_SAML_EXT_CHANNEL_BINDING #define LASSO_SAML_EXT_CHANNEL_BINDING "urn:oasis:names:tc:SAML:protocol:ext:channel-binding" #endif mod_auth_mellon-0.12.0/configure0000755000175100017510000046567212667763114016266 0ustar olavmoolavmo#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for mod_auth_mellon 0.12.0. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: olav.morken@uninett.no about your system, including any $0: error possibly output before this message. Then install $0: a modern shell, or manually run the script under such a $0: shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='mod_auth_mellon' PACKAGE_TARNAME='mod_auth_mellon' PACKAGE_VERSION='0.12.0' PACKAGE_STRING='mod_auth_mellon 0.12.0' PACKAGE_BUGREPORT='olav.morken@uninett.no' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS EGREP GREP CPP GLIB_LIBS GLIB_CFLAGS OPENSSL_LIBS OPENSSL_CFLAGS CURL_LIBS CURL_CFLAGS LASSO_LIBS LASSO_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG APXS2 NAMEVER OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_apxs2 ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR LASSO_CFLAGS LASSO_LIBS CURL_CFLAGS CURL_LIBS OPENSSL_CFLAGS OPENSSL_LIBS GLIB_CFLAGS GLIB_LIBS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures mod_auth_mellon 0.12.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/mod_auth_mellon] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of mod_auth_mellon 0.12.0:";; esac cat <<\_ACEOF Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-apxs2=PATH Full path to the apxs2 executable. Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path LASSO_CFLAGS C compiler flags for LASSO, overriding pkg-config LASSO_LIBS linker flags for LASSO, overriding pkg-config CURL_CFLAGS C compiler flags for CURL, overriding pkg-config CURL_LIBS linker flags for CURL, overriding pkg-config OPENSSL_CFLAGS C compiler flags for OPENSSL, overriding pkg-config OPENSSL_LIBS linker flags for OPENSSL, overriding pkg-config GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config GLIB_LIBS linker flags for GLIB, overriding pkg-config CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF mod_auth_mellon configure 0.12.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ------------------------------------- ## ## Report this to olav.morken@uninett.no ## ## ------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by mod_auth_mellon $as_me 0.12.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # We require support for C99. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; } if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } int main () { // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c99" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c99" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno; then : fi NAMEVER=mod_auth_mellon-0.12.0 # This section defines the --with-apxs2 option. # Check whether --with-apxs2 was given. if test "${with_apxs2+set}" = set; then : withval=$with_apxs2; APXS2=${withval} fi if test "x$APXS2" = "x"; then # The user didn't specify the --with-apxs2-option. # Search for apxs2 in the specified directories # Extract the first word of "apxs2", so it can be a program name with args. set dummy apxs2; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_APXS2+:} false; then : $as_echo_n "(cached) " >&6 else case $APXS2 in [\\/]* | ?:[\\/]*) ac_cv_path_APXS2="$APXS2" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" for as_dir in $as_dummy do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_APXS2="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi APXS2=$ac_cv_path_APXS2 if test -n "$APXS2"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $APXS2" >&5 $as_echo "$APXS2" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$APXS2" = "x"; then # Didn't find apxs2 in any of the specified directories. # Search for apxs instead. # Extract the first word of "apxs", so it can be a program name with args. set dummy apxs; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_APXS2+:} false; then : $as_echo_n "(cached) " >&6 else case $APXS2 in [\\/]* | ?:[\\/]*) ac_cv_path_APXS2="$APXS2" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" for as_dir in $as_dummy do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_APXS2="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi APXS2=$ac_cv_path_APXS2 if test -n "$APXS2"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $APXS2" >&5 $as_echo "$APXS2" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi # Test if $APXS2 exists and is an executable. if test ! -x "$APXS2"; then # $APXS2 isn't a executable file. as_fn_error $? " Could not find apxs2. Please spesify the path to apxs2 using the --with-apxs2=/full/path/to/apxs2 option. The executable may also be named 'apxs'. " "$LINENO" 5 fi # Replace any occurances of @APXS2@ with the value of $APXS2 in the Makefile. # We need the lasso library for SAML2 communication. if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LASSO" >&5 $as_echo_n "checking for LASSO... " >&6; } if test -n "$LASSO_CFLAGS"; then pkg_cv_LASSO_CFLAGS="$LASSO_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"lasso\""; } >&5 ($PKG_CONFIG --exists --print-errors "lasso") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LASSO_CFLAGS=`$PKG_CONFIG --cflags "lasso" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LASSO_LIBS"; then pkg_cv_LASSO_LIBS="$LASSO_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"lasso\""; } >&5 ($PKG_CONFIG --exists --print-errors "lasso") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LASSO_LIBS=`$PKG_CONFIG --libs "lasso" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LASSO_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "lasso" 2>&1` else LASSO_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "lasso" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LASSO_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (lasso) were not met: $LASSO_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables LASSO_CFLAGS and LASSO_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables LASSO_CFLAGS and LASSO_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else LASSO_CFLAGS=$pkg_cv_LASSO_CFLAGS LASSO_LIBS=$pkg_cv_LASSO_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi saved_LIBS=$LIBS; LIBS="$LIBS $LASSO_LIBS"; { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lasso_server_new_from_buffers in -llasso" >&5 $as_echo_n "checking for lasso_server_new_from_buffers in -llasso... " >&6; } if ${ac_cv_lib_lasso_lasso_server_new_from_buffers+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llasso $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lasso_server_new_from_buffers (); int main () { return lasso_server_new_from_buffers (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lasso_lasso_server_new_from_buffers=yes else ac_cv_lib_lasso_lasso_server_new_from_buffers=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lasso_lasso_server_new_from_buffers" >&5 $as_echo "$ac_cv_lib_lasso_lasso_server_new_from_buffers" >&6; } if test "x$ac_cv_lib_lasso_lasso_server_new_from_buffers" = xyes; then : LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_server_new_from_buffers" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lasso_server_load_metadata in -llasso" >&5 $as_echo_n "checking for lasso_server_load_metadata in -llasso... " >&6; } if ${ac_cv_lib_lasso_lasso_server_load_metadata+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llasso $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lasso_server_load_metadata (); int main () { return lasso_server_load_metadata (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lasso_lasso_server_load_metadata=yes else ac_cv_lib_lasso_lasso_server_load_metadata=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lasso_lasso_server_load_metadata" >&5 $as_echo "$ac_cv_lib_lasso_lasso_server_load_metadata" >&6; } if test "x$ac_cv_lib_lasso_lasso_server_load_metadata" = xyes; then : LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_server_load_metadata" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lasso_profile_set_signature_verify_hint in -llasso" >&5 $as_echo_n "checking for lasso_profile_set_signature_verify_hint in -llasso... " >&6; } if ${ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llasso $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lasso_profile_set_signature_verify_hint (); int main () { return lasso_profile_set_signature_verify_hint (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint=yes else ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint" >&5 $as_echo "$ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint" >&6; } if test "x$ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint" = xyes; then : LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_profile_set_signature_verify_hint" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lasso_ecp_request_new in -llasso" >&5 $as_echo_n "checking for lasso_ecp_request_new in -llasso... " >&6; } if ${ac_cv_lib_lasso_lasso_ecp_request_new+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llasso $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lasso_ecp_request_new (); int main () { return lasso_ecp_request_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lasso_lasso_ecp_request_new=yes else ac_cv_lib_lasso_lasso_ecp_request_new=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lasso_lasso_ecp_request_new" >&5 $as_echo "$ac_cv_lib_lasso_lasso_ecp_request_new" >&6; } if test "x$ac_cv_lib_lasso_lasso_ecp_request_new" = xyes; then : LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_ECP" fi LIBS=$saved_LIBS; # We need the curl library for HTTP-Artifact downloads. pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CURL" >&5 $as_echo_n "checking for CURL... " >&6; } if test -n "$CURL_CFLAGS"; then pkg_cv_CURL_CFLAGS="$CURL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcurl\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcurl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CURL_CFLAGS=`$PKG_CONFIG --cflags "libcurl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$CURL_LIBS"; then pkg_cv_CURL_LIBS="$CURL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcurl\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcurl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CURL_LIBS=`$PKG_CONFIG --libs "libcurl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then CURL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcurl" 2>&1` else CURL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcurl" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$CURL_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libcurl) were not met: $CURL_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables CURL_CFLAGS and CURL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables CURL_CFLAGS and CURL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else CURL_CFLAGS=$pkg_cv_CURL_CFLAGS CURL_LIBS=$pkg_cv_CURL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi # We also need openssl for its random number generator. pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 $as_echo_n "checking for OPENSSL... " >&6; } if test -n "$OPENSSL_CFLAGS"; then pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$OPENSSL_LIBS"; then pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl" 2>&1` else OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$OPENSSL_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (openssl) were not met: $OPENSSL_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables OPENSSL_CFLAGS and OPENSSL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables OPENSSL_CFLAGS and OPENSSL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi # We need at least version 2.12 of GLib. pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLIB" >&5 $as_echo_n "checking for GLIB... " >&6; } if test -n "$GLIB_CFLAGS"; then pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.12\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.12") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.12" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$GLIB_LIBS"; then pkg_cv_GLIB_LIBS="$GLIB_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.12\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.12") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.12" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.12" 2>&1` else GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.12" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$GLIB_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (glib-2.0 >= 2.12) were not met: $GLIB_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GLIB_CFLAGS and GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables GLIB_CFLAGS and GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS GLIB_LIBS=$pkg_cv_GLIB_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi # Test to see if we can include lasso/utils.h # AC_CHECK_HEADER won't work correctly unless we specifiy the include directories # found in the LASSO_CFLAGS. Save and restore CFLAGS and CPPFLAGS. saved_CFLAGS=$CFLAGS saved_CPPFLAGS=$CPPFLAGS CFLAGS="$CFLAGS $pkg_cv_LASSO_CFLAGS" CPPFLAGS="$CPPFLAGS $pkg_cv_LASSO_CFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_header_mongrel "$LINENO" "lasso/utils.h" "ac_cv_header_lasso_utils_h" "$ac_includes_default" if test "x$ac_cv_header_lasso_utils_h" = xyes; then : LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_LASSO_UTILS_H" fi CFLAGS=$saved_CFLAGS CPPFLAGS=$saved_CPPFLAGS # Create Makefile from Makefile.in ac_config_files="$ac_config_files Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by mod_auth_mellon $as_me 0.12.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ mod_auth_mellon config.status 0.12.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi mod_auth_mellon-0.12.0/configure.ac0000644000175100017510000000533612667763074016635 0ustar olavmoolavmoAC_INIT([mod_auth_mellon],[0.12.0],[olav.morken@uninett.no]) # We require support for C99. AC_PROG_CC_C99 AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION()) # This section defines the --with-apxs2 option. AC_ARG_WITH( [apxs2], [ --with-apxs2=PATH Full path to the apxs2 executable.], [ APXS2=${withval} ],) if test "x$APXS2" = "x"; then # The user didn't specify the --with-apxs2-option. # Search for apxs2 in the specified directories AC_PATH_PROG(APXS2, apxs2,, /usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin) if test "x$APXS2" = "x"; then # Didn't find apxs2 in any of the specified directories. # Search for apxs instead. AC_PATH_PROG(APXS2, apxs,, /usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin) fi fi # Test if $APXS2 exists and is an executable. if test ! -x "$APXS2"; then # $APXS2 isn't a executable file. AC_MSG_ERROR([ Could not find apxs2. Please spesify the path to apxs2 using the --with-apxs2=/full/path/to/apxs2 option. The executable may also be named 'apxs'. ]) fi # Replace any occurances of @APXS2@ with the value of $APXS2 in the Makefile. AC_SUBST(APXS2) # We need the lasso library for SAML2 communication. PKG_CHECK_MODULES(LASSO, lasso) saved_LIBS=$LIBS; LIBS="$LIBS $LASSO_LIBS"; AC_CHECK_LIB(lasso, lasso_server_new_from_buffers, LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_server_new_from_buffers") AC_CHECK_LIB(lasso, lasso_server_load_metadata, LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_server_load_metadata") AC_CHECK_LIB(lasso, lasso_profile_set_signature_verify_hint, LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_profile_set_signature_verify_hint") AC_CHECK_LIB(lasso, lasso_ecp_request_new, LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_ECP") LIBS=$saved_LIBS; AC_SUBST(LASSO_CFLAGS) AC_SUBST(LASSO_LIBS) # We need the curl library for HTTP-Artifact downloads. PKG_CHECK_MODULES(CURL, libcurl) AC_SUBST(CURL_CFLAGS) AC_SUBST(CURL_LIBS) # We also need openssl for its random number generator. PKG_CHECK_MODULES(OPENSSL, openssl) AC_SUBST(OPENSSL_CFLAGS) AC_SUBST(OPENSSL_LIBS) # We need at least version 2.12 of GLib. PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.12]) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) # Test to see if we can include lasso/utils.h # AC_CHECK_HEADER won't work correctly unless we specifiy the include directories # found in the LASSO_CFLAGS. Save and restore CFLAGS and CPPFLAGS. saved_CFLAGS=$CFLAGS saved_CPPFLAGS=$CPPFLAGS CFLAGS="$CFLAGS $pkg_cv_LASSO_CFLAGS" CPPFLAGS="$CPPFLAGS $pkg_cv_LASSO_CFLAGS" AC_CHECK_HEADER([lasso/utils.h], LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_LASSO_UTILS_H") CFLAGS=$saved_CFLAGS CPPFLAGS=$saved_CPPFLAGS # Create Makefile from Makefile.in AC_CONFIG_FILES([Makefile]) AC_OUTPUT mod_auth_mellon-0.12.0/Makefile.in0000644000175100017510000000262112667763074016406 0ustar olavmoolavmo # Source files. mod_auth_mellon.c must be the first file. SRC=mod_auth_mellon.c \ auth_mellon_cache.c auth_mellon_config.c \ auth_mellon_cookie.c auth_mellon_handler.c \ auth_mellon_util.c \ auth_mellon_session.c \ auth_mellon_httpclient.c # Files to include when making a .tar.gz-file for distribution DISTFILES=$(SRC) \ auth_mellon.h \ auth_mellon_compat.h \ lasso_compat.h \ configure \ configure.ac \ Makefile.in \ autogen.sh \ README \ ECP.rst \ COPYING \ NEWS all: mod_auth_mellon.la mod_auth_mellon.la: $(SRC) auth_mellon.h auth_mellon_compat.h @APXS2@ -Wc,"-std=c99 @OPENSSL_CFLAGS@ @LASSO_CFLAGS@ @CURL_CFLAGS@ @GLIB_CFLAGS@ @CFLAGS@" -Wl,"@OPENSSL_LIBS@ @LASSO_LIBS@ @CURL_LIBS@ @GLIB_LIBS@" -Wc,-Wall -Wc,-g -c $(SRC) # Building configure (for distribution) configure: configure.ac ./autogen.sh @NAMEVER@.tar.gz: $(DISTFILES) tar -c --transform="s#^#@NAMEVER@/#" -vzf $@ $(DISTFILES) .PHONY: install install: mod_auth_mellon.la @APXS2@ -i -n auth_mellon mod_auth_mellon.la .PHONY: distfile distfile: @NAMEVER@.tar.gz .PHONY: clean clean: rm -f mod_auth_mellon.la rm -f $(SRC:%.c=%.lo) rm -f $(SRC:%.c=%.slo) rm -rf .libs/ .PHONY: distclean distclean: clean rm -f Makefile config.log config.status @NAMEVER@.tar.gz *~ \ build-stamp config.guess config.sub rm -rf debian/mod-auth-mellon rm -f debian/files .PHONY: fullclean fullclean: distclean rm -f configure aclocal.m4 mod_auth_mellon-0.12.0/autogen.sh0000755000175100017510000000007612354722415016327 0ustar olavmoolavmo#!/bin/sh autoreconf --force --install rm -rf autom4te.cache/ mod_auth_mellon-0.12.0/README0000644000175100017510000010701412667763074015223 0ustar olavmoolavmo=========================================================================== README file for mod_auth_mellon =========================================================================== mod_auth_mellon is a authentication module for Apache. It authenticates the user against a SAML 2.0 IdP, and grants access to directories depending on attributes received from the IdP. =========================================================================== Dependencies =========================================================================== mod_auth_mellon has four dependencies: * pkg-config * Apache (>=2.0) * OpenSSL * lasso (>=2.1) You will also require development headers and tools for all of the dependencies. If OpenSSL or lasso are installed in a "strange" directory, then you may have to specify the directory containing "lasso.pc" and/or "openssl.pc" in the PKG_CONFIG_PATH environment variable. For example, if openssl is installed in /usr/local/openssl (with openssl.pc in /usr/local/openssl/lib/pkgconfig/) and lasso is installed in /opt/lasso (lasso.pc in /opt/lasso/lib/pkgconfig/), then you can set PKG_CONFIG_PATH before running configure like this: PKG_CONFIG_PATH=/usr/local/openssl/lib/pkgconfig:/opt/lasso/lib/pkgconfig export PKG_CONFIG_PATH If Apache is installed in a "strange" directory, then you may have to specify the path to apxs2 using the --with-apxs2=/full/path/to/apxs2 option to configure. If, for example, Apache is installed in /opt/apache, with apxs2 in /opt/apache/bin, then you run ./configure --with-apxs2=/opt/apache2/bin/apxs2 Note that, depending on your distribution, apxs2 may be named apxs. =========================================================================== Installing mod_auth_mellon =========================================================================== mod_auth_mellon uses autoconf, and can be installed by running the following commands: ./configure make make install =========================================================================== Configuring mod_auth_mellon =========================================================================== Here we are going to assume that your web servers hostname is 'example.com', and that the directory you are going to protect is 'http://example.com/secret/'. We are also going to assume that you have configured your web site to use SSL. You need to edit the configuration file for your web server. Depending on your distribution, it may be named '/etc/apache/httpd.conf' or something different. You need to add a LoadModule directive for mod_auth_mellon. This will look similar to this: LoadModule auth_mellon_module /usr/lib/apache2/modules/mod_auth_mellon.so To find the full path to mod_auth_mellon.so, you may run: apxs2 -q LIBEXECDIR This will print the path where Apache stores modules. mod_auth_mellon.so will be stored in that directory. You will also need to make sure that Apache's authn_core module is also enabled. Most likely you also want authz_user to be enabled. After you have added the LoadModule directive, you must add configuration for mod_auth_mellon. The following is an example configuration: ########################################################################### # Global configuration for mod_auth_mellon. This configuration is shared by # every virtual server and location in this instance of apache. ########################################################################### # MellonCacheSize sets the maximum number of sessions which can be active # at once. When mod_auth_mellon reaches this limit, it will begin removing # the least recently used sessions. The server must be restarted before any # changes to this option takes effect. # 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 # option takes effect. # Default: MellonLockFile "/var/run/mod_auth_mellon.lock" MellonLockFile "/var/run/mod_auth_mellon.lock" # MellonPostDirectory is the full path of a directory where POST requests # are saved during authentication. This directory must writable by the # Apache user. It should not be writable (or readable) by other users. # Default: None # Example: MellonPostDirectory "/var/cache/mod_auth_mellon_postdata" # MellonPostTTL is the delay in seconds before a saved POST request can # be flushed. # Default: MellonPostTTL 900 (15 mn) MellonPostTTL 900 # MellonPostSize is the maximum size for saved POST requests # Default: MellonPostSize 1073741824 (1 GB) MellonPostSize 1073741824 # MellonPostCount is the maximum amount of saved POST requests # Default: MellonPostCount 100 MellonPostCount 100 ########################################################################### # End of global configuration for mod_auth_mellon. ########################################################################### # This defines a directory where mod_auth_mellon should do access control. # These are standard Apache apache configuration directives. # You must have enabled Apache's authn_core and authz_user modules. # See http://httpd.apache.org/docs/2.4/mod/mod_authn_core.html and # http://httpd.apache.org/docs/2.4/mod/mod_authz_user.html # about them. Require valid-user AuthType "Mellon" # MellonEnable is used to enable auth_mellon on a location. # It has three possible values: "off", "info" and "auth". # They have the following meanings: # "off": mod_auth_mellon will not do anything in this location. # This is the default state. # "info": If the user is authorized to access the resource, then # we will populate the environment with information about # the user. If the user isn't authorized, then we won't # populate the environment, but we won't deny the user # access either. # "auth": We will populate the environment with information about # the user if he is authorized. If he is authenticated # (logged in), but not authorized (according to the # MellonRequire and MellonCond directives, then we will # return a 403 Forbidden error. If he isn't authenticated # then we will redirect him to the login page of the IdP. # # Default: MellonEnable "off" MellonEnable "auth" # MellonDecoder is an obsolete option which is a no-op but is # still accepted for backwards compatibility. # MellonVariable is used to select the name of the cookie which # mod_auth_mellon should use to remember the session id. If you # want to have different sites running on the same host, then # you will have to choose a different name for the cookie for each # site. # Default: "cookie" MellonVariable "cookie" # MellonSecureCookie enforces the HttpOnly and secure flags # for the mod_mellon cookie # Default: Off MellonSecureCookie On # MellonCookieDomain allows to specify of the cookie which auth_mellon # will set. # Default: the domain for the received request (the Host: header if # present, of the ServerName of the VirtualHost declaration, or if # absent a reverse resolution on the local IP) # MellonCookieDomain example.com # MellonCookiePath is the path of the cookie which auth_mellon will set. # Default: / MellonCookiePath / # MellonUser selects which attribute we should use for the username. # The username is passed on to other apache modules and to the web # page the user visits. NAME_ID is an attribute which we set to # the id we get from the IdP. # Note: If MellonUser refers to a multi-valued attribute, any single # value from that attribute may be used. Do not rely on it selecting a # specific value. # Default: MellonUser "NAME_ID" MellonUser "NAME_ID" # MellonIdP selects in which attribute we should dump the remote # IdP providerId. This is passed to other apache modules and to # the web pages the user visits. # Default: none # MellonIdP "IDP" # MellonSetEnv configuration directives allows you to map # attribute names received from the IdP to names you choose # yourself. The syntax is 'MellonSetEnv '. # You can list multiple MellonSetEnv directives. # Default. None set. MellonSetEnv "e-mail" "mail" # MellonSetEnvNoPrefix is identical to MellonSetEnv, except this # does not prepend 'MELLON_' to the constructed environment variable. # The syntax is 'MellonSetEnvNoPrefix '. # You can list multiple MellonSetEnvNoPrefix directives. # Default. None set. MellonSetEnvNoPrefix "DISPLAY_NAME" "displayName" # MellonMergeEnvVars merges multiple values of environment variables # set using MellonSetEnv into single variable: # ie: MYENV_VAR => val1;val2;val3 instead of default behaviour of: # MYENV_VAR_0 => val1, MYENV_VAR_1 => val2 ... etc. # Second optional parameter specifies the separator, to override the # default semicolon. # Default: MellonMergeEnvVars Off MellonMergeEnvVars On MellonMergeEnvVars On ":" # MellonEnvVarsIndexStart specifies if environment variables for # multi-valued attributes should start indexing from 0 or 1 # The syntax is 'MellonEnvVarsIndexStart <0|1>'. # Default: MellonEnvVarsIndexStart 0 MellonEnvVarsIndexStart 1 # MellonEnvVarsSetCount specifies if number of values for any given # attribute should also be stored in variable suffixed _N. # ie: if user is member of two groups, the following will be set: # MELLON__groups=group1 # MELLON__groups_0=group1 # MELLON__groups_1=group2 # MELLON__groups_N=2 # Default: MellonEnvVarsIndexStart Off MellonEnvVarsIndexStart On # If MellonSessionDump is set, then the SAML session will be # available in the MELLON_SESSION environment variable MellonSessionDump Off # If MellonSamlResponseDump is set, then the SAML authentication # response will be available in the MELLON_SAML_RESPONSE environment # variable MellonSamlResponseDump Off # MellonRequire allows you to limit access to those with specific # attributes. The syntax is # 'MellonRequire '. # Note that the attribute name is the name we received from the # IdP. # # If you don't list any MellonRequire directives (and any # MellonCond directives, see below), then any user authenticated # by the IdP will have access to this service. If you list several # MellonRequire directives, then all of them will have to match. # If you use multiple MellonRequire directive on the same # attribute, the last overrides the previous ones. # # Default: None set. MellonRequire "eduPersonAffiliation" "student" "employee" # MellonCond provides the same function as MellonRequire, with # extra functionality (MellonRequire is retained for backward # compatibility). The syntax is # 'MellonCond []' # # is an attribute value to match. Unlike with MellonRequire, # multiples values are not allowed. # # If the [REG] flag is specified (see below), is a regular # expression. The syntax for backslash escape is the same as in # Apache's 's directives. # # Format strings are substituted into prior evaluation. # Here are the supported syntaxes: # %n With n being a digit between 0 and 9. If [REG,REF] # flags (see below) were used in an earlier matching # MellonCond, then regular expression back references # are substituted. # %{num} Same as %n, with num being a number that may be # greater than 9. # %{ENV:x} Substitute Apache environment variable x. # %% Escape substitution to get a literal %. # # is an optional, comma-separated list of option # enclosed with brackets. Here is an example: [NOT,NC] # The valid options are: # OR If this MellonCond evaluated to false, then the # next one will be checked. If it evaluates to true, # then the overall check succeeds. # NOT This MellonCond evaluates to true if the attribute # does not match the value. # SUB Substring match, evaluates to true if value is # included in attribute. # REG Value to check is a regular expression. # NC Perform case insensitive match. # MAP Attempt to search an attribute with name remapped by # MellonSetEnv. Fallback to non remapped name if not # found. # REF Used with REG, track regular expression back references, # So that they can be substituted in an upcoming # MellonCond directive. # # It is allowed to have multiple MellonCond on the same # attribute, and to mix MellonCond and MellonRequire. # Note that this can create tricky situations, since the OR # option has effect on a following MellonRequire directive. # # Default: none set # MellonCond "mail" "@example\.net$" [OR,REG] # MellonCond "mail" "@example\.com$" [OR,REG] # MellonCond "uid" "superuser" # MellonEndpointPath selects which directory mod_auth_mellon # should assume contains the SAML 2.0 endpoints. Any request to # this directory will be handled by mod_auth_mellon. # # The path is the full path (from the root of the web server) to # the directory. The directory must be a sub-directory of this # . # Default: MellonEndpointPath "/mellon" MellonEndpointPath "/secret/endpoint" # MellonDefaultLoginPath is the location where one should be # redirected after an IdP-initiated login. Default is "/" # Default: MellonDefaultLoginPath "/" MellonDefaultLoginPath "/" # MellonSessionLength sets the maximum lifetime of a session, in # seconds. The actual lifetime may be shorter, depending on the # conditions received from the IdP. The default length is 86400 # seconds, which is one day. # Default: MellonSessionLength 86400 MellonSessionLength 86400 # MellonNoCookieErrorPage is the full path to a page which # mod_auth_mellon will redirect the user to if he returns from the # IdP without a cookie with a session id. # Note that the user may also get this error if he for some reason # loses the cookie between being redirected to the IdP's login page # and returning from it. # If this option is unset, then mod_auth_mellon will return a # 400 Bad Request error if the cookie is missing. # Default: unset MellonNoCookieErrorPage "https://example.com/no_cookie.html" # MellonSPMetadataFile is the full path to the file containing # the metadata for this service provider. # If mod_auth_mellon was compiled against Lasso version 2.2.2 # or higher, this option is optional. Otherwise, it is mandatory. # Default: None set. MellonSPMetadataFile /etc/apache2/mellon/sp-metadata.xml # If you choose to autogenerate metadata, this option # can be used to control the SP entityId # MellonSPentityId "https://www.example.net/foo" # # If you choose to autogenerate metadata, these options # can be used to fill the element. They # all follow the syntax "option [lang] value": # MellonOrganizationName "random-service" # MellonOrganizationDisplayName "en" "Random service" # MellonOrganizationDisplayName "fr" "Service quelconque" # MellonOrganizationURL "http://www.espci.fr" # MellonSPPrivateKeyFile is a .pem file which contains the private # key of the service provider. The .pem-file cannot be encrypted # with a password. If built with lasso-2.2.2 or higher, the # private key only needs to be readable by root, otherwise it has # to be readable by the Apache pseudo user. # Default: None set. MellonSPPrivateKeyFile /etc/apache2/mellon/sp-private-key.pem # MellonSPCertFile is a .pem file with the certificate for the # service provider. This directive is optional. # Default: None set. MellonSPCertFile /etc/apache2/mellon/sp-cert.pem # MellonIdPMetadataFile is the full path to the file which contains # metadata for the IdP you are authenticating against. This # directive is required. Multiple IdP metadata can be configured # by using multiple MellonIdPMetadataFile directives. # # If your lasso library is recent enough (higher than 2.3.5), # then MellonIdPMetadataFile will accept an XML file containing # descriptors for multiple IdP. An optional validating chain can # be supplied as a second argument to MellonIdPMetadataFile. If # omitted, no metadata validation will take place. # # Default: None set. MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml # MellonIdPMetadataGlob is a glob(3) pattern enabled alternative # to MellonIdPMetadataFile. Like MellonIdPMetadataFile it will # accept an optional validating chain if lasso is recent enough. # # Default: None set. #MellonIdPMetadataGlob /etc/apache2/mellon/*-metadata.xml # MellonIdpPublicKeyFile is the full path of the public key of the # IdP. This parameter is optional if the public key is embedded # in the IdP's metadata file, or if a certificate authority is # used. This parameter cannot be used if multiple IdP are configured. # Default: None set. MellonIdPPublicKeyFile /etc/apache2/mellon/idp-public-key.pem # MellonIdPCAFile is the full path to the certificate of the # certificate authority. This can be used instead of an # certificate for the IdP. # Default: None set. MellonIdPCAFile /etc/apache2/mellon/ca.pem # MellonIdPIgnore lists IdP entityId that should not loaded # from XML federation metadata files. This is useful if an # IdP cause bugs. Multiple entityId may be specified through # single MellonIdPIgnore, and multiple MellonIdPIgnore are allowed. # Default: None set. #MellonIdPIgnore "https://bug.example.net/saml/idp" # MellonDiscoveryURL is the URL for IdP discovery service. # This is used for selecting among multiple configured IdP. # On initial user authentication, it is redirected to the # IdP discovery URL, with the following arguments set: # # entityID SP providerID URL, where our metadata # are published. # returnIDParam Argument that IdP discovery must send back. # return Return URL the IdP discovery should return to. # # The IdP discovery must redirect the user to the return URL, # with returnIDParam set to the selected IdP providerID. # # The builtin:get-metadata discovery URL is not supported anymore # starting with 0.3.1. See MellonProbeDiscoveryTimeout for # a replacement. # # Default: None set. MellonDiscoveryURL "http://www.example.net/idp-discovery" # MellonProbeDiscoveryTimeout sets the timeout of the # IdP probe discovery service, which is available on the # probeDisco endoint. # # This will cause the SP to send HTTP GET requests on the # configured IdP PorviderID URL. Theses URL should be used to # publish metadata, though this is not mandatory. If the IdP # returns an HTTP status 200, then the IdP is selected. # If the PorviderID URL requires SSL, MellonIdPCAFile is used # as a trusted CA bundle. # # Default: unset, which means the feature is disabled # MellonProbeDiscoveryTimeout 1 # MellonProbeDiscoveryIdP can be used to restrict the # list of IdP queried by the IdP probe discovery service. # # Default unset, which means that all configured IdP are # queried. # MellonProbeDiscoveryIdP http://idp1.example.com/saml/metadata # MellonProbeDiscoveryIdP http://idp2.example.net/saml/metadata # This option will make the SAML authentication assertion # available in the MELLON_SAML_RESPONSE environment # variable. This assertion holds a verifiable signature # that can be checked again. Default is Off. MellonSamlResponseDump Off # This option will make the Lasso session available in # the MELLON_SESSION environment variable. Default is Off. MellonSessionDump Off # This option will request specific authentication security-level # through the AuthnContextClassRef element of the AuthnRequest It will # also request enforcement of this level when receiving an # authenticating Assertion. # If the assertion does not have the required security level, an HTTP # Forbidden status code is returned to the browser. # MellonAuthnContextClassRef "urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos" # MellonAuthnContextClassRef "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" # MellonAuthnContextClassRef "urn:oasis:names:tc:SAML:2.0:ac:classes:SoftwarePKI" # MellonSubjectConfirmationDataAddressCheck is used to control # the checking of client IP address against the address returned by the # IdP in Address attribute of the SubjectConfirmationData node. Can be useful if your SP is # behind a reverse proxy or any kind of strange network topology making IP address of client # different for the IdP and the SP. Default is on. # MellonSubjectConfirmationDataAddressCheck On # Does not check signature on logout messages exchanges with idp1 # MellonDoNotVerifyLogoutSignature http://idp1.example.com/saml/metadata # Whether to enable replay of POST requests after authentication. When this option is # enabled, POST requests that trigger authentication will be saved until the # authentication is completed, and then replayed. If this option isn't enabled, # the requests will be turned into normal GET requests after authentication. # # Note that if this option is enabled, you must also # set the MellonPostDirectory option in the server configuration. # # The default is that it is "Off". # MellonPostReplay Off # Page to redirect to if the IdP sends an error in response to # the authentication request. # # Example: # MellonNoSuccessErrorPage https://sp.example.org/login_failed.html # # The default is to not redirect, but rather send a # 401 Unautorized error. # This option controls whether to include a list of IDP's when # sending an ECP PAOS message to an ECP client. MellonECPSendIDPList Off # List of domains that we allow redirects to. # The special name "[self]" means the domain of the current request. # The domain names can also use wildcards. # # Example: # * Allow redirects to example.com and all subdomains: # MellonRedirectDomains example.com *.example.com # * Allow redirects to the host running mod_auth_mellon, as well as the # web page at www.example.com: # MellonRedirectDomains [self] www.example.com # * Allow redirects to all domains: # MellonRedirectDomains * # # Default: # MellonRedirectDomains [self] MellonRedirectDomains [self] =========================================================================== Service provider metadata =========================================================================== The contents of the metadata will depend on your hostname and on what path you selected with the MellonEndpointPath configuration directive. You can supply the metadata using the MellonSPMetadataFile directive, or you can just let it be autogenerated. The following is an example of metadata for the example configuration: urn:oasis:names:tc:SAML:2.0:nameid-format:transient You should update entityID="https://example.com" and the two Location attributes. Note that '/secret/endpoint' in the two Location attributes matches the path set in MellonEndpointPath. To use HTTP-Artifact binding instead of the HTTP-POST binding, change the AssertionConsumerService-element to something like this: The metadata are published at the 'endpoint/metadata' URL. =========================================================================== Using mod_auth_mellon =========================================================================== After you have set up mod_auth_mellon, you should be able to visit (in our example) https://example.com/secret/, and be redirected to the IdP's login page. After logging in you should be returned to https://example.com/secret/, and get the contents of that page. When authenticating a user, mod_auth_mellon will set some environment variables to the attributes it received from the IdP. The name of the variables will be MELLON_. If you have specified a different name with the MellonSetEnv or MellonSetEnvNoPrefix configuration directive, then that name will be used instead. In the case of MellonSetEnv, the name will still be prefixed by 'MELLON_'. If an attribute has multiple values, then they will be stored as MELLON__0, MELLON__1, MELLON__2, ... Since mod_auth_mellon doesn't know which attributes may have multiple values, it will store every attribute at least twice. Once named MELLON_, and once named _0. In the case of multivalued attributes MELLON_ will contain the first value. NOTE: if MellonMergeEnvVars is set to On multiple values of attributes will be stored in single environment variable, separated by ";" MELLON_ -> "value1;value2;value3[;valueX]" and variables MELLON__0, MELLON__1, MELLON__2 will not be created. The following code is a simple php-script which prints out all the variables: $value) { if(substr($key, 0, 7) == 'MELLON_') { echo($key . '=' . $value . "\r\n"); } } ?> =========================================================================== Manual login =========================================================================== It is possible to manually trigger login operations. This can be done by accessing "/login". That endpoint accepts three parameters: - ReturnTo: A mandatory parameter which contains the URL we should return to after login. - IdP: The entity ID of the IdP we should send a login request to. This parameter is optional. - IsPassive: This parameter can be set to "true" to send a passive authentication request to the IdP. =========================================================================== Logging out =========================================================================== mod_auth_mellon supports both IdP initiated and SP initiated logout through the same endpoint. The endpoint is located at "/logout". "/logoutRequest" is an alias for this endpoint, provided for compatibility with version 0.0.6 and earlier of mod_auth_mellon. To initiate a logout from your web site, you should redirect or link to "/logout?ReturnTo=". Note that the ReturnTo parameter is mandatory. For example, if the web site is located at "https://www.example.com/secret", and the mellon endpoints are located under "https://www.example.com/secret/endpoint", then the web site could contain a link element like the following: Log out This will return the user to "https://www.example.org/logged_out.html" after the logout operation has completed. =========================================================================== Probe IdP discovery =========================================================================== mod_auth_mellon has an IdP probe discovery service that sends HTTP GET to IdP and picks the first that answers. This can be used as a poor man's failover setup that redirects to your organisation internal IdP. Here is a sample configuration: MellonEndpointPath "/saml" (...) MellonDiscoveryUrl "/saml/probeDisco" MellonProbeDiscoveryTimeout 1 The SP will sends HTTP GET to each configured IdP providerId URL until it gets an HTTP 200 response within the 1 second timeout. It will then proceed with that IdP. If you are in a federation, then your IdP login page will need to provide an IdP selection feature aimed at users from other institutions (after such a choice, the user would be redirected to the SP's /saml/login endpoint, with ReturnTo and IdP set appropriately). In such a setup, you will want to configure external IdP in mod_auth_mellon, but not use them for IdP probe discovery. The MellonProbeDiscoveryIdP directive can be used to limit the usable IdP for probe discovery: MellonEndpointPath "/saml" (...) MellonDiscoveryUrl "/saml/probeDisco" MellonProbeDiscoveryTimeout 1 MellonProbeDiscoveryIdP "https://idp1.example.net/saml/metadata" MellonProbeDiscoveryIdP "https://idp2.example.net/saml/metadata" =========================================================================== Replaying POST requests =========================================================================== By default, POST requests received when the user isn't logged in are turned into GET requests after authentication. mod_auth_mellon can instead save the received POST request and replay / repost it after authentication. To enable this: 1. Create a data directory where mod_auth_mellon can store the saved data: mkdir /var/cache/mod_auth_mellon_postdata 2. Set the appropriate permissions on this directory. It needs to be accessible for the web server, but nobody else. chown www-data /var/cache/mod_auth_mellon_postdata chgrp www-data /var/cache/mod_auth_mellon_postdata chmod 0700 /var/cache/mod_auth_mellon_postdata 3. Set the MellonPostDirectory option in your server configuration: MellonPostDirectory "/var/cache/mod_auth_mellon_postdata" 4. Enable POST replay functionality for the locations you want: MellonEnable auth [...] MellonPostReplay On After you restart Apache to activate the new configuration, any POST requests that trigger authentication should now be stored while the user logs in. =========================================================================== Mellon & User Agent Caching behavior =========================================================================== For each content within Apache Location enabled with "info" or "auth" mod_auth_mellon sends by default HTTP1.1 Cache-Control header with values "private, must-revalidate": - private-value protects content against caching by any proxy servers. - must-revalidate-value obligates user agent to revalidate maybe locally cached or stored content each time on accessing location. This default behavior ensures that user agent never shows cached static HTML pages after logout without revalidating. So that user couldn't be misled about malfunction of logout procedure. Revalidating content after logout leads to new authentication procedure via mellon. But mod_auth_mellon will never prohibit specifically any user agent to cache or store content locally, that have to be revalidated. So that during the session user agent only revalidates data by server 304-Not-Modified response and does not have to download content again. For special content types like images it could make sense to disable revalidation completely, so that user agent can provide cached and stored content directly to user. This can be achieved by using other Apache modules mod_headers and mod_setenvif. E.g. for PNG images: Using Apache 2.2 configuration options: SetEnvIf Request_URI "\.png$" DISABLE_REVALIDATION Header always unset Cache-Control env=DISABLE_REVALIDATION For Apache 2.4 exists shorter notation: Header always unset Cache-Control expr=%{CONTENT_TYPE}==image/png Editing, appending, overwriting headers is possible in other cases. =========================================================================== Support =========================================================================== There's a mailing list for discussion and support. To subscribe: https://sympa.uninett.no/lists/uninett.no/subscribe/modmellon List archives: https://sympa.uninett.no/lists/uninett.no/arc/modmellon =========================================================================== Contributors =========================================================================== Thanks to Emmanuel Dreyfus for many new features, including: - Metadata autogeneration support. - Support for multiple IdPs. - IdP discovery service support. - SOAP logout support. Benjamin Dauvergne has contributed many patches, both with bugfixes and new features: - Cookie settings, for specifying domain and path of cookie. - Support for SAML 2.0 authentication contexts. - Support for running behind a reverse proxy. - Logout improvements, including support for local logout. mod_auth_mellon-0.12.0/ECP.rst0000644000175100017510000003024112576273035015471 0ustar olavmoolavmoGuide to using ECP ================== Introduction ------------ The **Enhanced Client or Proxy** (ECP) profile of SAML2 The Enhanced Client or Proxy (ECP) Profile supports several SSO use cases, in particular: * Clients with capabilities beyond those of a browser, allowing them to more actively participate in IdP discovery and message flow. * Using a proxy server, for example a WAP gateway in front of a mobile device which has limited functionality. * When other bindings are precluded (e.g. where the client does not support redirects, or when auto form post is not possible without Javascript, or when the artifact binding is ruled out because the identity provider and service provider cannot directly communicate. An enhanced client or proxy (ECP) is a system entity that knows how to contact an appropriate identity provider, possibly in a context-dependent fashion, and also supports the Reverse SOAP (PAOS) binding. An example scenario enabled by ECP profile is as follows: A principal, wielding an ECP, uses it to either access a resource at a service provider, or access an identity provider such that the service provider and desired resource are understood or implicit. The principal authenticates (or has already authenticated) with the identity provider [1]_, which then produces an authentication assertion (possibly with input from the service provider). The service provider then consumes the assertion and subsequently establishes a security context for the principal. During this process, a name identifier might also be established between the providers for the principal, subject to the parameters of the interaction and the consent of the principal. SAML2 Profile for ECP (Section 4.2) defines these steps for an ECP transaction: 1. ECP issues HTTP Request to SP 2. SP issues to ECP using PAOS 3. ECP determines IdP 4. ECP conveys to IdP using SOAP 5. IdP identifies principal 6. IdP issues to ECP, targeted at SP using SOAP 7. ECP conveys to SP using PAOS 8. SP grants or denies access to principal mod_auth_mellon and ECP ----------------------- mod_auth_mellon plays the role of the SP in an ECP transaction. mod_auth_mellon utilizes the Lasso library to provide it's SAML2 functionality. Fully functioning SAML2 ECP support in Lasso is relatively new. When mod_auth_mellon is built it detects the presence of SAML2 ECP in Lasso and only compiles in the ECP code in mod_auth_mellon if it's present in Lasso. How does mod_auth_mellon recognize a request is from an ECP client? ``````````````````````````````````````````````````````````````````` In Step 1. when the ECP client issues the HTTP Request to the SP it **MUST** include `application/vnd.paos+xml` as a mime type in the HTTP `Accept` header field and include an HTTP `PAOS` header specifying a PAOS version of `urn:liberty:paos:2003-08` and an ECP service declaration of `urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp` [2]_, for example:: Accept: text/html, application/vnd.paos+xml PAOS: ver="urn:liberty:paos:2003-08";"urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp" If mod_auth_mellon sees this in the incoming request it knows the client is ECP aware and capable. If authentication is required mod_auth_mellon will initiate an ECP flow. The role of IdP's in ECP ```````````````````````` The SAML2 ECP profile states it is the ECP client which determines the IdP that will be used for authentication. This is in contrast to the Web SSO flow where the SP determines the IdP. However, the ECP protocol permits an SP to send the ECP client a list of IdP's it trusts. It is optional if the SP sends an IDPList, if it does the ECP client should select the IdP from the SP provided IDPList otherwise the ECP client is free to select any IdP it wishes. If the mellon configuration option `MellonECPSendIDPList` is true then mod_auth_mellon will include an IDPList when it returns a PAOS to the ECP client. To build the IDPList mod_auth_mellon scans it's list of loaded IdP's selecting those which are ECP capable. To support ECP an IdP must advertise the SingleSignOn service utilizing the SOAP binding. ECP specific mod_auth_mellon configuration directives ````````````````````````````````````````````````````` These configuration directives are specific to ECP: MellonECPSendIDPList If `On` mod_auth_mellon will send an IdP list to the ECP client containing only those IdP's capable of ECP flow. The ECP client should select an IdP only from this list. If this option is `Off` no IdP list will be sent and the ECP client is free to select any IdP. Example ECP client `````````````````` To illustrate a simple ECP client based on Lasso we'll use the Lasso Python binding (as opposed to pseudo code, Python is quite readable). All error checking and another necessary ancillary code has been eliminated in order to clearly illustrate only the ECP operations. .. code-block:: python import lasso import requests ecp = lasso.Ecp(server) session = requests.Session() MEDIA_TYPE_PAOS = 'application/vnd.paos+xml' PAOS_HEADER = 'ver="%s";"%s"' % (lasso.PAOS_HREF,lasso.ECP_HREF) # Step 1: Request protected resource, indicate ECP capable response = session.get(protected, headers={'Accept': MEDIA_TYPE_PAOS, 'PAOS': PAOS_HEADER}) # Process returned PAOS wrapped ecp.processAuthnRequestMsg(response.text) # Post SOAP wrapped to IdP, use Digest Auth to authenticate response = session.post(ecp.msgUrl, data=ecp.msgBody, auth=requests.auth.HTTPDigestAuth(user, password) headers={'Content-Type': 'application/soap+xml'}) # Process returned SOAP wrapped from IdP ecp.processResponseMsg(response.text) # Post PASO wrapped to SP, response is protected resource response = session.post(ecp.msgUrl, data=ecp.msgBody, headers={'Content-Type': 'application/vnd.paos+xml'}) mod_auth_mellon internal ECP implementation notes ------------------------------------------------- Notes on ECP vs. Web SSO flow ````````````````````````````` Web SSO (Single Sign-On) flow is by far the most common and what most people are familiar with when they think of SAML. The Web SSO profile is designed so that browsers ignorant of SAML can perform SAML authentication without modification. This is accomplished with existing HTTP paradigms such as redirects, form posts, etc. which a browser will process normally yielding the desired result. ECP (Enhanced Client or Proxy) is a different SAML profile that also accomplishes SSO (Single Sign-On). The distinction is an ECP client is fully SAML aware and actively participates in the SAML conversation. Web SSO and ECP have very different flows, mod_auth_mellon must support both flows. mod_auth_mellon is a SP (Service Provider). IdP Selection Differences ````````````````````````` With Web SSO the SP determines the IdP and redirects there. With ECP the ECP client determines the IdP, the SP has no a prori knowledge of the target IdP, although the SP may provide a suggested list of IdP's when responding to the ECP client. Since with ECP it is the ECP client which selects the IdP the set of IdP's loaded into mod_auth_mellon are not relevant **except** if `MellonECPSendIDPList` is enabled. In this case mod_auth_mellon will filter the set of loaded IdP's and forward those IdP's supporting SingleSignOn with the SOAP binding. Apache request processing pipeline `````````````````````````````````` Apache implements a request processing pipeline composed of stages. An Apache extension module can participate in the pipeline by asking to be called at specific stages (steps) by registering a hook function for that stage. Final content returned to the HTTP client in the HTTP response is generated in the "handler", one of the final stages in the request processing pipeline. One of the stages in the request pipeline is determining authentication and authorization for protected resources. If a resource is protected and the authentication and authorization pipeline stages deny access or fail the request processing pipeline is aborted early, a non-success HTTP response is returned, the content handler is never reached. With Web SSO if authentication needs to be performed a redirect will be returned that redirects to a SAML endpoint (login) on our SP. This in turn generates the SAML with a redirect to the IdP. All of this is very vanilla standard HTTP easily accommodated by Apache's request processing pipeline which is designed to handle these types of flows. ECP requires special handling ````````````````````````````` However ECP has a very different flow. When an ECP client sends a request to the SP it includes a special HTTP headers indicating it is ECP capable. If the SP determines the resource is protected and authentication is needed and the client has signaled it is ECP capable then the SP responds successfully (200) with a SAML wrapped in PAOS. *This is very different than conventional HTTP request processing.* Here we have a case where there is a protected resource that has **not** been authenticated yet the web server will responds with an HTTP 200 success and content! One might normally expect a HTTP 401 or redirect response for a protected resource when there is no authenticated user. *This is clearly contrary to the expectations of Apache's request processing pipeline.* Reaching the Apache content handler ``````````````````````````````````` In order to be able to return a successful (HTTP 200) PAOS response when doing the ECP we have to reach the part of Apache's request processing pipeline that generates the response. In Apache terminology this is called a (content) handler. At an early stage we detect if authentication is required. For the normal Web SSO profile we would redirect the client back to our login endpoint which will be handled by our handler in a different request. But for ECP the current request must proceed. We set a flag on the request indicating ECP authentication is required. The pipeline continues. When the pipeline reaches the authentication and authorization stages we check the ECP flag on the request, if ECP authentication is indicated we lie and tell the pipeline the user is authenticated and authorized. We do this only so we can reach the handler stage (otherwise because the request is for a protected resource the pipeline would terminate with an error). Despite our having forced authentication and authorization to be valid for the protected resource the request processing pipeline *will not return the protected resource* because we will subsequently intercept the request in our handler before the pipeline reaches the point of returning the protected resource. At the handler stage ```````````````````` Once our handler is invoked it has 3 possible actions to perform: 1. The request is for one of our SAML endpoints (e.g. login, logout, metadata, etc.) We dispatch to the handler for the specific action. We detect this case by matching the request URI to our SAML endpoints. We signal to the pipeline that our hook handled the request. 2. The request is for a protected resource and needs ECP authentication performed. We detect this case by examining the ECP flag set on the request by an earlier hook function. The request URI is for the protected resource and has nothing to do with our SAML endpoints. We generate the PAOS and respond with success (200) and signal to the pipeline that our hook handled the request. Note, we have not returned the protected resource, instead we've returned the PAOS request. 3. The request has nothing to do with us, we decline to handle it. The pipeline proceeds to the next handler. .. [1] The means by which a principal authenticates with an identity provider is outside of the scope of SAML. Typically an ECP client will utilize an HTTP authentication method when posting the SOAP message to the IdP. .. [2] Contrary to most HTTP headers the values in the PAOS header must be enclosed in double quotes. A semicolon is used to separate the values. mod_auth_mellon-0.12.0/COPYING0000644000175100017510000006131412354722415015363 0ustar olavmoolavmomod_auth_mellon is distributed under 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. In addition, as a special exception, permission is granted to link the code of this release of mod_mellon with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same licence as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU General Public License version 2 in all respects for all of the code used other than "OpenSSL". If you modify the code, you may extend this exception to your version of the code, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. The full text of the GNU General Public License: =============================================================================== GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. The full text of the OpenSSL License: =============================================================================== LICENSE ISSUES ============== The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact openssl-core@openssl.org. OpenSSL License --------------- /* ==================================================================== * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ Original SSLeay License ----------------------- /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ mod_auth_mellon-0.12.0/NEWS0000644000175100017510000003005412667763074015041 0ustar olavmoolavmoVersion 0.12.0 --------------------------------------------------------------------------- Security fixes: * [CVE-2016-2145] Fix DOS attack (Apache worker process crash) due to incorrect error handling when reading POST data from client. * [CVE-2016-2146] Fix DOS attack (Apache worker process crash / resource exhaustion) due to missing size checks when reading POST data. In addition this release contains the following new features and fixes: * Add MellonRedirecDomains option to limit the sites that mod_auth_mellon can redirect to. This option is enabled by default. * Add support for ECP service options in PAOS requests. * Fix AssertionConsumerService lookup for PAOS requests. Version 0.11.1 --------------------------------------------------------------------------- Security fixes: * [CVE-2016-2145] Fix DOS attack (Apache worker process crash) due to incorrect error handling when reading POST data from client. * [CVE-2016-2146] Fix DOS attack (Apache worker process crash / resource exhaustion) due to missing size checks when reading POST data. Version 0.11.0 --------------------------------------------------------------------------- * Add SAML 2.0 ECP support. * The MellonDecode option has been disabled. It was used to decode attributes in a Feide-specific encoding that is no longer used. * Set max-age=0 in Cache-Control header, to ensure that all browsers verifies the data on each request. * MellonMergeEnvVars On now accepts second optional parameter, the separator to be used instead of the default ';'. * Add option MellonEnvVarsSetCount to specify if the number of values for any attribute should also be stored in environment variable suffixed _N. * Add option MellonEnvVarsIndexStart to specify if environment variables for multi-valued attributes should start indexing with 0 (default) or with 1. * Bugfixes: * Fix error about missing authentication with DirectoryIndex in Apache 2.4. Version 0.10.0 --------------------------------------------------------------------------- * Make sure that we fail in the unlikely case where OpenSSL is not able to provide us with a secure session id. * Increase the number of key-value pairs in the session to 2048. * Add MellonMergeEnvVars-option to store multi-valued attributes in a single environment variable, separated with ';'. * Bugfixes: * Fix the [MAP] option for MellonCond. * Fix cookie deletion for the session cookie. (Logout is not dependent on the cookie being deleted, so this only fixes the cookie showing up after the session is deleted.) Version 0.9.1 --------------------------------------------------------------------------- * Bugfixes: * Fix session offset calculation that prevented us from having active sessions at once. * Run mod_auth_mellon request handler before most other handlers, so that other handlers cannot block it by accident. Version 0.9.0 --------------------------------------------------------------------------- * Set the AssertionConsumerServiceURL attribute in authentication requests. * Bugfixes: * Fix use of uninitialized data during logout. * Fix session entry overflow leading to segmentation faults. * Fix looking up sessions by NameID, which is used during logout. Version 0.8.1 --------------------------------------------------------------------------- This is a security release with fixes backported from version 0.9.1. It turned out that session overflow bugs fixes in version 0.9.0 and 0.9.1 can lead to information disclosure, where data from one session is leaked to another session. Depending on how this data is used by the web application, this may lead to data from one session being disclosed to an user in a different session. (CVE-2014-8566) In addition to the information disclosure, this release contains some fixes for logout processing, where logout requests would crash the Apache web server. (CVE-2014-8567) Version 0.8.0 --------------------------------------------------------------------------- * Add support for receiving HTTP-Artifact identifiers as POST data. * Simplify caching headers. * Map login errors into more appropriate HTTP error codes than 400 Bad Request. * Add MellonNoSuccessErrorPage option to redirect to a error page on login failure. * Turn session storage into a dynamic pool of memory, which means that attribute values (and other items) can have arbitrary sizes as long as they fit in the session as a whole. * Various bugfixes: * Fix for compatibility with recent versions of CURL. * Fix broken option MellonDoNotVerifyLogoutSignature. * Fix deadlock that could occur during logout processing. * Fix some compile warnings. * Fix some NULL derefernce bugs that may lead to segmentation faults. * Fix a minor memory leak during IdP metadata loading. Version 0.7.0 --------------------------------------------------------------------------- * Add MellonSPentityId to control entityId in autogenerated metadata * Fix compatibility with Apache 2.4. * Handle empty RelayState the same as missing RelayState. * Add MellonSetEvnNoPrefix directive to set environment variables without "MELLON_"-prefix. Version 0.6.1 --------------------------------------------------------------------------- * Fix the POST replay functionality when multiple users logging in at once. * Add a fallback for the case where the POST replay data has expired before the user logs in. Version 0.6.0 --------------------------------------------------------------------------- Backwards-incompatible changes: * The POST replay functionality has been disabled by default, and the automatic creation of the MellonPostDirectory target directory has been removed. If you want to use the POST replay functionality, take a look at the README file for instructions for how to enable this. * Start discovery service when accessing the login endpoint. We used to bypass the discovery service in this case, and just pick the first IdP. This has been changed to send a request to the discovery service instead, if one is configured. * The MellonLockFile default path has been changed to: /var/run/mod_auth_mellon.lock This only affects platforms where a lock file is required and where Apache doesn't have write access to that directory during startup. (Apache can normally create files in that directory during startup.) Other changes: * Fix support for SOAP logout. * Local logout when IdP does not support SAML 2.0 Single Logout. * MellonDoNotVerifyLogoutSignature option to disable logout signature validation. * Support for relative file paths in configuration. * The debian build-directory has been removed from the repository. * Various cleanups and bugfixes: * Fix cookie parsing header parsing for some HTTP libraries. * Fix inheritance of MellonAuthnContextClassRef option. * Use ap_set_content_type() instead of accessing request->content_type. * README indentation cleanups. * Support for even older versions of GLib. * Fixes for error handling during session initialization. * Directly link with GLib rather than relying on the Lasso library linking to it for us. * Some code cleanups. Version 0.5.0 --------------------------------------------------------------------------- * Honour MellonProbeDiscoveryIdP order when sending probes. * MellonAuthnContextClassRef configuration directive, to limit authentication to specific authentication methods. * Support for the HTTP-POST binding when sending authentication requests to the IdP. * MellonSubjectConfirmationDataAddressCheck option to disable received address checking. * Various cleanups and bugfixes: * Support for older versions of GLib and APR. * Send the correct SP entityID to the discovery service. * Do not set response headers twice. * Several cleanups in the code that starts authentication. Version 0.4.0 --------------------------------------------------------------------------- * Allow MellonUser variable to be translated through MellonSetEnv * A /mellon/probeDisco endpoint replaces the builtin:get-metadata IdP dicovery URL scheme * New MellonCond directive to enable attribute filtering beyond MellonRequire functionalities. * New MellonIdPMetadataGlob directive to load mulitple IdP metadata using a glob(3) pattern. * Support for running behind reverse proxy. * MellonCookieDomain and MellonCookiePath options to configure cookie settings. * Support for loading federation metadata files. * Several bugfixes. Version 0.3.0 --------------------------------------------------------------------------- * New login-endpoint, which allows easier manual initiation of login requests, and specifying parameters such as IsPassive. * Validation of Conditions and SubjectConfirmation data in the assertion we receive from the IdP. * Various bugfixes. Version 0.2.7 --------------------------------------------------------------------------- * Optionaly save the remote IdP entityId in the environment * Shibboleth 2 interoperability Version 0.2.6 --------------------------------------------------------------------------- * Fix XSS/DOS vulnerability in repost handler. Version 0.2.5 --------------------------------------------------------------------------- * Replay POST requests after been sent to the IdP * Fix HTTP response splitting vulnerability. Version 0.2.4 --------------------------------------------------------------------------- * Fix for downloads of files with Internet Explorer with SSL enabled. * Mark session as disabled as soon as logout starts, in case the IdP doesn't respond. Version 0.2.3 --------------------------------------------------------------------------- * Bugfix for session lifetime. Take the session lifetime from the SessionNotOnOrAfter attribute if it is present. Version 0.2.2 --------------------------------------------------------------------------- * Improve metadata autogeneration: cleanup certificate, allow Organizarion element data to be supplied from Apache configuration Version 0.2.1 --------------------------------------------------------------------------- * Make SAML authentication assertion and Lasso session available in the environment. Version 0.2.0 --------------------------------------------------------------------------- * Autogeneration of SP metadata. (Requires Lasso 2.2.2 or newer.) * Multiple IdP support, with discovery service. * Built in discovery service which tests the availability of each IdP, and uses the first available IdP. * Fix a mutex leak. Version 0.1.1 --------------------------------------------------------------------------- * MellonSecureCookie option, which enables Secure + HttpOnly flags on session cookies. * Better handling of logout request when the user is already logged out. Version 0.1.0 --------------------------------------------------------------------------- * Better support for BSD. * Support for setting a IdP CA certificate and SP certificate. * Support for loading the private key during web server initialization. With this, the private key only needs to be readable by root. This requires a recent version of Lasso to work. * Better DOS resistance, by only allocating a session when the user has authenticated with the IdP. * Support for IdP initiated login. The MellonDefaultLoginPath option can be to configure which page the user should land on after authentication. Version 0.0.7 --------------------------------------------------------------------------- * Renamed the logout endpoint from "logoutRequest" to "logout". "logoutRequest" is now an alias for "logout", and may be removed in the future. * Added SP initiated logout. To initiate a logout from the web site, link the user to the logout endpoint, with a ReturnTo parameter with the url the user should be redirected to after being logged out. Example url: "https://www.example.com/secret/endpoint/logout ?ReturnTo=http://www.example.com/". (Note that this should be on a single line.) * Fixed a memory leak on login. * Increased maximum Lasso session size to 8192 from 3074. This allows us to handle users with more attributes. * Fixed handling of multiple AttributeValue elements in response.