mod_auth_mellon-0.7.0/mod_auth_mellon.c0000644000175100017510000001604212151575071017563 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) { am_cache_entry_t *table; apr_size_t mem_size; am_mod_cfg_rec *mod; int rv, i; 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); /* find out the memory size of the cache */ mem_size = sizeof(am_cache_entry_t) * 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. */ table = apr_shm_baseaddr_get(mod->cache); for (i = 0; i < mod->cache_size; i++) { table[i].key[0] = '\0'; table[i].access = 0; } /* 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 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_handler(am_handler, NULL, NULL, APR_HOOK_MIDDLE); 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.7.0/auth_mellon_cache.c0000644000175100017510000004245312151575217020056 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" /* 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; am_cache_entry_t *table; int 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: if (strlen(key) > AM_CACHE_MAX_LASSO_IDENTITY_SIZE) return NULL; 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++) { const char *tablekey; switch (type) { case AM_CACHE_SESSION: tablekey = table[i].key; break; case AM_CACHE_NAMEID: /* tablekey may be NULL */ tablekey = am_cache_env_fetch_first(&table[i], "NAME_ID"); break; default: tablekey = NULL; break; } if (tablekey == NULL) continue; if(strcmp(tablekey, key) == 0) { /* We found the entry. */ if(table[i].expires > apr_time_now()) { /* And it hasn't expired. */ return &table[i]; } } } /* We didn't find a entry matching the key. Unlock the table and * return NULL; */ apr_global_mutex_unlock(mod_cfg->lock); return NULL; } /* This function locks the session table and creates a new session entry. * It will first attempt to locate a free session. If it doesn't find a * free session, then it will take the least recentry used session. * * Remember to unlock the table with am_cache_unlock(...) afterwards. * * Parameters: * server_rec *s The current server. * const char *key The key of the session to allocate. * * Returns: * The new session entry on success. NULL if key is a invalid session * key. */ am_cache_entry_t *am_cache_new(server_rec *s, const char *key) { am_cache_entry_t *t; am_mod_cfg_rec *mod_cfg; am_cache_entry_t *table; apr_time_t current_time; int i; apr_time_t age; 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 = &table[0]; /* Iterate over the session table. Update 't' to match the "best" * entry (the least recently used). 't' will point a free entry * if we find one. Otherwise, 't' will point to the least recently * used entry. */ for(i = 0; i < mod_cfg->init_cache_size; i++) { if(table[i].key[0] == '\0') { /* This entry is free. Update 't' to this entry * and exit loop. */ t = &table[i]; break; } if(table[i].expires <= current_time) { /* This entry is expired, and is therefore free. * Update 't' and exit loop. */ t = &table[i]; break; } if(table[i].access < t->access) { /* This entry is older than 't' - update 't'. */ t = &table[i]; } } if(t->key[0] != '\0' && t->expires > current_time) { /* We dropped a LRU entry. Calculate the age in seconds. */ age = (current_time - t->access) / 1000000; if(age < 3600) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "Dropping LRU entry entry with age = %" 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; t->user[0] = '\0'; t->lasso_identity[0] = '\0'; t->lasso_session[0] = '\0'; return t; } /* This function unlocks a session entry. * * Parameters: * server_rec *s The current server. * am_cache_entry_t *entry The session entry. * * Returns: * Nothing. */ void am_cache_unlock(server_rec *s, am_cache_entry_t *entry) { am_mod_cfg_rec *mod_cfg; /* Update access time. */ entry->access = apr_time_now(); mod_cfg = am_get_mod_cfg(s); apr_global_mutex_unlock(mod_cfg->lock); } /* This function updates the expire-timestamp of a session, if the new * timestamp is earlier than the previous. * * Parameters: * am_cache_entry_t *t The current session. * apr_time_t expires The new timestamp. * * Returns: * Nothing. */ void am_cache_update_expires(am_cache_entry_t *t, apr_time_t expires) { /* Check if we should update the expires timestamp. */ if(t->expires == 0 || t->expires > expires) { t->expires = expires; } } /* This function appends a name-value pair to a session. It is possible to * store several values with the same name. This is the method used to store * multivalued fields. * * Parameters: * am_cache_entry_t *t The current session. * const char *var The name of the value to be stored. * const char *val The value which should be stored in the session. * * Returns: * OK on success or HTTP_INTERNAL_SERVER_ERROR on failure. */ int am_cache_env_append(am_cache_entry_t *t, const char *var, const char *val) { /* Make sure that the name and value will fit inside the * fixed size buffer. */ if(strlen(val) >= AM_CACHE_VALSIZE || strlen(var) >= AM_CACHE_VARSIZE) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Unable to store session data because it is to big. " "Name = \"%s\"; Value = \"%s\".", var, val); return HTTP_INTERNAL_SERVER_ERROR; } if(t->size >= AM_CACHE_ENVSIZE) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Unable to store attribute value because we have" " reached the maximum number of name-value pairs for" " this session."); return HTTP_INTERNAL_SERVER_ERROR; } strcpy(t->env[t->size].varname, var); strcpy(t->env[t->size].value, val); t->size++; return OK; } /* This function 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) { int i; for (i = 0; t->size; i++) { if (strcmp(t->env[i].varname, var) == 0) return 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; int *count; d = am_get_dir_cfg(r); /* Check if the user attribute has been set, and set it if it * hasn't been set. */ if(t->user[0] == '\0') { for(i = 0; i < t->size; ++i) { if(strcmp(t->env[i].varname, d->userattr) == 0) { strcpy(t->user, t->env[i].value); } } } /* Allocate a set of counters for duplicate variables in the list. */ counters = apr_hash_make(r->pool); /* Populate the subprocess environment with the attributes we * received from the IdP. */ for(i = 0; i < t->size; ++i) { varname = t->env[i].varname; 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 = t->env[i].value; /* * If we find a variable remapping to MellonUser, use it. */ if ((t->user[0] == '\0') && (strcmp(varname, d->userattr) == 0)) strcpy(t->user, value); /* Find the number of times this variable has been set. */ count = apr_hash_get(counters, varname, APR_HASH_KEY_STRING); if(count == NULL) { /* This is the first time. Create a counter for this variable. */ count = apr_palloc(r->pool, sizeof(int)); *count = 0; apr_hash_set(counters, varname, APR_HASH_KEY_STRING, count); /* Add the variable without a suffix. */ apr_table_set(r->subprocess_env, apr_pstrcat(r->pool, varname_prefix, varname, NULL), value); } /* Add the variable with a suffix indicating how many times it has * been added before. */ apr_table_set(r->subprocess_env, apr_psprintf(r->pool, "%s%s_%d", varname_prefix, varname, *count), value); /* Increase the count. */ ++(*count); } if(t->user[0] != '\0') { /* We have a user-"name". Set r->user and r->ap_auth_type. */ r->user = apr_pstrdup(r->pool, t->user); r->ap_auth_type = apr_pstrdup(r->pool, "Mellon"); } else { /* We don't have a user-"name". Log error. */ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Didn't find the attribute \"%s\" in the attributes" " which were received from the IdP. Cannot set a user" " for this request without a valid user attribute.", d->userattr); } /* Populate with the session? */ if (d->dump_session) { char *session; int srclen, dstlen; srclen = strlen(t->lasso_session); dstlen = apr_base64_encode_len(srclen); session = apr_palloc(r->pool, dstlen); (void)apr_base64_encode(session, t->lasso_session, srclen); apr_table_set(r->subprocess_env, "MELLON_SESSION", session); } if (d->dump_saml_response) apr_table_set(r->subprocess_env, "MELLON_SAML_RESPONSE", t->lasso_saml_response); } /* 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) { if(lasso_identity != NULL) { if(strlen(lasso_identity) < AM_CACHE_MAX_LASSO_IDENTITY_SIZE) { strcpy(session->lasso_identity, lasso_identity); } else { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Lasso identity is to big for storage. Size of lasso" " identity is %" APR_SIZE_T_FMT ", max size is %" APR_SIZE_T_FMT ".", (apr_size_t)strlen(lasso_identity), (apr_size_t)AM_CACHE_MAX_LASSO_IDENTITY_SIZE - 1); return HTTP_INTERNAL_SERVER_ERROR; } } else { /* No identity dump to save. */ strcpy(session->lasso_identity, ""); } if(lasso_session != NULL) { if(strlen(lasso_session) < AM_CACHE_MAX_LASSO_SESSION_SIZE) { strcpy(session->lasso_session, lasso_session); } else { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Lasso session is to big for storage. Size of lasso" " session is %" APR_SIZE_T_FMT ", max size is %" APR_SIZE_T_FMT ".", (apr_size_t)strlen(lasso_session), (apr_size_t)AM_CACHE_MAX_LASSO_SESSION_SIZE - 1); return HTTP_INTERNAL_SERVER_ERROR; } } else { /* No session dump to save. */ strcpy(session->lasso_session, ""); } if(lasso_saml_response != NULL) { if(strlen(lasso_saml_response) < AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE) { strcpy(session->lasso_saml_response, lasso_saml_response); } else { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Lasso SAML response is to big for storage. " "Size of lasso session is %" APR_SIZE_T_FMT ", max size is %" APR_SIZE_T_FMT ".", (apr_size_t)strlen(lasso_saml_response), (apr_size_t)AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE - 1); return HTTP_INTERNAL_SERVER_ERROR; } } else { /* No session dump to save. */ strcpy(session->lasso_saml_response, ""); } return OK; } /* This function retrieves a lasso identity dump from the session object. * * Parameters: * am_cache_entry_t *session The session object. * * Returns: * The identity dump, or NULL if we don't have a session dump. */ const char *am_cache_get_lasso_identity(am_cache_entry_t *session) { if(strlen(session->lasso_identity) == 0) { return NULL; } return session->lasso_identity; } /* This function retrieves a lasso session dump from the session object. * * Parameters: * am_cache_entry_t *session The session object. * * Returns: * The session dump, or NULL if we don't have a session dump. */ const char *am_cache_get_lasso_session(am_cache_entry_t *session) { if(strlen(session->lasso_session) == 0) { return NULL; } return session->lasso_session; } mod_auth_mellon-0.7.0/auth_mellon_config.c0000644000175100017510000015257112151575217020263 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 #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; /* 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 MellonDecoder configuration directive. * This directive can be set to "none" or "feide". * * 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_decoder_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if(!strcasecmp(arg, "none")) { d->decoder = am_decoder_none; } else if(!strcasecmp(arg, "feide")) { d->decoder = am_decoder_feide; } else { return "MellonDecoder must be 'none' or 'feide'"; } 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 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( "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, "Select which decoder mod_auth_mellon should use to decode attribute" " values. This option can be se to either 'none' or 'feide'. 'none'" " is the default, and will store the attributes as they are received" " from the IdP. 'feide' is for decoding base64-encoded values which" " are separated by a underscore." ), 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( "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, NULL, 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." ), {NULL} }; /* 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->decoder = am_decoder_default; dir->varname = default_cookie_name; dir->secure = default_secure_cookie; 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->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; 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->decoder = (add_cfg->decoder != am_decoder_default ? add_cfg->decoder : base_cfg->decoder); 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->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->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); 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->init_cache_size = 0; mod->init_lock_file = NULL; 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.7.0/auth_mellon_cookie.c0000644000175100017510000001430712151575071020257 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); } /* 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) { 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. */ value = (const char *)ap_get_module_config(r->request_config, &auth_mellon_module); 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) { const char *name; char *cookie; 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 (id == NULL) return; if (cfg->cookie_domain) { cookie_domain = cfg->cookie_domain; } if (cfg->cookie_path) { cookie_path = cfg->cookie_path; } secure_cookie = cfg->secure; name = am_cookie_name(r); cookie = apr_psprintf(r->pool, "%s=%s; Version=1; Path=%s; Domain=%s%s;", name, id, cookie_path, cookie_domain, secure_cookie ? "; HttpOnly; secure" : ""); 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. */ ap_set_module_config(r->request_config, &auth_mellon_module, 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; char *cookie; name = am_cookie_name(r); /* Format a cookie. To delete a cookie we set the expires-timestamp * to the past. */ cookie = apr_psprintf(r->pool, "%s=NULL;" " version=1;" " expires=Thu, 01-Jan-1970 00:00:00 GMT;" " path=/", name); apr_table_addn(r->err_headers_out, "Set-Cookie", cookie); } mod_auth_mellon-0.7.0/auth_mellon_handler.c0000644000175100017510000031771712151575071020436 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" #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\ %s\n\ ", sp_entity_id, cfg->sp_entity_id ? "" : "metadata", cert, 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) { const char *idp_public_key_file; apr_size_t index; if (cfg->idp_metadata->nelts == 1) idp_public_key_file = cfg->idp_public_key_file; else idp_public_key_file = NULL; 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) g_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; 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. */ /* Delete the session. */ if (session != NULL && res != LASSO_PROFILE_ERROR_SESSION_NOT_FOUND) am_delete_request_session(r, session); /* 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: 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; } 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; } /* 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); } } /* This function is for decoding and storing attributes with the feide * encoding. It takes in an attribute name and a value. The value is split * into multiple values. We base64 decode these values, and store them in the * session data. * * Parameters: * request_rec *r The current request. * am_cache_entry_t *session The current session. * const char *name Name of the attribute. * const char *value The value(s) of the attribute. * * Returns: * OK on success or an error from am_cache_env_append(...) if it was unable * to store the attribute. */ static int am_store_attribute_feide(request_rec *r, am_cache_entry_t *session, const char *name, const char *value) { char *edit_value; char *start; char *next; int len; int ret; /* We need to be able to change the value. */ edit_value = apr_pstrdup(r->pool, value); for(start = edit_value; start != NULL; start = next) { /* The values are separated by '_'. */ next = strchr(start, '_'); if(next != NULL) { /* Insert null-terminator after current value. */ *next = '\0'; /* The next value begins at next+1. */ next++; } /* Now start points to the current value, which we have * null-terminated. next points to the next value, or NULL if * this is the last value. */ /* base64-decode current value. * From looking at the source of apr_base64_decode_binary, it * appears to be safe to use in-place. */ len = apr_base64_decode_binary((unsigned char *)start, start); /* Add null-terminator at end of string. */ start[len] = '\0'; /* Store current name-value-pair. */ ret = am_cache_env_append(session, name, start); if(ret != OK) { return ret; } } return OK; } /* This function is for storing attributes without any encoding. We just store * the attribute as it is. * * Parameters: * request_rec *r The current request. * am_cache_entry_t *session The current session. * const char *name The name of the attribute. * const char *value The value of the attribute. * * Returns: * OK on success or an error from am_cache_env_append(...) if it failed. */ static int am_store_attribute_none(request_rec *r, am_cache_entry_t *session, const char *name, const char *value) { /* Store current name-value-pair. */ return am_cache_env_append(session, name, value); } /* This function passes a name-value pair to the decoder selected by the * MellonDecoder configuration option. The decoder will decode the value * and store it in the session data. * * Parameters: * request_rec *r The current request. * am_cache_entry_t *session The current session. * const char *name The name of the attribute. * const char *value The value of the attribute. * * Returns: * OK on success or an error from the attribute decoder if it failed. */ static int am_store_attribute(request_rec *r, am_cache_entry_t *session, const char *name, const char *value) { am_dir_cfg_rec *dir_cfg; dir_cfg = am_get_dir_cfg(r); switch(dir_cfg->decoder) { case am_decoder_none: return am_store_attribute_none(r, session, name, value); case am_decoder_feide: return am_store_attribute_feide(r, session, name, value); default: return am_store_attribute_none(r, session, name, value); } } /* 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; LassoMiscTextNode *value_text; 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; } /* Verify that this is a LassoMiscTextNode object. */ if(!LASSO_IS_MISC_TEXT_NODE(value->any->data)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "AttributeValue element contained an " " element which wasn't a text node."); continue; } value_text = LASSO_MISC_TEXT_NODE(value->any->data); /* Decode and save the attribute. */ ret = am_store_attribute(r, session, attribute->Name, value_text->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. * * 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) { 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(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; } 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; /* Make sure that this is a POST request. */ if(r->method_number != M_POST) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Exptected 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); return HTTP_BAD_REQUEST; } /* 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); } /* 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; /* Make sure that this is a GET request. */ if(r->method_number != M_GET) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Exptected GET 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. */ 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; } /* 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. */ relay_state = am_extract_query_parameter(r->pool, r->args, "RelayState"); /* Finish handling the reply with the common handler. */ return am_handle_reply_common(r, login, relay_state, ""); } /* 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 *); 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; } 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 } /* Send AuthnRequest using HTTP-Redirect binding. * * Note that this method frees the LassoLogin object. * * Parameters: * request_rec *r * LassoLogin *login * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_send_authn_request_redirect(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); lasso_login_destroy(login); /* We don't want to include POST data (in case this was a POST request). */ return HTTP_SEE_OTHER; } /* Send AuthnRequest using HTTP-POST binding. * * Note that this method frees the LassoLogin object. * * 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_send_authn_request_post(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); lasso_login_destroy(login); 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; } /* 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 Whether to send a passive request. * * Returns: * HTTP response code indicating success or failure. */ static int am_send_authn_request(request_rec *r, const char *idp, const char *return_to, int is_passive) { LassoServer *server; LassoProvider *provider; LassoLogin *login; LassoSamlp2AuthnRequest *request; LassoHttpMethod http_method; char *sso_url; gint ret; am_dir_cfg_rec *dir_cfg; dir_cfg = am_get_dir_cfg(r); /* 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; sso_url = lasso_provider_get_metadata_one( provider, "SingleSignOnService HTTP-Redirect"); if (sso_url == NULL) { /* HTTP-Redirect unsupported - try HTTP-POST. */ http_method = LASSO_HTTP_METHOD_POST; sso_url = lasso_provider_get_metadata_one( provider, "SingleSignOnService HTTP-POST"); } if (sso_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; } login = lasso_login_new(server); if(login == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error creating LassoLogin object from LassoServer."); g_free(sso_url); return HTTP_INTERNAL_SERVER_ERROR; } 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)); g_free(sso_url); lasso_login_destroy(login); 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."); g_free(sso_url); lasso_login_destroy(login); return HTTP_INTERNAL_SERVER_ERROR; } request->ForceAuthn = FALSE; request->IsPassive = is_passive; request->NameIDPolicy->AllowCreate = TRUE; 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); } } /* * 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 (LASSO_SAMLP2_REQUEST_ABSTRACT(request)->Destination == NULL) { LASSO_SAMLP2_REQUEST_ABSTRACT(request)->Destination = g_strdup(sso_url); } /* sso_url no longer needed. */ g_free(sso_url); LASSO_PROFILE(login)->msg_relayState = g_strdup(return_to); 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)); lasso_login_destroy(login); return HTTP_INTERNAL_SERVER_ERROR; } /* Time to actually send the authentication request. */ switch (http_method) { case LASSO_HTTP_METHOD_REDIRECT: return am_send_authn_request_redirect(r, login); case LASSO_HTTP_METHOD_POST: return am_send_authn_request_post(r, login); default: /* We should never get here. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unsupported http_method."); lasso_login_destroy(login); return HTTP_INTERNAL_SERVER_ERROR; } } /* 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_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; char *is_passive_str; 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; } 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; } } is_passive_str = am_extract_query_parameter(r->pool, r->args, "IsPassive"); if(is_passive_str != NULL) { ret = am_urldecode((char*)is_passive_str); if(ret != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Error urldecoding IsPassive parameter."); return ret; } if(!strcmp(is_passive_str, "true")) { is_passive = TRUE; } else if(!strcmp(is_passive_str, "false")) { is_passive = FALSE; } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid value for IsPassive parameter - must be \"true\" or \"false\"."); return HTTP_BAD_REQUEST; } } else { is_passive = FALSE; } 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_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; } 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); const char *endpoint; /* 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, "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; /* check if we are a subrequest. if we are, then just return OK * without any checking since these cannot be injected (heh). */ if (r->main) 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; } /* Disable all caching within this location. */ am_set_nocache(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); } /* Send the user to the authentication page on the IdP. */ return am_start_auth(r); } /* 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; /* check if we are a subrequest. if we are, then just return OK * without any checking since these cannot be injected (heh). */ if (r->main) return OK; /* 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 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.7.0/auth_mellon_util.c0000644000175100017510000012234112151575071017761 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; } /* 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; /* * if MAP flag is set, check for remapped * attribute name with mellonSetEnv */ if (ce->flags & AM_COND_FLAG_MAP) varname = apr_hash_get(dir_cfg->envattr, session->env[j].varname, APR_HASH_KEY_STRING); /* * Otherwise or if not found, use the attribute name * sent by the IdP. */ if (varname == NULL) varname = session->env[j].varname; if (strcmp(varname, ce->varname) != 0) continue; value = 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 disables caching of the response to this request. It does * this by setting the Pragme: no-cache and Cache-Control: no-cache headers. * * Parameters: * request_rec *r The request we are handling. * * Returns: * Nothing. */ void am_set_nocache(request_rec *r) { const char *user_agent; /* Setting the headers inn err_headers_out ensures that they will be * sent for all responses. */ apr_table_setn(r->err_headers_out, "Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); apr_table_setn(r->err_headers_out, "Cache-Control", "private, must-revalidate"); /* * Never use Cache-Control: no-cache for IE */ user_agent = apr_table_get(r->headers_in, "User-Agent"); if ((user_agent == NULL) || (strstr(user_agent, "compatible; MSIE ") == NULL) || (strstr(user_agent, "Opera") != NULL)) { apr_table_addn(r->err_headers_out, "Cache-Control", "no-cache, no-store"); } } /* 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 (length != NULL) { *length = len; } *data = (char *)apr_palloc(r->pool, len + 1); /* 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 or error, the * number of bytes otherwise. */ read_length = ap_get_client_block(r, &(*data)[bytes_read], bytes_left); 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; 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_pseudo_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; } if(rc == 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Random data is not cryptographically strong."); } 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)) { value = am_xstrtok(r, NULL, ":", &l2); 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)) { attr_value = am_xstrtok(r, NULL, "=", &l2); 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; } mod_auth_mellon-0.7.0/auth_mellon_session.c0000644000175100017510000000673512151575071020477 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.7.0/auth_mellon_httpclient.c0000644000175100017510000005155011304733502021157 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; } /* Enable SSL peer certificate verification. */ res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to enable SSL peer certificate verification:" " [%u] %s", res, curl_error); goto cleanup_fail; } /* Enable SSL peer hostname verification. */ res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); if(res != CURLE_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to enable SSL peer hostname verification:" " [%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.7.0/auth_mellon.h0000644000175100017510000003202712151575217016734 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 /* 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_VARSIZE 128 #define AM_CACHE_VALSIZE 512-AM_CACHE_VARSIZE #define AM_CACHE_ENVSIZE 128 #define AM_CACHE_USERSIZE 512 #define AM_CACHE_MAX_LASSO_IDENTITY_SIZE 1024 #define AM_CACHE_MAX_LASSO_SESSION_SIZE 32768 #define AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE 65536 /* This is the length of the id we use (for session IDs and * replaying POST data). */ #define AM_ID_LENGTH 32 #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) 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; /* 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_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_decoder_default, am_decoder_none, am_decoder_feide, } am_decoder_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; /* The decoder attribute is used to specify which decoder we should use * when parsing attributes. */ am_decoder_t decoder; const char *varname; int secure; 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; /* 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; } am_dir_cfg_rec; typedef struct am_cache_env_t { char varname[AM_CACHE_VARSIZE]; char value[AM_CACHE_VALSIZE]; } 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; char user[AM_CACHE_USERSIZE]; /* Variables used to store lasso state between login requests *and logout requests. */ char lasso_identity[AM_CACHE_MAX_LASSO_IDENTITY_SIZE]; char lasso_session[AM_CACHE_MAX_LASSO_SESSION_SIZE]; char lasso_saml_response[AM_CACHE_MAX_LASSO_SAML_RESPONSE_SIZE]; am_cache_env_t env[AM_CACHE_ENVSIZE]; } 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[]; /* 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; 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); am_cache_entry_t *am_cache_lock(server_rec *s, am_cache_key_t type, const char *key); 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_check_permissions(request_rec *r, am_cache_entry_t *session); void am_set_nocache(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); 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.7.0/auth_mellon_compat.h0000644000175100017510000000303412151575071020271 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.7.0/configure0000755000175100017510000040306312151601312016147 0ustar olavmoolavmo#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.67 for mod_auth_mellon 0.7.0. # # Report bugs to . # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 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. 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 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" 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" 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 : # 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. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV export CONFIG_SHELL exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} 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_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; } # 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 -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' 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 if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # 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.7.0' PACKAGE_STRING='mod_auth_mellon 0.7.0' PACKAGE_BUGREPORT='olav.morken@uninett.no' PACKAGE_URL='' ac_subst_vars='LTLIBOBJS LIBOBJS GLIB_LIBS GLIB_CFLAGS OPENSSL_LIBS OPENSSL_CFLAGS CURL_LIBS CURL_CFLAGS OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC LASSO_LIBS LASSO_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG APXS2 NAMEVER 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 PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR LASSO_CFLAGS LASSO_LIBS CC CFLAGS LDFLAGS LIBS CPPFLAGS CURL_CFLAGS CURL_LIBS OPENSSL_CFLAGS OPENSSL_LIBS GLIB_CFLAGS GLIB_LIBS' # 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 $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used" >&2 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.7.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.7.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: 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 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 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 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.7.0 generated by GNU Autoconf 2.67 Copyright (C) 2010 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; test "x$as_lineno_stack" = x && { as_lineno=; 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 || $as_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; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_link 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.7.0, which was generated by GNU Autoconf 2.67. 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 NAMEVER=mod_auth_mellon-0.7.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 test "${ac_cv_path_APXS2+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 test "${ac_cv_path_APXS2+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 test "${ac_cv_path_PKG_CONFIG+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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` 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` 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 "lasso" 2>&1` else LASSO_PKG_ERRORS=`$PKG_CONFIG --print-errors "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"; 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 test "${ac_cv_prog_CC+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 test "${ac_cv_prog_ac_ct_CC+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 test "${ac_cv_prog_CC+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 test "${ac_cv_prog_CC+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 test "${ac_cv_prog_CC+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 test "${ac_cv_prog_ac_ct_CC+set}" = set; 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 test "${ac_cv_objext+set}" = set; 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 test "${ac_cv_c_compiler_gnu+set}" = set; 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 test "${ac_cv_prog_cc_g+set}" = set; 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 test "${ac_cv_prog_cc_c89+set}" = set; 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 #include #include /* 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 lasso_server_new_from_buffers in -llasso" >&5 $as_echo_n "checking for lasso_server_new_from_buffers in -llasso... " >&6; } if test "${ac_cv_lib_lasso_lasso_server_new_from_buffers+set}" = set; 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" = x""yes; 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 test "${ac_cv_lib_lasso_lasso_server_load_metadata+set}" = set; 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" = x""yes; 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 test "${ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint+set}" = set; 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" = x""yes; then : LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_profile_set_signature_verify_hint" 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` 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` 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 "libcurl" 2>&1` else CURL_PKG_ERRORS=`$PKG_CONFIG --print-errors "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` 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` 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 "openssl" 2>&1` else OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors "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` 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` 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 "glib-2.0 >= 2.12" 2>&1` else GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors "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 # 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 test "x$cache_file" != "x/dev/null" && { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} cat confcache >$cache_file 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. 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 -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' 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 if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # 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.7.0, which was generated by GNU Autoconf 2.67. 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.7.0 configured by $0, generated by GNU Autoconf 2.67, with options \\"\$ac_cs_config\\" Copyright (C) 2010 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= trap 'exit_status=$? { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$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 -n "$tmp" && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 # 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 {' >"$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 >>"\$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 >>"\$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 < "$tmp/subs1.awk" > "$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="$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 >"$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 "$tmp/subs.awk" >$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' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$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 "$tmp/stdin" case $ac_file in -) cat "$tmp/out" && rm -f "$tmp/out";; *) rm -f "$ac_file" && mv "$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.7.0/configure.ac0000644000175100017510000000421212151601301016515 0ustar olavmoolavmoAC_INIT([mod_auth_mellon],[0.7.0],[olav.morken@uninett.no]) 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") 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) # Create Makefile from Makefile.in AC_CONFIG_FILES([Makefile]) AC_OUTPUT mod_auth_mellon-0.7.0/Makefile.in0000644000175100017510000000254212151601301016300 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 \ configure \ configure.ac \ Makefile.in \ autogen.sh \ README \ COPYING \ NEWS all: mod_auth_mellon.la mod_auth_mellon.la: $(SRC) auth_mellon.h auth_mellon_compat.h @APXS2@ -Wc,"@OPENSSL_CFLAGS@ @LASSO_CFLAGS@ @CURL_CFLAGS@ @GLIB_CFLAGS@" -Wl,"@OPENSSL_LIBS@ @LASSO_LIBS@ @CURL_LIBS@ @GLIB_LIBS@" -Wc,-Wall -Wc,-g -c $(SRC) # 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.7.0/autogen.sh0000755000175100017510000000007611245431661016251 0ustar olavmoolavmo#!/bin/sh autoreconf --force --install rm -rf autom4te.cache/ mod_auth_mellon-0.7.0/README0000644000175100017510000007547612151575217015153 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 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. 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 # 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 writeable by the # Apache user. It should not be writeable (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 MB) 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. # See http://httpd.apache.org/docs/2.2/mod/core.html for information # 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 used to select which decoder mod_auth_mellon # will use when decoding attribute values. # There are two possible values: "none" and "feide". "none" is the # default. # They have the following meanings: # "none": mod_auth_mellon will store the attribute as it is # received from the IdP. This is the default behaviour. # "feide": *DEPRECATED* Feide used to transmit attributes with a # special encoding. That is no longer necessary, and # this decoder should therefore no longer be used. # Default: MellonDecoder "none" MellonDecoder "none" # 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. # 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" # 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 =========================================================================== 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="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_'. The value of the attribute will be base64 decoded. mod_auth_mellon supports multivalued attributes with the following format: __... 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. 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. =========================================================================== 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.7.0/COPYING0000644000175100017510000006131412151575071015306 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.7.0/NEWS0000644000175100017510000001645312151601301014740 0ustar olavmoolavmoVersion 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 environement. 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.