mod_auth_mellon-0.16.0/mod_auth_mellon.c0000664002412700241270000002074213607335617020220 0ustar jhrozekjhrozek/* * * 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 #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(auth_mellon); #endif /* This function is called after the configuration of the server is parsed * (it's a post-config hook). * * It initializes the shared memory and the mutex which is used to protect * the shared memory. * * Parameters: * apr_pool_t *conf The configuration pool. Valid as long as this * configuration is valid. * apr_pool_t *log A pool for memory which is cleared after each read * through the config files. * apr_pool_t *tmp A pool for memory which will be destroyed after * all the post_config hooks are run. * server_rec *s The current server record. * * Returns: * OK on successful initialization, or !OK on failure. */ static int am_global_init(apr_pool_t *conf, apr_pool_t *log, apr_pool_t *tmp, server_rec *s) { apr_size_t mem_size; am_mod_cfg_rec *mod; int rv; const char userdata_key[] = "auth_mellon_init"; char buffer[512]; void *data; /* Apache tests loadable modules by loading them (as is the only way). * This has the effect that all modules are loaded and initialised twice, * and we just want to initialise shared memory and mutexes when the * module loads for real! * * To accomplish this, we store a piece of data as userdata in the * process pool the first time the function is run. This data can be * detected on all subsequent runs, and then we know that this isn't the * first time this function runs. */ apr_pool_userdata_get(&data, userdata_key, s->process->pool); if (!data) { /* This is the first time this function is run. */ apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool); return OK; } mod = am_get_mod_cfg(s); /* If the session store is initialized then we can't change it. */ if(mod->cache != NULL) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "auth_mellon session store already initialized -" " reinitialization skipped."); return OK; } /* Copy from the variables set by the configuration file into variables * which will be set only once. We do this to avoid confusion if the user * tries to change the parameters of the session store after it is * initialized. */ mod->init_cache_size = mod->cache_size; mod->init_lock_file = apr_pstrdup(conf, mod->lock_file); mod->init_entry_size = mod->entry_size; if (mod->init_entry_size < AM_CACHE_MIN_ENTRY_SIZE) { mod->init_entry_size = AM_CACHE_MIN_ENTRY_SIZE; } /* find out the memory size of the cache */ mem_size = mod->init_entry_size * mod->init_cache_size; /* Create the shared memory, exit if it fails. */ rv = apr_shm_create(&(mod->cache), mem_size, NULL, conf); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "shm_create: Error [%d] \"%s\"", rv, apr_strerror(rv, buffer, sizeof(buffer))); return !OK; } /* Initialize the session table. */ am_cache_init(mod); /* Now create the mutex that we need for locking the shared memory, then * test for success. we really need this, so we exit on failure. */ rv = apr_global_mutex_create(&(mod->lock), mod->init_lock_file, APR_LOCK_DEFAULT, conf); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "mutex_create: Error [%d] \"%s\"", rv, apr_strerror(rv, buffer, sizeof(buffer))); return !OK; } #ifdef AP_NEED_SET_MUTEX_PERMS /* On some platforms the mutex is implemented as a file. To allow child * processes running as a different user to open it, it is necessary to * change the permissions on it. */ rv = ap_unixd_set_global_mutex_perms(mod->lock); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, "Failed to set permissions on session table lock;" " check User and Group directives"); return rv; } #endif return OK; } /* This function is run when each child process of apache starts. * apr_global_mutex_child_init must be run on the session data mutex for * every child process of apache. * * Parameters: * apr_pool_t *p This pool is for data associated with this * child process. * server_rec *s The server record for the current server. * * Returns: * Nothing. */ static void am_child_init(apr_pool_t *p, server_rec *s) { am_mod_cfg_rec *m = am_get_mod_cfg(s); apr_status_t rv; CURLcode curl_res; /* Reinitialize the mutex for the child process. */ rv = apr_global_mutex_child_init(&(m->lock), m->init_lock_file, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "Child process could not connect to mutex"); } /* lasso_init() must be run before any other lasso-functions. */ lasso_init(); /* curl_global_init() should be called before any other curl * function. Relying on curl_easy_init() to call curl_global_init() * isn't thread safe. */ curl_res = curl_global_init(CURL_GLOBAL_SSL); if(curl_res != CURLE_OK) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Failed to initialize curl library: %u", curl_res); } return; } static int am_create_request(request_rec *r) { am_req_cfg_rec *req_cfg; req_cfg = apr_pcalloc(r->pool, sizeof(am_req_cfg_rec)); req_cfg->cookie_value = NULL; #ifdef HAVE_ECP req_cfg->ecp_authn_req = false; #endif /* HAVE_ECP */ #ifdef ENABLE_DIAGNOSTICS req_cfg->diag_emitted = false; #endif ap_set_module_config(r->request_config, &auth_mellon_module, req_cfg); return OK; } static void register_hooks(apr_pool_t *p) { /* Our handler needs to run before mod_proxy so that it can properly * return ECP AuthnRequest messages when running as a reverse proxy. * See: https://github.com/Uninett/mod_auth_mellon/pull/196 */ static const char * const run_handler_before[]={ "mod_proxy.c", NULL }; ap_hook_access_checker(am_auth_mellon_user, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_check_user_id(am_check_uid, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_config(am_global_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(am_child_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_create_request(am_create_request, NULL, NULL, APR_HOOK_MIDDLE); /* Add the hook to handle requests to the mod_auth_mellon endpoint. * * This is APR_HOOK_FIRST because we do not expect nor require users * to add a SetHandler option for the endpoint. Instead, simply * setting MellonEndpointPath should be enough. * * Therefore this hook must run before any handler that may check * r->handler and decide that it is the only handler for this URL. */ ap_hook_handler(am_handler, NULL, run_handler_before, APR_HOOK_FIRST); #ifdef ENABLE_DIAGNOSTICS ap_hook_open_logs(am_diag_log_init,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_log_transaction(am_diag_finalize_request,NULL,NULL,APR_HOOK_REALLY_LAST); #endif } module AP_MODULE_DECLARE_DATA auth_mellon_module = { STANDARD20_MODULE_STUFF, auth_mellon_dir_config, auth_mellon_dir_merge, auth_mellon_server_config, auth_mellon_srv_merge, auth_mellon_commands, register_hooks }; mod_auth_mellon-0.16.0/auth_mellon_cache.c0000664002412700241270000006266313607335617020514 0ustar jhrozekjhrozek/* * * 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" #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(auth_mellon); #endif /* Calculate the pointer to a cache entry. * * Parameters: * am_mod_cfg_rec *mod_cfg The module configuration. * void *table The base pointer for the table. * apr_size_t index The index we are looking for. * * Returns: * The session entry with the given index. */ static inline am_cache_entry_t *am_cache_entry_ptr(am_mod_cfg_rec *mod_cfg, void *table, apr_size_t index) { uint8_t *table_calc; table_calc = table; return (am_cache_entry_t *)&table_calc[mod_cfg->init_entry_size * index]; } /* Initialize the session table. * * Parameters: * am_mod_cfg_rec *mod_cfg The module configuration. * * Returns: * Nothing. */ void am_cache_init(am_mod_cfg_rec *mod_cfg) { void *table; apr_size_t i; /* Initialize the session table. */ table = apr_shm_baseaddr_get(mod_cfg->cache); for (i = 0; i < mod_cfg->init_cache_size; i++) { am_cache_entry_t *e = am_cache_entry_ptr(mod_cfg, table, i); e->key[0] = '\0'; e->access = 0; } } /* This function locks the session table and locates a session entry. * Unlocks the table and returns NULL if the entry wasn't found. * If a entry was found, then you _must_ unlock it with am_cache_unlock * after you are done with it. * * Parameters: * request_rec *r The request we are processing. * am_cache_key_t type AM_CACHE_SESSION or AM_CACHE_NAMEID * const char *key The session key or user * * Returns: * The session entry on success or NULL on failure. */ am_cache_entry_t *am_cache_lock(request_rec *r, am_cache_key_t type, const char *key) { am_mod_cfg_rec *mod_cfg; void *table; apr_size_t i; int rv; char buffer[512]; /* Check if we have a valid session key. We abort if we don't. */ if (key == NULL) return NULL; switch (type) { case AM_CACHE_SESSION: if (strlen(key) != AM_ID_LENGTH) return NULL; break; case AM_CACHE_NAMEID: break; default: return NULL; break; } mod_cfg = am_get_mod_cfg(r->server); /* Lock the table. */ if((rv = apr_global_mutex_lock(mod_cfg->lock)) != APR_SUCCESS) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "apr_global_mutex_lock() failed [%d]: %s", rv, apr_strerror(rv, buffer, sizeof(buffer))); return NULL; } table = apr_shm_baseaddr_get(mod_cfg->cache); for(i = 0; i < mod_cfg->init_cache_size; i++) { am_cache_entry_t *e = am_cache_entry_ptr(mod_cfg, table, i); const char *tablekey; if (e->key[0] == '\0') { /* This entry is empty. Skip it. */ continue; } switch (type) { case AM_CACHE_SESSION: tablekey = e->key; break; case AM_CACHE_NAMEID: /* tablekey may be NULL */ tablekey = am_cache_env_fetch_first(e, "NAME_ID"); break; default: tablekey = NULL; break; } if (tablekey == NULL) continue; if(strcmp(tablekey, key) == 0) { apr_time_t now = apr_time_now(); /* We found the entry. */ if(e->expires > now) { /* And it hasn't expired. */ return e; } else { am_diag_log_cache_entry(r, 0, e, "found expired session, now %s\n", am_diag_time_t_to_8601(r, now)); } } } /* We didn't find a entry matching the key. Unlock the table and * return NULL; */ apr_global_mutex_unlock(mod_cfg->lock); return NULL; } static inline bool am_cache_entry_slot_is_empty(am_cache_storage_t *slot) { return (slot->ptr == 0); } static inline void am_cache_storage_null(am_cache_storage_t *slot) { slot->ptr = 0; } static inline void am_cache_entry_env_null(am_cache_entry_t *e) { for (int i = 0; i < AM_CACHE_ENVSIZE; i++) { am_cache_storage_null(&e->env[i].varname); am_cache_storage_null(&e->env[i].value); } } static inline apr_size_t am_cache_entry_pool_left(am_cache_entry_t *e) { return e->pool_size - e->pool_used; } static inline apr_size_t am_cache_entry_pool_size(am_mod_cfg_rec *cfg) { return cfg->init_entry_size - sizeof(am_cache_entry_t); } /* This function sets a string into the specified storage on the entry. * * NOTE: The string pointer may be NULL, in that case storage is freed * and set to NULL. * * Parametrs: * am_cache_entry_t *entry Pointer to an entry * am_cache_storage_t *slot Pointer to storage * const char *string Pointer to a replacement string * * Returns: * 0 on success, HTTP_INTERNAL_SERVER_ERROR on error. */ static int am_cache_entry_store_string(am_cache_entry_t *entry, am_cache_storage_t *slot, const char *string) { char *datastr = NULL; apr_size_t datalen = 0; apr_size_t str_len = 0; if (string == NULL) return 0; if (slot->ptr != 0) { datastr = &entry->pool[slot->ptr]; datalen = strlen(datastr) + 1; } str_len = strlen(string) + 1; if (str_len - datalen <= 0) { memcpy(datastr, string, str_len); return 0; } /* recover space if slot happens to point to the last allocated space */ if (slot->ptr + datalen == entry->pool_used) { entry->pool_used -= datalen; slot->ptr = 0; } if (am_cache_entry_pool_left(entry) < str_len) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "apr_cache_entry_store_string() asked %" APR_SIZE_T_FMT " available: %" APR_SIZE_T_FMT ". " "It may be a good idea to increase MellonCacheEntrySize.", str_len, am_cache_entry_pool_left(entry)); return HTTP_INTERNAL_SERVER_ERROR; } slot->ptr = entry->pool_used; datastr = &entry->pool[slot->ptr]; memcpy(datastr, string, str_len); entry->pool_used += str_len; return 0; } /* Returns a pointer to the string in the storage slot specified * * * Parametrs: * am_cache_entry_t *entry Pointer to an entry * am_cache_storage_t *slot Pointer to storage slot * * Returns: * A string or NULL if the slot is empty. */ const char *am_cache_entry_get_string(am_cache_entry_t *e, am_cache_storage_t *slot) { char *ret = NULL; if (slot->ptr != 0) { ret = &e->pool[slot->ptr]; } return ret; } /* This function locks the session table and creates a new session entry. * It will first attempt to locate a free session. If it doesn't find a * free session, then it will take the least recentry used session. * * Remember to unlock the table with am_cache_unlock(...) afterwards. * * Parameters: * request_rec *r The request we are processing. * const char *key The key of the session to allocate. * const char *cookie_token The cookie token to tie the session to. * * Returns: * The new session entry on success. NULL if key is a invalid session * key. */ am_cache_entry_t *am_cache_new(request_rec *r, const char *key, const char *cookie_token) { am_cache_entry_t *t; am_mod_cfg_rec *mod_cfg; void *table; apr_time_t current_time; int i; apr_time_t age; int rv; char buffer[512]; /* Check if we have a valid session key. We abort if we don't. */ if(key == NULL || strlen(key) != AM_ID_LENGTH) { return NULL; } mod_cfg = am_get_mod_cfg(r->server); /* Lock the table. */ if((rv = apr_global_mutex_lock(mod_cfg->lock)) != APR_SUCCESS) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "apr_global_mutex_lock() failed [%d]: %s", rv, apr_strerror(rv, buffer, sizeof(buffer))); return NULL; } table = apr_shm_baseaddr_get(mod_cfg->cache); /* Get current time. If we find a entry with expires <= the current * time, then we can use it. */ current_time = apr_time_now(); /* We will use 't' to remember the best/oldest entry. We * initalize it to the first entry in the table to simplify the * following code (saves test for t == NULL). */ t = am_cache_entry_ptr(mod_cfg, table, 0); /* Iterate over the session table. Update 't' to match the "best" * entry (the least recently used). 't' will point a free entry * if we find one. Otherwise, 't' will point to the least recently * used entry. */ for(i = 0; i < mod_cfg->init_cache_size; i++) { am_cache_entry_t *e = am_cache_entry_ptr(mod_cfg, table, i); if (e->key[0] == '\0') { /* This entry is free. Update 't' to this entry * and exit loop. */ t = e; break; } if (e->expires <= current_time) { /* This entry is expired, and is therefore free. * Update 't' and exit loop. */ t = e; am_diag_log_cache_entry(r, 0, e, "%s ejecting expired sessions, now %s\n", __func__, am_diag_time_t_to_8601(r, current_time)); break; } if (e->access < t->access) { /* This entry is older than 't' - update 't'. */ t = e; } } if(t->key[0] != '\0' && t->expires > current_time) { /* We dropped a LRU entry. Calculate the age in seconds. */ age = (current_time - t->access) / 1000000; if(age < 3600) { AM_LOG_RERROR(APLOG_MARK, APLOG_NOTICE, 0, r, "Dropping LRU entry entry with age = %" APR_TIME_T_FMT "s, which is less than one hour. It may be a good" " idea to increase MellonCacheSize.", age); } } /* Now 't' points to the entry we are going to use. We initialize * it and returns it. */ strcpy(t->key, key); /* Far far into the future. */ t->expires = 0x7fffffffffffffffLL; t->logged_in = 0; t->size = 0; am_cache_storage_null(&t->cookie_token); am_cache_storage_null(&t->user); am_cache_storage_null(&t->lasso_identity); am_cache_storage_null(&t->lasso_session); am_cache_storage_null(&t->lasso_saml_response); am_cache_entry_env_null(t); t->pool_size = am_cache_entry_pool_size(mod_cfg); t->pool[0] = '\0'; t->pool_used = 1; rv = am_cache_entry_store_string(t, &t->cookie_token, cookie_token); if (rv != 0) { /* For some strange reason our cookie token is too big to fit in the * session. This should never happen outside of absurd configurations. */ AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Unable to store cookie token in new session."); t->key[0] = '\0'; /* Mark the entry as free. */ apr_global_mutex_unlock(mod_cfg->lock); return NULL; } am_diag_printf(r, "%s created new session, id=%s at %s" " cookie_token=\"%s\"\n", __func__, t->key, am_diag_time_t_to_8601(r, current_time), cookie_token); return t; } /* This function unlocks a session entry. * * Parameters: * request_rec *r The request we are processing. * am_cache_entry_t *entry The session entry. * * Returns: * Nothing. */ void am_cache_unlock(request_rec *r, 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(r->server); 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: * request_rec *r The request we are processing. * am_cache_entry_t *t The current session. * apr_time_t expires The new timestamp. * * Returns: * Nothing. */ void am_cache_update_expires(request_rec *r, am_cache_entry_t *t, apr_time_t expires) { /* Check if we should update the expires timestamp. */ if(t->expires == 0 || t->expires > expires) { t->expires = expires; } } /* This function appends a name-value pair to a session. It is possible to * store several values with the same name. This is the method used to store * multivalued fields. * * Parameters: * am_cache_entry_t *t The current session. * const char *var The name of the value to be stored. * const char *val The value which should be stored in the session. * * Returns: * OK on success or HTTP_INTERNAL_SERVER_ERROR on failure. */ int am_cache_env_append(am_cache_entry_t *t, const char *var, const char *val) { int status; /* Make sure that the name and value will fit inside the * fixed size buffer. */ if(t->size >= AM_CACHE_ENVSIZE) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Unable to store attribute value because we have" " reached the maximum number of name-value pairs for" " this session. The maximum number is %d.", AM_CACHE_ENVSIZE); return HTTP_INTERNAL_SERVER_ERROR; } status = am_cache_entry_store_string(t, &t->env[t->size].varname, var); if (status != 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Unable to store session data because there is no more " "space in the session. Attribute Name = \"%s\".", var); return HTTP_INTERNAL_SERVER_ERROR; } status = am_cache_entry_store_string(t, &t->env[t->size].value, val); if (status != 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Unable to store session data because there is no more " "space in the session. Attribute Value = \"%s\".", val); return HTTP_INTERNAL_SERVER_ERROR; } t->size++; return OK; } /* This function fetches a value from a session. * If multiple values are available, the first one is returned. * * Parameters: * am_cache_entry_t *t The current session. * const char *var The name of the value to be stored. * * Returns: * The first value, NULL if it does not exist. */ const char *am_cache_env_fetch_first(am_cache_entry_t *t, const char *var) { const char *str; int i; for (i = 0; i < t->size; i++) { str = am_cache_entry_get_string(t, &t->env[i].varname); if (str == NULL) break; if (strcmp(str, var) == 0) return am_cache_entry_get_string(t, &t->env[i].value); } return NULL; } /* This function populates the subprocess environment with data received * from the IdP. * * Parameters: * request_rec *r The request we should add the data to. * am_cache_entry_t *t The session data. * * Returns: * Nothing. */ void am_cache_env_populate(request_rec *r, am_cache_entry_t *t) { am_dir_cfg_rec *d; int i; apr_hash_t *counters; am_envattr_conf_t *env_varname_conf; const char *varname; const char *varname_prefix; const char *value; const char *prefixed_varname; int *count; int status; d = am_get_dir_cfg(r); /* Check if the user attribute has been set, and set it if it * hasn't been set. */ if (am_cache_entry_slot_is_empty(&t->user)) { for(i = 0; i < t->size; ++i) { varname = am_cache_entry_get_string(t, &t->env[i].varname); if (strcasecmp(varname, d->userattr) == 0) { value = am_cache_entry_get_string(t, &t->env[i].value); status = am_cache_entry_store_string(t, &t->user, value); if (status != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_NOTICE, 0, r, "Unable to store the user name because there" " is no more space in the session. " "Username = \"%s\".", value); } } } } /* Allocate a set of counters for duplicate variables in the list. */ counters = apr_hash_make(r->pool); /* Populate the subprocess environment with the attributes we * received from the IdP. */ for(i = 0; i < t->size; ++i) { varname = am_cache_entry_get_string(t, &t->env[i].varname); varname_prefix = d->env_prefix; /* Check if we should map this name into another name. */ env_varname_conf = (am_envattr_conf_t *)apr_hash_get( d->envattr, varname, APR_HASH_KEY_STRING); if(env_varname_conf != NULL) { varname = env_varname_conf->name; if (!env_varname_conf->prefixed) { varname_prefix = ""; } } value = am_cache_entry_get_string(t, &t->env[i].value); /* * If we find a variable remapping to MellonUser, use it. */ if (am_cache_entry_slot_is_empty(&t->user) && (strcasecmp(varname, d->userattr) == 0)) { status = am_cache_entry_store_string(t, &t->user, value); if (status != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_NOTICE, 0, r, "Unable to store the user name because there" " is no more space in the session. " "Username = \"%s\".", value); } } prefixed_varname = apr_pstrcat(r->pool, varname_prefix, varname, NULL); /* Find the number of times this variable has been set. */ count = apr_hash_get(counters, varname, APR_HASH_KEY_STRING); if(count == NULL) { /* This is the first time. Create a counter for this variable. */ count = apr_palloc(r->pool, sizeof(int)); *count = 0; apr_hash_set(counters, varname, APR_HASH_KEY_STRING, count); /* Add the variable without a suffix. */ apr_table_set(r->subprocess_env,prefixed_varname,value); } /* Check if merging of environment variables is disabled. * This is either if it is NULL (default value if not configured * by user) or an empty string (if specifically disabled by the user). */ if (d->merge_env_vars == NULL || *d->merge_env_vars == '\0') { /* Add the variable with a suffix indicating how many times it has * been added before. */ apr_table_set(r->subprocess_env, apr_psprintf(r->pool, "%s_%d", prefixed_varname, (d->env_vars_index_start > -1 ? *count + d->env_vars_index_start : *count)), value); } else if (*count > 0) { /* * Merge multiple values, separating by default with ";" * this makes auth_mellon work same way mod_shib is: * https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPAttributeAccess */ apr_table_set(r->subprocess_env, prefixed_varname, apr_pstrcat(r->pool, apr_table_get(r->subprocess_env,prefixed_varname), d->merge_env_vars, value, NULL)); } /* Increase the count. */ ++(*count); if (d->env_vars_count_in_n > 0) { apr_table_set(r->subprocess_env, apr_pstrcat(r->pool, prefixed_varname, "_N", NULL), apr_itoa(r->pool, *count)); } } if (!am_cache_entry_slot_is_empty(&t->user)) { /* We have a user-"name". Set r->user and r->ap_auth_type. */ r->user = apr_pstrdup(r->pool, am_cache_entry_get_string(t, &t->user)); r->ap_auth_type = apr_pstrdup(r->pool, "Mellon"); } else { /* We don't have a user-"name". Log error. */ AM_LOG_RERROR(APLOG_MARK, APLOG_NOTICE, 0, r, "Didn't find the attribute \"%s\" in the attributes" " which were received from the IdP. Cannot set a user" " for this request without a valid user attribute.", d->userattr); } /* Populate with the session? */ if (d->dump_session) { char *session; const char *srcstr; int srclen, dstlen; srcstr = am_cache_entry_get_string(t, &t->lasso_session); srclen = strlen(srcstr); dstlen = apr_base64_encode_len(srclen); session = apr_palloc(r->pool, dstlen); (void)apr_base64_encode(session, srcstr, srclen); apr_table_set(r->subprocess_env, "MELLON_SESSION", session); } if (d->dump_saml_response) { const char *sr = am_cache_entry_get_string(t, &t->lasso_saml_response); if (sr) { apr_table_set(r->subprocess_env, "MELLON_SAML_RESPONSE", sr); } } } /* This function deletes a given key from the session store. * * Parameters: * request_rec *r The request we are processing. * am_cache_entry_t *cache The entry we are deleting. * * Returns: * Nothing. */ void am_cache_delete(request_rec *r, 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(r, cache); } /* This function stores a lasso identity dump and a lasso session dump in * the given session object. * * Parameters: * am_cache_entry_t *session The session object. * const char *lasso_identity The identity dump. * const char *lasso_session The session dump. * * Returns: * OK on success or HTTP_INTERNAL_SERVER_ERROR if the lasso state information * is to big to fit in our session. */ int am_cache_set_lasso_state(am_cache_entry_t *session, const char *lasso_identity, const char *lasso_session, const char *lasso_saml_response) { int status; status = am_cache_entry_store_string(session, &session->lasso_identity, lasso_identity); if (status != 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Lasso identity is too big for storage. Size of lasso" " identity is %" APR_SIZE_T_FMT ".", (apr_size_t)strlen(lasso_identity)); return HTTP_INTERNAL_SERVER_ERROR; } status = am_cache_entry_store_string(session, &session->lasso_session, lasso_session); if (status != 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Lasso session is too big for storage. Size of lasso" " session is %" APR_SIZE_T_FMT ".", (apr_size_t)strlen(lasso_session)); return HTTP_INTERNAL_SERVER_ERROR; } status = am_cache_entry_store_string(session, &session->lasso_saml_response, lasso_saml_response); if (status != 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Lasso SAML response is too big for storage. Size of " "lasso SAML Response is %" APR_SIZE_T_FMT ".", (apr_size_t)strlen(lasso_saml_response)); return HTTP_INTERNAL_SERVER_ERROR; } return OK; } /* This function retrieves a lasso identity dump from the session object. * * Parameters: * am_cache_entry_t *session The session object. * * Returns: * The identity dump, or NULL if we don't have a session dump. */ const char *am_cache_get_lasso_identity(am_cache_entry_t *session) { return am_cache_entry_get_string(session, &session->lasso_identity); } /* This function retrieves a lasso session dump from the session object. * * Parameters: * am_cache_entry_t *session The session object. * * Returns: * The session dump, or NULL if we don't have a session dump. */ const char *am_cache_get_lasso_session(am_cache_entry_t *session) { return am_cache_entry_get_string(session, &session->lasso_session); } mod_auth_mellon-0.16.0/auth_mellon_config.c0000664002412700241270000023302013607336001020665 0ustar jhrozekjhrozek/* * * auth_mellon_config.c: an authentication apache module * Copyright © 2003-2007 UNINETT (http://www.uninett.no/) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "auth_mellon.h" #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(auth_mellon); #endif /* 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 prefix to use for attributes received from the * server. Customizable using the MellonEnvPrefix option */ static const char *default_env_prefix = "MELLON_"; /* 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 is to not enforce secure flag */ static const int default_secure_cookie = 0; /* The default setting for cookie is to not enforce HttpOnly flag */ static const int default_http_only_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; /* maximum saved POST sessions * the MellonPostCount configuration directive if you change this. */ static const int post_count = 100; #ifdef ENABLE_DIAGNOSTICS /* Default filename for mellon diagnostics log file. * Relative pathname is relative to server root. */ static const char *default_diag_filename = "logs/mellon_diagnostics"; /* Default state for diagnostics is off */ static am_diag_flags_t default_diag_flags = AM_DIAG_FLAG_DISABLE; #endif /* whether to merge env. vars or not * the MellonMergeEnvVars configuration directive if you change this. */ static const char *default_merge_env_vars = NULL; /* for env. vars with multiple values, the index start * the MellonEnvVarsIndexStart configuration directive if you change this. */ static const int default_env_vars_index_start = -1; /* whether to also populate env. var _N with number of values * the MellonEnvVarsSetCount configuration directive if you change this. */ static const int default_env_vars_count_in_n = -1; /* The default list of trusted redirect domains. */ static const char * const default_redirect_domains[] = { "[self]", NULL }; /* The default setting to enabled the invalidation session endpoint */ static const int default_enabled_invalidation_session = 0; /* 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. The file contents are immediately read. * * 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_file_contents_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { const char *path; apr_status_t rv; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; int offset; am_file_data_t **p_file_data, *file_data; path = ap_server_root_relative(cmd->pool, arg); if (!path) { return apr_pstrcat(cmd->pool, cmd->cmd->name, ": Invalid file path ", arg, NULL); } offset = (int)(long)cmd->info; p_file_data = (am_file_data_t **)((char *)cfg + offset); *p_file_data = am_file_data_new(cmd->pool, path); file_data = *p_file_data; rv = am_file_read(file_data); if (rv != APR_SUCCESS) { return file_data->strerror; } return NULL; } /* This function handles configuration directives which set a file * pathname in the module configuration. The file is checked for * existence. * * 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_file_pathname_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { const char *path; apr_status_t rv; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; int offset; am_file_data_t **p_file_data, *file_data; path = ap_server_root_relative(cmd->pool, arg); if (!path) { return apr_pstrcat(cmd->pool, cmd->cmd->name, ": Invalid file_data path ", arg, NULL); } offset = (int)(long)cmd->info; p_file_data = (am_file_data_t **)((char *)cfg + offset); *p_file_data = am_file_data_new(cmd->pool, path); file_data = *p_file_data; rv = am_file_stat(file_data); if (rv != APR_SUCCESS) { return file_data->strerror; } if (file_data->finfo.filetype != APR_REG) { return apr_psprintf(cmd->pool, "file \"%s\" is not a regular file", file_data->path); } return NULL; } /* 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; am_file_data_t *idp_file_data = NULL; am_file_data_t *chain_file_data = NULL; #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 */ idp_file_data = am_file_data_new(pconf, metadata); if (am_file_stat(idp_file_data) != APR_SUCCESS) { return idp_file_data->strerror; } if (chain) { chain_file_data = am_file_data_new(pconf, chain); if (am_file_stat(chain_file_data) != APR_SUCCESS) { return chain_file_data->strerror; } } else { chain_file_data = NULL; } am_metadata_t *idp_metadata = apr_array_push(cfg->idp_metadata); idp_metadata->metadata = idp_file_data; idp_metadata->chain = chain_file_data; 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 MellonDiagnosticsFile configuration directive. * It emits as warning in the log file if Mellon is not built with * diagnostics enabled. * * 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 *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_diag_file_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { #ifdef ENABLE_DIAGNOSTICS return ap_set_file_slot(cmd, am_get_diag_cfg(cmd->server), arg); #else ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server, "%s has no effect because Mellon was not compiled with" " diagnostics enabled, use ./configure --enable-diagnostics" " at build time to turn this feature on.", cmd->directive->directive); return NULL; #endif } /* This function handles configuration directives which sets the * diagnostics flags 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 *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_diag_flags_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { #ifdef ENABLE_DIAGNOSTICS am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(cmd->server); if (strcasecmp(arg, "on") == 0) { diag_cfg->flags = AM_DIAG_FLAG_ENABLE_ALL; } else if (strcasecmp(arg, "off") == 0) { diag_cfg->flags = AM_DIAG_FLAG_DISABLE; } else { return apr_psprintf(cmd->pool, "%s: must be one of: 'on', 'off'", cmd->cmd->name); } return NULL; #else ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server, "%s has no effect because Mellon was not compiled with" " diagnostics enabled, use ./configure --enable-diagnostics" " at build time to turn this feature on.", cmd->directive->directive); return NULL; #endif } /* This function handles the MellonCookieSameSite configuration directive. * This directive can be set to "lax" or "strict" * * 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_samesite_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if(!strcasecmp(arg, "lax")) { d->cookie_samesite = am_samesite_lax; } else if(!strcasecmp(arg, "strict")) { d->cookie_samesite = am_samesite_strict; } else if(!strcasecmp(arg, "none")) { d->cookie_samesite = am_samesite_none; } else { return "The MellonCookieSameSite parameter must be 'lax' or 'strict'"; } return NULL; } /* 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 MellonSecureCookie configuration directive. * This directive can be set to "on", "off", "secure" or "httponly". * * 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_secure_slots(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if(!strcasecmp(arg, "on")) { d->secure = 1; d->http_only = 1; } else if(!strcasecmp(arg, "secure")) { d->secure = 1; } else if(!strcasecmp(arg, "httponly")) { d->http_only = 1; } else if(strcasecmp(arg, "off")) { return "parameter must be 'on', 'off', 'secure' or 'httponly'"; } return NULL; } /* This function handles the obsolete MellonDecoder configuration directive. * It is a no-op. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL */ static const char *am_set_decoder_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { return NULL; } /* This function handles the MellonEndpointPath configuration directive. * If the path doesn't end with a '/', then we will append one. * * Parameters: * cmd_parms *cmd The command structure for the MellonEndpointPath * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *arg The string argument containing the path of the * endpoint directory. * * Returns: * This function will always return NULL. */ static const char *am_set_endpoint_path(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; /* Make sure that the path ends with '/'. */ if(strlen(arg) == 0 || arg[strlen(arg) - 1] != '/') { d->endpoint_path = apr_pstrcat(cmd->pool, arg, "/", NULL); } else { d->endpoint_path = arg; } return NULL; } /* This function handles the MellonSetEnv configuration directive. * This directive allows the user to change the name of attributes. * * Parameters: * cmd_parms *cmd The command structure for the MellonSetEnv * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *newName The new name of the attribute. * const char *oldName The old name of the attribute. * * Returns: * This function will always return NULL. */ static const char *am_set_setenv_slot(cmd_parms *cmd, void *struct_ptr, const char *newName, const char *oldName) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; /* Configure as prefixed attribute name */ am_envattr_conf_t *envattr_conf = (am_envattr_conf_t *)apr_palloc(cmd->pool, sizeof(am_envattr_conf_t)); envattr_conf->name = newName; envattr_conf->prefixed = 1; apr_hash_set(d->envattr, oldName, APR_HASH_KEY_STRING, envattr_conf); return NULL; } /* This function handles the MellonSetEnvNoPrefix configuration directive. * This directive allows the user to change the name of attributes without prefixing them with MELLON_. * * Parameters: * cmd_parms *cmd The command structure for the MellonSetEnv * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *newName The new name of the attribute. * const char *oldName The old name of the attribute. * * Returns: * This function will always return NULL. */ static const char *am_set_setenv_no_prefix_slot(cmd_parms *cmd, void *struct_ptr, const char *newName, const char *oldName) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; /* Configure as not prefixed attribute name */ am_envattr_conf_t *envattr_conf = (am_envattr_conf_t *)apr_palloc(cmd->pool, sizeof(am_envattr_conf_t)); envattr_conf->name = newName; envattr_conf->prefixed = 0; apr_hash_set(d->envattr, oldName, APR_HASH_KEY_STRING, envattr_conf); return NULL; } /* This function handles the MellonAuthnContextComparisonType option. * It could be set to "exact", "minimum", "maximum" or "better" * * 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_authn_context_comparison_type_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if (!strcasecmp(arg, LASSO_LIB_AUTHN_CONTEXT_COMPARISON_EXACT)) { d->authn_context_comparison_type = LASSO_LIB_AUTHN_CONTEXT_COMPARISON_EXACT; } else if (!strcasecmp(arg, LASSO_LIB_AUTHN_CONTEXT_COMPARISON_MINIMUM)) { d->authn_context_comparison_type = LASSO_LIB_AUTHN_CONTEXT_COMPARISON_MINIMUM; } else if (!strcasecmp(arg, LASSO_LIB_AUTHN_CONTEXT_COMPARISON_MAXIMUM)) { d->authn_context_comparison_type = LASSO_LIB_AUTHN_CONTEXT_COMPARISON_MAXIMUM; } else if (!strcasecmp(arg, LASSO_LIB_AUTHN_CONTEXT_COMPARISON_BETTER)) { d->authn_context_comparison_type = LASSO_LIB_AUTHN_CONTEXT_COMPARISON_BETTER; } else { return "parameter must be 'exact', 'minimum', 'maximum' or 'better'"; } return NULL; } /* This function decodes MellonCond flags, such as [NOT,REG] * * Parameters: * const char *arg Pointer to the flags string * * Returns: * flags, or -1 on error */ static int am_cond_flags(const char *arg) { int flags = AM_COND_FLAG_NULL; static const char * const options[] = { "OR", /* AM_EXPIRE_FLAG_OR */ "NOT", /* AM_EXPIRE_FLAG_NOT */ "REG", /* AM_EXPIRE_FLAG_REG */ "NC", /* AM_EXPIRE_FLAG_NC */ "MAP", /* AM_EXPIRE_FLAG_MAP */ "REF", /* AM_EXPIRE_FLAG_REF */ "SUB", /* AM_EXPIRE_FLAG_SUB */ /* The other options (IGN, REQ, FSTR, ...) are only internally used */ }; apr_size_t options_count = sizeof(options) / sizeof(*options); /* Skip inital [ */ if (arg[0] == '[') arg++; else return -1; do { apr_size_t i; for (i = 0; i < options_count; i++) { apr_size_t optlen = strlen(options[i]); if (strncmp(arg, options[i], optlen) == 0) { /* Make sure we have a separator next */ if (arg[optlen] && !strchr("]\t ,", (int)arg[optlen])) return -1; flags |= (1 << i); arg += optlen; break; } /* no match */ if (i == options_count) return -1; /* skip spaces, tabs and commas */ arg += strspn(arg, " \t,"); /* * End of option, but we fire an error if * there is trailing garbage */ if (*arg == ']') { arg++; return (*arg == '\0') ? flags : -1; } } } while (*arg); /* Missing trailing ] */ return -1; } /* This function handles the MellonCond configuration directive, which * allows the user to restrict access based on attributes received from * the IdP. * * Parameters: * cmd_parms *cmd The command structure for the MellonCond * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *attribute Pointer to the attribute name * const char *value Pointer to the attribute value or regex * const char *options Pointer to options * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_cond_slot(cmd_parms *cmd, void *struct_ptr, const char *attribute, const char *value, const char *options) { am_dir_cfg_rec *d = struct_ptr; int flags = AM_COND_FLAG_NULL; am_cond_t *element; if (attribute == NULL || *attribute == '\0' || value == NULL || *value == '\0') return apr_pstrcat(cmd->pool, cmd->cmd->name, " takes at least two arguments", NULL); if (options != NULL && *options != '\0') flags = am_cond_flags(options); if (flags == -1) return apr_psprintf(cmd->pool, "%s - invalid flags %s", cmd->cmd->name, options); element = (am_cond_t *)apr_array_push(d->cond); element->varname = attribute; element->flags = flags; element->str = NULL; element->regex = NULL; element->directive = apr_pstrcat(cmd->pool, cmd->directive->directive, " ", cmd->directive->args, NULL); if (element->flags & AM_COND_FLAG_REG) { int regex_flags = AP_REG_EXTENDED|AP_REG_NOSUB; if (element->flags & AM_COND_FLAG_NC) regex_flags |= AP_REG_ICASE; element->regex = ap_pregcomp(cmd->pool, value, regex_flags); if (element->regex == NULL) return apr_psprintf(cmd->pool, "%s - invalid regex %s", cmd->cmd->name, value); } /* * Flag values containing format strings to that we do * not have to process the others at runtime. */ if (strchr(value, '%') != NULL) element->flags |= AM_COND_FLAG_FSTR; /* * We keep the string also for regex, so that we can * print it for debug purpose and perform substitutions on it. */ element->str = value; return NULL; } /* This function handles the MellonRequire configuration directive, which * allows the user to restrict access based on attributes received from * the IdP. * * Parameters: * cmd_parms *cmd The command structure for the MellonRequire * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg Pointer to the configuration string. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_require_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = struct_ptr; char *attribute, *value; int i; am_cond_t *element; am_cond_t *first_element; attribute = ap_getword_conf(cmd->pool, &arg); value = ap_getword_conf(cmd->pool, &arg); if (*attribute == '\0' || *value == '\0') { return apr_pstrcat(cmd->pool, cmd->cmd->name, " takes at least two arguments", NULL); } /* * MellonRequire overwrites previous conditions on this attribute * We just tag the am_cond_t with the ignore flag, as it is * easier (and probably faster) than to really remove it. */ for (i = 0; i < d->cond->nelts; i++) { am_cond_t *ce = &((am_cond_t *)(d->cond->elts))[i]; if ((strcmp(ce->varname, attribute) == 0) && (ce->flags & AM_COND_FLAG_REQ)) ce->flags |= AM_COND_FLAG_IGN; } first_element = NULL; do { element = (am_cond_t *)apr_array_push(d->cond); element->varname = attribute; element->flags = AM_COND_FLAG_OR|AM_COND_FLAG_REQ; element->str = value; element->regex = NULL; /* * When multiple values are given, we track the first one * in order to retreive the directive */ if (first_element == NULL) { element->directive = apr_pstrcat(cmd->pool, cmd->directive->directive, " ", cmd->directive->args, NULL); first_element = element; } else { element->directive = first_element->directive; } } while (*(value = ap_getword_conf(cmd->pool, &arg)) != '\0'); /* * Remove OR flag on last element */ element->flags &= ~AM_COND_FLAG_OR; return NULL; } /* This function handles the MellonOrganization* directives, which * which specify language-qualified strings * * Parameters: * cmd_parms *cmd The command structure for the MellonOrganization* * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *lang Pointer to the language string (optional) * const char *value Pointer to the data * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_langstring_slot(cmd_parms *cmd, void *struct_ptr, const char *lang, const char *value) { apr_hash_t *h = *(apr_hash_t **)(struct_ptr + (apr_size_t)cmd->info); if (value == NULL || *value == '\0') { value = lang; lang = ""; } apr_hash_set(h, lang, APR_HASH_KEY_STRING, apr_pstrdup(cmd->server->process->pconf, value)); return NULL; } /* This function handles the MellonAuthnContextClassRef directive. * * Parameters: * cmd_parms *cmd The command structure for the MellonAuthnContextClassRef * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *arg An URI for an SAMLv2 AuthnContextClassRef * * Returns: * This function will always return NULL. */ static const char *am_set_authn_context_class_ref(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; apr_pool_t *p= cmd->pool; char **context_class_ref_p; if(strlen(arg) == 0) { return NULL; } context_class_ref_p = apr_array_push(d->authn_context_class_ref); *context_class_ref_p = apr_pstrdup(p, arg); return NULL; } /* This function handles the MellonDoNotVerifyLogoutSignature configuration directive, * it is identical to the am_set_hash_string_slot function. You can refer to it. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *key The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_do_not_verify_logout_signature(cmd_parms *cmd, void *struct_ptr, const char *key) { #ifdef HAVE_lasso_profile_set_signature_verify_hint return am_set_hash_string_slot(cmd, struct_ptr, key, NULL); #else return apr_pstrcat(cmd->pool, cmd->cmd->name, " is not usable as modmellon was compiled against " "a version of the lasso library which miss the " "function lasso_profile_set_signature_verify_hint.", NULL); #endif } /* This function handles the MellonMergeEnvVars configuration directive, * it sets merge_env_vars to nonempty separator (default semicolon), * or empty string to denote no merging. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *flag On/Off flag * const char *sep Optional separator, should be only present with On * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_merge_env_vars(cmd_parms *cmd, void *struct_ptr, const char *flag, const char *sep) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; apr_pool_t *p= cmd->pool; if (strcasecmp(flag, "on") == 0) { if (sep && *sep) { /* * TAKE12 will not give us the second argument if it is * empty string so we cannot complain about it, we will just * silently use semicolon */ d->merge_env_vars = apr_pstrdup(p, sep); } else { d->merge_env_vars = ";"; } } else if (strcasecmp(flag, "off") == 0) { if (sep) { return apr_pstrcat(cmd->pool, cmd->cmd->name, " separator should not be used with Off", NULL); } d->merge_env_vars = ""; } else { return apr_pstrcat(cmd->pool, cmd->cmd->name, " first parameer must be On or Off", NULL); } return NULL; } /* Handle MellonRedirectDomains option. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * int argc Number of redirect domains. * char *const argv[] List of redirect domains. * * Returns: * NULL on success, or errror string on failure. */ static const char *am_set_redirect_domains(cmd_parms *cmd, void *struct_ptr, int argc, char *const argv[]) { am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; const char **redirect_domains; int i; if (argc < 1) return apr_psprintf(cmd->pool, "%s takes at least one arguments", cmd->cmd->name); redirect_domains = apr_palloc(cmd->pool, sizeof(const char *) * (argc + 1)); for (i = 0; i < argc; i++) { redirect_domains[i] = argv[i]; } redirect_domains[argc] = NULL; cfg->redirect_domains = redirect_domains; return NULL; } /* This function handles the MellonSignatureMethod configuration directive. * This directive can be set to one of: * * 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_signature_method_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; char *valid_methods = "rsa-sha1" #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA256 " rsa-sha256" #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA384 " rsa-sha384" #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA512 " rsa-sha512" #endif ; if (!strcasecmp(arg, "rsa-sha1")) { d->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA1; } #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA256 else if (!strcasecmp(arg, "rsa-sha256")) { d->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA256; } #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA384 else if (!strcasecmp(arg, "rsa-sha384")) { d->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA384; } #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA512 else if (!strcasecmp(arg, "rsa-sha512")) { d->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA512; } #endif else { return apr_psprintf(cmd->pool, "%s: Invalid method \"%s\", must be one of: %s", cmd->cmd->name, arg, valid_methods); } return NULL; } /* This function handles the MellonEnabledInvalidateSessionEndpoint configuration directive. * This directive can be set to "on" or "off" (default). * * 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_invalidate_session_slots(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if (strcasecmp(arg, "on") == 0) { d->enabled_invalidation_session = 1; } else if (strcasecmp(arg, "off") == 0) { d->enabled_invalidation_session = 0; } else { return apr_psprintf(cmd->pool, "%s: must be one of: 'on', 'off'", cmd->cmd->name); } return NULL; } /* This array contains all the configuration directive which are handled * by auth_mellon. */ const command_rec auth_mellon_commands[] = { /* Global configuration directives. */ AP_INIT_TAKE1( "MellonCacheSize", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, cache_size), RSRC_CONF, "The number of sessions we can keep track of at once. You must" " restart the server before any changes to this directive will" " take effect. The default value is 100." ), AP_INIT_TAKE1( "MellonCacheEntrySize", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, entry_size), RSRC_CONF, "The maximum size for a single session entry. You must" " restart the server before any changes to this directive will" " take effect. The default value is 192KiB." ), AP_INIT_TAKE1( "MellonLockFile", am_set_module_config_file_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, lock_file), RSRC_CONF, "The lock file for session synchronization." " Default value is \"/var/run/mod_auth_mellon.lock\"." ), 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." " Not set by default." ), 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 900 (15 minutes)." ), 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 1048576 (1 MB)." ), AP_INIT_TAKE1( "MellonDiagnosticsFile", am_set_module_diag_file_slot, #ifdef ENABLE_DIAGNOSTICS (void *)APR_OFFSETOF(am_diag_cfg_rec, filename), #else NULL, #endif RSRC_CONF, "Diagnostics log file. [file|pipe] " "If file then file is a filename, relative to the ServerRoot." "If pipe then the filename is a pipe character \"|\", " "followed by the path to a program to receive the log information " "on its standard input. " " Default value is \"logs/mellon_diagnostics\"." ), AP_INIT_ITERATE( "MellonDiagnosticsEnable", am_set_module_diag_flags_slot, NULL, RSRC_CONF, "Diagnostics flags. [on|off] " " Default value is \"off\"." ), /* Per-location configuration directives. */ AP_INIT_TAKE1( "MellonEnable", am_set_enable_slot, NULL, OR_AUTHCFG, "Enable auth_mellon on a location. This can be set to 'off', 'info'" " and 'auth'. 'off' disables auth_mellon for a location, 'info'" " will only populate the environment with attributes if the user" " has logged in already. 'auth' will redirect the user to the IdP" " if he hasn't logged in yet, but otherwise behaves like 'info'." ), AP_INIT_TAKE1( "MellonDecoder", am_set_decoder_slot, NULL, OR_AUTHCFG, "Obsolete option, now a no-op for backwards compatibility." ), AP_INIT_TAKE1( "MellonVariable", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, varname), OR_AUTHCFG, "The name of the cookie which auth_mellon will set. Defaults to" " 'cookie'. This string is appended to 'mellon-' to create the" " cookie name, and the default name of the cookie will therefore" " be 'mellon-cookie'." ), AP_INIT_TAKE1( "MellonSecureCookie", am_set_secure_slots, NULL, OR_AUTHCFG, "Whether the cookie set by auth_mellon should have HttpOnly and" " secure flags set. Default is 'off'. Once 'on' - both flags will" " be set. Values 'httponly' or 'secure' will respectively set only" " one flag." ), 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( "MellonCookieSameSite", am_set_samesite_slot, NULL, OR_AUTHCFG, "The SameSite value for the auth_mellon cookie. Defaults to" " having no SameSite value. Accepts values of Lax or Strict." ), 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 the" " prefix. The prefix defaults to MELLON_ but can be changed with" " MellonEnvPrefix." " 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_TAKE1( "MellonEnvPrefix", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, env_prefix), OR_AUTHCFG, "The prefix to use for attributes received from the server." ), AP_INIT_FLAG( "MellonSessionDump", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, dump_session), OR_AUTHCFG, "Dump session in environment. Default is off" ), AP_INIT_FLAG( "MellonSamlResponseDump", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, dump_saml_response), OR_AUTHCFG, "Dump SAML authentication response in environment. Default is off" ), AP_INIT_RAW_ARGS( "MellonRequire", am_set_require_slot, NULL, OR_AUTHCFG, "Attribute requirements for authorization. Allows you to restrict" " access based on attributes received from the IdP. If you list" " several MellonRequire configuration directives, then all of them" " must match. Every MellonRequire can list several allowed values" " for the attribute. The syntax is:" " MellonRequire [value2....]." ), AP_INIT_TAKE23( "MellonCond", am_set_cond_slot, NULL, OR_AUTHCFG, "Attribute requirements for authorization. Allows you to restrict" " access based on attributes received from the IdP. The syntax is:" " MellonRequire []." ), AP_INIT_TAKE1( "MellonSessionLength", ap_set_int_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, session_length), OR_AUTHCFG, "Maximum number of seconds a session will be valid for. Defaults" " to 86400 seconds (1 day)." ), AP_INIT_TAKE1( "MellonNoCookieErrorPage", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, no_cookie_error_page), OR_AUTHCFG, "Web page to display if the user has disabled cookies. We will" " return a 400 Bad Request error if this is unset and the user" " ha disabled cookies." ), AP_INIT_TAKE1( "MellonNoSuccessErrorPage", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, no_success_error_page), OR_AUTHCFG, "Web page to display if the idp posts with a failed" " authentication error. We will return a 401 Unauthorized error" " if this is unset and the idp posts such assertion." ), AP_INIT_TAKE1( "MellonSPMetadataFile", am_set_file_contents_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_file_contents_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_file_contents_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", am_set_file_pathname_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", am_set_file_pathname_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 in seconds of IdP probe discovery service. " "The default is unset, which means that this feature is disabled." ), 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_TAKE1( "MellonAuthnContextComparisonType", am_set_authn_context_comparison_type_slot, NULL, OR_AUTHCFG, "An AuthnContextComparisonType attribute as part of the AuthnRequest." ), 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_FLAG( "MellonSendCacheControlHeader", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, send_cache_control_header), OR_AUTHCFG, "Send the cache-control header on responses. Default is on." ), AP_INIT_TAKE1( "MellonDoNotVerifyLogoutSignature", am_set_do_not_verify_logout_signature, (void *)APR_OFFSETOF(am_dir_cfg_rec, do_not_verify_logout_signature), OR_AUTHCFG, "A list of entity of IdP whose logout requests signatures will not " "be valided" ), AP_INIT_FLAG( "MellonPostReplay", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, post_replay), OR_AUTHCFG, "Whether we should replay POST requests that trigger authentication. Default is off." ), AP_INIT_TAKE12( "MellonMergeEnvVars", am_set_merge_env_vars, NULL, OR_AUTHCFG, "Whether to merge environment variables multi-values or not. Default is off." "When first parameter is on, optional second parameter is the separator, " "defaulting to semicolon." ), AP_INIT_TAKE1( "MellonEnvVarsIndexStart", ap_set_int_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, env_vars_index_start), OR_AUTHCFG, "Start indexing environment variables for multivalues with 0 or 1. Default is 0." ), AP_INIT_FLAG( "MellonEnvVarsSetCount", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, env_vars_count_in_n), OR_AUTHCFG, "Whether to also populate environment variable suffixed _N with number of values. Default is off." ), AP_INIT_FLAG( "MellonECPSendIDPList", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, ecp_send_idplist), OR_AUTHCFG, "Whether to send an ECP client a list of IdP's. Default is off." ), AP_INIT_TAKE_ARGV( "MellonRedirectDomains", am_set_redirect_domains, NULL, OR_AUTHCFG, "List of domains we can redirect to." ), AP_INIT_TAKE1( "MellonSignatureMethod", am_set_signature_method_slot, NULL, OR_AUTHCFG, "Signature method used to sign SAML messages sent by Mellon" ), AP_INIT_TAKE1( "MellonEnabledInvalidateSessionEndpoint", am_set_invalidate_session_slots, NULL, OR_AUTHCFG, "Enabled the session invalidation endpoint. Default is 'off'." ), {NULL} }; const am_error_map_t auth_mellon_errormap[] = { { LASSO_PROFILE_ERROR_STATUS_NOT_SUCCESS, HTTP_UNAUTHORIZED }, #ifdef LASSO_PROFILE_ERROR_REQUEST_DENIED { LASSO_PROFILE_ERROR_REQUEST_DENIED, HTTP_UNAUTHORIZED }, #endif { 0, 0 } }; /* Release a lasso_server object associated with this configuration. * * Parameters: * void *data The pointer to the configuration data. * * Returns: * Always APR_SUCCESS. */ static apr_status_t auth_mellon_free_server(void *data) { am_dir_cfg_rec *dir = data; if (dir->server != NULL) { lasso_server_destroy(dir->server); dir->server = NULL; } return APR_SUCCESS; } /* This function creates and initializes a directory configuration * object for auth_mellon. * * Parameters: * apr_pool_t *p The pool we should allocate memory from. * char *d Unused, always NULL. * * Returns: * The new directory configuration object. */ void *auth_mellon_dir_config(apr_pool_t *p, char *d) { am_dir_cfg_rec *dir = apr_palloc(p, sizeof(*dir)); apr_pool_cleanup_register(p, dir, auth_mellon_free_server, auth_mellon_free_server); dir->enable_mellon = am_enable_default; dir->varname = default_cookie_name; dir->secure = default_secure_cookie; dir->http_only = default_http_only_cookie; dir->merge_env_vars = default_merge_env_vars; dir->env_vars_index_start = default_env_vars_index_start; dir->env_vars_count_in_n = default_env_vars_count_in_n; dir->cond = apr_array_make(p, 0, sizeof(am_cond_t)); dir->cookie_domain = NULL; dir->cookie_path = NULL; dir->cookie_samesite = am_samesite_default; dir->envattr = apr_hash_make(p); dir->env_prefix = default_env_prefix; dir->userattr = default_user_attribute; dir->idpattr = NULL; dir->signature_method = inherit_signature_method; dir->dump_session = default_dump_session; dir->dump_saml_response = default_dump_saml_response; dir->endpoint_path = default_endpoint_path; dir->session_length = -1; /* -1 means use default. */ dir->no_cookie_error_page = NULL; dir->no_success_error_page = NULL; dir->sp_metadata_file = NULL; dir->sp_private_key_file = NULL; dir->sp_cert_file = NULL; dir->idp_metadata = apr_array_make(p, 0, sizeof(am_metadata_t)); dir->idp_public_key_file = NULL; dir->idp_ca_file = NULL; dir->idp_ignore = NULL; dir->login_path = default_login_path; dir->discovery_url = NULL; dir->probe_discovery_timeout = -1; /* -1 means no probe discovery */ dir->probe_discovery_idp = apr_table_make(p, 0); dir->sp_entity_id = NULL; dir->sp_org_name = apr_hash_make(p); dir->sp_org_display_name = apr_hash_make(p); dir->sp_org_url = apr_hash_make(p); apr_thread_mutex_create(&dir->server_mutex, APR_THREAD_MUTEX_DEFAULT, p); dir->inherit_server_from = dir; dir->server = NULL; dir->authn_context_class_ref = apr_array_make(p, 0, sizeof(char *)); dir->authn_context_comparison_type = NULL; dir->subject_confirmation_data_address_check = inherit_subject_confirmation_data_address_check; dir->send_cache_control_header = inherit_send_cache_control_header; dir->do_not_verify_logout_signature = apr_hash_make(p); dir->post_replay = inherit_post_replay; dir->redirect_domains = default_redirect_domains; dir->ecp_send_idplist = inherit_ecp_send_idplist; dir->enabled_invalidation_session = default_enabled_invalidation_session; return dir; } /* Determine whether this configuration changes anything relevant to the * lasso_server configuration. * * Parameters: * am_dir_cfg_rec *add_cfg The new configuration. * * Returns: * true if we can inherit the lasso_server object, false if not. */ static bool cfg_can_inherit_lasso_server(const am_dir_cfg_rec *add_cfg) { if (add_cfg->endpoint_path != default_endpoint_path) return false; if (add_cfg->sp_metadata_file != NULL || add_cfg->sp_private_key_file != NULL || add_cfg->sp_cert_file != NULL) return false; if (add_cfg->idp_metadata->nelts > 0 || add_cfg->idp_public_key_file != NULL || add_cfg->idp_ca_file != NULL || add_cfg->idp_ignore != NULL) return false; if (apr_hash_count(add_cfg->sp_org_name) > 0 || apr_hash_count(add_cfg->sp_org_display_name) > 0 || apr_hash_count(add_cfg->sp_org_url) > 0) return false; return true; } /* This function merges two am_dir_cfg_rec structures. * It will try to inherit from the base where possible. * * Parameters: * apr_pool_t *p The pool we should allocate memory from. * void *base The original structure. * void *add The structure we should add to base. * * Returns: * The merged structure. */ void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add) { am_dir_cfg_rec *base_cfg = (am_dir_cfg_rec *)base; am_dir_cfg_rec *add_cfg = (am_dir_cfg_rec *)add; am_dir_cfg_rec *new_cfg; new_cfg = (am_dir_cfg_rec *)apr_palloc(p, sizeof(*new_cfg)); apr_pool_cleanup_register(p, new_cfg, auth_mellon_free_server, auth_mellon_free_server); new_cfg->enable_mellon = (add_cfg->enable_mellon != am_enable_default ? add_cfg->enable_mellon : base_cfg->enable_mellon); new_cfg->varname = (add_cfg->varname != default_cookie_name ? add_cfg->varname : base_cfg->varname); new_cfg->secure = (add_cfg->secure != default_secure_cookie ? add_cfg->secure : base_cfg->secure); new_cfg->http_only = (add_cfg->http_only != default_http_only_cookie ? add_cfg->http_only : base_cfg->http_only); new_cfg->merge_env_vars = (add_cfg->merge_env_vars != default_merge_env_vars ? add_cfg->merge_env_vars : base_cfg->merge_env_vars); new_cfg->env_vars_index_start = (add_cfg->env_vars_index_start != default_env_vars_index_start ? add_cfg->env_vars_index_start : base_cfg->env_vars_index_start); new_cfg->env_vars_count_in_n = (add_cfg->env_vars_count_in_n != default_env_vars_count_in_n ? add_cfg->env_vars_count_in_n : base_cfg->env_vars_count_in_n); new_cfg->cookie_domain = (add_cfg->cookie_domain != NULL ? add_cfg->cookie_domain : base_cfg->cookie_domain); new_cfg->cookie_path = (add_cfg->cookie_path != NULL ? add_cfg->cookie_path : base_cfg->cookie_path); new_cfg->cookie_samesite = (add_cfg->cookie_samesite != am_samesite_default ? add_cfg->cookie_samesite : base_cfg->cookie_samesite); 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->env_prefix = (add_cfg->env_prefix != default_env_prefix ? add_cfg->env_prefix : base_cfg->env_prefix); 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->signature_method = CFG_MERGE(add_cfg, base_cfg, signature_method); new_cfg->dump_session = (add_cfg->dump_session != default_dump_session ? add_cfg->dump_session : base_cfg->dump_session); new_cfg->dump_saml_response = (add_cfg->dump_saml_response != default_dump_saml_response ? add_cfg->dump_saml_response : base_cfg->dump_saml_response); new_cfg->endpoint_path = ( add_cfg->endpoint_path != default_endpoint_path ? add_cfg->endpoint_path : base_cfg->endpoint_path ); new_cfg->session_length = (add_cfg->session_length != -1 ? add_cfg->session_length : base_cfg->session_length); new_cfg->no_cookie_error_page = (add_cfg->no_cookie_error_page != NULL ? add_cfg->no_cookie_error_page : base_cfg->no_cookie_error_page); new_cfg->no_success_error_page = (add_cfg->no_success_error_page != NULL ? add_cfg->no_success_error_page : base_cfg->no_success_error_page); new_cfg->sp_metadata_file = (add_cfg->sp_metadata_file ? add_cfg->sp_metadata_file : base_cfg->sp_metadata_file); new_cfg->sp_private_key_file = (add_cfg->sp_private_key_file ? add_cfg->sp_private_key_file : base_cfg->sp_private_key_file); new_cfg->sp_cert_file = (add_cfg->sp_cert_file ? add_cfg->sp_cert_file : base_cfg->sp_cert_file); new_cfg->idp_metadata = (add_cfg->idp_metadata->nelts ? add_cfg->idp_metadata : base_cfg->idp_metadata); new_cfg->idp_public_key_file = (add_cfg->idp_public_key_file ? add_cfg->idp_public_key_file : base_cfg->idp_public_key_file); new_cfg->idp_ca_file = (add_cfg->idp_ca_file ? add_cfg->idp_ca_file : base_cfg->idp_ca_file); new_cfg->idp_ignore = add_cfg->idp_ignore != NULL ? add_cfg->idp_ignore : base_cfg->idp_ignore; new_cfg->sp_entity_id = (add_cfg->sp_entity_id ? add_cfg->sp_entity_id : base_cfg->sp_entity_id); new_cfg->sp_org_name = apr_hash_copy(p, (apr_hash_count(add_cfg->sp_org_name) > 0) ? add_cfg->sp_org_name : base_cfg->sp_org_name); new_cfg->sp_org_display_name = apr_hash_copy(p, (apr_hash_count(add_cfg->sp_org_display_name) > 0) ? add_cfg->sp_org_display_name : base_cfg->sp_org_display_name); new_cfg->sp_org_url = apr_hash_copy(p, (apr_hash_count(add_cfg->sp_org_url) > 0) ? add_cfg->sp_org_url : base_cfg->sp_org_url); new_cfg->login_path = (add_cfg->login_path != default_login_path ? add_cfg->login_path : base_cfg->login_path); new_cfg->discovery_url = (add_cfg->discovery_url ? add_cfg->discovery_url : base_cfg->discovery_url); new_cfg->probe_discovery_timeout = (add_cfg->probe_discovery_timeout != -1 ? add_cfg->probe_discovery_timeout : base_cfg->probe_discovery_timeout); new_cfg->probe_discovery_idp = apr_table_copy(p, (!apr_is_empty_table(add_cfg->probe_discovery_idp)) ? add_cfg->probe_discovery_idp : base_cfg->probe_discovery_idp); if (cfg_can_inherit_lasso_server(add_cfg)) { new_cfg->inherit_server_from = base_cfg->inherit_server_from; } else { apr_thread_mutex_create(&new_cfg->server_mutex, APR_THREAD_MUTEX_DEFAULT, p); new_cfg->inherit_server_from = new_cfg; } new_cfg->server = NULL; new_cfg->authn_context_class_ref = (add_cfg->authn_context_class_ref->nelts ? add_cfg->authn_context_class_ref : base_cfg->authn_context_class_ref); new_cfg->authn_context_comparison_type = (add_cfg->authn_context_comparison_type != NULL ? add_cfg->authn_context_comparison_type : base_cfg->authn_context_comparison_type); 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->send_cache_control_header = CFG_MERGE(add_cfg, base_cfg, send_cache_control_header); new_cfg->post_replay = CFG_MERGE(add_cfg, base_cfg, post_replay); new_cfg->ecp_send_idplist = CFG_MERGE(add_cfg, base_cfg, ecp_send_idplist); new_cfg->redirect_domains = (add_cfg->redirect_domains != default_redirect_domains ? add_cfg->redirect_domains : base_cfg->redirect_domains); new_cfg->enabled_invalidation_session = (add_cfg->enabled_invalidation_session != default_enabled_invalidation_session ? add_cfg->enabled_invalidation_session : base_cfg->enabled_invalidation_session); 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)); #ifdef ENABLE_DIAGNOSTICS srv->diag_cfg.filename = default_diag_filename; srv->diag_cfg.fd = NULL; srv->diag_cfg.flags = default_diag_flags; srv->diag_cfg.dir_cfg_emitted = apr_table_make(p, 0); #endif /* we want to keeep our global configuration of shared memory and * mutexes, so we try to find it in the userdata before doing anything * else */ apr_pool_userdata_get((void **)&mod, key, p); if (mod) { srv->mc = mod; return srv; } /* the module has not been initiated at all */ mod = apr_palloc(p, sizeof(*mod)); mod->cache_size = 100; /* ought to be enough for everybody */ mod->lock_file = "/var/run/mod_auth_mellon.lock"; mod->post_dir = NULL; mod->post_ttl = post_ttl; mod->post_count = post_count; mod->post_size = post_size; mod->entry_size = AM_CACHE_DEFAULT_ENTRY_SIZE; mod->init_cache_size = 0; mod->init_lock_file = NULL; mod->init_entry_size = 0; mod->cache = NULL; mod->lock = NULL; apr_pool_userdata_set(mod, key, apr_pool_cleanup_null, p); srv->mc = mod; return srv; } /* This function merges two am_srv_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_srv_merge(apr_pool_t *p, void *base, void *add) { am_srv_cfg_rec *base_cfg = (am_srv_cfg_rec *)base; am_srv_cfg_rec *new_cfg; new_cfg = (am_srv_cfg_rec *)apr_palloc(p, sizeof(*new_cfg)); new_cfg->mc = base_cfg->mc; #ifdef ENABLE_DIAGNOSTICS am_srv_cfg_rec *add_cfg = (am_srv_cfg_rec *)add; new_cfg->diag_cfg.filename = (add_cfg->diag_cfg.filename != default_diag_filename ? add_cfg->diag_cfg.filename : base_cfg->diag_cfg.filename); new_cfg->diag_cfg.fd = NULL; new_cfg->diag_cfg.flags = (add_cfg->diag_cfg.flags != default_diag_flags ? add_cfg->diag_cfg.flags : base_cfg->diag_cfg.flags); new_cfg->diag_cfg.dir_cfg_emitted = apr_table_make(p, 0); #endif return new_cfg; } mod_auth_mellon-0.16.0/auth_mellon_cookie.c0000664002412700241270000002041513614046140020673 0ustar jhrozekjhrozek/* * * 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" #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(auth_mellon); #endif /* This function retrieves the name of our cookie. * * Parameters: * request_rec *r The current request. Used to find the identifier of * the cookie. We also allocate memory from r->pool. * * Returns: * The name of the cookie. */ static const char *am_cookie_name(request_rec *r) { am_dir_cfg_rec *dir_cfg; dir_cfg = am_get_dir_cfg(r); return apr_pstrcat(r->pool, "mellon-", dir_cfg->varname, NULL); } /* Calculate the cookie parameters. * * Parameters: * request_rec *r The request we should set the cookie in. * * Returns: * The cookie parameters as a string. */ static const char *am_cookie_params(request_rec *r) { int secure_cookie; int http_only_cookie; const char *cookie_domain = ap_get_server_name(r); const char *cookie_path = "/"; const char *cookie_samesite = ""; const char *env_var_value = NULL; am_dir_cfg_rec *cfg = am_get_dir_cfg(r); if (cfg->cookie_domain) { cookie_domain = cfg->cookie_domain; } if (cfg->cookie_path) { cookie_path = cfg->cookie_path; } if (r->subprocess_env != NULL){ env_var_value = apr_table_get(r->subprocess_env, AM_DISABLE_SAMESITE_ENV_VAR); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "%s : %s", AM_DISABLE_SAMESITE_ENV_VAR, env_var_value); } if (env_var_value == NULL){ if (cfg->cookie_samesite == am_samesite_lax) { cookie_samesite = "; SameSite=Lax"; } else if (cfg->cookie_samesite == am_samesite_strict) { cookie_samesite = "; SameSite=Strict"; } else if (cfg->cookie_samesite == am_samesite_none) { cookie_samesite = "; SameSite=None"; } } secure_cookie = cfg->secure; http_only_cookie = cfg->http_only; return apr_psprintf(r->pool, "Version=1; Path=%s; Domain=%s%s%s%s", cookie_path, cookie_domain, http_only_cookie ? "; HttpOnly" : "", secure_cookie ? "; secure" : "", cookie_samesite); } /* This functions finds the value of our cookie. * * Parameters: * request_rec *r The request we should find the cookie in. * * Returns: * The value of the cookie, or NULL if we don't find the cookie. */ const char *am_cookie_get(request_rec *r) { am_req_cfg_rec *req_cfg; const char *name; const char *value; const char *cookie; char *buffer, *end; /* don't run for subrequests */ if (r->main) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "cookie_get: Subrequest, so return NULL"); return NULL; } /* Check if we have added a note on the current request. */ req_cfg = am_get_req_cfg(r); value = req_cfg->cookie_value; if(value != NULL) { return value; } name = am_cookie_name(r); cookie = apr_table_get(r->headers_in, "Cookie"); if(cookie == NULL) { return NULL; } for(value = ap_strstr_c(cookie, name); value != NULL; value = ap_strstr_c(value + 1, name)) { if(value != cookie) { /* value isn't pointing to the start of the string. */ switch(value[-1]) { /* We allow the name in the cookie-string to be * preceeded by [\t; ]. Note that only ' ' should be used * by browsers. We test against the others just to be sure. */ case '\t': case ';': case ' ': break; default: /* value isn't preceeded by one of the listed characters, and * therefore we assume that it is part of another cookie. */ continue; /* Search for the next instance of the name. */ } } if(value[strlen(name)] != '=') { /* We don't have an equal-sign right after the name. Therefore we * assume that what we have matched is only part of a longer name. * We continue searching. */ continue; } /* Now we have something that matches /[^ ,\t]=/. The value * (following the equal-sign) can be found at value + strlen(name) + 1. */ value += strlen(name) + 1; /* The cookie value may be double-quoted. */ if(*value == '"') { value += 1; } buffer = apr_pstrdup(r->pool, value); end = strchr(buffer, '"'); if(end) { /* Double-quoted string. */ *end = '\0'; } end = strchr(buffer, ';'); if(end) { *end = '\0'; } return buffer; } /* We didn't find the cookie. */ return NULL; } /* This function sets the value of our cookie. * * Parameters: * request_rec *r The request we should set the cookie in. * const char *id The value ve should store in the cookie. * * Returns: * Nothing. */ void am_cookie_set(request_rec *r, const char *id) { am_req_cfg_rec *req_cfg; const char *name; const char *cookie_params; char *cookie; if (id == NULL) return; name = am_cookie_name(r); cookie_params = am_cookie_params(r); cookie = apr_psprintf(r->pool, "%s=%s; %s", name, id, cookie_params); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "cookie_set: %s", cookie); /* Setting the headers inn err_headers_out ensures that they will be * sent for all responses. */ apr_table_addn(r->err_headers_out, "Set-Cookie", cookie); /* Add a note on the current request, to allow us to retrieve this * cookie in the current request. */ req_cfg = am_get_req_cfg(r); req_cfg->cookie_value = apr_pstrdup(r->pool, id); } /* This function deletes the cookie. * * Parameters: * request_rec *r The request we should clear the cookie in. We will * allocate any neccesary memory from r->pool. * * Returns: * Nothing. */ void am_cookie_delete(request_rec *r) { const char *name; const char *cookie_params; char *cookie; name = am_cookie_name(r); cookie_params = am_cookie_params(r); /* Format a cookie. To delete a cookie we set the expires-timestamp * to the past. */ cookie = apr_psprintf(r->pool, "%s=NULL;" " expires=Thu, 01-Jan-1970 00:00:00 GMT;" " %s", name, cookie_params); apr_table_addn(r->err_headers_out, "Set-Cookie", cookie); } /* Get string that is used to tie a session to a specific cookie. * * request_rec *r The current request. * Returns: * The cookie token, as a fixed length byte buffer. */ const char *am_cookie_token(request_rec *r) { const char *cookie_name = am_cookie_name(r); const char *cookie_domain = ap_get_server_name(r); const char *cookie_path = "/"; am_dir_cfg_rec *cfg = am_get_dir_cfg(r); if (cfg->cookie_domain) { cookie_domain = cfg->cookie_domain; } if (cfg->cookie_path) { cookie_path = cfg->cookie_path; } return apr_psprintf(r->pool, "Name='%s' Domain='%s' Path='%s'", cookie_name, cookie_domain, cookie_path ); } mod_auth_mellon-0.16.0/auth_mellon_diagnostics.c0000664002412700241270000011546513607336001021743 0ustar jhrozekjhrozek#include "auth_mellon.h" #ifdef ENABLE_DIAGNOSTICS #if APR_HAVE_UNISTD_H #include #endif #if APR_HAVE_PROCESS_H #include /* for getpid() on Win32 */ #endif /*============================= Internal Static ==============================*/ /*------------------ Defines ------------------*/ #define AM_DIAG_ENABLED(diag_cfg) \ (diag_cfg && diag_cfg->fd && (diag_cfg->flags & AM_DIAG_FLAG_ENABLED)) /*------------------ Typedefs ------------------*/ typedef struct iter_callback_data { apr_file_t *diag_fd; int level; } iter_callback_data; /*------------------ Prototypes ------------------*/ static const char * indent(int level); static void write_indented_text(apr_file_t *diag_fd, int level, const char* text); static void am_diag_format_line(apr_pool_t *pool, apr_file_t *diag_fd, int level, const char *fmt, va_list ap); static const char * am_diag_cond_flag_str(request_rec *r, am_cond_flag_t flags); static const char * am_diag_enable_str(request_rec *r, am_enable_t enable); static const char * am_diag_samesite_str(request_rec *r, am_samesite_t samesite); static const char * am_diag_httpd_error_level_str(request_rec *r, int level); static const char * am_diag_signature_method_str(request_rec *r, LassoSignatureMethod signature_method); static apr_size_t am_diag_time_t_to_8601_buf(char *buf, apr_size_t buf_size, apr_time_t t); static int am_diag_open_log(server_rec *s, apr_pool_t *p); static int am_table_count(void *rec, const char *key, const char *value); static int log_headers(void *rec, const char *key, const char *value); static int log_probe_discovery_idp(void *rec, const char *key, const char *value); static void am_diag_log_dir_cfg(request_rec *r, int level, am_dir_cfg_rec *cfg, const char *fmt, ...) __attribute__((format(printf,4,5))); static bool am_diag_initialize_req(request_rec *r, am_diag_cfg_rec *diag_cfg, am_req_cfg_rec *req_cfg); /*------------------ Functions ------------------*/ static const char * indent(int level) { static const char * const indents[] = { "", /* 0 */ " ", /* 1 */ " ", /* 2 */ " ", /* 3 */ " ", /* 4 */ " ", /* 5 */ " ", /* 6 */ " ", /* 7 */ " ", /* 8 */ " ", /* 9 */ }; int n_indents = sizeof(indents)/sizeof(indents[0]); if (level < 0) { return ""; } if (level < n_indents) { return indents[level]; } return indents[n_indents-1]; } static void write_indented_text(apr_file_t *diag_fd, int level, const char* text) { const char *start, *end, *prefix; size_t len, prefix_len; bool crlf = false; if (!text) return; prefix = indent(level); prefix_len = strlen(prefix); start = end = text; while (*end) { /* find end of line */ for (; *end && *end != '\n'; end++); if (*end == '\n') { /* was this a crlf sequence? */ if (end > text && end[-1] == '\r') crlf = true; /* advance past line ending */ end += 1; } /* length of line including line ending */ len = end - start; /* write indent prefix */ apr_file_write_full(diag_fd, prefix, prefix_len, NULL); /* write line including line ending */ apr_file_write_full(diag_fd, start, len, NULL); /* begin again where we left off */ start = end; } /* always write a trailing line ending */ if (end > text && end[-1] != '\n') { if (crlf) { apr_file_write_full(diag_fd, "\r\n", 2, NULL); } else { apr_file_write_full(diag_fd, "\n", 1, NULL); } } } static void am_diag_format_line(apr_pool_t *pool, apr_file_t *diag_fd, int level, const char *fmt, va_list ap) { char * buf = NULL; apr_size_t buf_len; if (fmt) { buf = apr_pvsprintf(pool, fmt, ap); buf_len = strlen(buf); if (buf_len > 0) { const char *prefix = indent(level); apr_size_t prefix_len = strlen(prefix); apr_file_write_full(diag_fd, prefix, prefix_len, NULL); apr_file_write_full(diag_fd, buf, buf_len, NULL); apr_file_putc('\n', diag_fd); } } } static const char * am_diag_cond_flag_str(request_rec *r, am_cond_flag_t flags) { char *str; char *comma; str = apr_pstrcat(r->pool, "[", flags & AM_COND_FLAG_OR ? "OR," : "", flags & AM_COND_FLAG_NOT ? "NOT," : "", flags & AM_COND_FLAG_REG ? "REG," : "", flags & AM_COND_FLAG_NC ? "NC," : "", flags & AM_COND_FLAG_MAP ? "MAP," : "", flags & AM_COND_FLAG_REF ? "REF," : "", flags & AM_COND_FLAG_SUB ? "SUB," : "", flags & AM_COND_FLAG_IGN ? "IGN," : "", flags & AM_COND_FLAG_REQ ? "REQ," : "", flags & AM_COND_FLAG_FSTR ? "FSTR," : "", "]", NULL); /* replace trailing ",]" with "]" */ comma = rindex(str, ','); if (comma) { *comma = ']'; *(comma+1) = 0; } return str; } static const char * am_diag_enable_str(request_rec *r, am_enable_t enable) { switch(enable) { case am_enable_default: return "default"; case am_enable_off: return "off"; case am_enable_info: return "info"; case am_enable_auth: return "auth"; default: return apr_psprintf(r->pool, "unknown (%d)", enable); } } static const char * am_diag_samesite_str(request_rec *r, am_samesite_t samesite) { switch(samesite) { case am_samesite_default: return "default"; case am_samesite_lax: return "lax"; case am_samesite_strict: return "strict"; case am_samesite_none: return "none"; default: return apr_psprintf(r->pool, "unknown (%d)", samesite); } } static const char * am_diag_httpd_error_level_str(request_rec *r, int level) { switch(level) { case APLOG_EMERG: return "APLOG_EMERG"; case APLOG_ALERT: return "APLOG_ALERT"; case APLOG_CRIT: return "APLOG_CRIT"; case APLOG_ERR: return "APLOG_ERR"; case APLOG_WARNING: return "APLOG_WARNING"; case APLOG_NOTICE: return "APLOG_NOTICE"; case APLOG_INFO: return "APLOG_INFO"; case APLOG_DEBUG: return "APLOG_DEBUG"; case APLOG_TRACE1: return "APLOG_TRACE1"; case APLOG_TRACE2: return "APLOG_TRACE2"; case APLOG_TRACE3: return "APLOG_TRACE3"; case APLOG_TRACE4: return "APLOG_TRACE4"; case APLOG_TRACE5: return "APLOG_TRACE5"; case APLOG_TRACE6: return "APLOG_TRACE6"; case APLOG_TRACE7: return "APLOG_TRACE7"; case APLOG_TRACE8: return "APLOG_TRACE8"; default: return apr_psprintf(r->pool, "APLOG_%d", level); } } static const char * am_diag_signature_method_str(request_rec *r, LassoSignatureMethod signature_method) { switch(signature_method) { case LASSO_SIGNATURE_METHOD_RSA_SHA1: return "rsa-sha1"; #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA256 case LASSO_SIGNATURE_METHOD_RSA_SHA256: return "rsa-sha256"; #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA384 case LASSO_SIGNATURE_METHOD_RSA_SHA384: return "rsa-sha384"; #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA512 case LASSO_SIGNATURE_METHOD_RSA_SHA512: return "rsa-sha512"; #endif default: return apr_psprintf(r->pool, "unknown (%d)", signature_method); } } static apr_size_t am_diag_time_t_to_8601_buf(char *buf, apr_size_t buf_size, apr_time_t t) { apr_size_t ret_size; apr_time_exp_t tm; const char fmt[] = "%FT%TZ"; apr_time_exp_gmt(&tm, t); apr_strftime(buf, &ret_size, buf_size, fmt, &tm); /* on errror assure string is null terminated */ if (ret_size == 0) buf[0] = 0; return ret_size; } static int am_diag_open_log(server_rec *s, apr_pool_t *p) { const char *server_name = NULL; const char *server_desc = NULL; am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(s); /* Build the ServerName as it would appear in the ServerName directive */ if (s->server_scheme) { server_name = apr_psprintf(p, "%s://%s", s->server_scheme, s->server_hostname); } else { server_name = apr_psprintf(p, "%s", s->server_hostname); } if (s->port) { server_name = apr_psprintf(p, "%s:%u", server_name, s->port); } if (s->is_virtual) { server_desc = apr_psprintf(p, "virtual server %s:%d (%s:%u)" " ServerName=%s", s->addrs->virthost, s->addrs->host_port, s->defn_name, s->defn_line_number, server_name); } else { server_desc = apr_psprintf(p, "main server, ServerName=%s", server_name); } if (!(diag_cfg->flags & AM_DIAG_FLAG_ENABLED)) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mellon diagnostics disabled for %s", server_desc); return 1; } else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mellon diagnostics enabled for %s, " "diagnostics filename=%s", server_desc, diag_cfg->filename); } if (!diag_cfg->filename || diag_cfg->fd) return 1; if (*diag_cfg->filename == '|') { piped_log *pl; const char *pname = ap_server_root_relative(p, diag_cfg->filename + 1); pl = ap_open_piped_log(p, pname); if (pl == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "couldn't spawn mellon diagnostics log pipe %s", diag_cfg->filename); return 0; } diag_cfg->fd = ap_piped_log_write_fd(pl); } else { const char *fname = ap_server_root_relative(p, diag_cfg->filename); apr_status_t rv; if ((rv = apr_file_open(&diag_cfg->fd, fname, APR_WRITE | APR_APPEND | APR_CREATE, APR_OS_DEFAULT, p)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "could not open mellon diagnostics log file %s.", fname); return 0; } } return 1; } static int am_table_count(void *rec, const char *key, const char *value) { int *n_items = (int *)rec; (*n_items)++; return 1; } static int log_headers(void *rec, const char *key, const char *value) { iter_callback_data *iter_data = (iter_callback_data *)rec; apr_file_printf(iter_data->diag_fd, "%s%s: %s\n", indent(iter_data->level), key, value); return 1; } static int log_probe_discovery_idp(void *rec, const char *key, const char *value) { iter_callback_data *iter_data = (iter_callback_data *)rec; apr_file_printf(iter_data->diag_fd, "%s%s: %s\n", indent(iter_data->level), key, value); return 1; } static void am_diag_log_dir_cfg(request_rec *r, int level, am_dir_cfg_rec *cfg, const char *fmt, ...) { va_list ap; am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(r->server); am_req_cfg_rec *req_cfg = am_get_req_cfg(r); int i, n_items; apr_hash_index_t *hash_item; GList *list_item; iter_callback_data iter_data; if (!AM_DIAG_ENABLED(diag_cfg)) return; if (!am_diag_initialize_req(r, diag_cfg, req_cfg)) return; iter_data.diag_fd = diag_cfg->fd; iter_data.level = level+1; va_start(ap, fmt); am_diag_format_line(r->pool, diag_cfg->fd, level, fmt, ap); va_end(ap); if (!cfg) { apr_file_flush(diag_cfg->fd); return; } apr_file_printf(diag_cfg->fd, "%sMellonEnable (enable): %s\n", indent(level+1), am_diag_enable_str(r, cfg->enable_mellon)); apr_file_printf(diag_cfg->fd, "%sMellonVariable (varname): %s\n", indent(level+1), cfg->varname); apr_file_printf(diag_cfg->fd, "%sMellonSecureCookie (secure): %s\n", indent(level+1), cfg->secure ? "On":"Off"); /* FIXME, should be combined? */ apr_file_printf(diag_cfg->fd, "%sMellonSecureCookie (httpd_only): %s\n", indent(level+1), cfg->http_only ? "On":"Off"); apr_file_printf(diag_cfg->fd, "%sMellonMergeEnvVars (merge_env_vars): %s\n", indent(level+1), cfg->merge_env_vars); apr_file_printf(diag_cfg->fd, "%sMellonEnvVarsIndexStart (env_vars_index_start): %d\n", indent(level+1), cfg->env_vars_index_start); apr_file_printf(diag_cfg->fd, "%sMellonEnvVarsSetCount (env_vars_count_in_n): %s\n", indent(level+1), cfg->env_vars_count_in_n ? "On":"Off"); apr_file_printf(diag_cfg->fd, "%sMellonCookieDomain (cookie_domain): %s\n", indent(level+1), cfg->cookie_domain); apr_file_printf(diag_cfg->fd, "%sMellonCookiePath (cookie_path): %s\n", indent(level+1), cfg->cookie_path); apr_file_printf(diag_cfg->fd, "%sMellonCookieSameSite (cookie_samesite): %s\n", indent(level+1), am_diag_samesite_str(r, cfg->cookie_samesite)); apr_file_printf(diag_cfg->fd, "%sMellonEnvPrefix (env_prefix): %s\n", indent(level+1), cfg->env_prefix); apr_file_printf(diag_cfg->fd, "%sMellonCond (cond): %d items\n", indent(level+1), cfg->cond->nelts); for (i = 0; i < cfg->cond->nelts; i++) { const am_cond_t *cond = &((am_cond_t *)(cfg->cond->elts))[i]; apr_file_printf(diag_cfg->fd, "%s[%2d]: %s\n", indent(level+2), i, am_diag_cond_str(r, cond)); } apr_file_printf(diag_cfg->fd, "%sMellonSetEnv (envattr): %u items\n", indent(level+1), apr_hash_count(cfg->envattr)); for (hash_item = apr_hash_first(r->pool, cfg->envattr); hash_item; hash_item = apr_hash_next(hash_item)) { const char *key; const am_envattr_conf_t *envattr_conf; const char *name; apr_hash_this(hash_item, (void *)&key, NULL, (void *)&envattr_conf); if (envattr_conf->prefixed) { name = apr_pstrcat(r->pool, cfg->env_prefix, envattr_conf->name, NULL); } else { name = envattr_conf->name; } apr_file_printf(diag_cfg->fd, "%s%s ==> %s\n", indent(level+2), key, name); } apr_file_printf(diag_cfg->fd, "%sMellonUser (userattr): %s\n", indent(level+1), cfg->userattr); apr_file_printf(diag_cfg->fd, "%sMellonIdP (idpattr): %s\n", indent(level+1), cfg->idpattr); apr_file_printf(diag_cfg->fd, "%sMellonSessionDump (dump_session): %s\n", indent(level+1), cfg->dump_session ? "On":"Off"); apr_file_printf(diag_cfg->fd, "%sMellonSamlResponseDump (dump_saml_response): %s\n", indent(level+1), cfg->dump_saml_response ? "On":"Off"); apr_file_printf(diag_cfg->fd, "%sMellonEndpointPath (endpoint_path): %s\n", indent(level+1), cfg->endpoint_path); am_diag_log_file_data(r, level+1, cfg->sp_metadata_file, "MellonSPMetadataFile (sp_metadata_file):"); am_diag_log_file_data(r, level+1, cfg->sp_private_key_file, "MellonSPPrivateKeyFile (sp_private_key_file):"); am_diag_log_file_data(r, level+1, cfg->sp_cert_file, "MellonSPCertFile (sp_cert_file):"); am_diag_log_file_data(r, level+1, cfg->idp_public_key_file, "MellonIdPPublicKeyFile (idp_public_key_file):"); am_diag_log_file_data(r, level+1, cfg->idp_ca_file, "MellonIdPCAFile (idp_ca_file):"); apr_file_printf(diag_cfg->fd, "%sMellonIdPMetadataFile (idp_metadata): %d items\n", indent(level+1), cfg->idp_metadata->nelts); for (i = 0; i < cfg->idp_metadata->nelts; i++) { const am_metadata_t *idp_metadata; idp_metadata = &(((const am_metadata_t*)cfg->idp_metadata->elts)[i]); am_diag_log_file_data(r, level+1, idp_metadata->metadata, "[%2d] Metadata", i); am_diag_log_file_data(r, level+1, idp_metadata->chain, "[%2d] Chain File", i); } apr_file_printf(diag_cfg->fd, "%sMellonIdPIgnore (idp_ignore):\n", indent(level+1)); for (list_item = cfg->idp_ignore, i = 0; list_item; list_item = g_list_next(list_item), i++) { apr_file_printf(diag_cfg->fd, "%s[%2d]: %s\n", indent(level+2), i, (char *)list_item->data); } apr_file_printf(diag_cfg->fd, "%sMellonSPentityId (sp_entity_id): %s\n", indent(level+1), cfg->sp_entity_id); apr_file_printf(diag_cfg->fd, "%sMellonOrganizationName (sp_org_name): %u items\n", indent(level+1), apr_hash_count(cfg->sp_org_name)); for (hash_item = apr_hash_first(r->pool, cfg->sp_org_name); hash_item; hash_item = apr_hash_next(hash_item)) { const char *lang; const char *value; apr_hash_this(hash_item, (void *)&lang, NULL, (void *)&value); apr_file_printf(diag_cfg->fd, "%s(lang=%s): %s\n", indent(level+2), lang, value); } apr_file_printf(diag_cfg->fd, "%sMellonOrganizationDisplayName (sp_org_display_name):" " %u items\n", indent(level+1), apr_hash_count(cfg->sp_org_display_name)); for (hash_item = apr_hash_first(r->pool, cfg->sp_org_display_name); hash_item; hash_item = apr_hash_next(hash_item)) { const char *lang; const char *value; apr_hash_this(hash_item, (void *)&lang, NULL, (void *)&value); apr_file_printf(diag_cfg->fd, "%s(lang=%s): %s\n", indent(level+2), lang, value); } apr_file_printf(diag_cfg->fd, "%sMellonOrganizationURL (sp_org_url): %u items\n", indent(level+1), apr_hash_count(cfg->sp_org_url)); for (hash_item = apr_hash_first(r->pool, cfg->sp_org_url); hash_item; hash_item = apr_hash_next(hash_item)) { const char *lang; const char *value; apr_hash_this(hash_item, (void *)&lang, NULL, (void *)&value); apr_file_printf(diag_cfg->fd, "%s(lang=%s): %s\n", indent(level+2), lang, value); } apr_file_printf(diag_cfg->fd, "%sMellonSessionLength (session_length): %d\n", indent(level+1), cfg->session_length); apr_file_printf(diag_cfg->fd, "%sMellonNoCookieErrorPage (no_cookie_error_page): %s\n", indent(level+1), cfg->no_cookie_error_page); apr_file_printf(diag_cfg->fd, "%sMellonNoSuccessErrorPage (no_success_error_page): %s\n", indent(level+1), cfg->no_success_error_page); apr_file_printf(diag_cfg->fd, "%sMellonDefaultLoginPath (login_path): %s\n", indent(level+1), cfg->login_path); apr_file_printf(diag_cfg->fd, "%sMellonDiscoveryURL (discovery_url): %s\n", indent(level+1), cfg->discovery_url); apr_file_printf(diag_cfg->fd, "%sMellonProbeDiscoveryTimeout (probe_discovery_timeout):" " %d\n", indent(level+1), cfg->probe_discovery_timeout); n_items = 0; apr_table_do(am_table_count, &n_items, cfg->probe_discovery_idp, NULL); apr_file_printf(diag_cfg->fd, "%sMellonProbeDiscoveryIdP (probe_discovery_idp):" " %d items\n", indent(level+1), n_items); apr_table_do(log_probe_discovery_idp, &iter_data, cfg->probe_discovery_idp, NULL); apr_file_printf(diag_cfg->fd, "%sMellonAuthnContextClassRef (authn_context_class_ref):" " %d items\n", indent(level+1), cfg->authn_context_class_ref->nelts); for(i = 0; i < cfg->authn_context_class_ref->nelts; i++) { const char *context_class; context_class = APR_ARRAY_IDX(cfg->authn_context_class_ref, i, char *); apr_file_printf(diag_cfg->fd, "%s[%2d]: %s\n", indent(level+2), i, context_class); } apr_file_printf(diag_cfg->fd, "%sMellonAuthnContextComparisonType (authn_context_comparison_type): %s\n", indent(level+1), cfg->authn_context_comparison_type); apr_file_printf(diag_cfg->fd, "%sMellonSubjectConfirmationDataAddressCheck" " (subject_confirmation_data_address_check): %s\n", indent(level+1), CFG_VALUE(cfg, subject_confirmation_data_address_check) ? "On":"Off"); apr_file_printf(diag_cfg->fd, "%sMellonDoNotVerifyLogoutSignature" " (do_not_verify_logout_signature): %u items\n", indent(level+1), apr_hash_count(cfg->do_not_verify_logout_signature)); for (hash_item = apr_hash_first(r->pool, cfg->do_not_verify_logout_signature); hash_item; hash_item = apr_hash_next(hash_item)) { const char *entity_id; apr_hash_this(hash_item, (void *)&entity_id, NULL, NULL); apr_file_printf(diag_cfg->fd, "%s%s\n", indent(level+2), entity_id); } apr_file_printf(diag_cfg->fd, "%sMellonSendCacheControlHeader" " (send_cache_control_header): %s\n", indent(level+1), CFG_VALUE(cfg, send_cache_control_header) ? "On":"Off"); apr_file_printf(diag_cfg->fd, "%sMellonPostReplay (post_replay): %s\n", indent(level+1), CFG_VALUE(cfg, post_replay) ? "On":"Off"); apr_file_printf(diag_cfg->fd, "%sMellonECPSendIDPList (ecp_send_idplist): %s\n", indent(level+1), CFG_VALUE(cfg, ecp_send_idplist) ? "On":"Off"); for (n_items = 0; cfg->redirect_domains[n_items] != NULL; n_items++); apr_file_printf(diag_cfg->fd, "%sMellonRedirectDomains (redirect_domains): %d items\n", indent(level+1), n_items); for (i = 0; cfg->redirect_domains[i] != NULL; i++) { apr_file_printf(diag_cfg->fd, "%s%s\n", indent(level+2), cfg->redirect_domains[i]); } apr_file_printf(diag_cfg->fd, "%sMellonSignatureMethod (signature_method): %s\n", indent(level+1), am_diag_signature_method_str(r, CFG_VALUE(cfg, signature_method))); apr_file_flush(diag_cfg->fd); } static bool am_diag_initialize_req(request_rec *r, am_diag_cfg_rec *diag_cfg, am_req_cfg_rec *req_cfg) { server_rec *s = r->server; am_dir_cfg_rec *dir_cfg; apr_os_thread_t tid = apr_os_thread_current(); iter_callback_data iter_data; int level = 0; if (!diag_cfg) return false; if (!diag_cfg->fd) return false; if (!req_cfg) return false; if (req_cfg->diag_emitted) return true; iter_data.diag_fd = diag_cfg->fd; iter_data.level = level+1; apr_file_puts("---------------------------------- New Request" " ---------------------------------\n", diag_cfg->fd); apr_file_printf(diag_cfg->fd, "%s - %s\n", r->method, r->uri); apr_file_printf(diag_cfg->fd, "log_id: %s\n", r->log_id); apr_file_printf(diag_cfg->fd, "server: scheme=%s hostname=%s port=%d\n", s->server_scheme, s->server_hostname, s->port); apr_file_printf(diag_cfg->fd, "pid: %" APR_PID_T_FMT ", tid: %pT\n", getpid(), &tid); apr_file_printf(diag_cfg->fd, "unparsed_uri: %s\n", r->unparsed_uri); apr_file_printf(diag_cfg->fd, "uri: %s\n", r->uri); apr_file_printf(diag_cfg->fd, "path_info: %s\n", r->path_info); apr_file_printf(diag_cfg->fd, "filename: %s\n", r->filename); apr_file_printf(diag_cfg->fd, "query args: %s\n", r->args); apr_file_printf(diag_cfg->fd, "Request Headers:\n"); apr_table_do(log_headers, &iter_data, r->headers_in, NULL); req_cfg->diag_emitted = true; /* Only emit directory configuration once */ if (!apr_table_get(diag_cfg->dir_cfg_emitted, r->uri)) { dir_cfg = am_get_dir_cfg(r); am_diag_log_dir_cfg(r, level, dir_cfg, "Mellon Directory Configuration for URL: %s", r->uri); apr_table_set(diag_cfg->dir_cfg_emitted, r->uri, "1"); } return true; } /*=============================== Public API =================================*/ int am_diag_log_init(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, server_rec *s) { for ( ; s ; s = s->next) { if (!am_diag_open_log(s, p)) { return HTTP_INTERNAL_SERVER_ERROR; } } return OK; } int am_diag_finalize_request(request_rec *r) { am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(r->server); am_req_cfg_rec *req_cfg = am_get_req_cfg(r); int level = 0; iter_callback_data iter_data; if (!AM_DIAG_ENABLED(diag_cfg)) return OK; if (!req_cfg) return OK; if (!req_cfg->diag_emitted) return OK; iter_data.diag_fd = diag_cfg->fd; iter_data.level = level+1; apr_file_puts("\n=== Response ===\n", diag_cfg->fd); apr_file_printf(diag_cfg->fd, "Status: %s(%d)\n", r->status_line, r->status); apr_file_printf(diag_cfg->fd, "user: %s auth_type=%s\n", r->user, r->ap_auth_type); apr_file_printf(diag_cfg->fd, "Response Headers:\n"); apr_table_do(log_headers, &iter_data, r->headers_out, NULL); apr_file_printf(diag_cfg->fd, "Response Error Headers:\n"); apr_table_do(log_headers, &iter_data, r->err_headers_out, NULL); apr_file_printf(diag_cfg->fd, "Environment:\n"); apr_table_do(log_headers, &iter_data, r->subprocess_env, NULL); return OK; } char * am_diag_time_t_to_8601(request_rec *r, apr_time_t t) { char *buf; buf = apr_palloc(r->pool, ISO_8601_BUF_SIZE); if (!buf) return NULL; am_diag_time_t_to_8601_buf(buf, ISO_8601_BUF_SIZE, t); return buf; } const char * am_diag_cond_str(request_rec *r, const am_cond_t *cond) { return apr_psprintf(r->pool, "varname=\"%s\" flags=%s str=\"%s\" directive=\"%s\"", cond->varname, am_diag_cond_flag_str(r, cond->flags), cond->str, cond->directive); } const char * am_diag_cache_key_type_str(am_cache_key_t key_type) { switch(key_type) { case AM_CACHE_SESSION: return "session"; case AM_CACHE_NAMEID : return "name id"; default: return "unknown"; } } const char * am_diag_lasso_http_method_str(LassoHttpMethod http_method) { switch(http_method) { case LASSO_HTTP_METHOD_NONE: return "LASSO_HTTP_METHOD_NONE"; case LASSO_HTTP_METHOD_ANY: return "LASSO_HTTP_METHOD_ANY"; case LASSO_HTTP_METHOD_IDP_INITIATED: return "LASSO_HTTP_METHOD_IDP_INITIATED"; case LASSO_HTTP_METHOD_GET: return "LASSO_HTTP_METHOD_GET"; case LASSO_HTTP_METHOD_POST: return "LASSO_HTTP_METHOD_POST"; case LASSO_HTTP_METHOD_REDIRECT: return "LASSO_HTTP_METHOD_REDIRECT"; case LASSO_HTTP_METHOD_SOAP: return "LASSO_HTTP_METHOD_SOAP"; case LASSO_HTTP_METHOD_ARTIFACT_GET: return "LASSO_HTTP_METHOD_ARTIFACT_GET"; case LASSO_HTTP_METHOD_ARTIFACT_POST: return "LASSO_HTTP_METHOD_ARTIFACT_POST"; case LASSO_HTTP_METHOD_PAOS: return "LASSO_HTTP_METHOD_PAOS"; default: return "unknown"; } } void am_diag_printf(request_rec *r, const char *fmt, ...) { va_list ap; am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(r->server); am_req_cfg_rec *req_cfg = am_get_req_cfg(r); char *buf; apr_size_t buf_len; if (!AM_DIAG_ENABLED(diag_cfg)) return; if (!am_diag_initialize_req(r, diag_cfg, req_cfg)) return; va_start(ap, fmt); buf = apr_pvsprintf(r->pool, fmt, ap); va_end(ap); buf_len = strlen(buf); if (buf_len > 0) { apr_file_write_full(diag_cfg->fd, buf, buf_len, NULL); } apr_file_flush(diag_cfg->fd); } void am_diag_rerror(const char *file, int line, int module_index, int level, apr_status_t status, request_rec *r, const char *fmt, ...) { va_list ap; am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(r->server); am_req_cfg_rec *req_cfg = am_get_req_cfg(r); char *buf; if (!AM_DIAG_ENABLED(diag_cfg)) return; if (!am_diag_initialize_req(r, diag_cfg, req_cfg)) return; buf = apr_psprintf(r->pool, "[%s %s:%d] ", am_diag_httpd_error_level_str(r, level), file, line); apr_file_puts(buf, diag_cfg->fd); va_start(ap, fmt); buf = apr_pvsprintf(r->pool, fmt, ap); va_end(ap); apr_file_puts(buf, diag_cfg->fd); apr_file_puts(APR_EOL_STR, diag_cfg->fd); apr_file_flush(diag_cfg->fd); } void am_diag_log_lasso_node(request_rec *r, int level, LassoNode *node, const char *fmt, ...) { va_list ap; am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(r->server); am_req_cfg_rec *req_cfg = am_get_req_cfg(r); gchar *xml = NULL; if (!AM_DIAG_ENABLED(diag_cfg)) return; if (!am_diag_initialize_req(r, diag_cfg, req_cfg)) return; va_start(ap, fmt); am_diag_format_line(r->pool, diag_cfg->fd, level, fmt, ap); va_end(ap); if (node) { xml = lasso_node_debug(node, 0); write_indented_text(diag_cfg->fd, level+1, xml); lasso_release_string(xml); } else { apr_file_printf(diag_cfg->fd, "%snode is NULL\n", indent(level+1)); } apr_file_flush(diag_cfg->fd); } void am_diag_log_file_data(request_rec *r, int level, am_file_data_t *file_data, const char *fmt, ...) { va_list ap; am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(r->server); am_req_cfg_rec *req_cfg = am_get_req_cfg(r); if (!AM_DIAG_ENABLED(diag_cfg)) return; if (!am_diag_initialize_req(r, diag_cfg, req_cfg)) return; va_start(ap, fmt); am_diag_format_line(r->pool, diag_cfg->fd, level, fmt, ap); va_end(ap); if (file_data) { if (file_data->generated) { apr_file_printf(diag_cfg->fd, "%sGenerated file contents:\n", indent(level+1)); write_indented_text(diag_cfg->fd, level+2, file_data->contents); } else { apr_file_printf(diag_cfg->fd, "%spathname: \"%s\"\n", indent(level+1), file_data->path); if (!file_data->read_time) { am_file_read(file_data); } if (file_data->rv == APR_SUCCESS) { write_indented_text(diag_cfg->fd, level+2, file_data->contents); } else { apr_file_printf(diag_cfg->fd, "%s%s\n", indent(level+1), file_data->strerror); } } } else { apr_file_printf(diag_cfg->fd, "%sfile_data: NULL\n", indent(level+1)); } apr_file_flush(diag_cfg->fd); } void am_diag_log_saml_status_response(request_rec *r, int level, LassoNode *node, const char *fmt, ...) { va_list ap; am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(r->server); am_req_cfg_rec *req_cfg = am_get_req_cfg(r); LassoSamlp2StatusResponse *response = (LassoSamlp2StatusResponse*)node; LassoSamlp2Status *status = NULL; const char *status_code1 = NULL; const char *status_code2 = NULL; if (!AM_DIAG_ENABLED(diag_cfg)) return; if (!am_diag_initialize_req(r, diag_cfg, req_cfg)) return; va_start(ap, fmt); am_diag_format_line(r->pool, diag_cfg->fd, level, fmt, ap); va_end(ap); if (response == NULL) { apr_file_printf(diag_cfg->fd, "%sresponse is NULL\n", indent(level+1)); return; } if (!LASSO_IS_SAMLP2_STATUS_RESPONSE(response)) { apr_file_printf(diag_cfg->fd, "%sERROR, expected LassoSamlp2StatusResponse " "but got %s\n", indent(level+1), lasso_node_get_name((LassoNode*)response)); return; } status = response->Status; if (status == NULL || !LASSO_IS_SAMLP2_STATUS(status) || status->StatusCode == NULL || status->StatusCode->Value == NULL) { apr_file_printf(diag_cfg->fd, "%sStatus missing\n", indent(level+1)); return; } status_code1 = status->StatusCode->Value; if (status->StatusCode->StatusCode) { status_code2 = status->StatusCode->StatusCode->Value; } apr_file_printf(diag_cfg->fd, "%sID: %s\n", indent(level+1), response->ID); apr_file_printf(diag_cfg->fd, "%sInResponseTo: %s\n", indent(level+1), response->InResponseTo); apr_file_printf(diag_cfg->fd, "%sVersion: %s\n", indent(level+1), response->Version); apr_file_printf(diag_cfg->fd, "%sIssueInstant: %s\n", indent(level+1), response->IssueInstant); apr_file_printf(diag_cfg->fd, "%sConsent: %s\n", indent(level+1), response->Consent); apr_file_printf(diag_cfg->fd, "%sIssuer: %s\n", indent(level+1), response->Issuer->content); apr_file_printf(diag_cfg->fd, "%sDestination: %s\n", indent(level+1), response->Destination); apr_file_printf(diag_cfg->fd, "%sStatus:\n", indent(level+1)); apr_file_printf(diag_cfg->fd, "%sTop Level Status code: %s\n", indent(level+2), status_code1); apr_file_printf(diag_cfg->fd, "%s2nd Level Status code: %s\n", indent(level+2), status_code2); apr_file_printf(diag_cfg->fd, "%sStatus Message: %s\n", indent(level+2), status->StatusMessage); am_diag_log_lasso_node(r, level+2, (LassoNode*)status->StatusDetail, "Status Detail:"); return; } void am_diag_log_profile(request_rec *r, int level, LassoProfile *profile, const char *fmt, ...) { va_list ap; am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(r->server); am_req_cfg_rec *req_cfg = am_get_req_cfg(r); LassoSession *session = lasso_profile_get_session(profile); GList *assertions = lasso_session_get_assertions(session, NULL); GList *iter = NULL; int i; if (!AM_DIAG_ENABLED(diag_cfg)) return; if (!am_diag_initialize_req(r, diag_cfg, req_cfg)) return; va_start(ap, fmt); am_diag_format_line(r->pool, diag_cfg->fd, level, fmt, ap); va_end(ap); if (profile) { apr_file_printf(diag_cfg->fd, "%sProfile Type: %s\n", indent(level+1), G_OBJECT_TYPE_NAME(profile)); for (iter = assertions, i=0; iter != NULL; iter = g_list_next(iter), i++) { LassoSaml2Assertion *assertion = NULL; assertion = LASSO_SAML2_ASSERTION(iter->data); if (!LASSO_IS_SAML2_ASSERTION(assertion)) { apr_file_printf(diag_cfg->fd, "%sObject at index %d in session assertion" " list is not LassoSaml2Assertion", indent(level+1), i); } else { am_diag_log_lasso_node(r, level+1, &assertion->parent, "Assertion %d", i); } } } else { apr_file_printf(diag_cfg->fd, "%sprofile is NULL\n", indent(level+1)); } apr_file_flush(diag_cfg->fd); } void am_diag_log_cache_entry(request_rec *r, int level, am_cache_entry_t *entry, const char *fmt, ...) { va_list ap; am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(r->server); am_req_cfg_rec *req_cfg = am_get_req_cfg(r); const char *name_id = NULL; if (!AM_DIAG_ENABLED(diag_cfg)) return; if (!am_diag_initialize_req(r, diag_cfg, req_cfg)) return; va_start(ap, fmt); am_diag_format_line(r->pool, diag_cfg->fd, level, fmt, ap); va_end(ap); if (entry) { name_id = am_cache_env_fetch_first(entry, "NAME_ID"); apr_file_printf(diag_cfg->fd, "%skey: %s\n", indent(level+1), entry->key); apr_file_printf(diag_cfg->fd, "%sname_id: %s\n", indent(level+1), name_id); apr_file_printf(diag_cfg->fd, "%sexpires: %s\n", indent(level+1), am_diag_time_t_to_8601(r, entry->expires)); apr_file_printf(diag_cfg->fd, "%saccess: %s\n", indent(level+1), am_diag_time_t_to_8601(r, entry->access)); apr_file_printf(diag_cfg->fd, "%slogged_in: %s\n", indent(level+1), entry->logged_in ? "True" : "False"); } else { apr_file_printf(diag_cfg->fd, "%sentry is NULL\n", indent(level+1)); } apr_file_flush(diag_cfg->fd); } #endif /* ENABLE_DIAGNOSTICS */ mod_auth_mellon-0.16.0/auth_mellon_handler.c0000664002412700241270000040435413607335622021057 0ustar jhrozekjhrozek/* * * 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 APLOG_USE_MODULE APLOG_USE_MODULE(auth_mellon); #endif /* * Note: * * Information on PAOS ECP vs. Web SSO flow processing can be found in * the ECP.rst file. */ #ifdef HAVE_lasso_server_new_from_buffers /* 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; am_diag_printf(r, "Generating SP metadata\n"); sp_entity_id = cfg->sp_entity_id ? cfg->sp_entity_id : url; if (cfg->sp_cert_file && cfg->sp_cert_file->contents) { 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->contents); cp = strstr(sp_cert_file, begin); if (cp != NULL) sp_cert_file = cp + strlen(begin); cp = strstr(sp_cert_file, end); if (cp != NULL) *cp = '\0'; /* * And remove any non printing char (CR, spaces...) */ bp = sp_cert_file; for (cp = sp_cert_file; *cp; cp++) { if (apr_isgraph(*cp)) *bp++ = *cp; } *bp = '\0'; cert = apr_psprintf(p, "" "" "" "%s" "" "" "" "" "" "" "%s" "" "" "", sp_cert_file, sp_cert_file); } return apr_psprintf(p, "\n\ \n\ \n\ %s\ \n\ \n\ urn:oasis:names:tc:SAML:2.0:nameid-format:transient\n\ \n\ \n\ \n\ \n\ %s\n\ ", sp_entity_id, cfg->sp_entity_id ? "" : "metadata", cert, url, url, url, url, url, am_optional_metadata(p, r)); } #endif /* HAVE_lasso_server_new_from_buffers */ /* * This function loads all IdP metadata in a lasso server * * Parameters: * am_dir_cfg_rec *cfg The server configuration. * request_rec *r The request we received. * * Returns: * number of loaded providers */ static guint am_server_add_providers(am_dir_cfg_rec *cfg, request_rec *r) { apr_size_t index; #ifndef HAVE_lasso_server_load_metadata const char *idp_public_key_file; if (cfg->idp_metadata->nelts == 1) idp_public_key_file = cfg->idp_public_key_file ? cfg->idp_public_key_file->path : NULL; else idp_public_key_file = NULL; #endif /* ! HAVE_lasso_server_load_metadata */ if (cfg->idp_metadata->nelts == 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error, URI \"%s\" has no IdP's defined", r->uri); return 0; } 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] ); am_diag_log_file_data(r, 0, idp_metadata->metadata, "Loading IdP Metadata"); if (idp_metadata->chain) { am_diag_log_file_data(r, 0, idp_metadata->chain, "Loading IdP metadata chain"); } #ifdef HAVE_lasso_server_load_metadata error = lasso_server_load_metadata(cfg->server, LASSO_PROVIDER_ROLE_IDP, idp_metadata->metadata->path, idp_metadata->chain ? idp_metadata->chain->path : NULL, 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) { AM_LOG_RERROR(APLOG_MARK, APLOG_DEBUG, 0, r, "loaded IdP \"%s\" from \"%s\".", (char *)idx->data, idp_metadata->metadata->path); } } if (loaded_idp != NULL) { for (GList *idx = loaded_idp; idx != NULL; idx = idx->next) { g_free(idx->data); } g_list_free(loaded_idp); } #else /* HAVE_lasso_server_load_metadata */ error = lasso_server_add_provider(cfg->server, LASSO_PROVIDER_ROLE_IDP, idp_metadata->metadata->path, idp_public_key_file, cfg->idp_ca_file ? cfg->idp_ca_file->path : NULL); #endif /* HAVE_lasso_server_load_metadata */ if (error != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error adding metadata \"%s\" to " "lasso server objects. Lasso error: [%i] %s", idp_metadata->metadata->path, error, 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_file_data_new(pool, NULL); cfg->sp_metadata_file->rv = APR_SUCCESS; cfg->sp_metadata_file->generated = true; cfg->sp_metadata_file->contents = am_generate_metadata(pool, r); #else AM_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 */ } #ifdef HAVE_lasso_server_new_from_buffers cfg->server = lasso_server_new_from_buffers(cfg->sp_metadata_file->contents, cfg->sp_private_key_file ? cfg->sp_private_key_file->contents : NULL, NULL, cfg->sp_cert_file ? cfg->sp_cert_file->contents : NULL); #else cfg->server = lasso_server_new(cfg->sp_metadata_file->path, cfg->sp_private_key_file ? cfg->sp_private_key_file->path : NULL, NULL, cfg->sp_cert_file ? cfg->sp_cert_file->path : NULL); #endif if (cfg->server == NULL) { AM_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) { AM_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; } cfg->server->signature_method = CFG_VALUE(cfg, signature_method); } 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)); AM_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)); AM_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) { AM_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) { AM_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) { AM_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) { AM_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) { AM_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) { AM_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 */ AM_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 { AM_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) { AM_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) { AM_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) { AM_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); } } am_diag_log_cache_entry(r, 0, am_session, "%s: Session Cache Entry", __func__); am_diag_log_profile(r, 0, profile, "%s: Restored Profile", __func__); } /* This function handles an IdP initiated logout request. * * Parameters: * request_rec *r The logout request. * LassoLogout *logout A LassoLogout object initiated with * the current session. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_handle_logout_request(request_rec *r, LassoLogout *logout, char *msg) { gint res = 0, rc = HTTP_OK; am_cache_entry_t *session = NULL; am_dir_cfg_rec *cfg = am_get_dir_cfg(r); am_diag_printf(r, "enter function %s\n", __func__); /* 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 && logout->parent.remote_providerID != NULL) { 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) { AM_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)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error processing logout request message." " No NameID found"); rc = HTTP_BAD_REQUEST; goto exit; } am_diag_printf(r, "%s name id %s\n", __func__, ((LassoSaml2NameID*)logout->parent.nameIdentifier)->content); session = am_get_request_session_by_nameid(r, ((LassoSaml2NameID*)logout->parent.nameIdentifier)->content); if (session == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error processing logout request message." " No session found for NameID %s", ((LassoSaml2NameID*)logout->parent.nameIdentifier)->content); } am_diag_log_cache_entry(r, 0, session, "%s", __func__); if (session == NULL) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "Error validating logout request." " Lasso error: [%i] %s", res, lasso_strerror(res)); rc = HTTP_INTERNAL_SERVER_ERROR; goto exit; } /* We continue with the logout despite those errors. They could be * caused by the IdP believing that we are logged in when we are not. */ if (session != NULL && res != LASSO_PROFILE_ERROR_SESSION_NOT_FOUND) { /* We found a matching session -- delete it. */ am_delete_request_session(r, session); session = NULL; } /* Create response message. */ res = lasso_logout_build_response_msg(logout); if(res != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error building logout response message." " Lasso error: [%i] %s", res, lasso_strerror(res)); rc = HTTP_INTERNAL_SERVER_ERROR; goto exit; } rc = am_return_logout_response(r, &logout->parent); exit: if (session != NULL) { am_release_request_session(r, session); } lasso_logout_destroy(logout); return rc; } /* This function handles an invalidate request. * * Parameters: * request_rec *r The logout request. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_handle_invalidate_request(request_rec *r) { int rc; char *return_to; am_cache_entry_t *session = am_get_request_session(r); am_dir_cfg_rec *cfg = am_get_dir_cfg(r); /* Check if the session invalidation endpoint is enabled. */ if (cfg->enabled_invalidation_session == 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Session Invalidation Endpoint is not enabled."); return HTTP_BAD_REQUEST; } am_diag_printf(r, "enter function %s\n", __func__); am_diag_log_cache_entry(r, 0, session, "%s\n", __func__); return_to = am_extract_query_parameter(r->pool, r->args, "ReturnTo"); if (return_to == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "No ReturnTo parameter provided for invalidate handler."); return HTTP_BAD_REQUEST; } /* Check for bad characters in ReturnTo. */ rc = am_check_url(r, return_to); if (rc != OK) { return rc; } rc = am_urldecode(return_to); if (rc != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r, "Could not urldecode ReturnTo value in invalidate" " response."); return HTTP_BAD_REQUEST; } /* Make sure that it is a valid redirect URL. */ rc = am_validate_redirect_url(r, return_to); if (rc != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in invalidate response ReturnTo parameter."); return rc; } if (session == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error processing invalidate request message." " No session found."); } else { am_delete_request_session(r, session); } apr_table_setn(r->headers_out, "Location", return_to); return HTTP_SEE_OTHER; } /* 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); am_diag_log_lasso_node(r, 0, LASSO_PROFILE(logout)->response, "SAML Response (%s):", __func__); #ifdef HAVE_lasso_profile_set_signature_verify_hint if(res != 0 && res != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND && logout->parent.remote_providerID != NULL) { 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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Unable to process logout response." " Lasso error: [%i] %s, SAML Response: %s", res, lasso_strerror(res), am_saml_response_status_str(r, LASSO_PROFILE(logout)->response)); lasso_logout_destroy(logout); return HTTP_BAD_REQUEST; } lasso_logout_destroy(logout); /* Delete the session. */ session = am_get_request_session(r); am_diag_log_cache_entry(r, 0, session, "%s\n", __func__); if(session != NULL) { am_delete_request_session(r, session); } return_to = am_extract_query_parameter(r->pool, r->args, "RelayState"); if(return_to == NULL) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r, "Could not urldecode RelayState value in logout" " response."); return HTTP_BAD_REQUEST; } /* Check for bad characters in RelayState. */ rc = am_check_url(r, return_to); if (rc != OK) { return rc; } /* Make sure that it is a valid redirect URL. */ rc = am_validate_redirect_url(r, return_to); if (rc != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in logout response RelayState parameter."); return rc; } apr_table_setn(r->headers_out, "Location", return_to); return HTTP_SEE_OTHER; } /* This function initiates a logout request and sends it to the IdP. * * Parameters: * request_rec *r The logout response request. * LassoLogout *logout A LassoLogout object initiated with * the current session. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_init_logout_request(request_rec *r, LassoLogout *logout) { char *return_to; int rc; am_cache_entry_t *mellon_session; gint res; char *redirect_to; LassoProfile *profile; LassoSession *session; GList *assertion_list; LassoNode *assertion_n; LassoSaml2Assertion *assertion; LassoSaml2AuthnStatement *authnStatement; LassoSamlp2LogoutRequest *request; return_to = am_extract_query_parameter(r->pool, r->args, "ReturnTo"); rc = am_urldecode(return_to); if (rc != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r, "Could not urldecode ReturnTo value."); return HTTP_BAD_REQUEST; } rc = am_validate_redirect_url(r, return_to); if (rc != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in logout request ReturnTo parameter."); return rc; } /* Disable the the local session (in case the IdP doesn't respond). */ mellon_session = am_get_request_session(r); if(mellon_session != NULL) { am_restore_lasso_profile_state(r, &logout->parent, mellon_session); mellon_session->logged_in = 0; am_release_request_session(r, mellon_session); } /* Create the logout request message. */ res = lasso_logout_init_request(logout, NULL, LASSO_HTTP_METHOD_REDIRECT); /* Early non failing return. */ if (res != 0) { if(res == LASSO_PROFILE_ERROR_SESSION_NOT_FOUND) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "Current identity provider " "does not support single logout. Destroying local session only."); } else if(res != 0) { AM_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) { AM_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) { AM_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) { AM_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) { AM_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) { AM_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. */ AM_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 handles requests to the invalidate handler. * * Parameters: * request_rec *r The request. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_handle_invalidate(request_rec *r) { LassoServer *server; server = am_get_lasso_server(r); if (server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } return am_handle_invalidate_request(r); } /* 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){ AM_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) { AM_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') { AM_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) { AM_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)) { AM_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)) { AM_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")) { AM_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)) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid timestamp in NotBefore in SubjectConfirmationData."); return HTTP_BAD_REQUEST; } if (t - 60000000 > now) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid timestamp in NotOnOrAfter in SubjectConfirmationData."); return HTTP_BAD_REQUEST; } if (now >= t + 60000000) { AM_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)) { AM_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))) { AM_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 (conditions == NULL) { /* An assertion without conditions -- nothing to validate. */ return OK; } if (!LASSO_IS_SAML2_CONDITIONS(conditions)) { AM_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. */ AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid timestamp in NotBefore in Condition."); return HTTP_BAD_REQUEST; } if (t - 60000000 > now) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid timestamp in NotOnOrAfter in Condition."); return HTTP_BAD_REQUEST; } if (now >= t + 60000000) { AM_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)) { AM_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)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid Audience in Conditions. Should be '%s', but was '%s'", providerID, ar->Audience ? ar->Audience : ""); 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)) { AM_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) { am_diag_printf(r, "%s failed to find" " Assertion.AuthnStatement.SessionNotOnOrAfter\n", __func__); continue; } /* Parse timestamp. */ t = am_parse_timestamp(r, not_on_or_after); if(t == 0) { continue; } am_diag_printf(r, "%s Assertion.AuthnStatement.SessionNotOnOrAfter:" " %s\n", __func__, am_diag_time_t_to_8601(r, t)); /* Updates the expires timestamp if this one is earlier than the * previous timestamp. */ am_cache_update_expires(r, session, t); } } /* Add all the attributes from an assertion to the session data for the * current user. * * Parameters: * am_cache_entry_t *s The current session. * request_rec *r The current request. * const char *name_id The name identifier we received from * the IdP. * LassoSaml2Assertion *assertion The assertion. * * Returns: * HTTP_BAD_REQUEST if we couldn't find the session id of the user, or * OK if no error occured. */ static int add_attributes(am_cache_entry_t *session, request_rec *r, const char *name_id, LassoSaml2Assertion *assertion) { am_dir_cfg_rec *dir_cfg; GList *atr_stmt_itr; LassoSaml2AttributeStatement *atr_stmt; GList *atr_itr; LassoSaml2Attribute *attribute; GList *value_itr; LassoSaml2AttributeValue *value; GList *any_itr; char *content; char *dump; int ret; dir_cfg = am_get_dir_cfg(r); /* Set expires to whatever is set by MellonSessionLength. */ if(dir_cfg->session_length == -1) { /* -1 means "use default. The current default is 86400 seconds. */ am_cache_update_expires(r, session, apr_time_now() + apr_time_make(86400, 0)); } else { am_cache_update_expires(r, 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)) { AM_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)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of Attribute node."); continue; } if (attribute->Name == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "SAML 2.0 attribute without name."); 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)) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "AttributeValue element was empty."); continue; } content = ""; for (any_itr = g_list_first(value->any); any_itr != NULL; any_itr = g_list_next(any_itr)) { /* Verify that this is a LassoNode object. */ if(!LASSO_NODE(any_itr->data)) { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "AttributeValue element contained an " " element which wasn't a Node."); continue; } dump = lasso_node_dump(LASSO_NODE(any_itr->data)); if (!dump) { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "AttributeValue content dump failed."); continue; } /* Use the request pool, no need to free results */ content = apr_pstrcat(r->pool, content, dump, NULL); g_free(dump); } /* Decode and save the attribute. */ am_diag_printf(r, "%s name=%s value=%s\n", __func__, attribute->Name, content); ret = am_cache_env_append(session, attribute->Name, content); if(ret != OK) { return ret; } } } } return OK; } /* This function validates that the received assertion verify the security level configured by * MellonAuthnContextClassRef directives */ static int am_validate_authn_context_class_ref(request_rec *r, LassoSaml2Assertion *assertion) { int i = 0; LassoSaml2AuthnStatement *authn_statement = NULL; LassoSaml2AuthnContext *authn_context = NULL; am_dir_cfg_rec *dir_cfg; apr_array_header_t *refs; dir_cfg = am_get_dir_cfg(r); refs = dir_cfg->authn_context_class_ref; if (! refs->nelts) return OK; if (! assertion->AuthnStatement) { AM_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) { AM_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) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_DEBUG, 0, r, "AuthnContextClassRef (%s) matches the " "MellonAuthnContextClassRef directive, " "access can be granted.", authn_context->AuthnContextClassRef); return OK; } } AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "AuthnContextClassRef (%s) does not match the " "MellonAuthnContextClassRef directive, returning " "Forbidden.", authn_context->AuthnContextClassRef); return HTTP_FORBIDDEN; } /* This function finishes handling of a login response after it has been parsed * by the HTTP-POST or HTTP-Artifact handler. * * Parameters: * request_rec *r The current request. * LassoLogin *login The login object which has been initialized with the * data we have received from the IdP. * char *relay_state The RelayState parameter from the POST data or from * the request url. This parameter is urlencoded, and * this function will urldecode it in-place. Therefore it * must be possible to overwrite the data. * is_paos If true then flow is PAOS ECP. * * Returns: * A HTTP status code which should be returned to the client. */ static int am_handle_reply_common(request_rec *r, LassoLogin *login, char *relay_state, char *saml_response, bool is_paos) { char *url; char *chr; const char *name_id; LassoSamlp2Response *response; LassoSaml2Assertion *assertion; const char *in_response_to; am_dir_cfg_rec *dir_cfg; am_cache_entry_t *session; int rc; const char *idp; url = am_reconstruct_url(r); chr = strchr(url, '?'); if (! chr) { chr = strchr(url, ';'); } if (chr) { *chr = '\0'; } dir_cfg = am_get_dir_cfg(r); if(LASSO_PROFILE(login)->nameIdentifier == NULL) { AM_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)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid Destination on Response. Should be '%s', but was '%s'", url, response->parent.Destination); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } } if (g_list_length(response->Assertion) == 0) { AM_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) { AM_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)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Wrong type of Assertion node."); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } rc = am_validate_subject(r, assertion, url); if (rc != OK) { lasso_login_destroy(login); return rc; } rc = am_validate_conditions(r, assertion, LASSO_PROVIDER(LASSO_PROFILE(login)->server)->ProviderID); if (rc != OK) { lasso_login_destroy(login); return rc; } in_response_to = response->parent.InResponseTo; if (!is_paos) { if(in_response_to != NULL) { /* This is SP-initiated login. Check that we have a cookie. */ if(am_cookie_get(r) == NULL) { /* Missing cookie. */ AM_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) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r, "Could not urldecode RelayState value."); return HTTP_BAD_REQUEST; } /* Check for bad characters in RelayState. */ rc = am_check_url(r, relay_state); if (rc != OK) { return rc; } rc = am_validate_redirect_url(r, relay_state); if (rc != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in logout response RelayState parameter."); return rc; } apr_table_setn(r->headers_out, "Location", relay_state); /* HTTP_SEE_OTHER should be a redirect where the browser doesn't repeat * the POST data to the new page. */ return HTTP_SEE_OTHER; } /* This function handles responses to login requests received with the * HTTP-POST binding. * * Parameters: * request_rec *r The request we received. * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_handle_post_reply(request_rec *r) { int rc; char *post_data; char *saml_response; LassoServer *server; LassoLogin *login; char *relay_state; am_dir_cfg_rec *dir_cfg = am_get_dir_cfg(r); int i, err; am_diag_printf(r, "enter function %s\n", __func__); /* Make sure that this is a POST request. */ if(r->method_number != M_POST) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Expected POST request for HTTP-POST endpoint." " Got a %s request instead.", r->method); /* According to the documentation for request_rec, a handler which * doesn't handle a request method, should set r->allowed to the * methods it handles, and return DECLINED. * However, the default handler handles GET-requests, so for GET * requests the handler should return HTTP_METHOD_NOT_ALLOWED. */ r->allowed = M_POST; if(r->method_number == M_GET) { return HTTP_METHOD_NOT_ALLOWED; } else { return DECLINED; } } /* Read POST-data. */ rc = am_read_post_data(r, &post_data, NULL); if (rc != OK) { AM_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) { AM_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) { AM_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) { AM_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); am_diag_log_lasso_node(r, 0, LASSO_PROFILE(login)->response, "SAML Response (%s):", __func__); if (rc != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error processing authn response." " Lasso error: [%i] %s, SAML Response: %s", rc, lasso_strerror(rc), am_saml_response_status_str(r, LASSO_PROFILE(login)->response)); lasso_login_destroy(login); err = HTTP_BAD_REQUEST; for (i = 0; auth_mellon_errormap[i].lasso_error != 0; i++) { if (auth_mellon_errormap[i].lasso_error == rc) { err = auth_mellon_errormap[i].http_error; break; } } if (err == HTTP_UNAUTHORIZED) { if (dir_cfg->no_success_error_page != NULL) { apr_table_setn(r->headers_out, "Location", dir_cfg->no_success_error_page); return HTTP_SEE_OTHER; } } return err; } /* Extract RelayState parameter. */ relay_state = am_extract_query_parameter(r->pool, post_data, "RelayState"); /* Finish handling the reply with the common handler. */ return am_handle_reply_common(r, login, relay_state, saml_response, false); } /* This function handles responses to login requests received with the * PAOS binding. * * Parameters: * request_rec *r The request we received. * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_handle_paos_reply(request_rec *r) { int rc; char *post_data; LassoServer *server; LassoLogin *login; char *relay_state = NULL; int i, err; am_diag_printf(r, "enter function %s\n", __func__); /* Make sure that this is a POST request. */ if(r->method_number != M_POST) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Expected POST request for paosResponse endpoint." " Got a %s request instead.", r->method); /* According to the documentation for request_rec, a handler which * doesn't handle a request method, should set r->allowed to the * methods it handles, and return DECLINED. * However, the default handler handles GET-requests, so for GET * requests the handler should return HTTP_METHOD_NOT_ALLOWED. */ r->allowed = M_POST; if(r->method_number == M_GET) { return HTTP_METHOD_NOT_ALLOWED; } else { return DECLINED; } } /* Read POST-data. */ rc = am_read_post_data(r, &post_data, NULL); if (rc != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r, "Error reading POST data."); return rc; } server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } login = lasso_login_new(server); if (login == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Failed to initialize LassoLogin object."); return HTTP_INTERNAL_SERVER_ERROR; } /* Process login response. */ rc = lasso_login_process_paos_response_msg(login, post_data); am_diag_log_lasso_node(r, 0, LASSO_PROFILE(login)->response, "SAML Response (%s):", __func__); if (rc != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error processing ECP authn response." " Lasso error: [%i] %s, SAML Response: %s", rc, lasso_strerror(rc), am_saml_response_status_str(r, LASSO_PROFILE(login)->response)); lasso_login_destroy(login); err = HTTP_BAD_REQUEST; for (i = 0; auth_mellon_errormap[i].lasso_error != 0; i++) { if (auth_mellon_errormap[i].lasso_error == rc) { err = auth_mellon_errormap[i].http_error; break; } } return err; } /* Extract RelayState parameter. */ if (LASSO_PROFILE(login)->msg_relayState) { relay_state = apr_pstrdup(r->pool, LASSO_PROFILE(login)->msg_relayState); } /* Finish handling the reply with the common handler. */ return am_handle_reply_common(r, login, relay_state, post_data, true); } /* This function handles responses to login requests which use the * HTTP-Artifact binding. * * Parameters: * request_rec *r The request we received. * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_handle_artifact_reply(request_rec *r) { int rc; LassoServer *server; LassoLogin *login; char *response; char *relay_state; char *saml_art; char *post_data; am_diag_printf(r, "enter function %s\n", __func__); /* Make sure that this is a GET request. */ if(r->method_number != M_GET && r->method_number != M_POST) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Expected GET or POST request for the HTTP-Artifact endpoint." " Got a %s request instead.", r->method); /* According to the documentation for request_rec, a handler which * doesn't handle a request method, should set r->allowed to the * methods it handles, and return DECLINED. * However, the default handler handles GET-requests, so for GET * requests the handler should return HTTP_METHOD_NOT_ALLOWED. * This endpoints handles GET requests, so it isn't necessary to * check for method_number == M_GET. */ r->allowed = M_GET; return DECLINED; } server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } login = lasso_login_new(server); if (login == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Failed to initialize LassoLogin object."); return HTTP_INTERNAL_SERVER_ERROR; } /* Parse artifact url. */ if (r->method_number == M_GET) { rc = lasso_login_init_request(login, r->args, LASSO_HTTP_METHOD_ARTIFACT_GET); if(rc != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Failed to handle login response." " Lasso error: [%i] %s", rc, lasso_strerror(rc)); lasso_login_destroy(login); return HTTP_BAD_REQUEST; } } else { rc = am_read_post_data(r, &post_data, NULL); if (rc != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r, "Error reading POST data."); return HTTP_BAD_REQUEST; } saml_art = am_extract_query_parameter(r->pool, post_data, "SAMLart"); if (saml_art == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r, "Error reading POST data missing SAMLart form parameter."); return HTTP_BAD_REQUEST; } ap_unescape_url(saml_art); rc = lasso_login_init_request(login, saml_art, LASSO_HTTP_METHOD_ARTIFACT_POST); if(rc != 0) { AM_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) { AM_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); am_diag_log_lasso_node(r, 0, LASSO_PROFILE(login)->response, "SAML Response (%s):", __func__); if(rc != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Failed to handle HTTP-Artifact response data." " Lasso error: [%i] %s, SAML Response: %s", rc, lasso_strerror(rc), am_saml_response_status_str(r, LASSO_PROFILE(login)->response)); lasso_login_destroy(login); return HTTP_INTERNAL_SERVER_ERROR; } /* Extract the RelayState parameter. */ if (r->method_number == M_GET) { relay_state = am_extract_query_parameter(r->pool, r->args, "RelayState"); } else { relay_state = am_extract_query_parameter(r->pool, post_data, "RelayState"); } /* Finish handling the reply with the common handler. */ return am_handle_reply_common(r, login, relay_state, "", false); } /* This function builds web form inputs for a saved POST request, * in multipart/form-data format. * * Parameters: * request_rec *r The request * const char *post_data The savec POST request * * Returns: * The web form fragment, or NULL on failure. */ const char *am_post_mkform_multipart(request_rec *r, const char *post_data) { const char *mime_part; const char *boundary; char *l1; char *post_form = ""; /* Replace CRLF by LF */ post_data = am_strip_cr(r, post_data); if ((boundary = am_xstrtok(r, post_data, "\n", &l1)) == NULL) { AM_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) { AM_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) { AM_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 = ""; char empty_value[] = ""; 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 = empty_value; if (am_urldecode(name) != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "urldecode(\"%s\") failed", name); return NULL; } if (am_urldecode(value) != OK) { AM_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; am_file_data_t *file_data; const char *post_data; const char *post_form; char *output; char *return_url; const char *(*post_mkform)(request_rec *, const char *); int rc; am_diag_printf(r, "enter function %s\n", __func__); if (am_cookie_get(r) == NULL) { AM_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) { AM_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) { AM_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 { AM_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) { AM_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 != '_')) { AM_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) { AM_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)) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid or missing query ReturnTo parameter."); return HTTP_BAD_REQUEST; } if (am_urldecode(return_url) != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Bad repost query: return"); return HTTP_BAD_REQUEST; } rc = am_validate_redirect_url(r, return_url); if (rc != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in repost request ReturnTo parameter."); return rc; } if ((file_data = am_file_data_new(r->pool, NULL)) == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "Bad repost query: cannot allocate file_data"); apr_table_setn(r->headers_out, "Location", return_url); return HTTP_SEE_OTHER; } file_data->path = apr_psprintf(file_data->pool, "%s/%s", mod_cfg->post_dir, psf_id); rc = am_file_read(file_data); if (rc != APR_SUCCESS) { /* Unable to load repost data. Just redirect us instead. */ AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "Bad repost query: %s", file_data->strerror); apr_table_setn(r->headers_out, "Location", return_url); return HTTP_SEE_OTHER; } else { post_data = file_data->contents; } if ((post_form = (*post_mkform)(r, post_data)) == NULL) { AM_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; am_diag_printf(r, "enter function %s\n", __func__); server = am_get_lasso_server(r); if(server == NULL) return HTTP_INTERNAL_SERVER_ERROR; cfg = cfg->inherit_server_from; data = cfg->sp_metadata_file ? cfg->sp_metadata_file->contents : NULL; 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 */ AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "metadata publishing require lasso 2.2.2 or higher"); return HTTP_NOT_FOUND; #endif } /* Use Lasso Login to set the HTTP content & headers for HTTP-Redirect binding. * * Parameters: * request_rec *r * LassoLogin *login * * Returns: * HTTP_SEE_OTHER on success, or an error on failure. */ static int am_set_authn_request_redirect_content(request_rec *r, LassoLogin *login) { char *redirect_to; /* The URL we should send the message to. */ redirect_to = apr_pstrdup(r->pool, LASSO_PROFILE(login)->msg_url); /* Check if the lasso library added the RelayState. If lasso didn't add * a RelayState parameter, then we add one ourself. This should hopefully * be removed in the future. */ if(strstr(redirect_to, "&RelayState=") == NULL && strstr(redirect_to, "?RelayState=") == NULL) { /* The url didn't contain the relaystate parameter. */ redirect_to = apr_pstrcat( r->pool, redirect_to, "&RelayState=", am_urlencode(r->pool, LASSO_PROFILE(login)->msg_relayState), NULL ); } apr_table_setn(r->headers_out, "Location", redirect_to); /* We don't want to include POST data (in case this was a POST request). */ return HTTP_SEE_OTHER; } /* Use Lasso Login to set the HTTP content & headers for HTTP-POST binding. * * Parameters: * request_rec *r The request we are processing. * LassoLogin *login The login message. * * Returns: * OK on success, or an error on failure. */ static int am_set_authn_request_post_content(request_rec *r, LassoLogin *login) { char *url; char *message; char *relay_state; char *output; url = am_htmlencode(r, LASSO_PROFILE(login)->msg_url); message = am_htmlencode(r, LASSO_PROFILE(login)->msg_body); relay_state = am_htmlencode(r, LASSO_PROFILE(login)->msg_relayState); output = apr_psprintf(r->pool, "\n" "\n" " \n" " \n" " POST data\n" " \n" " \n" " \n" "
\n" " \n" " \n" " \n" "
\n" " \n" "\n", url, message, relay_state); ap_set_content_type(r, "text/html"); ap_rputs(output, r); return OK; } /* Use Lasso Login to set the HTTP content & headers for PAOS binding. * * Parameters: * request_rec *r The request we are processing. * LassoLogin *login The login message. * * Returns: * OK on success, or an error on failure. */ static int am_set_authn_request_paos_content(request_rec *r, LassoLogin *login) { ap_set_content_type(r, MEDIA_TYPE_PAOS); ap_rputs(LASSO_PROFILE(login)->msg_body, r); return OK; } /* * Create and initialize LassoLogin object * * This function creates a LassoLogin object and initializes it to the * greatest extent possible to allow it to be shared by multiple * callers. There are two return values. The function return is an * error code, the login_return parameter is a pointer in which to * receive the LassoLogin object. The caller MUST free the returned * login object using lasso_login_destroy() in all cases (even when * this function returns an error), the only execption is if the * returned LassoLogin is NULL. * * Parameters: * r The request we are processing. * login_return The returned LassoLogin object (caller must free) * idp The provider id of remote Idp * [optional, may be NULL] * http_method Specifies the SAML profile to use * destination_url If the idp parameter is non-NULL this should be * the URL of the IdP endpoint the message is being sent to * [optional, may be NULL] * assertion_consumer_service_url * The URL of this SP's endpoint which will receive the * SAML assertion * return_to_url Used to initialize the RelayState value * is_passive The SAML IsPassive flag * * Returns: * OK on success, HTTP error code otherwise * */ static int am_init_authn_request_common(request_rec *r, LassoLogin **login_return, const char *idp, LassoHttpMethod http_method, const char *destination_url, const char *assertion_consumer_service_url, const char *return_to_url, int is_passive) { gint ret; am_dir_cfg_rec *dir_cfg; LassoServer *server; LassoLogin *login; LassoSamlp2AuthnRequest *request; const char *sp_name; *login_return = NULL; dir_cfg = am_get_dir_cfg(r); server = am_get_lasso_server(r); if (server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } login = lasso_login_new(server); if(login == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error creating LassoLogin object from LassoServer."); return HTTP_INTERNAL_SERVER_ERROR; } *login_return = login; ret = lasso_login_init_authn_request(login, idp, http_method); if(ret != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error creating login request." " Lasso error: [%i] %s", ret, lasso_strerror(ret)); return HTTP_INTERNAL_SERVER_ERROR; } request = LASSO_SAMLP2_AUTHN_REQUEST(LASSO_PROFILE(login)->request); if (request->NameIDPolicy == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error creating login request. Please verify the " "MellonSPMetadataFile directive."); return HTTP_INTERNAL_SERVER_ERROR; } /* * Make sure the Destination attribute is set to the IdP * SingleSignOnService endpoint. This is required for * Shibboleth 2 interoperability, and older versions of * lasso (at least up to 2.2.91) did not do it. */ if (destination_url && LASSO_SAMLP2_REQUEST_ABSTRACT(request)->Destination == NULL) { lasso_assign_string(LASSO_SAMLP2_REQUEST_ABSTRACT(request)->Destination, destination_url); } if (assertion_consumer_service_url) { lasso_assign_string(request->AssertionConsumerServiceURL, assertion_consumer_service_url); /* Can't set request->ProtocolBinding (which is usually set along side * AssertionConsumerServiceURL) as there is no immediate function * like lasso_provider_get_assertion_consumer_service_url to get them. * So leave that empty for now, it is not strictly required */ } request->ForceAuthn = FALSE; request->IsPassive = is_passive; request->NameIDPolicy->AllowCreate = TRUE; sp_name = am_get_config_langstring(dir_cfg->sp_org_display_name, NULL); if (sp_name) { lasso_assign_string(request->ProviderName, sp_name); } LASSO_SAMLP2_REQUEST_ABSTRACT(request)->Consent = g_strdup(LASSO_SAML2_CONSENT_IMPLICIT); /* Add AuthnContextClassRef */ if (dir_cfg->authn_context_class_ref->nelts) { apr_array_header_t *refs = dir_cfg->authn_context_class_ref; int i = 0; LassoSamlp2RequestedAuthnContext *req_authn_context; req_authn_context = (LassoSamlp2RequestedAuthnContext*) lasso_samlp2_requested_authn_context_new(); request->RequestedAuthnContext = req_authn_context; for (i = 0; i < refs->nelts; i++) { const char *ref = ((char **)refs->elts)[i]; req_authn_context->AuthnContextClassRef = g_list_append(req_authn_context->AuthnContextClassRef, g_strdup(ref)); AM_LOG_RERROR(APLOG_MARK, APLOG_DEBUG, 0, r, "adding AuthnContextClassRef %s to the " "AuthnRequest", ref); } if (dir_cfg->authn_context_comparison_type != NULL) { lasso_assign_string(request->RequestedAuthnContext->Comparison, dir_cfg->authn_context_comparison_type); } } LASSO_PROFILE(login)->msg_relayState = g_strdup(return_to_url); #ifdef HAVE_ECP { am_req_cfg_rec *req_cfg; ECPServiceOptions unsupported_ecp_options; req_cfg = am_get_req_cfg(r); /* * Currently we only support the WANT_AUTHN_SIGNED ECP option, * if a client sends us anything else let them know it's not * implemented. * * We do test for CHANNEL_BINDING below but that's because if * and when we support it we don't want to forget channel * bindings require the authn request to be signed. */ unsupported_ecp_options = req_cfg->ecp_service_options & ~ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED; if (unsupported_ecp_options) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Unsupported ECP service options [%s]", am_ecp_service_options_str(r->pool, unsupported_ecp_options)); return HTTP_NOT_IMPLEMENTED; } /* * The signature hint must be set prior to calling * lasso_login_build_authn_request_msg */ if (req_cfg->ecp_service_options & (ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED | ECP_SERVICE_OPTION_CHANNEL_BINDING)) { /* * authnRequest should be signed if the client requested it * or if channel bindings are enabled. */ lasso_profile_set_signature_hint(LASSO_PROFILE(login), LASSO_PROFILE_SIGNATURE_HINT_FORCE); } } #endif ret = lasso_login_build_authn_request_msg(login); if (ret != 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error building login request." " Lasso error: [%i] %s", ret, lasso_strerror(ret)); return HTTP_INTERNAL_SERVER_ERROR; } return OK; } /* Use Lasso Login to set the HTTP content & headers for selected binding. * * Parameters: * request_rec *r The request we are processing. * LassoLogin *login The login message. * * Returns: * HTTP response code */ static int am_set_authn_request_content(request_rec *r, LassoLogin *login) { am_diag_log_lasso_node(r, 0, LASSO_PROFILE(login)->request, "SAML AuthnRequest: http_method=%s URL=%s", am_diag_lasso_http_method_str(login->http_method), LASSO_PROFILE(login)->msg_url); switch (login->http_method) { case LASSO_HTTP_METHOD_REDIRECT: return am_set_authn_request_redirect_content(r, login); case LASSO_HTTP_METHOD_POST: return am_set_authn_request_post_content(r, login); case LASSO_HTTP_METHOD_PAOS: return am_set_authn_request_paos_content(r, login); default: /* We should never get here. */ AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Unsupported http_method."); return HTTP_INTERNAL_SERVER_ERROR; } } #ifdef HAVE_ECP /* Build an IDPList whose members have an endpoint supporing * the protocol_type and http_method. */ static LassoNode * am_get_idp_list(const LassoServer *server, LassoMdProtocolType protocol_type, LassoHttpMethod http_method) { GList *idp_entity_ids = NULL; GList *entity_id = NULL; GList *idp_entries = NULL; LassoSamlp2IDPList *idp_list; LassoSamlp2IDPEntry *idp_entry; idp_list = LASSO_SAMLP2_IDP_LIST(lasso_samlp2_idp_list_new()); idp_entity_ids = lasso_server_get_filtered_provider_list(server, LASSO_PROVIDER_ROLE_IDP, protocol_type, http_method); for (entity_id = g_list_first(idp_entity_ids); entity_id != NULL; entity_id = g_list_next(entity_id)) { idp_entry = LASSO_SAMLP2_IDP_ENTRY(lasso_samlp2_idp_entry_new()); idp_entry->ProviderID = g_strdup(entity_id->data); /* RFE: we should have a mechanism to obtain these values */ idp_entry->Name = NULL; idp_entry->Loc = NULL; idp_entries = g_list_append(idp_entries, idp_entry); } lasso_release_list_of_strings(idp_entity_ids); idp_list->IDPEntry = idp_entries; return LASSO_NODE(idp_list); } /* Send AuthnRequest using PAOS binding. * * Parameters: * request_rec *r * * Returns: * OK on success, or an error on failure. */ static int am_send_paos_authn_request(request_rec *r) { gint ret; am_dir_cfg_rec *dir_cfg; LassoServer *server; LassoLogin *login; const char *relay_state = NULL; char *assertion_consumer_service_url; int is_passive = FALSE; dir_cfg = am_get_dir_cfg(r); server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } relay_state = am_reconstruct_url(r); assertion_consumer_service_url = am_get_assertion_consumer_service_by_binding(LASSO_PROVIDER(server), "PAOS"); ret = am_init_authn_request_common(r, &login, NULL, LASSO_HTTP_METHOD_PAOS, NULL, assertion_consumer_service_url, relay_state, is_passive); g_free(assertion_consumer_service_url); if (ret != OK) { if (login) { lasso_login_destroy(login); } return ret; } if (CFG_VALUE(dir_cfg, ecp_send_idplist)) { lasso_profile_set_idp_list(LASSO_PROFILE(login), am_get_idp_list(LASSO_PROFILE(login)->server, LASSO_MD_PROTOCOL_TYPE_SINGLE_SIGN_ON, LASSO_HTTP_METHOD_SOAP)); } ret = am_set_authn_request_content(r, login); lasso_login_destroy(login); return ret; } #endif /* HAVE_ECP */ /* Create and send an authentication request. * * Parameters: * request_rec *r The request we are processing. * const char *idp The entityID of the IdP. * const char *return_to The URL we should redirect to when receiving the request. * int is_passive The value of the IsPassive flag in * * Returns: * HTTP response code indicating success or failure. */ static int am_send_login_authn_request(request_rec *r, const char *idp, const char *return_to_url, int is_passive) { int ret; LassoServer *server; LassoProvider *provider; LassoHttpMethod http_method; char *destination_url; char *assertion_consumer_service_url; LassoLogin *login; /* Add cookie for cookie test. We know that we should have * a valid cookie when we return from the IdP after SP-initiated * login. */ am_cookie_set(r, "cookietest"); server = am_get_lasso_server(r); if(server == NULL) { return HTTP_INTERNAL_SERVER_ERROR; } /* Find our IdP. */ provider = lasso_server_get_provider(server, idp); if (provider == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Could not find metadata for the IdP \"%s\".", idp); return HTTP_INTERNAL_SERVER_ERROR; } /* Determine what binding and endpoint we should use when * sending the request. */ http_method = LASSO_HTTP_METHOD_REDIRECT; destination_url = lasso_provider_get_metadata_one( provider, "SingleSignOnService HTTP-Redirect"); if (destination_url == NULL) { /* HTTP-Redirect unsupported - try HTTP-POST. */ http_method = LASSO_HTTP_METHOD_POST; destination_url = lasso_provider_get_metadata_one( provider, "SingleSignOnService HTTP-POST"); } if (destination_url == NULL) { /* Both HTTP-Redirect and HTTP-POST unsupported - give up. */ AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Could not find a supported SingleSignOnService endpoint" " for the IdP \"%s\".", idp); return HTTP_INTERNAL_SERVER_ERROR; } assertion_consumer_service_url = lasso_provider_get_assertion_consumer_service_url( LASSO_PROVIDER(server), NULL); ret = am_init_authn_request_common(r, &login, idp, http_method, destination_url, assertion_consumer_service_url, return_to_url, is_passive); g_free(destination_url); g_free(assertion_consumer_service_url); if (ret != OK) { if (login) { lasso_login_destroy(login); } return ret; } ret = am_set_authn_request_content(r, login); lasso_login_destroy(login); return ret; } /* Handle the "auth" endpoint. * * This endpoint is included for backwards-compatibility. * * Parameters: * request_rec *r The request we received. * * Returns: * OK or HTTP_SEE_OTHER on success, an error on failure. */ static int am_handle_auth(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); const char *relay_state; am_diag_printf(r, "enter function %s\n", __func__); relay_state = am_reconstruct_url(r); /* Check if IdP discovery is in use and no IdP was selected yet */ if ((cfg->discovery_url != NULL) && (am_extract_query_parameter(r->pool, r->args, "IdP") == NULL)) { return am_start_disco(r, relay_state); } /* If IdP discovery is in use and we have an IdP selected, * set the relay_state */ if (cfg->discovery_url != NULL) { char *return_url; return_url = am_extract_query_parameter(r->pool, r->args, "ReturnTo"); if ((return_url != NULL) && am_urldecode((char *)return_url) == 0) relay_state = return_url; } return am_send_login_authn_request(r, am_get_idp(r), relay_state, FALSE); } /* This function handles requests to the login handler. * * Parameters: * request_rec *r The request. * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_handle_login(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); char *idp_param; const char *idp; char *return_to; int is_passive; int ret; am_diag_printf(r, "enter function %s\n", __func__); return_to = am_extract_query_parameter(r->pool, r->args, "ReturnTo"); if(return_to == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Missing required ReturnTo parameter."); return HTTP_BAD_REQUEST; } ret = am_urldecode(return_to); if(ret != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error urldecoding ReturnTo parameter."); return ret; } ret = am_validate_redirect_url(r, return_to); if(ret != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in login request ReturnTo parameter."); return ret; } idp_param = am_extract_query_parameter(r->pool, r->args, "IdP"); if(idp_param != NULL) { ret = am_urldecode(idp_param); if(ret != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error urldecoding IdP parameter."); return ret; } } ret = am_get_boolean_query_parameter(r, "IsPassive", &is_passive, FALSE); if (ret != OK) { return ret; } if(idp_param != NULL) { idp = idp_param; } else if(cfg->discovery_url) { if(is_passive) { /* We cannot currently do discovery with passive authentication requests. */ AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Discovery service with passive authentication request unsupported."); return HTTP_INTERNAL_SERVER_ERROR; } return am_start_disco(r, return_to); } else { /* No discovery service -- just use the default IdP. */ idp = am_get_idp(r); } return am_send_login_authn_request(r, idp, return_to, is_passive); } /* This function probes an URL (HTTP GET) * * Parameters: * request_rec *r The request. * const char *url The URL * int timeout Timeout in seconds * * Returns: * OK on success, or an error if any of the steps fail. */ static int am_probe_url(request_rec *r, const char *url, int timeout) { void *dontcare; apr_size_t len; long status; int error; status = 0; if ((error = am_httpclient_get(r, url, &dontcare, &len, timeout, &status)) != OK) return error; if (status != HTTP_OK) { AM_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; am_diag_printf(r, "enter function %s\n", __func__); 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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "probe discovery handler invoked but not " "configured. Please 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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Missing required return parameter."); return HTTP_BAD_REQUEST; } ret = am_urldecode(return_to); if (ret != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, ret, r, "Could not urldecode return value."); return HTTP_BAD_REQUEST; } ret = am_validate_redirect_url(r, return_to); if (ret != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid target domain in probe discovery return parameter."); return ret; } idp_param = am_extract_query_parameter(r->pool, r->args, "returnIDParam"); if(idp_param == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Missing required returnIDParam parameter."); return HTTP_BAD_REQUEST; } ret = am_urldecode(idp_param); if (ret != OK) { AM_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, fail if a MellonProbeDiscoveryIdP * list was provided, otherwise try first IdP. */ if (disco_idp == NULL) { if (!apr_is_empty_table(cfg->probe_discovery_idp)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "probeDiscovery failed and non empty " "MellonProbeDiscoveryIdP was provided."); return HTTP_INTERNAL_SERVER_ERROR; } disco_idp = am_first_idp(r); if (disco_idp == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "probeDiscovery found no usable IdP."); return HTTP_INTERNAL_SERVER_ERROR; } else { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "probeDiscovery " "failed, trying default IdP %s", disco_idp); } } else { AM_LOG_RERROR(APLOG_MARK, APLOG_INFO, 0, r, "probeDiscovery using %s", disco_idp); } redirect_url = apr_psprintf(r->pool, "%s%s%s=%s", return_to, strchr(return_to, '?') ? "&" : "?", am_urlencode(r->pool, idp_param), am_urlencode(r->pool, disco_idp)); apr_table_setn(r->headers_out, "Location", redirect_url); return HTTP_SEE_OTHER; } /* This function handles responses to request on our endpoint * * Parameters: * request_rec *r The request we received. * * Returns: * OK on success, or an error on failure. */ int am_handler(request_rec *r) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); #ifdef HAVE_ECP am_req_cfg_rec *req_cfg = am_get_req_cfg(r); #endif /* HAVE_ECP */ const char *endpoint; /* * Normally this content handler is used to dispatch to the SAML * endpoints implmented by mod_auth_mellon. SAML endpoint dispatch * occurs when the URI begins with the SAML endpoint path. * * However, this handler is also responsible for generating ECP * authn requests, in this case the URL will be a protected * resource we're doing authtentication for. Early in the request * processing pipeline we detected we were doing ECP authn and set * a flag on the request. Here we test for that flag and if true * respond with the ECP PAOS authn request. * * If the request is neither for a SAML endpoint nor one that * requires generating an ECP authn we decline handling the request. */ #ifdef HAVE_ECP if (req_cfg->ecp_authn_req) { /* Are we doing ECP? */ return am_send_paos_authn_request(r); } #endif /* HAVE_ECP */ /* Check if this is a request for one of our endpoints. We check if * the uri starts with the path set with the MellonEndpointPath * configuration directive. */ if(strstr(r->uri, cfg->endpoint_path) != r->uri) return DECLINED; endpoint = &r->uri[strlen(cfg->endpoint_path)]; if (!strcmp(endpoint, "metadata")) { return am_handle_metadata(r); } else if (!strcmp(endpoint, "repost")) { return am_handle_repost(r); } else if(!strcmp(endpoint, "postResponse")) { return am_handle_post_reply(r); } else if(!strcmp(endpoint, "artifactResponse")) { return am_handle_artifact_reply(r); } else if(!strcmp(endpoint, "paosResponse")) { return am_handle_paos_reply(r); } else if(!strcmp(endpoint, "auth")) { return am_handle_auth(r); } else if(!strcmp(endpoint, "logout") || !strcmp(endpoint, "logoutRequest")) { /* logoutRequest is included for backwards-compatibility * with version 0.0.6 and older. */ return am_handle_logout(r); } else if(!strcmp(endpoint, "invalidate")) { return am_handle_invalidate(r); } else if(!strcmp(endpoint, "login")) { return am_handle_login(r); } else if(!strcmp(endpoint, "probeDisco")) { return am_handle_probe_discovery(r); } else { AM_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; am_diag_printf(r, "enter function %s\n", __func__); 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 { AM_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)); AM_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; const char *ajax_header; if (r->main) { /* We are a subrequest. Trust the main request to have * performed the authentication. */ return OK; } /* Check that the user has enabled authentication for this directory. */ if(dir->enable_mellon == am_enable_off || dir->enable_mellon == am_enable_default) { return DECLINED; } am_diag_printf(r, "enter function %s\n", __func__); /* Set defaut Cache-Control headers within this location */ if (CFG_VALUE(dir, send_cache_control_header)) { am_set_cache_control_headers(r); } /* Check if this is a request for one of our endpoints. We check if * the uri starts with the path set with the MellonEndpointPath * configuration directive. */ if(strstr(r->uri, dir->endpoint_path) == r->uri) { /* No access control on our internal endpoints. */ return OK; } /* Get the session of this request. */ session = am_get_request_session(r); if(dir->enable_mellon == am_enable_auth) { /* This page requires the user to be authenticated and authorized. */ if(session == NULL || !session->logged_in) { /* We don't have a valid session. */ am_diag_printf(r, "%s am_enable_auth, no valid session\n", __func__); if(session) { /* Release the session. */ am_release_request_session(r, session); } /* * If this is an AJAX request, we cannot proceed to the IdP, * Just fail early to save our resources */ ajax_header = apr_table_get(r->headers_in, "X-Requested-With"); if (ajax_header != NULL && strcmp(ajax_header, "XMLHttpRequest") == 0) { AM_LOG_RERROR(APLOG_MARK, APLOG_INFO, 0, r, "Deny unauthenticated X-Requested-With XMLHttpRequest " "(AJAX) request"); return HTTP_FORBIDDEN; } #ifdef HAVE_ECP /* * If PAOS set a flag on the request indicating we're * doing ECP and allow the request to proceed through the * request handlers until we reach am_handler which then * checks the flag and if True initiates an ECP transaction. * See am_check_uid for detailed explanation. */ bool is_paos; int error_code; is_paos = am_is_paos_request(r, &error_code); if (error_code) return HTTP_BAD_REQUEST; if (is_paos) { am_req_cfg_rec *req_cfg; req_cfg = am_get_req_cfg(r); req_cfg->ecp_authn_req = true; return OK; } else { /* Send the user to the authentication page on the IdP. */ return am_start_auth(r); } #else /* HAVE_ECP */ /* Send the user to the authentication page on the IdP. */ return am_start_auth(r); #endif /* HAVE_ECP */ } am_diag_printf(r, "%s am_enable_auth, have valid session\n", __func__); /* Verify that the user has access to this resource. */ return_code = am_check_permissions(r, session); if(return_code != OK) { am_diag_printf(r, "%s failed am_check_permissions, status=%d\n", __func__, return_code); 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) { am_diag_printf(r, "%s am_enable_info, have valid session\n", __func__); /* 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); } else { am_diag_printf(r, "%s am_enable_info, no valid session\n", __func__); } if(session != NULL) { /* Release the session. */ am_release_request_session(r, session); } /* We shouldn't really do any access control, so we always return * DECLINED. */ return DECLINED; } } int am_check_uid(request_rec *r) { am_dir_cfg_rec *dir = am_get_dir_cfg(r); am_cache_entry_t *session; int return_code = HTTP_UNAUTHORIZED; if (r->main) { /* We are a subrequest. Trust the main request to have * performed the authentication. */ if (r->main->user) { /* Make sure that the username from the main request is * available in the subrequest. */ r->user = apr_pstrdup(r->pool, r->main->user); } return OK; } /* 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; } am_diag_printf(r, "enter function %s\n", __func__); #ifdef HAVE_ECP am_req_cfg_rec *req_cfg = am_get_req_cfg(r); if (req_cfg->ecp_authn_req) { AM_LOG_RERROR(APLOG_MARK, APLOG_DEBUG, 0, r, "am_check_uid is performing ECP authn request flow"); /* * Normally when a protected resource requires authentication * the request processing pipeline is exited early by * responding with either a 401 or a redirect. But the flow * for ECP is different, there will be a successful response * (200) but instead of the response body containing the * protected resource it will contain a SAML AuthnRequest * with the Content-Type indicating it's PAOS ECP. * * In order to return a 200 Success with a PAOS body we have * to reach the handler stage of the request processing * pipeline. But this is a protected resource and we won't * reach the handler stage unless authn and authz are * satisfied. Therefore we lie and return results which * indicate authn and authz are satisfied. This is OK because * we're not actually going to respond with the protected * resource, instead we'll be responsing with a SAML request. * * Apache's internal request logic * (ap_process_request_internal) requires that after a * successful return from the check_user_id authentication * hook the r->user value be non-NULL. This makes sense * because authentication establishes who the authenticated * principal is. But with ECP flow there is no authenticated * user at this point, we're just faking successful * authentication in order to reach the handler stage. To get * around this problem we set r-user to the empty string to * keep Apache happy, otherwise it would throw an * error. mod_shibboleth does the same thing. */ r->user = ""; return OK; } #endif /* HAVE_ECP */ /* Check if this is a request for one of our endpoints. We check if * the uri starts with the path set with the MellonEndpointPath * configuration directive. */ if(strstr(r->uri, dir->endpoint_path) == r->uri) { /* No access control on our internal endpoints. */ r->user = ""; /* see above explanation */ return OK; } /* Get the session of this request. */ session = am_get_request_session(r); /* If we don't have a session, then we can't authorize the user. */ if(session == NULL) { am_diag_printf(r, "%s no session, return HTTP_UNAUTHORIZED\n", __func__); return HTTP_UNAUTHORIZED; } /* If the user isn't logged in, then we can't authorize the user. */ if(!session->logged_in) { am_diag_printf(r, "%s session not logged in," " return HTTP_UNAUTHORIZED\n", __func__); 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_diag_printf(r, "%s failed am_check_permissions, status=%d\n", __func__, return_code); 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; } mod_auth_mellon-0.16.0/auth_mellon_util.c0000664002412700241270000024053113607335617020416 0ustar jhrozekjhrozek/* * * 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" #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(auth_mellon); #endif /* This function is used to get the url of the current request. * * Parameters: * request_rec *r The current request. * * Returns: * A string containing the full url of the current request. * The string is allocated from r->pool. */ char *am_reconstruct_url(request_rec *r) { char *url; /* This function will construct an full url for a given path relative to * the root of the web site. To configure what hostname and port this * function will use, see the UseCanonicalName configuration directive. */ url = ap_construct_url(r->pool, r->unparsed_uri, r); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "reconstruct_url: url==\"%s\", unparsed_uri==\"%s\"", url, r->unparsed_uri); return url; } /* Get the hostname of the current request. * * Parameters: * request_rec *r The current request. * * Returns: * The hostname of the current request. */ static const char *am_request_hostname(request_rec *r) { const char *url; apr_uri_t uri; int ret; url = am_reconstruct_url(r); ret = apr_uri_parse(r->pool, url, &uri); if (ret != APR_SUCCESS) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Failed to parse request URL: %s", url); return NULL; } if (uri.hostname == NULL) { /* This shouldn't happen, since the request URL is built with a hostname, * but log a message to make any debuggin around this code easier. */ AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "No hostname in request URL: %s", url); return NULL; } return uri.hostname; } /* Validate the redirect URL. * * Checks that the redirect URL is to a trusted domain & scheme. * * Parameters: * request_rec *r The current request. * const char *url The redirect URL to validate. * * Returns: * OK if the URL is valid, HTTP_BAD_REQUEST if not. */ int am_validate_redirect_url(request_rec *r, const char *url) { am_dir_cfg_rec *cfg = am_get_dir_cfg(r); apr_uri_t uri; int ret; ret = apr_uri_parse(r->pool, url, &uri); if (ret != APR_SUCCESS) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid redirect URL: %s", url); return HTTP_BAD_REQUEST; } /* Sanity check of the scheme of the domain. We only allow http and https. */ if (uri.scheme) { /* http and https schemes without hostname are invalid. */ if (!uri.hostname) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Preventing redirect with scheme but no hostname: %s", url); return HTTP_BAD_REQUEST; } if (strcasecmp(uri.scheme, "http") && strcasecmp(uri.scheme, "https")) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Only http or https scheme allowed in redirect URL: %s (%s)", url, uri.scheme); return HTTP_BAD_REQUEST; } } if (!uri.hostname) { return OK; /* No hostname to check. */ } for (int i = 0; cfg->redirect_domains[i] != NULL; i++) { const char *redirect_domain = cfg->redirect_domains[i]; if (!strcasecmp(redirect_domain, "[self]")) { if (!strcasecmp(uri.hostname, am_request_hostname(r))) { return OK; } } else if (apr_fnmatch(redirect_domain, uri.hostname, APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_SUCCESS) { return OK; } } AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Untrusted hostname (%s) in redirect URL: %s", uri.hostname, url); return HTTP_BAD_REQUEST; } /* This function builds an array of regexp backreferences * * Parameters: * request_rec *r The current request. * const am_cond_t *ce The condition * const char *value Attribute value * const ap_regmatch_t *regmatch regmatch_t from ap_regexec() * * Returns: * An array of collected backreference strings */ const apr_array_header_t *am_cond_backrefs(request_rec *r, const am_cond_t *ce, const char *value, const ap_regmatch_t *regmatch) { apr_array_header_t *backrefs; const char **ref; int nsub; int i; nsub = ce->regex->re_nsub + 1; /* +1 for %0 */ backrefs = apr_array_make(r->pool, nsub, sizeof(const char *)); backrefs->nelts = nsub; ref = (const char **)(backrefs->elts); for (i = 0; i < nsub; i++) { if ((regmatch[i].rm_so == -1) || (regmatch[i].rm_eo == -1)) { ref[i] = ""; } else { int len = regmatch[i].rm_eo - regmatch[i].rm_so; int off = regmatch[i].rm_so; ref[i] = apr_pstrndup(r->pool, value + off, len); } } return (const apr_array_header_t *)backrefs; } /* This function clones an am_cond_t and substitute value to * match (both regexp and string) with backreferences from * a previous regex match. * * Parameters: * request_rec *r The current request. * const am_cond_t *cond The am_cond_t to clone and substiture * const apr_array_header_t *backrefs Collected backreferences * * Returns: * The cloned am_cond_t */ const am_cond_t *am_cond_substitue(request_rec *r, const am_cond_t *ce, const apr_array_header_t *backrefs) { am_cond_t *c; const char *instr = ce->str; apr_size_t inlen = strlen(instr); const char *outstr = ""; size_t last; size_t i; c = (am_cond_t *)apr_pmemdup(r->pool, ce, sizeof(*ce)); 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) && (backrefs != NULL)) { /* * 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. * This is akin to how Apache itself does it during expression evaluation. */ value = apr_table_get(r->subprocess_env, name); if (value == NULL) { value = apr_table_get(r->notes, name); } if (value == NULL) { value = getenv(name); } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Resolving \"%s\" from ENV to \"%s\"", name, value == NULL ? "(nothing)" : value); } /* * 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); c->str = outstr; 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) { AM_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]; am_diag_printf(r, "%s processing condition %d of %d: %s ", __func__, i, dir_cfg->cond->nelts, am_diag_cond_str(r, ce)); /* * 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); am_diag_printf(r, "Skip, [OR] rule matched previously\n"); continue; } /* * look for a match on each value for this attribute, * stop on first match. */ for (j = 0; (j < session->size) && !match; j++) { const char *varname = NULL; am_envattr_conf_t *envattr_conf = NULL; /* * if MAP flag is set, check for remapped * attribute name with mellonSetEnv */ if (ce->flags & AM_COND_FLAG_MAP) { envattr_conf = (am_envattr_conf_t *)apr_hash_get(dir_cfg->envattr, am_cache_entry_get_string(session,&session->env[j].varname), APR_HASH_KEY_STRING); if (envattr_conf != NULL) varname = envattr_conf->name; } /* * Otherwise or if not found, use the attribute name * sent by the IdP. */ if (varname == NULL) varname = am_cache_entry_get_string(session, &session->env[j].varname); if (strcmp(varname, ce->varname) != 0) continue; value = am_cache_entry_get_string(session, &session->env[j].value); /* * Substiture backrefs if available */ if (ce->flags & AM_COND_FLAG_FSTR) ce = am_cond_substitue(r, ce, backrefs); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Evaluate %s vs \"%s\"", ce->directive, value); am_diag_printf(r, "evaluate value \"%s\" ", 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 = (ap_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); } am_diag_printf(r, "match=%s, ", match ? "yes" : "no"); } if (ce->flags & AM_COND_FLAG_NOT) { match = !match; am_diag_printf(r, "negating now match=%s ", match ? "yes" : "no"); } 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); am_diag_printf(r, "failed (no OR condition)" " returning HTTP_FORBIDDEN\n"); 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; am_diag_printf(r, "\n"); } am_diag_printf(r, "%s succeeds\n", __func__); return OK; } /* This function sets default Cache-Control headers. * * Parameters: * request_rec *r The request we are handling. * * Returns: * Nothing. */ void am_set_cache_control_headers(request_rec *r) { /* Send Cache-Control header to ensure that: * - no proxy in the path caches content inside this location (private), * - user agent have to revalidate content on server (must-revalidate). * - content is always stale as the session login status can change at any * time synchronously (Redirect logout, session cookie is removed) or * asynchronously (SOAP logout, session cookie still exists but is * invalid), * * But never prohibit specifically any user agent to cache or store content * * Setting the headers in err_headers_out ensures that they will be * sent for all responses. */ apr_table_setn(r->err_headers_out, "Cache-Control", "private, max-age=0, must-revalidate"); } /* This function reads the post data for a request. * * The data is stored in a buffer allocated from the request pool. * After successful operation *data contains a pointer to the data and * *length contains the length of the data. * The data will always be null-terminated. * * Parameters: * request_rec *r The request we read the form data from. * char **data Pointer to where we will store the pointer * to the data we read. * apr_size_t *length Pointer to where we will store the length * of the data we read. Pass NULL if you don't * need to know the length of the data. * * Returns: * OK if we successfully read the POST data. * An error if we fail to read the data. */ int am_read_post_data(request_rec *r, char **data, apr_size_t *length) { apr_size_t bytes_read; apr_size_t bytes_left; apr_size_t len; long read_length; int rc; /* Prepare to receive data from the client. We request that apache * dechunks data if it is chunked. */ rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); if (rc != OK) { return rc; } /* This function will send a 100 Continue response if the client is * waiting for that. If the client isn't going to send data, then this * function will return 0. */ if (!ap_should_client_block(r)) { len = 0; } else { len = r->remaining; } if (len >= 1024*1024) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Too large POST data payload (%lu bytes).", (unsigned long)len); return HTTP_BAD_REQUEST; } if (length != NULL) { *length = len; } *data = (char *)apr_palloc(r->pool, len + 1); if (*data == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Failed to allocate memory for %lu bytes of POST data.", (unsigned long)len); return HTTP_INTERNAL_SERVER_ERROR; } /* Make sure that the data is null-terminated. */ (*data)[len] = '\0'; bytes_read = 0; bytes_left = len; while (bytes_left > 0) { /* Read data from the client. Returns 0 on EOF and -1 on * error, the number of bytes otherwise. */ read_length = ap_get_client_block(r, &(*data)[bytes_read], bytes_left); if (read_length == 0) { /* got the EOF */ (*data)[bytes_read] = '\0'; if (length != NULL) { *length = bytes_read; } break; } else if (read_length < 0) { AM_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; } am_diag_printf(r, "POST data: %s\n", *data); return OK; } /* extract_query_parameter is a function which extracts the value of * a given parameter in a query string. The query string can be the * query_string parameter of a GET request, or it can be the data * passed to the web server in a POST request. * * Parameters: * apr_pool_t *pool The memory pool which the memory for * the value will be allocated from. * const char *query_string Either the query_string from a GET * request, or the data from a POST * request. * const char *name The name of the parameter to extract. * Note that the search for this name is * case sensitive. * * Returns: * The value of the parameter or NULL if we don't find the parameter. */ char *am_extract_query_parameter(apr_pool_t *pool, const char *query_string, const char *name) { const char *ip; const char *value_end; apr_size_t namelen; if (query_string == NULL) { return NULL; } ip = query_string; namelen = strlen(name); /* Find parameter. Searches for /[^&][&=$]/. * Moves ip to the first character after the name (either '&', '=' * or '\0'). */ for (;;) { /* First we find the name of the parameter. */ ip = strstr(ip, name); if (ip == NULL) { /* Parameter not found. */ return NULL; } /* Then we check what is before the parameter name. */ if (ip != query_string && ip[-1] != '&') { /* Name not preceded by [^&]. */ ip++; continue; } /* And last we check what follows the parameter name. */ if (ip[namelen] != '=' && ip[namelen] != '&' && ip[namelen] != '\0') { /* Name not followed by [&=$]. */ ip++; continue; } /* We have found the pattern. */ ip += namelen; break; } /* Now ip points to the first character after the name. If this * character is '&' or '\0', then this field doesn't have a value. * If this character is '=', then this field has a value. */ if (ip[0] == '=') { ip += 1; } /* The value is from ip to '&' or to the end of the string, whichever * comes first. */ value_end = strchr(ip, '&'); if (value_end != NULL) { /* '&' comes first. */ return apr_pstrndup(pool, ip, value_end - ip); } else { /* Value continues until the end of the string. */ return apr_pstrdup(pool, ip); } } /* Convert a hexadecimal digit to an integer. * * Parameters: * char c The digit we should convert. * * Returns: * The digit as an integer, or -1 if it isn't a hex digit. */ static int am_unhex_digit(char c) { if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'a' && c <= 'f') { return c - 'a' + 0xa; } else if (c >= 'A' && c <= 'F') { return c - 'A' + 0xa; } else { return -1; } } /* This function urldecodes a string in-place. * * Parameters: * char *data The string to urldecode. * * Returns: * OK if successful or HTTP_BAD_REQUEST if any escape sequence decodes to a * null-byte ('\0'), or if an invalid escape sequence is found. */ int am_urldecode(char *data) { char *ip; char *op; int c1, c2; if (data == NULL) { return HTTP_BAD_REQUEST; } ip = data; op = data; while (*ip) { switch (*ip) { case '+': *op = ' '; ip++; op++; break; case '%': /* Decode the hex digits. Note that we need to check the * result of the first conversion before attempting the * second conversion -- otherwise we may read past the end * of the string. */ c1 = am_unhex_digit(ip[1]); if (c1 < 0) { return HTTP_BAD_REQUEST; } c2 = am_unhex_digit(ip[2]); if (c2 < 0) { return HTTP_BAD_REQUEST; } *op = (c1 << 4) | c2; if (*op == '\0') { /* null-byte. */ return HTTP_BAD_REQUEST; } ip += 3; op++; break; default: *op = *ip; ip++; op++; } } *op = '\0'; return OK; } /* This function urlencodes a string. It will escape all characters * except a-z, A-Z, 0-9, '_' and '.'. * * Parameters: * apr_pool_t *pool The pool we should allocate memory from. * const char *str The string we should urlencode. * * Returns: * The urlencoded string, or NULL if str == NULL. */ char *am_urlencode(apr_pool_t *pool, const char *str) { const char *ip; apr_size_t length; char *ret; char *op; int hi, low; /* Return NULL if str is NULL. */ if(str == NULL) { return NULL; } /* Find the length of the output string. */ length = 0; for(ip = str; *ip; ip++) { if(*ip >= 'a' && *ip <= 'z') { length++; } else if(*ip >= 'A' && *ip <= 'Z') { length++; } else if(*ip >= '0' && *ip <= '9') { length++; } else if(*ip == '_' || *ip == '.') { length++; } else { length += 3; } } /* Add space for null-terminator. */ length++; /* Allocate memory for string. */ ret = (char *)apr_palloc(pool, length); /* Encode string. */ for(ip = str, op = ret; *ip; ip++, op++) { if(*ip >= 'a' && *ip <= 'z') { *op = *ip; } else if(*ip >= 'A' && *ip <= 'Z') { *op = *ip; } else if(*ip >= '0' && *ip <= '9') { *op = *ip; } else if(*ip == '_' || *ip == '.') { *op = *ip; } else { *op = '%'; op++; hi = (*ip & 0xf0) >> 4; if(hi < 0xa) { *op = '0' + hi; } else { *op = 'A' + hi - 0xa; } op++; low = *ip & 0x0f; if(low < 0xa) { *op = '0' + low; } else { *op = 'A' + low - 0xa; } } } /* Make output string null-terminated. */ *op = '\0'; return ret; } /* * Check that a URL is safe for redirect. * * Parameters: * request_rec *r The request we are processing. * const char *url The URL we should check. * * Returns: * OK on success, HTTP_BAD_REQUEST otherwise. */ int am_check_url(request_rec *r, const char *url) { const char *i; for (i = url; *i; i++) { if (*i >= 0 && *i < ' ') { /* Deny all control-characters. */ AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, HTTP_BAD_REQUEST, r, "Control character detected in URL."); return HTTP_BAD_REQUEST; } if (*i == '\\') { /* Reject backslash character, as it can be used to bypass * redirect URL validation. */ AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, HTTP_BAD_REQUEST, r, "Backslash character detected in URL."); return HTTP_BAD_REQUEST; } } return OK; } /* This function generates a given number of (pseudo)random bytes. * The current implementation uses OpenSSL's RAND_*-functions. * * Parameters: * request_rec *r The request we are generating random bytes for. * The request is used for configuration and * error/warning reporting. * void *dest The address if the buffer we should fill with data. * apr_size_t count The number of random bytes to create. * * Returns: * OK on success, or HTTP_INTERNAL_SERVER on failure. */ int am_generate_random_bytes(request_rec *r, void *dest, apr_size_t count) { int rc; rc = RAND_bytes((unsigned char *)dest, (int)count); if(rc != 1) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error generating random data: %lu", ERR_get_error()); return HTTP_INTERNAL_SERVER_ERROR; } return OK; } /* This function generates an id which is AM_ID_LENGTH characters long. * The id will consist of hexadecimal characters. * * Parameters: * request_rec *r The request we associate allocated memory with. * * Returns: * The session id, made up of AM_ID_LENGTH hexadecimal characters, * terminated by a null-byte. */ char *am_generate_id(request_rec *r) { int rc; char *ret; int rand_data_len; unsigned char *rand_data; int i; unsigned char b; int hi, low; ret = (char *)apr_palloc(r->pool, AM_ID_LENGTH + 1); /* We need to round the length of the random data _up_, in case the * length of the session id isn't even. */ rand_data_len = (AM_ID_LENGTH + 1) / 2; /* Fill the last rand_data_len bytes of the string with * random bytes. This allows us to overwrite from the beginning of * the string. */ rand_data = (unsigned char *)&ret[AM_ID_LENGTH - rand_data_len]; /* Generate random numbers. */ rc = am_generate_random_bytes(r, rand_data, rand_data_len); if(rc != OK) { return NULL; } /* Convert the random bytes to hexadecimal. Note that we will write * AM_ID_LENGTH+1 characters if we have a non-even length of the * session id. This is OK - we will simply overwrite the last character * with the null-terminator afterwards. */ for(i = 0; i < AM_ID_LENGTH; i += 2) { b = rand_data[i / 2]; hi = (b >> 4) & 0xf; low = b & 0xf; if(hi >= 0xa) { ret[i] = 'a' + hi - 0xa; } else { ret[i] = '0' + hi; } if(low >= 0xa) { ret[i+1] = 'a' + low - 0xa; } else { ret[i+1] = '0' + low; } } /* Add null-terminator- */ ret[AM_ID_LENGTH] = '\0'; return ret; } /* This returns the directroy part of a path, a la dirname(3) * * Parameters: * apr_pool_t p Pool to allocate memory from * const char *path Path to extract directory from * * Returns: * The directory part of path */ const char *am_filepath_dirname(apr_pool_t *p, const char *path) { char *cp; /* * Try Unix and then Windows style. Borrowed from * apr_match_glob(), it seems it cannot be made more * portable. */ if (((cp = strrchr(path, (int)'/')) == NULL) && ((cp = strrchr(path, (int)'\\')) == NULL)) return "."; return apr_pstrndup(p, path, cp - path); } /* * Allocate and initialize a am_file_data_t * * Parameters: * apr_pool_t *pool Allocation pool. * const char *path If non-NULL initialize file_data->path to copy of path * * Returns: * Newly allocated & initialized file_data_t */ am_file_data_t *am_file_data_new(apr_pool_t *pool, const char *path) { am_file_data_t *file_data = NULL; if ((file_data = apr_pcalloc(pool, sizeof(am_file_data_t))) == NULL) { return NULL; } file_data->pool = pool; file_data->rv = APR_EINIT; if (path) { file_data->path = apr_pstrdup(file_data->pool, path); } return file_data; } /* * Allocate a new am_file_data_t and copy * * Parameters: * apr_pool_t *pool Allocation pool. * am_file_data_t *src_file_data The src being copied. * * Returns: * Newly allocated & initialized from src_file_data */ am_file_data_t *am_file_data_copy(apr_pool_t *pool, am_file_data_t *src_file_data) { am_file_data_t *dst_file_data = NULL; if ((dst_file_data = am_file_data_new(pool, src_file_data->path)) == NULL) { return NULL; } dst_file_data->path = apr_pstrdup(pool, src_file_data->path); dst_file_data->stat_time = src_file_data->stat_time; dst_file_data->finfo = src_file_data->finfo; dst_file_data->contents = apr_pstrdup(pool, src_file_data->contents); dst_file_data->read_time = src_file_data->read_time; dst_file_data->rv = src_file_data->rv; dst_file_data->strerror = apr_pstrdup(pool, src_file_data->strerror); dst_file_data->generated = src_file_data->generated; return dst_file_data; } /* * Peform a stat on a file to get it's properties * * A stat is performed on the file. If there was an error the * result value is left in file_data->rv and an error description * string is formatted and left in file_data->strerror and function * returns the rv value. If the stat was successful the stat * information is left in file_data->finfo and APR_SUCCESS * set set as file_data->rv and returned as the function result. * * The file_data->stat_time indicates if and when the stat was * performed, a zero time value indicates the operation has not yet * been performed. * * Parameters: * am_file_data_t *file_data Struct containing file information * * Returns: * APR status code, same value as file_data->rv */ apr_status_t am_file_stat(am_file_data_t *file_data) { char buffer[512]; if (file_data == NULL) { return APR_EINVAL; } file_data->strerror = NULL; file_data->stat_time = apr_time_now(); file_data->rv = apr_stat(&file_data->finfo, file_data->path, APR_FINFO_SIZE, file_data->pool); if (file_data->rv != APR_SUCCESS) { file_data->strerror = apr_psprintf(file_data->pool, "apr_stat: Error opening \"%s\" [%d] \"%s\"", file_data->path, file_data->rv, apr_strerror(file_data->rv, buffer, sizeof(buffer))); } return file_data->rv; } /* * Read file into dynamically allocated buffer * * First a stat is performed on the file. If there was an error the * result value is left in file_data->rv and an error description * string is formatted and left in file_data->strerror and function * returns the rv value. If the stat was successful the stat * information is left in file_data->finfo. * * A buffer is dynamically allocated and the contents of the file is * read into file_data->contents. If there was an error the result * value is left in file_data->rv and an error description string is * formatted and left in file_data->strerror and the function returns * the rv value. * * The file_data->stat_time and file_data->read_time indicate if and * when those operations were performed, a zero time value indicates * the operation has not yet been performed. * * Parameters: * am_file_data_t *file_data Struct containing file information * * Returns: * APR status code, same value as file_data->rv */ apr_status_t am_file_read(am_file_data_t *file_data) { char buffer[512]; apr_file_t *fd; apr_size_t nbytes; if (file_data == NULL) { return APR_EINVAL; } file_data->rv = APR_SUCCESS; file_data->strerror = NULL; am_file_stat(file_data); if (file_data->rv != APR_SUCCESS) { return file_data->rv; } if ((file_data->rv = apr_file_open(&fd, file_data->path, APR_READ, 0, file_data->pool)) != 0) { file_data->strerror = apr_psprintf(file_data->pool, "apr_file_open: Error opening \"%s\" [%d] \"%s\"", file_data->path, file_data->rv, apr_strerror(file_data->rv, buffer, sizeof(buffer))); return file_data->rv; } file_data->read_time = apr_time_now(); nbytes = file_data->finfo.size; file_data->contents = (char *)apr_palloc(file_data->pool, nbytes + 1); file_data->rv = apr_file_read_full(fd, file_data->contents, nbytes, NULL); if (file_data->rv != 0) { file_data->strerror = apr_psprintf(file_data->pool, "apr_file_read_full: Error reading \"%s\" [%d] \"%s\"", file_data->path, file_data->rv, apr_strerror(file_data->rv, buffer, sizeof(buffer))); (void)apr_file_close(fd); return file_data->rv; } file_data->contents[nbytes] = '\0'; (void)apr_file_close(fd); return file_data->rv; } /* * 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) { AM_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) { AM_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; mod_cfg = am_get_mod_cfg(r->server); if (mod_cfg->post_dir == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "MellonPostReplay enabled but MellonPostDirectory not set " "-- cannot save post data"); return HTTP_INTERNAL_SERVER_ERROR; } 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 { AM_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"); } if ((psf_id = am_generate_id(r)) == NULL) { AM_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) { AM_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) { AM_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) { AM_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)) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "cannot close POST session file"); return HTTP_INTERNAL_SERVER_ERROR; } if (charset != NULL) charset = apr_psprintf(r->pool, "&charset=%s", am_urlencode(r->pool, charset)); else charset = ""; *relay_state = apr_psprintf(r->pool, "%srepost?id=%s&ReturnTo=%s&enctype=%s%s", am_get_endpoint_url(r), psf_id, am_urlencode(r->pool, *relay_state), content_type, charset); return OK; } /* * This function replaces CRLF by LF in a string * * Parameters: * request_rec *r The current request * const char *str The string * * Returns: * Output string */ const char *am_strip_cr(request_rec *r, const char *str) { char *output; const char *cp; apr_size_t i; output = apr_palloc(r->pool, strlen(str) + 1); i = 0; for (cp = str; *cp; cp++) { if ((*cp == '\r') && (*(cp + 1) == '\n')) continue; output[i++] = *cp; } output[i++] = '\0'; return (const char *)output; } /* * This function replaces LF by CRLF in a string * * Parameters: * request_rec *r The current request * const char *str The string * * Returns: * Output string */ const char *am_add_cr(request_rec *r, const char *str) { char *output; const char *cp; apr_size_t xlen; apr_size_t i; xlen = 0; for (cp = str; *cp; cp++) if (*cp == '\n') xlen++; output = apr_palloc(r->pool, strlen(str) + xlen + 1); i = 0; for (cp = str; *cp; cp++) { if (*cp == '\n') output[i++] = '\r'; output[i++] = *cp; } output[i++] = '\0'; return (const char *)output; } /* * This function tokenize a string, just like strtok_r, except that * the separator is a string instead of a character set. * * Parameters: * const char *str The string to tokenize * const char *sep The separator string * char **last Pointer to state (char *) * * Returns: * OK on success, HTTP_INTERNAL_SERVER_ERROR otherwise */ const char *am_xstrtok(request_rec *r, const char *str, const char *sep, char **last) { char *s; char *np; /* Resume */ if (str != NULL) s = apr_pstrdup(r->pool, str); else s = *last; /* End of string */ if (*s == '\0') return NULL; /* Next sep exists? */ if ((np = strstr(s, sep)) == NULL) { *last = s + strlen(s); } else { *last = np + strlen(sep); memset(np, 0, strlen(sep)); } return s; } /* This function strips leading spaces and tabs from a string * * Parameters: * const char **s Pointer to the string * */ void am_strip_blank(const char **s) { while ((**s == ' ') || (**s == '\t')) (*s)++; return; } /* This function extracts a MIME header from a MIME section * * Parameters: * request_rec *r The request * const char *m The MIME section * const char *h The header to extract (case insensitive) * * Returns: * The header value, or NULL on failure. */ const char *am_get_mime_header(request_rec *r, const char *m, const char *h) { const char *line; char *l1; const char *value; char *l2; for (line = am_xstrtok(r, m, "\n", &l1); line && *line; line = am_xstrtok(r, NULL, "\n", &l1)) { am_strip_blank(&line); if (((value = am_xstrtok(r, line, ":", &l2)) != NULL) && (strcasecmp(value, h) == 0)) { if ((value = am_xstrtok(r, NULL, ":", &l2)) != NULL) am_strip_blank(&value); return value; } } return NULL; } /* This function extracts an attribute from a header * * Parameters: * request_rec *r The request * const char *h The header * const char *v Optional header value to check (case insensitive) * const char *a Optional attribute to extract (case insensitive) * * Returns: * if i was provided, item value, or NULL on failure. * if i is NULL, the whole header, or NULL on failure. This is * useful for testing v. */ const char *am_get_header_attr(request_rec *r, const char *h, const char *v, const char *a) { const char *value; const char *attr; char *l1; const char *attr_value = NULL; /* Looking for * header-value; item_name="item_value"\n */ if ((value = am_xstrtok(r, h, ";", &l1)) == NULL) return NULL; am_strip_blank(&value); /* If a header value was provided, check it */ if ((v != NULL) && (strcasecmp(value, v) != 0)) return NULL; /* If no attribute name is provided, return everything */ if (a == NULL) return h; while ((attr = am_xstrtok(r, NULL, ";", &l1)) != NULL) { const char *attr_name = NULL; char *l2; am_strip_blank(&attr); attr_name = am_xstrtok(r, attr, "=", &l2); if ((attr_name != NULL) && (strcasecmp(attr_name, a) == 0)) { if ((attr_value = am_xstrtok(r, NULL, "=", &l2)) != NULL) am_strip_blank(&attr_value); break; } } /* Remove leading and trailing quotes */ if (attr_value != NULL) { apr_size_t len; len = strlen(attr_value); if ((len > 1) && (attr_value[len - 1] == '\"')) attr_value = apr_pstrndup(r->pool, attr_value, len - 1); if (attr_value[0] == '\"') attr_value++; } return attr_value; } /* This function checks for a header name/value existence * * Parameters: * request_rec *r The request * const char *h The header (case insensitive) * const char *v Optional header value to check (case insensitive) * * Returns: * 0 if header does not exists or does not has the value, 1 otherwise */ int am_has_header(request_rec *r, const char *h, const char *v) { return (am_get_header_attr(r, h, v, NULL) != NULL); } /* This function extracts the body from a MIME section * * Parameters: * request_rec *r The request * const char *mime The MIME section * * Returns: * The MIME section body, or NULL on failure. */ const char *am_get_mime_body(request_rec *r, const char *mime) { const char lflf[] = "\n\n"; const char *body; apr_size_t body_len; if ((body = strstr(mime, lflf)) == NULL) { AM_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) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "Cannot find provider service %s from metadata.", service_name); return NULL; } return url; } /*------------------------ Begin Token Parsing Code --------------------------*/ typedef enum { TOKEN_WHITESPACE = 1, TOKEN_SEMICOLON, TOKEN_COMMA, TOKEN_EQUAL, TOKEN_IDENTIFIER, TOKEN_DBL_QUOTE_STRING, } TokenType; typedef struct { TokenType type; /* The type of this token */ char *str; /* The string value of the token */ apr_size_t len; /* The number of characters in the token */ apr_size_t offset; /* The offset from the beginning of the string to the start of the token */ } Token; #ifdef DEBUG /* Return string representation of TokenType enumeration * * Parameters: * token_type A TokenType enumeration * Returns: String name of token_type */ static const char * token_type_str(TokenType token_type) { switch(token_type) { case TOKEN_WHITESPACE: return "WHITESPACE"; case TOKEN_SEMICOLON: return "SEMICOLON"; case TOKEN_COMMA: return "COMMA"; case TOKEN_EQUAL: return "EQUAL"; case TOKEN_IDENTIFIER: return "IDENTIFIER"; case TOKEN_DBL_QUOTE_STRING: return "DBL_QUOTE_STRING"; default: return "unknown"; } } static void dump_tokens(request_rec *r, apr_array_header_t *tokens) { apr_size_t i; for (i = 0; i < tokens->nelts; i++) { Token token = APR_ARRAY_IDX(tokens, i, Token); AM_LOG_RERROR(APLOG_MARK, APLOG_DEBUG, 0, r, "token[%2zd] %s \"%s\" offset=%lu len=%lu ", i, token_type_str(token.type), token.str, token.offset, token.len); } } #endif /* Initialize token and add to list of tokens * * Utility to assist tokenize function. * * A token object is created and added to the end of the list of * tokens. It is initialized with the type of token, a copy of the * string, it's length, and it's offset from the beginning of the * string where it was found. * * Tokens with special processing needs are also handled here. * * A double quoted string will: * * * Have it's delimiting quotes removed. * * Will unescape escaped characters. * * Parameters: * tokens Array of Token objects. * type The type of the token (e.g. TokenType). * str The string the token was parsed from, used to compute * the position of the token in the original string. * start The first character in the token. * end the last character in the token. */ static inline void push_token(apr_array_header_t *tokens, TokenType type, const char *str, const char *start, const char *end) { apr_size_t offset = start - str; Token *token = apr_array_push(tokens); if (type == TOKEN_DBL_QUOTE_STRING) { /* do not include quotes in token value */ start++; end--; } token->type = type; token->len = end - start; token->offset = offset; token->str = apr_pstrmemdup(tokens->pool, start, token->len); if (type == TOKEN_DBL_QUOTE_STRING) { /* * The original HTTP 1.1 spec was ambiguous with respect to * backslash quoting inside double quoted strings. This has since * been resolved in this errata: * * http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p1-messaging-16.html#rfc.section.3.2.3 * * Which states: * * Recipients that process the value of the quoted-string MUST * handle a quoted-pair as if it were replaced by the octet * following the backslash. * * Senders SHOULD NOT escape octets in quoted-strings that do not * require escaping (i.e., other than DQUOTE and the backslash * octet). */ char *p, *t; for (p = token->str; *p; p++) { if (p[0] == '\\' && p[1]) { /* * Found backslash with following character. * Move rest of string down 1 character. */ for (t = p; *t; t++) { t[0] = t[1]; } token->len--; } } } } /* Break a string into a series of tokens * * Given a string return an array of tokens. If the string cannot be * successfully parsed an error string is returned at the location * specified by the error parameter, if error is NULL then the parsing * was successful. If an error occured the returned array of tokens * will include all tokens parsed up until where the unrecognized * input occurred. The input str is never modified. * * Parameters: * pool memory allocation pool * str input string to be parsed. * ignore_whitespace if True whitespace tokens are not returned * error location where error string is returned * if NULL no error occurred * Returns: array of Token objects */ static apr_array_header_t * tokenize(apr_pool_t *pool, const char *str, bool ignore_whitespace, char **error) { apr_array_header_t *tokens = apr_array_make(pool, 10, sizeof(Token)); const char *p, *start; *error = NULL; p = start = str; while(*p) { if (apr_isspace(*p)) { /* whitespace */ p++; while(*p && apr_isspace(*p)) p++; if (!ignore_whitespace) { push_token(tokens, TOKEN_WHITESPACE, str, start, p); } start = p; } else if (apr_isalpha(*p)) { /* identifier: must begin with alpha then any alphanumeric or underscore */ p++; while(*p && (apr_isalnum(*p) || *p == '_')) p++; push_token(tokens, TOKEN_IDENTIFIER, str, start, p); start = p; } else if (*p == '"') { /* double quoted string */ p++; /* step over double quote */ while(*p) { if (*p == '\\') { /* backslash escape */ p++; /* step over backslash */ if (*p) { p++; /* step over escaped character */ } else { break; /* backslash at end of string, stop */ } } if (*p == '\"') break; /* terminating quote delimiter */ p++; /* keep scanning */ } if (*p != '\"') { *error = apr_psprintf(pool, "unterminated string beginning at " "position %" APR_SIZE_T_FMT " in \"%s\"", start-str, str); break; } p++; push_token(tokens, TOKEN_DBL_QUOTE_STRING, str, start, p); start = p; } else if (*p == '=') { /* equals */ p++; push_token(tokens, TOKEN_EQUAL, str, start, p); start = p; } else if (*p == ',') { /* comma */ p++; push_token(tokens, TOKEN_COMMA, str, start, p); start = p; } else if (*p == ';') { /* semicolon */ p++; push_token(tokens, TOKEN_SEMICOLON, str, start, p); start = p; } else { /* unrecognized token */ *error = apr_psprintf(pool, "unknown token at " "position %" APR_SIZE_T_FMT " in string \"%s\"", p-str, str); break; } } return tokens; } /* Test if the token is what we're looking for * * Given an index into the tokens array determine if the token type * matches. If the value parameter is non-NULL then the token's value * must also match. If the array index is beyond the last array item * false is returned. * * Parameters: * tokens Array of Token objects * index Index used to select the Token object from the Tokens array. * If the index is beyond the last array item False is returned. * type The token type which must match * value If non-NULL then the token string value must be equal to this. * Returns: True if the token matches, False otherwise. */ static bool is_token(apr_array_header_t *tokens, apr_size_t index, TokenType type, const char *value) { if (index >= tokens->nelts) { return false; } Token token = APR_ARRAY_IDX(tokens, index, Token); if (token.type != type) { return false; } if (value) { if (!g_str_equal(token.str, value)) { return false; } } return true; } /*------------------------- End Token Parsing Code ---------------------------*/ /* Return message describing position an error when parsing. * * When parsing we expect tokens to appear in a certain sequence. We * report the contents of the unexpected token and it's position in * the string. However if the parsing error is due to the fact we've * exhausted all tokens but are still expecting another token then our * error message indicates we reached the end of the string. * * Parameters: * tokens Array of Token objects. * index Index in tokens array where bad token was found */ static inline const char * parse_error_msg(apr_array_header_t *tokens, apr_size_t index) { if (index >= tokens->nelts) { return "end of string"; } return apr_psprintf(tokens->pool, "\"%s\" at position %" APR_SIZE_T_FMT, APR_ARRAY_IDX(tokens, index, Token).str, APR_ARRAY_IDX(tokens, index, Token).offset); } /* This function checks if an HTTP PAOS header is valid and * returns any service options which may have been specified. * * A PAOS header is composed of a mandatory PAOS version and service * values. A semicolon separates the version from the service values. * * Service values are delimited by semicolons, and options are * comma-delimited from the service value and each other. * * The PAOS version must be in the form ver="xxx" (note the version * string must be in double quotes). * * The ECP service must be specified, it MAY be followed by optional * comma seperated options, all values must be in double quotes. * * ECP Service * "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp" * * Recognized Options: * * Support for channel bindings * urn:oasis:names:tc:SAML:protocol:ext:channel-binding * * Support for Holder-of-Key subject confirmation * urn:oasis:names:tc:SAML:2.0:cm:holder-of-key * * Request for signed SAML request * urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp:2.0:WantAuthnRequestsSigned * * Request to delegate credentials to the service provider * urn:oasis:names:tc:SAML:2.0:conditions:delegation * * * Example PAOS HTTP header:: * * PAOS: ver="urn:liberty:paos:2003-08"; * "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp", * "urn:oasis:names:tc:SAML:protocol:ext:channel-binding", * "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key" * * Parameters: * request_rec *r The request * const char *header The PAOS header value * ECPServiceOptions *options_return * Pointer to location to receive options, * may be NULL. Bitmask of option flags. * * Returns: * true if the PAOS header is valid, false otherwise. If options is non-NULL * then the set of option flags is returned there. * */ bool am_parse_paos_header(request_rec *r, const char *header, ECPServiceOptions *options_return) { bool result = false; ECPServiceOptions options = 0; apr_array_header_t *tokens; apr_size_t i; char *error; AM_LOG_RERROR(APLOG_MARK, APLOG_DEBUG, 0, r, "PAOS header: \"%s\"", header); tokens = tokenize(r->pool, header, true, &error); #ifdef DEBUG dump_tokens(r, tokens); #endif if (error) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "%s", error); goto cleanup; } /* Header must begin with "ver=xxx" where xxx is paos version */ if (!is_token(tokens, 0, TOKEN_IDENTIFIER, "ver") || !is_token(tokens, 1, TOKEN_EQUAL, NULL) || !is_token(tokens, 2, TOKEN_DBL_QUOTE_STRING, LASSO_PAOS_HREF)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected header to begin with ver=\"%s\", " "actual header=\"%s\"", LASSO_PAOS_HREF, header); goto cleanup; } /* Next is the service value, separated from the version by a semicolon */ if (!is_token(tokens, 3, TOKEN_SEMICOLON, NULL)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected semicolon after PAOS version " "but found %s in header=\"%s\"", parse_error_msg(tokens, 3), header); goto cleanup; } if (!is_token(tokens, 4, TOKEN_DBL_QUOTE_STRING, LASSO_ECP_HREF)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected service token to be \"%s\", " "but found %s in header=\"%s\"", LASSO_ECP_HREF, parse_error_msg(tokens, 4), header); goto cleanup; } /* After the service value there may be optional flags separated by commas */ if (tokens->nelts == 5) { /* no options */ result = true; goto cleanup; } /* More tokens after the service value, must be options, iterate over them */ for (i = 5; i < tokens->nelts; i++) { if (!is_token(tokens, i, TOKEN_COMMA, NULL)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected comma after PAOS service " "but found %s in header=\"%s\"", parse_error_msg(tokens, i), header); goto cleanup; } if (++i > tokens->nelts) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected option after comma " "in header=\"%s\"", header); goto cleanup; } Token token = APR_ARRAY_IDX(tokens, i, Token); if (token.type != TOKEN_DBL_QUOTE_STRING) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "invalid PAOS header, " "expected quoted string after comma " "but found %s in header=\"%s\"", parse_error_msg(tokens, i), header); goto cleanup; } /* Have an option string, convert it to a bit flag */ const char *value = token.str; if (g_str_equal(value, LASSO_SAML_EXT_CHANNEL_BINDING)) { options |= ECP_SERVICE_OPTION_CHANNEL_BINDING; } else if (g_str_equal(value, LASSO_SAML2_CONFIRMATION_METHOD_HOLDER_OF_KEY)) { options |= ECP_SERVICE_OPTION_HOLDER_OF_KEY; } else if (g_str_equal(value, LASSO_SAML2_ECP_PROFILE_WANT_AUTHN_SIGNED)) { options |= ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED; } else if (g_str_equal(value, LASSO_SAML2_CONDITIONS_DELEGATION)) { options |= ECP_SERVICE_OPTION_DELEGATION; } else { AM_LOG_RERROR(APLOG_MARK, APLOG_WARNING, 0, r, "Unknown PAOS service option = \"%s\"", value); goto cleanup; } } result = true; cleanup: if (options_return) { *options_return = options; } return result; } /* This function checks if Accept header has a media type * * Given an Accept header value like this: * * "text/html,application/xhtml+xml,application/xml;q=0.9" * * Parse the string and find name of each media type, ignore any parameters * bound to the name. Test to see if the name matches the input media_type. * * Parameters: * request_rec *r The request * const char *header The header value * const char *media_type media type header value to check (case insensitive) * * Returns: * true if media type is in header, false otherwise */ bool am_header_has_media_type(request_rec *r, const char *header, const char *media_type) { bool result = false; char **comma_tokens = NULL; char **media_ranges = NULL; char *media_range = NULL; if (header == NULL) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "invalid Accept header, NULL"); goto cleanup; } /* * Split the header into a list of media_range tokens separated by * a comma and iterate over the list. */ comma_tokens = g_strsplit(header, ",", 0); for (media_ranges = comma_tokens, media_range = *media_ranges; media_range; media_range = *(++media_ranges)) { char **semicolon_tokens = NULL; char *name = NULL; /* * Split the media_range into a name and parameters, each * separated by a semicolon. The first element in the list is * the media_type name, subsequent params are optional and ignored. */ media_range = g_strstrip(media_range); semicolon_tokens = g_strsplit(media_range, ";", 0); /* * Does the media_type match our required media_type? * If so clean up and return success. */ name = g_strstrip(semicolon_tokens[0]); if (name && g_str_equal(name, media_type)) { result = true; g_strfreev(semicolon_tokens); goto cleanup; } g_strfreev(semicolon_tokens); } cleanup: g_strfreev(comma_tokens); return result; } /* * Lookup a config string in a specific language. If lang is NULL and * the config string had been defined without a language qualifier * return the unqualified value. If not found NULL is returned. */ const char *am_get_config_langstring(apr_hash_t *h, const char *lang) { char *string; if (lang == NULL) { lang = ""; } string = (char *)apr_hash_get(h, lang, APR_HASH_KEY_STRING); return string; } /* * Get the value of boolean query parameter. * * Parameters: * request_rec *r The request * const char *name The name of the query parameter * int *return_value The address of the variable to receive * the boolean value * int default_value The value returned if parameter is absent or * in event of an error * * Returns: * OK on success, HTTP error otherwise * * Looks for the named parameter in the query parameters, if found * parses the value which must be one of: * * * true * * false * * If value cannot be parsed HTTP_BAD_REQUEST is returned. * * If not found, or if there is an error, the returned value is set to * default_value. */ int am_get_boolean_query_parameter(request_rec *r, const char *name, int *return_value, int default_value) { char *value_str; int ret = OK; *return_value = default_value; value_str = am_extract_query_parameter(r->pool, r->args, name); if (value_str != NULL) { ret = am_urldecode(value_str); if (ret != OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error urldecoding \"%s\" boolean query parameter, " "value=\"%s\"", name, value_str); return ret; } if(!strcmp(value_str, "true")) { *return_value = TRUE; } else if(!strcmp(value_str, "false")) { *return_value = FALSE; } else { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Invalid value for \"%s\" boolean query parameter, " "value=\"%s\"", name, value_str); ret = HTTP_BAD_REQUEST; } } return ret; } /* * Get the URL of the AssertionConsumerServer having specific protocol * binding. * * Parameters: * LassoProvider *provider The provider whose endpoints will be scanned. * const char *binding The required binding short name. * * Returns: * The endpoint URL or NULL if not found. Must be freed with g_free(). * * Lasso does not provide a public API to select a provider endpoint * by binding. The best we can do is iterate over a list of endpoint * descriptors and select a matching descriptor. * * Lasso does not document the format of these descriptor names but * essentially a descriptor is a space separated concatenation of the * endpoint properties. For SAML2 one can assume it is the endpoint * type, optionally followed by the protocol binding name, optionally * followd by the index (if the endpoint type is indexed). If the * endpoint is a response location then "ResponseLocation" will be * appended as the final token. For example here is a list of * descriptors returned for a service provider (note they are * unordered). * * "AssertionConsumerService HTTP-POST 0" * "AuthnRequestsSigned" * "AssertionConsumerService PAOS 2" * "SingleLogoutService HTTP-Redirect" * "SingleLogoutService SOAP" * "AssertionConsumerService HTTP-Artifact 1" * "NameIDFormat" * "SingleLogoutService HTTP-POST ResponseLocation" * * The possible binding names are: * * "SOAP" * "HTTP-Redirect" * "HTTP-POST" * "HTTP-Artifact" * "PAOS" * "URI" * * We know the AssertionConsumerService is indexed. If there is more * than one endpoint with the required binding we select the one with * the lowest index assuming it is preferred. */ char *am_get_assertion_consumer_service_by_binding(LassoProvider *provider, const char *binding) { GList *descriptors; char *url; char *selected_descriptor; char *descriptor; char **tokens; guint n_tokens; GList *i; char *endptr; long descriptor_index, min_index; url = NULL; selected_descriptor = NULL; min_index = LONG_MAX; /* The descriptor list is unordered */ descriptors = lasso_provider_get_metadata_keys_for_role(provider, LASSO_PROVIDER_ROLE_SP); for (i = g_list_first(descriptors), tokens=NULL; i; i = g_list_next(i), g_strfreev(tokens)) { descriptor = i->data; descriptor_index = LONG_MAX; /* * Split the descriptor into tokens, only consider descriptors * which have at least 3 tokens and whose first token is * AssertionConsumerService */ tokens = g_strsplit(descriptor, " ", 0); n_tokens = g_strv_length(tokens); if (n_tokens < 3) continue; if (!g_str_equal(tokens[0], "AssertionConsumerService")) continue; if (!g_str_equal(tokens[1], binding)) continue; descriptor_index = strtol(tokens[2], &endptr, 10); if (tokens[2] == endptr) continue; /* could not parse int */ if (descriptor_index < min_index) { selected_descriptor = descriptor; min_index = descriptor_index; } } if (selected_descriptor) { url = lasso_provider_get_metadata_one_for_role(provider, LASSO_PROVIDER_ROLE_SP, selected_descriptor); } lasso_release_list_of_strings(descriptors); return url; } #ifdef HAVE_ECP /* String representation of ECPServiceOptions bitmask * * ECPServiceOptions is a bitmask of flags. Return a comma separated string * of all the flags. If any bit in the bitmask is unaccounted for an * extra string will be appended of the form "(unknown bits = x)". * * Parameters: * pool memory allocation pool * options bitmask of PAOS options */ char *am_ecp_service_options_str(apr_pool_t *pool, ECPServiceOptions options) { apr_array_header_t *names = apr_array_make(pool, 4, sizeof(const char *)); if (options & ECP_SERVICE_OPTION_CHANNEL_BINDING) { APR_ARRAY_PUSH(names, const char *) = "channel-binding"; options &= ~ECP_SERVICE_OPTION_CHANNEL_BINDING; } if (options & ECP_SERVICE_OPTION_HOLDER_OF_KEY) { APR_ARRAY_PUSH(names, const char *) = "holder-of-key"; options &= ~ECP_SERVICE_OPTION_HOLDER_OF_KEY; } if (options & ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED) { APR_ARRAY_PUSH(names, const char *) = "want-authn-signed"; options &= ~ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED; } if (options & ECP_SERVICE_OPTION_DELEGATION) { APR_ARRAY_PUSH(names, const char *) = "delegation"; options &= ~ECP_SERVICE_OPTION_DELEGATION; } if (options) { APR_ARRAY_PUSH(names, const char *) = apr_psprintf(pool, "(unknown bits = %#x)", options); } return apr_array_pstrcat(pool, names, ','); } /* Determine if request is compatible with PAOS, decode headers * * To indicate support for the ECP profile, and the PAOS binding, the * request MUST include the following HTTP header fields: * * 1. An Accept header indicating acceptance of the MIME type * "application/vnd.paos+xml" * * 2. A PAOS header specifying the PAOS version with a value, at minimum, of * "urn:liberty:paos:2003-08" and a supported service value of * "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp". The service value MAY * contain option values. * * This function validates the Accept header the the PAOS header, if * all condidtions are met it returns true, false otherwise. If the * validation succeeds any ECP options specified along with the * ECP service are parsed and stored in req_cfg->ecp_service_options * * Any error discovered during processing are returned in the * error_code parameter, zero indicates success. This function never * returns true if an error occurred. * * Parameters: * request_rec *r The current request. * int * error_code Return error code here * */ bool am_is_paos_request(request_rec *r, int *error_code) { const char *accept_header = NULL; const char *paos_header = NULL; bool have_paos_media_type = false; bool valid_paos_header = false; bool is_paos = false; ECPServiceOptions ecp_service_options = 0; *error_code = 0; accept_header = apr_table_get(r->headers_in, "Accept"); paos_header = apr_table_get(r->headers_in, "PAOS"); if (accept_header) { if (am_header_has_media_type(r, accept_header, MEDIA_TYPE_PAOS)) { have_paos_media_type = true; } } if (paos_header) { if (am_parse_paos_header(r, paos_header, &ecp_service_options)) { valid_paos_header = true; } else { if (*error_code == 0) *error_code = AM_ERROR_INVALID_PAOS_HEADER; } } if (have_paos_media_type) { if (valid_paos_header) { is_paos = true; } else { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "request supplied PAOS media type in Accept header " "but omitted valid PAOS header"); if (*error_code == 0) *error_code = AM_ERROR_MISSING_PAOS_HEADER; } } else { if (valid_paos_header) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "request supplied valid PAOS header " "but omitted PAOS media type in Accept header"); if (*error_code == 0) *error_code = AM_ERROR_MISSING_PAOS_MEDIA_TYPE; } } AM_LOG_RERROR(APLOG_MARK, APLOG_DEBUG, 0, r, "have_paos_media_type=%s valid_paos_header=%s is_paos=%s " "error_code=%d ecp options=[%s]", have_paos_media_type ? "True" : "False", valid_paos_header ? "True" : "False", is_paos ? "True" : "False", *error_code, am_ecp_service_options_str(r->pool, ecp_service_options)); if (is_paos) { am_req_cfg_rec *req_cfg; req_cfg = am_get_req_cfg(r); req_cfg->ecp_service_options = ecp_service_options; } return is_paos; } #endif /* HAVE_ECP */ char * am_saml_response_status_str(request_rec *r, LassoNode *node) { LassoSamlp2StatusResponse *response = (LassoSamlp2StatusResponse*)node; LassoSamlp2Status *status = NULL; const char *status_code1 = NULL; const char *status_code2 = NULL; if (!LASSO_IS_SAMLP2_STATUS_RESPONSE(response)) { return apr_psprintf(r->pool, "error, expected LassoSamlp2StatusResponse " "but got %s", lasso_node_get_name((LassoNode*)response)); } status = response->Status; if (status == NULL || !LASSO_IS_SAMLP2_STATUS(status) || status->StatusCode == NULL || status->StatusCode->Value == NULL) { return apr_psprintf(r->pool, "Status missing"); } status_code1 = status->StatusCode->Value; if (status->StatusCode->StatusCode) { status_code2 = status->StatusCode->StatusCode->Value; } return apr_psprintf(r->pool, "StatusCode1=\"%s\", StatusCode2=\"%s\", " "StatusMessage=\"%s\"", status_code1, status_code2, status->StatusMessage); } mod_auth_mellon-0.16.0/auth_mellon_session.c0000664002412700241270000001244113607335617021121 0ustar jhrozekjhrozek/* * * 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" #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(auth_mellon); #endif /* Retrieve a session from the cache and validate its cookie settings * * Parameters: * request_rec *r The request we received from the user. * am_cache_key_t type AM_CACHE_SESSION or AM_CACHE_NAMEID * const char *key The session key or user * * Returns: * The session associated, or NULL if unable to retrieve the given session. */ am_cache_entry_t *am_lock_and_validate(request_rec *r, am_cache_key_t type, const char *key) { am_cache_entry_t *session = NULL; am_diag_printf(r, "searching for session with key %s (%s) ... ", key, am_diag_cache_key_type_str(type)); session = am_cache_lock(r, type, key); if (session == NULL) { am_diag_printf(r, "not found\n"); return NULL; } else { am_diag_printf(r, "found.\n"); am_diag_log_cache_entry(r, 0, session, "Session Cache Entry"); } const char *cookie_token_session = am_cache_entry_get_string( session, &session->cookie_token); const char *cookie_token_target = am_cookie_token(r); if (strcmp(cookie_token_session, cookie_token_target)) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Session cookie parameter mismatch. " "Session created with {%s}, but current " "request has {%s}.", cookie_token_session, cookie_token_target); am_cache_unlock(r, session); return NULL; } return session; } /* 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_lock_and_validate(r, 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_lock_and_validate(r, 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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Error creating session id."); return NULL; } /* Set session id. */ am_cookie_set(r, session_id); const char *cookie_token = am_cookie_token(r); am_diag_printf(r, "%s id=%s cookie_token=\"%s\"\n", __func__, session_id, cookie_token); return am_cache_new(r, session_id, cookie_token); } /* 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, 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) { am_diag_log_cache_entry(r, 0, session, "delete session"); /* Delete the cookie. */ am_cookie_delete(r); if(session == NULL) { return; } /* Delete session from the session store. */ am_cache_delete(r, session); } mod_auth_mellon-0.16.0/auth_mellon_httpclient.c0000664002412700241270000005041313607336014021605 0ustar jhrozekjhrozek/* * * 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 #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(auth_mellon); #endif /* 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; while(size > 0) { /* Find the number of bytes we should write to this block. */ num_cpy = AM_HC_BLOCK_SIZE - block->used; if(num_cpy == 0) { /* This block is full -- allocate a new block. */ block->next = am_hc_block_alloc(pool); block = block->next; num_cpy = AM_HC_BLOCK_SIZE; } if(num_cpy > size) { num_cpy = size; } /* Copy data to this block. */ memcpy(&block->data[block->used], data, num_cpy); block->used += num_cpy; size -= num_cpy; data += 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) { AM_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) { AM_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) { AM_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) { AM_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) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set the timeout of the curl download:" " [%u] %s", res, curl_error); goto cleanup_fail; } /* If we have a CA configured, try to use it */ if (cfg->idp_ca_file != NULL) { res = curl_easy_setopt(curl, CURLOPT_CAINFO, cfg->idp_ca_file->path); if(res != CURLE_OK) { AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r, "Failed to set SSL CA info %s:" " [%u] %s", cfg->idp_ca_file->path, res, curl_error); goto cleanup_fail; } } /* Enable fail on http error. */ res = curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); if(res != CURLE_OK) { AM_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) { AM_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) { AM_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) { AM_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. * int 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, int 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, (long)timeout); if(res != CURLE_OK) { AM_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, (long)timeout); if(res != CURLE_OK) { AM_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) { AM_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) { AM_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) { AM_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) { AM_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) { AM_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) { AM_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) { AM_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.16.0/auth_mellon.h0000664002412700241270000005243413614046140017355 0ustar jhrozekjhrozek/* * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lasso_compat.h" /* The following are redefined in ap_config_auto.h */ #undef PACKAGE_BUGREPORT #undef PACKAGE_NAME #undef PACKAGE_STRING #undef PACKAGE_TARNAME #undef PACKAGE_VERSION #undef HAVE_TIMEGM /* is redefined again in ap_config.h */ #include "apr_base64.h" #include "apr_time.h" #include "apr_strings.h" #include "apr_shm.h" #include "apr_md5.h" #include "apr_file_info.h" #include "apr_file_io.h" #include "apr_xml.h" #include "apr_lib.h" #include "apr_fnmatch.h" #include "ap_config.h" #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_protocol.h" #include "http_request.h" #include "mod_ssl.h" /* Backwards-compatibility helpers. */ #include "auth_mellon_compat.h" /* Size definitions for the session cache. */ #define AM_CACHE_KEYSIZE 120 #define AM_CACHE_ENVSIZE 2048 #define AM_CACHE_USERSIZE 512 #define AM_CACHE_DEFAULT_ENTRY_SIZE 196608 #define AM_CACHE_MIN_ENTRY_SIZE 65536 /* Internal error codes */ #define AM_ERROR_INVALID_PAOS_HEADER 1 #define AM_ERROR_MISSING_PAOS_HEADER 2 #define AM_ERROR_MISSING_PAOS_MEDIA_TYPE 3 #ifdef ENABLE_DIAGNOSTICS typedef enum { AM_DIAG_FLAG_ENABLED = (1 << 0), AM_DIAG_FLAG_DISABLE = 0, AM_DIAG_FLAG_ENABLE_ALL = ~0, } am_diag_flags_t; #endif /* Disable SameSite Environment Value */ #define AM_DISABLE_SAMESITE_ENV_VAR "MELLON_DISABLE_SAMESITE" /* This is the length of the id we use (for session IDs and * replaying POST data). */ #define AM_ID_LENGTH 32 #define MEDIA_TYPE_PAOS "application/vnd.paos+xml" #define am_get_srv_cfg(s) (am_srv_cfg_rec *)ap_get_module_config((s)->module_config, &auth_mellon_module) #define am_get_mod_cfg(s) (am_get_srv_cfg((s)))->mc #define am_get_dir_cfg(r) (am_dir_cfg_rec *)ap_get_module_config((r)->per_dir_config, &auth_mellon_module) #define am_get_req_cfg(r) (am_req_cfg_rec *)ap_get_module_config((r)->request_config, &auth_mellon_module) #ifdef ENABLE_DIAGNOSTICS #define am_get_diag_cfg(s) (&(am_get_srv_cfg((s)))->diag_cfg) #endif typedef struct am_mod_cfg_rec { int cache_size; const char *lock_file; const char *post_dir; apr_time_t post_ttl; int post_count; apr_size_t post_size; int entry_size; /* These variables can't be allowed to change after the session store * has been initialized. Therefore we copy them before initializing * the session store. */ int init_cache_size; const char *init_lock_file; apr_size_t init_entry_size; apr_shm_t *cache; apr_global_mutex_t *lock; } am_mod_cfg_rec; #ifdef ENABLE_DIAGNOSTICS typedef struct am_diag_cfg_rec { const char *filename; apr_file_t *fd; am_diag_flags_t flags; apr_table_t *dir_cfg_emitted; } am_diag_cfg_rec; #endif typedef struct am_srv_cfg_rec { am_mod_cfg_rec *mc; #ifdef ENABLE_DIAGNOSTICS am_diag_cfg_rec diag_cfg; #endif } am_srv_cfg_rec; typedef enum { am_enable_default, am_enable_off, am_enable_info, am_enable_auth } am_enable_t; typedef enum { am_samesite_default, am_samesite_lax, am_samesite_strict, am_samesite_none, } am_samesite_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[]; /* * am_file_data_t is used to maintain information about a file: * * * The filesystem pathname * * Stat information about the file (e.g. type, size, times, etc.) * * If and when the file was stat'ed or read * * Error code of failed operation and error string description * * Contents of the file * * Flag indicating if contents were generated instead of being read * from a file. */ typedef struct am_file_data_t { apr_pool_t *pool; /* allocation pool */ const char *path; /* filesystem pathname, NULL for generated file */ apr_time_t stat_time; /* when stat was performed, zero indicates never */ apr_finfo_t finfo; /* stat data */ char *contents; /* file contents */ apr_time_t read_time; /* when contents was read, zero indicates never */ apr_status_t rv; /* most recent result value */ const char *strerror; /* if rv is error then this is error description */ bool generated; /* true if contents generated instead of being read from path */ } am_file_data_t; typedef struct { const char *varname; int flags; const char *str; ap_regex_t *regex; const char *directive; } am_cond_t; typedef struct am_metadata { am_file_data_t *metadata; /* Metadata file with one or many IdP */ am_file_data_t *chain; /* Validating chain */ } am_metadata_t; typedef struct am_dir_cfg_rec { /* enable_mellon is used to enable auth_mellon for a location. */ am_enable_t enable_mellon; const char *varname; int secure; int http_only; const char *merge_env_vars; int env_vars_index_start; int env_vars_count_in_n; const char *cookie_domain; const char *cookie_path; am_samesite_t cookie_samesite; apr_array_header_t *cond; apr_hash_t *envattr; const char *env_prefix; const char *userattr; const char *idpattr; LassoSignatureMethod signature_method; 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. */ am_file_data_t *sp_metadata_file; am_file_data_t *sp_private_key_file; am_file_data_t *sp_cert_file; apr_array_header_t *idp_metadata; am_file_data_t *idp_public_key_file; am_file_data_t *idp_ca_file; GList *idp_ignore; /* metadata autogeneration helper */ char *sp_entity_id; apr_hash_t *sp_org_name; apr_hash_t *sp_org_display_name; apr_hash_t *sp_org_url; /* Maximum number of seconds a session is valid for. */ int session_length; /* No cookie error page. */ const char *no_cookie_error_page; /* Authorization error page. */ const char *no_success_error_page; /* Login path for IdP initiated logins */ const char *login_path; /* IdP discovery service */ const char *discovery_url; int probe_discovery_timeout; apr_table_t *probe_discovery_idp; /* The configuration record we "inherit" the lasso server object from. */ struct am_dir_cfg_rec *inherit_server_from; /* Mutex to prevent us from creating several lasso server objects. */ apr_thread_mutex_t *server_mutex; /* AuthnContextClassRef list */ apr_array_header_t *authn_context_class_ref; /* AuthnContextComparisonType */ const char *authn_context_comparison_type; /* Controls the checking of SubjectConfirmationData.Address attribute */ int subject_confirmation_data_address_check; /* MellonDoNotVerifyLogoutSignature idp set */ apr_hash_t *do_not_verify_logout_signature; /* Controls whether the cache control header is set */ int send_cache_control_header; /* Whether we should replay POST data after authentication. */ int post_replay; /* Cached lasso server object. */ LassoServer *server; /* Whether to send an ECP client a list of IdP's */ int ecp_send_idplist; /* List of domains we can redirect to. */ const char * const *redirect_domains; /* Enabled the session invalidate endpoint. */ int enabled_invalidation_session; } am_dir_cfg_rec; /* Bitmask for PAOS service options */ typedef enum { ECP_SERVICE_OPTION_CHANNEL_BINDING = 1, ECP_SERVICE_OPTION_HOLDER_OF_KEY = 2, ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED = 4, ECP_SERVICE_OPTION_DELEGATION = 8, } ECPServiceOptions; typedef struct am_req_cfg_rec { char *cookie_value; #ifdef HAVE_ECP bool ecp_authn_req; ECPServiceOptions ecp_service_options; #endif /* HAVE_ECP */ #ifdef ENABLE_DIAGNOSTICS bool diag_emitted; #endif } am_req_cfg_rec; typedef struct am_cache_storage_t { apr_size_t ptr; } am_cache_storage_t; typedef struct am_cache_env_t { am_cache_storage_t varname; am_cache_storage_t value; } am_cache_env_t; typedef struct am_cache_entry_t { char key[AM_CACHE_KEYSIZE]; am_cache_storage_t cookie_token; apr_time_t access; apr_time_t expires; int logged_in; unsigned short size; am_cache_storage_t user; /* Variables used to store lasso state between login requests *and logout requests. */ am_cache_storage_t lasso_identity; am_cache_storage_t lasso_session; am_cache_storage_t lasso_saml_response; am_cache_env_t env[AM_CACHE_ENVSIZE]; apr_size_t pool_size; apr_size_t pool_used; char pool[]; } am_cache_entry_t; typedef enum { AM_CACHE_SESSION, AM_CACHE_NAMEID } am_cache_key_t; /* Type for configuring environment variable names */ typedef struct am_envattr_conf_t { // Name of the variable const char *name; // Should a prefix be added int prefixed; } am_envattr_conf_t; extern const command_rec auth_mellon_commands[]; typedef struct am_error_map_t { int lasso_error; int http_error; } am_error_map_t; extern const am_error_map_t auth_mellon_errormap[]; /* When using a value from a directory configuration structure, a special value is used * to state "inherit" from parent, when reading a value and the value is still inherit from, it * means that no value has ever been set for this directive, in this case, we use the default * value. * * This macro expects that if your variable is called "name" there is a static const variable named * "default_name" which holds the default value for this variable. */ #define CFG_VALUE(container, name) \ (container->name == inherit_##name ? default_##name : container->name) #define CFG_MERGE(add_cfg, base_cfg, name) \ (add_cfg->name == inherit_##name ? base_cfg->name : add_cfg->name) /** Default and inherit value for SubjectConfirmationData Address check setting. */ static const int default_subject_confirmation_data_address_check = 1; static const int inherit_subject_confirmation_data_address_check = -1; /** Default values for seting the cache-control header */ static const int default_send_cache_control_header = 1; static const int inherit_send_cache_control_header = -1; /* Default and inherit values for MellonPostReplay option. */ static const int default_post_replay = 0; static const int inherit_post_replay = -1; /* Whether to send an ECP client a list of IdP's */ static const int default_ecp_send_idplist = 0; static const int inherit_ecp_send_idplist = -1; /* Algorithm to use when signing Mellon SAML messages */ static const LassoSignatureMethod default_signature_method = #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA256 LASSO_SIGNATURE_METHOD_RSA_SHA256; #else LASSO_SIGNATURE_METHOD_RSA_SHA1; #endif static const int inherit_signature_method = -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); void *auth_mellon_srv_merge(apr_pool_t *p, void *base, void *add); 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); const char *am_cookie_token(request_rec *r); void am_cache_init(am_mod_cfg_rec *mod_cfg); am_cache_entry_t *am_cache_lock(request_rec *r, am_cache_key_t type, const char *key); const char *am_cache_entry_get_string(am_cache_entry_t *e, am_cache_storage_t *slot); am_cache_entry_t *am_cache_new(request_rec *r, const char *key, const char *cookie_token); void am_cache_unlock(request_rec *r, am_cache_entry_t *entry); void am_cache_update_expires(request_rec *r, 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(request_rec *r, am_cache_entry_t *session); int am_cache_set_lasso_state(am_cache_entry_t *session, const char *lasso_identity, const char *lasso_session, const char *lasso_saml_response); const char *am_cache_get_lasso_identity(am_cache_entry_t *session); const char *am_cache_get_lasso_session(am_cache_entry_t *session); am_cache_entry_t *am_get_request_session(request_rec *r); am_cache_entry_t *am_get_request_session_by_nameid(request_rec *r, char *nameid); am_cache_entry_t *am_new_request_session(request_rec *r); void am_release_request_session(request_rec *r, am_cache_entry_t *session); void am_delete_request_session(request_rec *r, am_cache_entry_t *session); char *am_reconstruct_url(request_rec *r); int am_validate_redirect_url(request_rec *r, const char *url); int am_check_permissions(request_rec *r, am_cache_entry_t *session); void am_set_cache_control_headers(request_rec *r); int am_read_post_data(request_rec *r, char **data, apr_size_t *length); char *am_extract_query_parameter(apr_pool_t *pool, const char *query_string, const char *name); char *am_urlencode(apr_pool_t *pool, const char *str); int am_urldecode(char *data); int am_check_url(request_rec *r, const char *url); char *am_generate_id(request_rec *r); am_file_data_t *am_file_data_new(apr_pool_t *pool, const char *path); am_file_data_t *am_file_data_copy(apr_pool_t *pool, am_file_data_t *src_file_data); apr_status_t am_file_read(am_file_data_t *file_data); apr_status_t am_file_stat(am_file_data_t *file_data); char *am_get_endpoint_url(request_rec *r); int am_postdir_cleanup(request_rec *s); char *am_htmlencode(request_rec *r, const char *str); int am_save_post(request_rec *r, const char **relay_state); const char *am_filepath_dirname(apr_pool_t *p, const char *path); const char *am_strip_cr(request_rec *r, const char *str); const char *am_add_cr(request_rec *r, const char *str); const char *am_xstrtok(request_rec *r, const char *str, const char *sep, char **last); void am_strip_blank(const char **s); const char *am_get_header_attr(request_rec *r, const char *h, const char *v, const char *a); int am_has_header(request_rec *r, const char *h, const char *v); const char *am_get_mime_header(request_rec *r, const char *m, const char *h); const char *am_get_mime_body(request_rec *r, const char *mime); char *am_get_service_url(request_rec *r, LassoProfile *profile, char *service_name); bool am_parse_paos_header(request_rec *r, const char *header, ECPServiceOptions *options_return); bool am_header_has_media_type(request_rec *r, const char *header, const char *media_type); const char *am_get_config_langstring(apr_hash_t *h, const char *lang); int am_get_boolean_query_parameter(request_rec *r, const char *name, int *return_value, int default_value); char *am_get_assertion_consumer_service_by_binding(LassoProvider *provider, const char *binding); #ifdef HAVE_ECP char *am_ecp_service_options_str(apr_pool_t *pool, ECPServiceOptions options); bool am_is_paos_request(request_rec *r, int *error_code); #endif /* HAVE_ECP */ char * am_saml_response_status_str(request_rec *r, LassoNode *node); 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, int 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; #ifdef ENABLE_DIAGNOSTICS #if AP_SERVER_MAJORVERSION_NUMBER < 2 || \ (AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER < 4) #error "Diagnostics requires Apache version 2.4 or newer." #endif /* Initializing an apr_time_t to 0x7fffffffffffffffLL yields an * iso 8601 time with 1 second precision of "294247-01-10T04:00:54Z" * this is 22 characters, +1 for null terminator. */ #define ISO_8601_BUF_SIZE 23 typedef struct { bool req_headers_written; } am_diag_request_data; const char * am_diag_cache_key_type_str(am_cache_key_t key_type); const char * am_diag_cond_str(request_rec *r, const am_cond_t *cond); int am_diag_finalize_request(request_rec *r); const char * am_diag_lasso_http_method_str(LassoHttpMethod http_method); void am_diag_log_cache_entry(request_rec *r, int level, am_cache_entry_t *entry, const char *fmt, ...) __attribute__((format(printf,4,5))); void am_diag_log_file_data(request_rec *r, int level, am_file_data_t *file_data, const char *fmt, ...) __attribute__((format(printf,4,5))); int am_diag_log_init(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, server_rec *s); void am_diag_log_lasso_node(request_rec *r, int level, LassoNode *node, const char *fmt, ...) __attribute__((format(printf,4,5))); void am_diag_log_saml_status_response(request_rec *r, int level, LassoNode *node, const char *fmt, ...) __attribute__((format(printf,4,5))); void am_diag_log_profile(request_rec *r, int level, LassoProfile *profile, const char *fmt, ...) __attribute__((format(printf,4,5))); void am_diag_printf(request_rec *r, const char *fmt, ...) __attribute__((format(printf,2,3))); void am_diag_rerror(const char *file, int line, int module_index, int level, apr_status_t status, request_rec *r, const char *fmt, ...); char * am_diag_time_t_to_8601(request_rec *r, apr_time_t t); /* Define AM_LOG_RERROR log to both the Apache log and diagnostics log */ #define AM_LOG_RERROR(...) AM_LOG_RERROR__(__VA_ARGS__) /* need additional step to expand macros */ #define AM_LOG_RERROR__(file, line, mi, level, status, r, ...) \ { \ ap_log_rerror(file, line, mi, level, status, r, __VA_ARGS__); \ am_diag_rerror(file, line, mi, level, status, r, __VA_ARGS__); \ } #else /* ENABLE_DIAGNOSTICS */ #define am_diag_log_cache_entry(...) do {} while(0) #define am_diag_log_file_data(...) do {} while(0) #define am_diag_log_lasso_node(...) do {} while(0) #define am_diag_log_saml_status_response(...) do {} while(0) #define am_diag_log_profile(...) do {} while(0) #define am_diag_printf(...) do {} while(0) /* Define AM_LOG_RERROR log only to the Apache log */ #define AM_LOG_RERROR(...) ap_log_rerror(__VA_ARGS__) #endif /* ENABLE_DIAGNOSTICS */ #endif /* MOD_AUTH_MELLON_H */ mod_auth_mellon-0.16.0/auth_mellon_compat.h0000664002412700241270000000303413367654776020741 0ustar jhrozekjhrozek#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.16.0/lasso_compat.h0000664002412700241270000000420713607335617017541 0ustar jhrozekjhrozek#ifdef HAVE_LASSO_UTILS_H #include #else #define lasso_assign_string(dest,src) \ { \ char *__tmp = g_strdup(src); \ lasso_release_string(dest); \ dest = __tmp; \ } #define lasso_release_string(dest) \ lasso_release_full(dest, g_free) #define lasso_release_full(dest, free_function) \ { \ if (dest) { \ free_function(dest); dest = NULL; \ } \ } #define lasso_check_type_equality(a,b) #define lasso_release_full2(dest, free_function, type) \ { \ lasso_check_type_equality(dest, type); \ if (dest) { \ free_function(dest); dest = NULL; \ } \ } #define lasso_release_list(dest) \ lasso_release_full2(dest, g_list_free, GList*) #define lasso_release_list_of_full(dest, free_function) \ { \ GList **__tmp = &(dest); \ if (*__tmp) { \ g_list_foreach(*__tmp, (GFunc)free_function, NULL); \ lasso_release_list(*__tmp); \ } \ } #define lasso_release_list_of_strings(dest) \ lasso_release_list_of_full(dest, g_free) #endif #ifndef LASSO_SAML2_ECP_PROFILE_WANT_AUTHN_SIGNED #define LASSO_SAML2_ECP_PROFILE_WANT_AUTHN_SIGNED "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp:2.0:WantAuthnRequestsSigned" #endif #ifndef LASSO_SAML2_CONDITIONS_DELEGATION #define LASSO_SAML2_CONDITIONS_DELEGATION "urn:oasis:names:tc:SAML:2.0:conditions:delegation" #endif #ifndef LASSO_SAML_EXT_CHANNEL_BINDING #define LASSO_SAML_EXT_CHANNEL_BINDING "urn:oasis:names:tc:SAML:protocol:ext:channel-binding" #endif mod_auth_mellon-0.16.0/config.h.in0000664002412700241270000000450113614046733016720 0ustar jhrozekjhrozek/* config.h.in. Generated from configure.ac by autoheader. */ /* build with diagnostics */ #undef ENABLE_DIAGNOSTICS /* Define to 1 if you have the declaration of `', and to 0 if you don't. */ #undef HAVE_DECL_ /* Define to 1 if you have the declaration of `LASSO_SIGNATURE_METHOD_RSA_SHA256', and to 0 if you don't. */ #undef HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA256 /* Define to 1 if you have the declaration of `LASSO_SIGNATURE_METHOD_RSA_SHA384', and to 0 if you don't. */ #undef HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA384 /* Define to 1 if you have the declaration of `LASSO_SIGNATURE_METHOD_RSA_SHA512', and to 0 if you don't. */ #undef HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA512 /* lasso library supports ECP profile */ #undef HAVE_ECP /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* lasso library exports lasso_profile_set_signature_verify_hint */ #undef HAVE_lasso_profile_set_signature_verify_hint /* lasso library exports lasso_server_load_metadata */ #undef HAVE_lasso_server_load_metadata /* lasso library exports lasso_server_new_from_buffers */ #undef HAVE_lasso_server_new_from_buffers /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS mod_auth_mellon-0.16.0/configure0000775002412700241270000050366413614046733016622 0ustar jhrozekjhrozek#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for mod_auth_mellon 0.16.0. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: https://github.com/latchset/mod_auth_mellon/issues $0: about your system, including any error possibly output $0: before this message. Then install a modern shell, or $0: manually run the script under such a shell if you do $0: have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='mod_auth_mellon' PACKAGE_TARNAME='mod_auth_mellon' PACKAGE_VERSION='0.16.0' PACKAGE_STRING='mod_auth_mellon 0.16.0' PACKAGE_BUGREPORT='https://github.com/latchset/mod_auth_mellon/issues' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS EGREP GREP CPP MELLON_CFLAGS GLIB_LIBS GLIB_CFLAGS OPENSSL_LIBS OPENSSL_CFLAGS CURL_LIBS CURL_CFLAGS LASSO_LIBS LASSO_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG APXS2 NAMEVER OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_apxs2 enable_diagnostics ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR LASSO_CFLAGS LASSO_LIBS CURL_CFLAGS CURL_LIBS OPENSSL_CFLAGS OPENSSL_LIBS GLIB_CFLAGS GLIB_LIBS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures mod_auth_mellon 0.16.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.16.0:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-diagnostics Build with diagnostic support Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-apxs2=PATH Full path to the apxs2 executable. Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path LASSO_CFLAGS C compiler flags for LASSO, overriding pkg-config LASSO_LIBS linker flags for LASSO, overriding pkg-config CURL_CFLAGS C compiler flags for CURL, overriding pkg-config CURL_LIBS linker flags for CURL, overriding pkg-config OPENSSL_CFLAGS C compiler flags for OPENSSL, overriding pkg-config OPENSSL_LIBS linker flags for OPENSSL, overriding pkg-config GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config GLIB_LIBS linker flags for GLIB, overriding pkg-config CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF mod_auth_mellon configure 0.16.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ----------------------------------------------------------------- ## ## Report this to https://github.com/latchset/mod_auth_mellon/issues ## ## ----------------------------------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl 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.16.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" # We require support for C99. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; } if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } int main () { // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c99" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c99" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno; then : fi NAMEVER=mod_auth_mellon-0.16.0 # This section defines the --with-apxs2 option. # Check whether --with-apxs2 was given. if test "${with_apxs2+set}" = set; then : withval=$with_apxs2; APXS2=${withval} fi if test "x$APXS2" = "x"; then # The user didn't specify the --with-apxs2-option. # Search for apxs2 in the specified directories # Extract the first word of "apxs2", so it can be a program name with args. set dummy apxs2; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_APXS2+:} false; then : $as_echo_n "(cached) " >&6 else case $APXS2 in [\\/]* | ?:[\\/]*) ac_cv_path_APXS2="$APXS2" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" for as_dir in $as_dummy do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_APXS2="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi APXS2=$ac_cv_path_APXS2 if test -n "$APXS2"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $APXS2" >&5 $as_echo "$APXS2" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$APXS2" = "x"; then # Didn't find apxs2 in any of the specified directories. # Search for apxs instead. # Extract the first word of "apxs", so it can be a program name with args. set dummy apxs; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_APXS2+:} false; then : $as_echo_n "(cached) " >&6 else case $APXS2 in [\\/]* | ?:[\\/]*) ac_cv_path_APXS2="$APXS2" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" for as_dir in $as_dummy do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_APXS2="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi APXS2=$ac_cv_path_APXS2 if test -n "$APXS2"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $APXS2" >&5 $as_echo "$APXS2" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi # Test if $APXS2 exists and is an executable. if test ! -x "$APXS2"; then # $APXS2 isn't a executable file. as_fn_error $? " Could not find apxs2. Please spesify the path to apxs2 using the --with-apxs2=/full/path/to/apxs2 option. The executable may also be named 'apxs'. " "$LINENO" 5 fi # Check whether --enable-diagnostics was given. if test "${enable_diagnostics+set}" = set; then : enableval=$enable_diagnostics; else enable_diagnostics=no fi if test "x$enable_diagnostics" != xno; then : $as_echo "#define ENABLE_DIAGNOSTICS /**/" >>confdefs.h fi # Replace any occurances of @APXS2@ with the value of $APXS2 in the Makefile. # We need the lasso library for SAML2 communication. if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LASSO" >&5 $as_echo_n "checking for LASSO... " >&6; } if test -n "$LASSO_CFLAGS"; then pkg_cv_LASSO_CFLAGS="$LASSO_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"lasso\""; } >&5 ($PKG_CONFIG --exists --print-errors "lasso") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LASSO_CFLAGS=`$PKG_CONFIG --cflags "lasso" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LASSO_LIBS"; then pkg_cv_LASSO_LIBS="$LASSO_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"lasso\""; } >&5 ($PKG_CONFIG --exists --print-errors "lasso") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LASSO_LIBS=`$PKG_CONFIG --libs "lasso" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LASSO_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "lasso" 2>&1` else LASSO_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "lasso" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LASSO_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (lasso) were not met: $LASSO_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables LASSO_CFLAGS and LASSO_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables LASSO_CFLAGS and LASSO_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else LASSO_CFLAGS=$pkg_cv_LASSO_CFLAGS LASSO_LIBS=$pkg_cv_LASSO_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi saved_LIBS=$LIBS; LIBS="$LIBS $LASSO_LIBS"; { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lasso_server_new_from_buffers in -llasso" >&5 $as_echo_n "checking for lasso_server_new_from_buffers in -llasso... " >&6; } if ${ac_cv_lib_lasso_lasso_server_new_from_buffers+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llasso $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lasso_server_new_from_buffers (); int main () { return lasso_server_new_from_buffers (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lasso_lasso_server_new_from_buffers=yes else ac_cv_lib_lasso_lasso_server_new_from_buffers=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lasso_lasso_server_new_from_buffers" >&5 $as_echo "$ac_cv_lib_lasso_lasso_server_new_from_buffers" >&6; } if test "x$ac_cv_lib_lasso_lasso_server_new_from_buffers" = xyes; then : $as_echo "#define HAVE_lasso_server_new_from_buffers /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lasso_server_load_metadata in -llasso" >&5 $as_echo_n "checking for lasso_server_load_metadata in -llasso... " >&6; } if ${ac_cv_lib_lasso_lasso_server_load_metadata+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llasso $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lasso_server_load_metadata (); int main () { return lasso_server_load_metadata (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lasso_lasso_server_load_metadata=yes else ac_cv_lib_lasso_lasso_server_load_metadata=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lasso_lasso_server_load_metadata" >&5 $as_echo "$ac_cv_lib_lasso_lasso_server_load_metadata" >&6; } if test "x$ac_cv_lib_lasso_lasso_server_load_metadata" = xyes; then : $as_echo "#define HAVE_lasso_server_load_metadata /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lasso_profile_set_signature_verify_hint in -llasso" >&5 $as_echo_n "checking for lasso_profile_set_signature_verify_hint in -llasso... " >&6; } if ${ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llasso $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lasso_profile_set_signature_verify_hint (); int main () { return lasso_profile_set_signature_verify_hint (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint=yes else ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint" >&5 $as_echo "$ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint" >&6; } if test "x$ac_cv_lib_lasso_lasso_profile_set_signature_verify_hint" = xyes; then : $as_echo "#define HAVE_lasso_profile_set_signature_verify_hint /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lasso_ecp_request_new in -llasso" >&5 $as_echo_n "checking for lasso_ecp_request_new in -llasso... " >&6; } if ${ac_cv_lib_lasso_lasso_ecp_request_new+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llasso $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lasso_ecp_request_new (); int main () { return lasso_ecp_request_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lasso_lasso_ecp_request_new=yes else ac_cv_lib_lasso_lasso_ecp_request_new=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lasso_lasso_ecp_request_new" >&5 $as_echo "$ac_cv_lib_lasso_lasso_ecp_request_new" >&6; } if test "x$ac_cv_lib_lasso_lasso_ecp_request_new" = xyes; then : $as_echo "#define HAVE_ECP /**/" >>confdefs.h fi LIBS=$saved_LIBS; # We need the curl library for HTTP-Artifact downloads. pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CURL" >&5 $as_echo_n "checking for CURL... " >&6; } if test -n "$CURL_CFLAGS"; then pkg_cv_CURL_CFLAGS="$CURL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcurl\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcurl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CURL_CFLAGS=`$PKG_CONFIG --cflags "libcurl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$CURL_LIBS"; then pkg_cv_CURL_LIBS="$CURL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcurl\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcurl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CURL_LIBS=`$PKG_CONFIG --libs "libcurl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then CURL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcurl" 2>&1` else CURL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcurl" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$CURL_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libcurl) were not met: $CURL_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables CURL_CFLAGS and CURL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables CURL_CFLAGS and CURL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else CURL_CFLAGS=$pkg_cv_CURL_CFLAGS CURL_LIBS=$pkg_cv_CURL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi # We also need openssl for its random number generator. pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 $as_echo_n "checking for OPENSSL... " >&6; } if test -n "$OPENSSL_CFLAGS"; then pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$OPENSSL_LIBS"; then pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl" 2>&1` else OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$OPENSSL_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (openssl) were not met: $OPENSSL_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables OPENSSL_CFLAGS and OPENSSL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables OPENSSL_CFLAGS and OPENSSL_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi # We need at least version 2.12 of GLib. pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLIB" >&5 $as_echo_n "checking for GLIB... " >&6; } if test -n "$GLIB_CFLAGS"; then pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.12\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.12") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.12" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$GLIB_LIBS"; then pkg_cv_GLIB_LIBS="$GLIB_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.12\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.12") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.12" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.12" 2>&1` else GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.12" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$GLIB_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (glib-2.0 >= 2.12) were not met: $GLIB_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GLIB_CFLAGS and GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables GLIB_CFLAGS and GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS GLIB_LIBS=$pkg_cv_GLIB_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi # Test to see if we can include lasso/utils.h # AC_CHECK_HEADER won't work correctly unless we specifiy the include directories # found in the LASSO_CFLAGS. Save and restore CFLAGS and CPPFLAGS. saved_CFLAGS=$CFLAGS saved_CPPFLAGS=$CPPFLAGS CFLAGS="$CFLAGS $pkg_cv_LASSO_CFLAGS" CPPFLAGS="$CPPFLAGS $pkg_cv_LASSO_CFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_header_mongrel "$LINENO" "lasso/utils.h" "ac_cv_header_lasso_utils_h" "$ac_includes_default" if test "x$ac_cv_header_lasso_utils_h" = xyes; then : LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_LASSO_UTILS_H" fi CFLAGS=$saved_CFLAGS CPPFLAGS=$saved_CPPFLAGS # Determine what definitions exist in Lasso saved_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $pkg_cv_LASSO_CFLAGS" ac_fn_c_check_decl "$LINENO" "LASSO_SIGNATURE_METHOD_RSA_SHA256" "ac_cv_have_decl_LASSO_SIGNATURE_METHOD_RSA_SHA256" "#include " if test "x$ac_cv_have_decl_LASSO_SIGNATURE_METHOD_RSA_SHA256" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA256 $ac_have_decl _ACEOF ac_fn_c_check_decl "$LINENO" "LASSO_SIGNATURE_METHOD_RSA_SHA384" "ac_cv_have_decl_LASSO_SIGNATURE_METHOD_RSA_SHA384" "#include " if test "x$ac_cv_have_decl_LASSO_SIGNATURE_METHOD_RSA_SHA384" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA384 $ac_have_decl _ACEOF ac_fn_c_check_decl "$LINENO" "LASSO_SIGNATURE_METHOD_RSA_SHA512" "ac_cv_have_decl_LASSO_SIGNATURE_METHOD_RSA_SHA512" "#include " if test "x$ac_cv_have_decl_LASSO_SIGNATURE_METHOD_RSA_SHA512" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA512 $ac_have_decl _ACEOF ac_fn_c_check_decl "$LINENO" "" "ac_cv_have_decl_" "#include " if test "x$ac_cv_have_decl_" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_ $ac_have_decl _ACEOF CFLAGS=$saved_CFLAGS # Create Makefile from Makefile.in ac_config_files="$ac_config_files Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by mod_auth_mellon $as_me 0.16.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _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 --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers 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.16.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --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 "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "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 test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; 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.16.0/configure.ac0000664002412700241270000000716113614046140017160 0ustar jhrozekjhrozekAC_INIT([mod_auth_mellon],[0.16.0],[https://github.com/latchset/mod_auth_mellon/issues]) AC_CONFIG_HEADERS([config.h]) # We require support for C99. AC_PROG_CC_C99 AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION()) # This section defines the --with-apxs2 option. AC_ARG_WITH( [apxs2], [ --with-apxs2=PATH Full path to the apxs2 executable.], [ APXS2=${withval} ],) if test "x$APXS2" = "x"; then # The user didn't specify the --with-apxs2-option. # Search for apxs2 in the specified directories AC_PATH_PROG(APXS2, apxs2,, /usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin) if test "x$APXS2" = "x"; then # Didn't find apxs2 in any of the specified directories. # Search for apxs instead. AC_PATH_PROG(APXS2, apxs,, /usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin) fi fi # Test if $APXS2 exists and is an executable. if test ! -x "$APXS2"; then # $APXS2 isn't a executable file. AC_MSG_ERROR([ Could not find apxs2. Please spesify the path to apxs2 using the --with-apxs2=/full/path/to/apxs2 option. The executable may also be named 'apxs'. ]) fi AC_ARG_ENABLE( [diagnostics], [AS_HELP_STRING([--enable-diagnostics], [Build with diagnostic support])], [], [enable_diagnostics=no]) AS_IF([test "x$enable_diagnostics" != xno], [AC_DEFINE([ENABLE_DIAGNOSTICS],[],[build with diagnostics])]) # 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, [AC_DEFINE([HAVE_lasso_server_new_from_buffers],[], [lasso library exports lasso_server_new_from_buffers])]) AC_CHECK_LIB(lasso, lasso_server_load_metadata, [AC_DEFINE([HAVE_lasso_server_load_metadata],[], [lasso library exports lasso_server_load_metadata])]) AC_CHECK_LIB(lasso, lasso_profile_set_signature_verify_hint, [AC_DEFINE([HAVE_lasso_profile_set_signature_verify_hint],[], [lasso library exports lasso_profile_set_signature_verify_hint])]) AC_CHECK_LIB(lasso, lasso_ecp_request_new, [AC_DEFINE([HAVE_ECP],[], [lasso library supports ECP profile])]) 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) AC_SUBST(MELLON_CFLAGS) # Test to see if we can include lasso/utils.h # AC_CHECK_HEADER won't work correctly unless we specifiy the include directories # found in the LASSO_CFLAGS. Save and restore CFLAGS and CPPFLAGS. saved_CFLAGS=$CFLAGS saved_CPPFLAGS=$CPPFLAGS CFLAGS="$CFLAGS $pkg_cv_LASSO_CFLAGS" CPPFLAGS="$CPPFLAGS $pkg_cv_LASSO_CFLAGS" AC_CHECK_HEADER([lasso/utils.h], LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_LASSO_UTILS_H") CFLAGS=$saved_CFLAGS CPPFLAGS=$saved_CPPFLAGS # Determine what definitions exist in Lasso saved_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $pkg_cv_LASSO_CFLAGS" AC_CHECK_DECLS([LASSO_SIGNATURE_METHOD_RSA_SHA256, LASSO_SIGNATURE_METHOD_RSA_SHA384, LASSO_SIGNATURE_METHOD_RSA_SHA512, ], [], [], [#include ]) CFLAGS=$saved_CFLAGS # Create Makefile from Makefile.in AC_CONFIG_FILES([Makefile]) AC_OUTPUT mod_auth_mellon-0.16.0/Makefile.in0000664002412700241270000000365513613771501016750 0ustar jhrozekjhrozek# 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_diagnostics.c \ auth_mellon_handler.c \ auth_mellon_util.c \ auth_mellon_session.c \ auth_mellon_httpclient.c # Documentation files USER_GUIDE_FILES=\ doc/user_guide/mellon_user_guide.adoc \ doc/user_guide/Guardfile \ doc/user_guide/README \ doc/user_guide/images/chrome_SAML_Chrome_Panel.png \ doc/user_guide/images/chrome_SAML_Chrome_Panel.svg \ doc/user_guide/images/saml-tracer.png \ doc/user_guide/images/saml-tracer.svg \ doc/user_guide/images/saml-web-sso.svg # Files to include when making a .tar.gz-file for distribution DISTFILES=$(SRC) \ auth_mellon.h \ auth_mellon_compat.h \ lasso_compat.h \ config.h.in \ configure \ configure.ac \ Makefile.in \ autogen.sh \ README.md \ ECP.rst \ COPYING \ NEWS \ mellon_create_metadata.sh \ doc/mellon_create_metadata.8 \ $(USER_GUIDE_FILES) all: mod_auth_mellon.la mod_auth_mellon.la: $(SRC) auth_mellon.h auth_mellon_compat.h @APXS2@ -Wc,"-std=c99 @MELLON_CFLAGS@ @OPENSSL_CFLAGS@ @LASSO_CFLAGS@ @CURL_CFLAGS@ @GLIB_CFLAGS@ @CFLAGS@" -Wl,"@OPENSSL_LIBS@ @LASSO_LIBS@ @CURL_LIBS@ @GLIB_LIBS@" -Wc,-Wall -Wc,-g -c $(SRC) # Building configure (for distribution) configure: configure.ac ./autogen.sh @NAMEVER@.tar.gz: $(DISTFILES) tar -c --transform="s#^#@NAMEVER@/#" -vzf $@ $(DISTFILES) .PHONY: install install: mod_auth_mellon.la @APXS2@ -i -n auth_mellon mod_auth_mellon.la .PHONY: distfile distfile: @NAMEVER@.tar.gz .PHONY: clean clean: rm -f mod_auth_mellon.la rm -f $(SRC:%.c=%.o) 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.16.0/autogen.sh0000775002412700241270000000007613367654776016722 0ustar jhrozekjhrozek#!/bin/sh autoreconf --force --install rm -rf autom4te.cache/ mod_auth_mellon-0.16.0/README.md0000664002412700241270000011700013614046140016143 0ustar jhrozekjhrozek# mod_auth_mellon mod_auth_mellon is an authentication module for Apache. It authenticates the user against a SAML 2.0 IdP, and grants access to directories depending on attributes received from the IdP. ## Dependencies mod_auth_mellon has four dependencies: * pkg-config * Apache (>=2.0) * OpenSSL * lasso (>=2.1) You will also require development headers and tools for all of the dependencies. If OpenSSL or lasso are installed in a "strange" directory, then you may have to specify the directory containing "lasso.pc" and/or "openssl.pc" in the PKG_CONFIG_PATH environment variable. For example, if OpenSSL is installed in /usr/local/openssl (with openssl.pc in /usr/local/openssl/lib/pkgconfig/) and lasso is installed in /opt/lasso (lasso.pc in /opt/lasso/lib/pkgconfig/), then you can set PKG_CONFIG_PATH before running configure like this: ``` PKG_CONFIG_PATH=/usr/local/openssl/lib/pkgconfig:/opt/lasso/lib/pkgconfig export PKG_CONFIG_PATH ``` If Apache is installed in a "strange" directory, then you may have to specify the path to apxs2 using the `--with-apxs2=/full/path/to/apxs2` option to configure. If, for example, Apache is installed in /opt/apache, with apxs2 in /opt/apache/bin, then you run ``` ./configure --with-apxs2=/opt/apache2/bin/apxs2 ``` Note that, depending on your distribution, apxs2 may be named apxs. ## Installing mod_auth_mellon mod_auth_mellon uses autoconf, and can be installed by running the following commands: ``` ./configure make make install ``` ## Configuring mod_auth_mellon Here we are going to assume that your web server's hostname is 'example.com', and that the directory you are going to protect is 'https://example.com/secret/'. We are also going to assume that you have configured your web site to use SSL. You need to edit the configuration file for your web server. Depending on your distribution, it may be named '/etc/apache/httpd.conf' or something different. You need to add a LoadModule directive for mod_auth_mellon. This will look similar to this: ``` LoadModule auth_mellon_module /usr/lib/apache2/modules/mod_auth_mellon.so ``` To find the full path to mod_auth_mellon.so, you may run: ``` apxs2 -q LIBEXECDIR ``` This will print the path where Apache stores modules. mod_auth_mellon.so will be stored in that directory. You will also need to make sure that Apache's authn_core module is also enabled. Most likely you also want authz_user to be enabled. After you have added the LoadModule directive, you must add configuration for mod_auth_mellon. The following is an example configuration: ```ApacheConf ########################################################################### # Global configuration for mod_auth_mellon. This configuration is shared by # every virtual server and location in this instance of apache. ########################################################################### # MellonCacheSize sets the maximum number of sessions which can be active # at once. When mod_auth_mellon reaches this limit, it will begin removing # the least recently used sessions. The server must be restarted before any # changes to this option takes effect. # Default: MellonCacheSize 100 MellonCacheSize 100 # MellonCacheEntrySize sets the maximum size for a single session entry in # bytes. When mod_auth_mellon reaches this limit, it cannot store any more # data in the session and will return an error. The minimum entry size is # 65536 bytes, values lower than that will be ignored and the minimum will # be used. # Default: MellonCacheEntrySize 196608 # MellonLockFile is the full path to a file used for synchronizing access # to the session data. The path should only be used by one instance of # apache at a time. The server must be restarted before any changes to this # option takes effect. # Default: MellonLockFile "/var/run/mod_auth_mellon.lock" MellonLockFile "/var/run/mod_auth_mellon.lock" # MellonPostDirectory is the full path of a directory where POST requests # are saved during authentication. This directory must writable by the # Apache user. It should not be writable (or readable) by other users. # Default: None # Example: MellonPostDirectory "/var/cache/mod_auth_mellon_postdata" # MellonPostTTL is the delay in seconds before a saved POST request can # be flushed. # Default: MellonPostTTL 900 (15 mn) MellonPostTTL 900 # MellonPostSize is the maximum size for saved POST requests # Default: MellonPostSize 1048576 (1 MB) MellonPostSize 1048576 # MellonPostCount is the maximum amount of saved POST requests # Default: MellonPostCount 100 MellonPostCount 100 # MellonDiagnosticsFile If Mellon was built with diagnostic capability # then diagnostic is written here, it may be either a filename or a pipe. # If it's a filename then the resulting path is relative to the ServerRoot. # If the value is preceeded by the pipe character "|" it should be followed # by a path to a program to receive the log information on its standard input. # This is a server context directive, hence it may be specified in the # main server config area or within a directive. # Default: logs/mellon_diagnostics MellonDiagnosticsFile logs/mellon_diagnostics # MellonDiagnosticsEnable If Mellon was built with diagnostic capability # then this is a list of words controlling diagnostic output. # Currently only On and Off are supported. # This is a server context directive, hence it may be specified in the # main server config area or within a directive. # Default: Off MellonDiagnosticsEnable Off ########################################################################### # End of global configuration for mod_auth_mellon. ########################################################################### # This defines a directory where mod_auth_mellon should do access control. # These are standard Apache apache configuration directives. # You must have enabled Apache's authn_core and authz_user modules. # See http://httpd.apache.org/docs/2.4/mod/mod_authn_core.html and # http://httpd.apache.org/docs/2.4/mod/mod_authz_user.html # about them. Require valid-user AuthType "Mellon" # MellonEnable is used to enable auth_mellon on a location. # It has three possible values: "off", "info" and "auth". # They have the following meanings: # "off": mod_auth_mellon will not do anything in this location. # This is the default state. # "info": If the user is authorized to access the resource, then # we will populate the environment with information about # the user. If the user isn't authorized, then we won't # populate the environment, but we won't deny the user # access either. # # You can also use this to set up the Mellon SSO paramaters # transparently at the top level of your site, and then use # "auth" to protect individual paths elsewhere in the site. # "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. # # There is a special handling of AJAX requests, that are # identified by the "X-Requested-With: XMLHttpRequest" HTTP # header. Since no user interaction can happen there, # we always fail unauthenticated (not logged in) requests # with a 403 Forbidden error without redirecting to the IdP. # # Default: MellonEnable "off" MellonEnable "auth" # MellonDecoder is an obsolete option which is a no-op but is # still accepted for backwards compatibility. # MellonVariable is used to select the name of the cookie which # mod_auth_mellon should use to remember the session id. If you # want to have different sites running on the same host, then # you will have to choose a different name for the cookie for each # site. # Default: "cookie" MellonVariable "cookie" # Whether the cookie set by auth_mellon should have HttpOnly and # secure flags set. Once "On" - both flags will be set. Values # "httponly" or "secure" will respectively set only one flag. # 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 / # MellonCookieSameSite allows control over the SameSite value used # for the authentication cookie. # The setting accepts values of "Strict", "Lax", or "None". # When using none, you should set "MellonSecureCookie On" to prevent # compatibility issues with newer browsers. # If not set, the SameSite attribute is not set on the cookie. In newer # browsers, this may cause SameSite to default to "Lax" # Default: not set # MellonCookieSameSite lax # Some browsers will reject cookies if SameSite is specified. # MELLON_DISABLE_SAMESITE environment variable suppresses # unnecessary setting of SameSite cookies # SetEnvIf User-Agent ^.*Chrome\/(5[1-9]|6[0-6]).*$ MELLON_DISABLE_SAMESITE # SetEnvIf User-Agent ^.*Android.*UCBrowser\/([0-9]|1[0-1]).*$ MELLON_DISABLE_SAMESITE # SetEnvIf User-Agent ^.*Android.*UCBrowser\/12\.([0-9]|1[0-2]).*$ MELLON_DISABLE_SAMESITE # SetEnvIf User-Agent ^.*Android.*UCBrowser\/12\.13\.[0-1].*$ MELLON_DISABLE_SAMESITE # SetEnvIf User-Agent ^.*iPhone; CPU iPhone OS 1[0-2].*$ MELLON_DISABLE_SAMESITE # SetEnvIf User-Agent ^.*iPad; CPU OS 1[0-2].*$ MELLON_DISABLE_SAMESITE # SetEnvIf User-Agent ^.*iPod touch; CPU iPhone OS 1[0-2].*$ MELLON_DISABLE_SAMESITE # SetEnvIf User-Agent ^.*Macintosh; Intel Mac OS X.*Version\/1[0-2].*Safari.*$ MELLON_DISABLE_SAMESITE # MellonUser selects which attribute we should use for the username. # The username is passed on to other apache modules and to the web # page the user visits. NAME_ID is an attribute which we set to # the id we get from the IdP. # Note: If MellonUser refers to a multi-valued attribute, any single # value from that attribute may be used. Do not rely on it selecting a # specific value. # Default: MellonUser "NAME_ID" MellonUser "NAME_ID" # MellonIdP selects in which attribute we should dump the remote # IdP entityId. 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" # MellonEnvPrefix changes the string the variables passed from the # IdP are prefixed with. # Default: MELLON_ MellonEnvPrefix "NOLLEM_" # MellonMergeEnvVars merges multiple values of environment variables # set using MellonSetEnv into single variable: # ie: MYENV_VAR => val1;val2;val3 instead of default behaviour of: # MYENV_VAR_0 => val1, MYENV_VAR_1 => val2 ... etc. # Second optional parameter specifies the separator, to override the # default semicolon. # Default: MellonMergeEnvVars Off MellonMergeEnvVars On MellonMergeEnvVars On ":" # MellonEnvVarsIndexStart specifies if environment variables for # multi-valued attributes should start indexing from 0 or 1 # The syntax is 'MellonEnvVarsIndexStart <0|1>'. # Default: MellonEnvVarsIndexStart 0 MellonEnvVarsIndexStart 1 # MellonEnvVarsSetCount specifies if number of values for any given # attribute should also be stored in variable suffixed _N. # ie: if user is member of two groups, the following will be set: # MELLON__groups=group1 # MELLON__groups_0=group1 # MELLON__groups_1=group2 # MELLON__groups_N=2 # Default: MellonEnvVarsSetCount Off MellonEnvVarsSetCount On # If MellonSessionDump is set, then the SAML session will be # available in the MELLON_SESSION environment variable MellonSessionDump Off # If MellonSamlResponseDump is set, then the SAML authentication # response will be available in the MELLON_SAML_RESPONSE environment # variable MellonSamlResponseDump Off # MellonRequire allows you to limit access to those with specific # attributes. The syntax is # 'MellonRequire '. # Note that the attribute name is the name we received from the # IdP. # # If you don't list any MellonRequire directives (and any # MellonCond directives, see below), then any user authenticated # by the IdP will have access to this service. If you list several # MellonRequire directives, then all of them will have to match. # If you use multiple MellonRequire directive on the same # attribute, the last overrides the previous ones. # # Default: None set. MellonRequire "eduPersonAffiliation" "student" "employee" # MellonCond provides the same function as MellonRequire, with # extra functionality (MellonRequire is retained for backward # compatibility). The syntax is # 'MellonCond []' # # is an attribute value to match. Unlike with MellonRequire, # multiples values are not allowed. # # If the [REG] flag is specified (see below), is a regular # expression. The syntax for backslash escape is the same as in # Apache's 's directives. # # Format strings are substituted into prior evaluation. # Here are the supported syntaxes: # %n With n being a digit between 0 and 9. If [REG,REF] # flags (see below) were used in an earlier matching # MellonCond, then regular expression back references # are substituted. # %{num} Same as %n, with num being a number that may be # greater than 9. # %{ENV:x} Substitute Apache environment variable x. # %% Escape substitution to get a literal %. # # is an optional, comma-separated list of option # enclosed with brackets. Here is an example: [NOT,NC] # The valid options are: # OR If this MellonCond evaluated to false, then the # next one will be checked. If it evaluates to true, # then the overall check succeeds. # NOT This MellonCond evaluates to true if the attribute # does not match the value. # SUB Substring match, evaluates to true if value is # included in attribute. # REG Value to check is a regular expression. # NC Perform case insensitive match. # MAP Attempt to search an attribute with name remapped by # MellonSetEnv. Fallback to non remapped name if not # found. # REF Used with REG, track regular expression back references, # So that they can be substituted in an upcoming # MellonCond directive. # # It is allowed to have multiple MellonCond on the same # attribute, and to mix MellonCond and MellonRequire. # Note that this can create tricky situations, since the OR # option has effect on a following MellonRequire directive. # # Default: none set # MellonCond "mail" "@example\.net$" [OR,REG] # MellonCond "mail" "@example\.com$" [OR,REG] # MellonCond "uid" "superuser" # MellonEndpointPath selects which directory mod_auth_mellon # should assume contains the SAML 2.0 endpoints. Any request to # this directory will be handled by mod_auth_mellon. # # The path is the full path (from the root of the web server) to # the directory. The directory must be a sub-directory of this # . # Default: MellonEndpointPath "/mellon" MellonEndpointPath "/secret/endpoint" # MellonDefaultLoginPath is the location where one should be # redirected after an IdP-initiated login. Default is "/" # Default: MellonDefaultLoginPath "/" MellonDefaultLoginPath "/" # MellonSessionLength sets the maximum lifetime of a session, in # seconds. The actual lifetime may be shorter, depending on the # conditions received from the IdP. The default length is 86400 # seconds, which is one day. # Default: MellonSessionLength 86400 MellonSessionLength 86400 # MellonNoCookieErrorPage is the full path to a page which # mod_auth_mellon will redirect the user to if he returns from the # IdP without a cookie with a session id. # Note that the user may also get this error if he for some reason # loses the cookie between being redirected to the IdP's login page # and returning from it. # If this option is unset, then mod_auth_mellon will return a # 400 Bad Request error if the cookie is missing. # Default: unset MellonNoCookieErrorPage "https://example.com/no_cookie.html" # MellonSPMetadataFile is the full path to the file containing # the metadata for this service provider. # If mod_auth_mellon was compiled against Lasso version 2.2.2 # or higher, this option is optional. Otherwise, it is mandatory. # Default: None set. MellonSPMetadataFile /etc/apache2/mellon/sp-metadata.xml # If you choose to autogenerate metadata, this option # can be used to control the SP entityId # MellonSPentityId "https://www.example.net/foo" # # If you choose to autogenerate metadata, these options # can be used to fill the element. They # all follow the syntax "option [lang] value": # MellonOrganizationName "random-service" # MellonOrganizationDisplayName "en" "Random service" # MellonOrganizationDisplayName "fr" "Service quelconque" # MellonOrganizationURL "http://www.espci.fr" # MellonSPPrivateKeyFile is a .pem file which contains the private # key of the service provider. The .pem-file cannot be encrypted # with a password. If built with lasso-2.2.2 or higher, the # private key only needs to be readable by root, otherwise it has # to be readable by the Apache pseudo user. # Default: None set. MellonSPPrivateKeyFile /etc/apache2/mellon/sp-private-key.pem # MellonSPCertFile is a .pem file with the certificate for the # service provider. This directive is optional. # Default: None set. MellonSPCertFile /etc/apache2/mellon/sp-cert.pem # MellonIdPMetadataFile is the full path to the file which contains # metadata for the IdP you are authenticating against. This # directive is required. Multiple IdP metadata can be configured # by using multiple MellonIdPMetadataFile directives. # # If your lasso library is recent enough (higher than 2.3.5), # then MellonIdPMetadataFile will accept an XML file containing # descriptors for multiple IdP. An optional validating chain can # be supplied as a second argument to MellonIdPMetadataFile. If # omitted, no metadata validation will take place. # # Default: None set. MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml # MellonIdPMetadataGlob is a glob(3) pattern enabled alternative # to MellonIdPMetadataFile. Like MellonIdPMetadataFile it will # accept an optional validating chain if lasso is recent enough. # # Default: None set. #MellonIdPMetadataGlob /etc/apache2/mellon/*-metadata.xml # MellonIdpPublicKeyFile is the full path of the public key of the # IdP. This parameter is optional if the public key is embedded # in the IdP's metadata file, or if a certificate authority is # used. This parameter cannot be used if multiple IdP are configured. # Default: None set. MellonIdPPublicKeyFile /etc/apache2/mellon/idp-public-key.pem # MellonIdPCAFile is the full path to the certificate of the # certificate authority. This can be used instead of an # certificate for the IdP. # Default: None set. MellonIdPCAFile /etc/apache2/mellon/ca.pem # MellonIdPIgnore lists IdP entityId that should not loaded # from XML federation metadata files. This is useful if an # IdP cause bugs. Multiple entityId may be specified through # single MellonIdPIgnore, and multiple MellonIdPIgnore are allowed. # Default: None set. #MellonIdPIgnore "https://bug.example.net/saml/idp" # MellonDiscoveryURL is the URL for IdP discovery service. # This is used for selecting among multiple configured IdP. # On initial user authentication, it is redirected to the # IdP discovery URL, with the following arguments set: # # entityID SP entityId 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 entityId. # # 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. # If probe discovery fails and this is provided, an # HTTP error 500 is returned, instead of proceeding # with first available IdP. # # 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" # This option will set the "Comparsion" attribute within the AuthnRequest # It could be set to "exact", "minimum", "maximum" or "better" # MellonAuthnContextComparisonType "minimum" # MellonSubjectConfirmationDataAddressCheck is used to control # the checking of client IP address against the address returned by the # IdP in Address attribute of the SubjectConfirmationData node. Can be useful if your SP is # behind a reverse proxy or any kind of strange network topology making IP address of client # different for the IdP and the SP. Default is on. # MellonSubjectConfirmationDataAddressCheck On # Does not check signature on logout messages exchanges with idp1 # MellonDoNotVerifyLogoutSignature http://idp1.example.com/saml/metadata # Whether to enable replay of POST requests after authentication. When this option is # enabled, POST requests that trigger authentication will be saved until the # authentication is completed, and then replayed. If this option isn't enabled, # the requests will be turned into normal GET requests after authentication. # # Note that if this option is enabled, you must also # set the MellonPostDirectory option in the server configuration. # # The default is that it is "Off". # MellonPostReplay Off # Page to redirect to if the IdP sends an error in response to # the authentication request. # # Example: # MellonNoSuccessErrorPage https://sp.example.org/login_failed.html # # The default is to not redirect, but rather send a # 401 Unautorized error. # This option controls whether to include a list of IDP's when # sending an ECP PAOS message to an ECP client. MellonECPSendIDPList Off # This option controls whether the Cache-control header is sent # back in responses. # Default: On # MellonSendCacheControlHeader Off # List of domains that we allow redirects to. # The special name "[self]" means the domain of the current request. # The domain names can also use wildcards. # # Example: # * Allow redirects to example.com and all subdomains: # MellonRedirectDomains example.com *.example.com # * Allow redirects to the host running mod_auth_mellon, as well as the # web page at www.example.com: # MellonRedirectDomains [self] www.example.com # * Allow redirects to all domains: # MellonRedirectDomains * # # Default: # MellonRedirectDomains [self] MellonRedirectDomains [self] # This option controls the signature method used to sign SAML # messages generated by Mellon, it may be one of the following # (depending if feature was supported when Mellon was built): # # rsa-sha1 # rsa-sha256 # rsa-sha384 # rsa-sha512 # # Default: rsa-sha256 # MellonSignatureMethod ``` ## 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: ```xml urn:oasis:names:tc:SAML:2.0:nameid-format:transient ``` You should update `entityID="https://example.com"` and the two Location attributes. Note that '/secret/endpoint' in the two Location attributes matches the path set in MellonEndpointPath. To use the HTTP-Artifact binding instead of the HTTP-POST binding, change the AssertionConsumerService-element to something like this: ```xml ``` The metadata are published at the 'endpoint/metadata' URL. ## Using mod_auth_mellon After you have set up mod_auth_mellon, you should be able to visit (in our example) https://example.com/secret/, and be redirected to the IdP's login page. After logging in you should be returned to https://example.com/secret/, and get the contents of that page. When authenticating a user, mod_auth_mellon will set some environment variables to the attributes it received from the IdP. The name of the variables will be `MELLON_`. If you have specified a different name with the MellonSetEnv or MellonSetEnvNoPrefix configuration directive, then that name will be used instead. In the case of MellonSetEnv, the name will still be prefixed by `MELLON_`. If an attribute has multiple values, then they will be stored as `MELLON__0`, `MELLON__1`, `MELLON__2`, ... Since mod_auth_mellon doesn't know which attributes may have multiple values, it will store every attribute at least twice: once named `MELLON_`, and once named `_0`. In the case of multivalued attributes, `MELLON_` will contain the first value. NOTE: If MellonMergeEnvVars is set to On, multiple values of attributes will be stored in single environment variable, separated by semicolons: ``` MELLON_="value1;value2;value3[;valueX]" ``` and variables `MELLON__0`, `MELLON__1`, `MELLON__2`, ... will not be created. The following code is a simple PHP script which prints out all the variables: ```php $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: ```html Log out ``` This will return the user to "https://www.example.org/logged_out.html" after the logout operation has completed. ## Invalidating session It is possible to invalidate the current mod_auth_mellon session, without calling SLO. The mod_auth_mellon cookie session will be invalidated and the session will be removed from the mod_auth_mellon cache. SLO will not be possible after the mod_auth_mellon session is invalidated. If this functionality is enabled, invalidate the session by calling the endpoint "/invalidate". Here is a sample configuration to enabled this feature: ```ApacheConf MellonEnabledInvalidateSessionEndpoint On ``` Default value is Off ## 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: ```ApacheConf MellonEndpointPath "/saml" (...) MellonDiscoveryUrl "/saml/probeDisco" MellonProbeDiscoveryTimeout 1 ``` The SP will send an HTTP GET to each configured IdP entityId 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: ```ApacheConf 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. ## Example to support both SAML and Basic Auth The below snippet will allow for preemptive basic auth (such as from a REST client) for the "/auth" path, but if accessed interactively will trigger SAML auth with mod_auth_mellon. ```ApacheConf MellonEnable "info" MellonVariable "cookie" MellonEndpointPath "/sso" Mellon... # Other parameters as needed AuthName "My Auth" AuthBasicProvider ldap AuthType basic AuthLDAP* # Other basic auth config parms as needed require ldap-group .... Require valid-user AuthType "Mellon" MellonEnable "auth" ``` ## Mellon & User Agent Caching behavior For each content within an Apache Location enabled with "info" or "auth", mod_auth_mellon sends by default the HTTP/1.1 Cache-Control header with value `private, must-revalidate`: - `private` protects content against caching by any proxy servers. - `must-revalidate` obligates the user agent to revalidate maybe locally cached or stored content each time on accessing location. This default behavior ensures that the user agent never shows cached static HTML pages after logout without revalidating. So the user can't be misled about a malfunction of the logout procedure. Revalidating content after logout leads to a new authentication procedure via mellon. But mod_auth_mellon will never prohibit specifically any user agent from caching or storing content locally, that has to be revalidated. So that during the session, a user agent only revalidates data by the server `304 Not Modified` response, and does not have to download content again. For special content types like images it could make sense to disable revalidation completely, so that user agents can provide cached and stored content directly to the user. This can be achieved by using other Apache modules mod_headers and mod_setenvif. E.g. for PNG images: * Using Apache 2.2 configuration options: SetEnvIf Request_URI "\.png$" DISABLE_REVALIDATION Header always unset Cache-Control env=DISABLE_REVALIDATION * In Apache 2.4, there's a shorter notation: Header always unset Cache-Control expr=%{CONTENT_TYPE}==image/png Editing, appending, and overwriting headers is possible in other cases. ## Support There's a mailing list for discussion and support. * To subscribe: https://sympa.uninett.no/lists/uninett.no/subscribe/modmellon * List archives: https://sympa.uninett.no/lists/uninett.no/arc/modmellon ## Reporting security vulnerabilities For reporting security vulnerabilities in mod_auth_mellon, please contact the maintainers directly at the following email address: jhrozek@redhat.com simo@redhat.com This allows us to coordinate the disclosure of the vulnerability with the fixes for the vulnerability. ## Contributors Thanks to [Emmanuel Dreyfus](mailto:manu@netbsd.org) for many new features, including: - Metadata autogeneration support. - Support for multiple IdPs. - IdP discovery service support. - SOAP logout support. [Benjamin Dauvergne](mailto:bdauvergne@entrouvert.com) 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.16.0/ECP.rst0000664002412700241270000003022513367654776016061 0ustar jhrozekjhrozekGuide to using ECP ================== Introduction ------------ The **Enhanced Client or Proxy** (ECP) profile of SAML2 The Enhanced Client or Proxy (ECP) Profile supports several SSO use cases, in particular: * Clients with capabilities beyond those of a browser, allowing them to more actively participate in IdP discovery and message flow. * Using a proxy server, for example a WAP gateway in front of a mobile device which has limited functionality. * When other bindings are precluded (e.g. where the client does not support redirects, or when auto form post is not possible without Javascript, or when the artifact binding is ruled out because the identity provider and service provider cannot directly communicate. An enhanced client or proxy (ECP) is a system entity that knows how to contact an appropriate identity provider, possibly in a context-dependent fashion, and also supports the Reverse SOAP (PAOS) binding. An example scenario enabled by ECP profile is as follows: A principal, wielding an ECP, uses it to either access a resource at a service provider, or access an identity provider such that the service provider and desired resource are understood or implicit. The principal authenticates (or has already authenticated) with the identity provider [1]_, which then produces an authentication assertion (possibly with input from the service provider). The service provider then consumes the assertion and subsequently establishes a security context for the principal. During this process, a name identifier might also be established between the providers for the principal, subject to the parameters of the interaction and the consent of the principal. SAML2 Profile for ECP (Section 4.2) defines these steps for an ECP transaction: 1. ECP issues HTTP Request to SP 2. SP issues to ECP using PAOS 3. ECP determines IdP 4. ECP conveys to IdP using SOAP 5. IdP identifies principal 6. IdP issues to ECP, targeted at SP using SOAP 7. ECP conveys to SP using PAOS 8. SP grants or denies access to principal mod_auth_mellon and ECP ----------------------- mod_auth_mellon plays the role of the SP in an ECP transaction. mod_auth_mellon utilizes the Lasso library to provide it's SAML2 functionality. Fully functioning SAML2 ECP support in Lasso is relatively new. When mod_auth_mellon is built it detects the presence of SAML2 ECP in Lasso and only compiles in the ECP code in mod_auth_mellon if it's present in Lasso. How does mod_auth_mellon recognize a request is from an ECP client? ``````````````````````````````````````````````````````````````````` In Step 1. when the ECP client issues the HTTP Request to the SP it **MUST** include `application/vnd.paos+xml` as a mime type in the HTTP `Accept` header field and include an HTTP `PAOS` header specifying a PAOS version of `urn:liberty:paos:2003-08` and an ECP service declaration of `urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp` [2]_, for example:: Accept: text/html, application/vnd.paos+xml PAOS: ver="urn:liberty:paos:2003-08";"urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp" If mod_auth_mellon sees this in the incoming request it knows the client is ECP aware and capable. If authentication is required mod_auth_mellon will initiate an ECP flow. The role of IdP's in ECP ```````````````````````` The SAML2 ECP profile states it is the ECP client which determines the IdP that will be used for authentication. This is in contrast to the Web SSO flow where the SP determines the IdP. However, the ECP protocol permits an SP to send the ECP client a list of IdP's it trusts. It is optional if the SP sends an IDPList, if it does the ECP client should select the IdP from the SP provided IDPList otherwise the ECP client is free to select any IdP it wishes. If the mellon configuration option `MellonECPSendIDPList` is true then mod_auth_mellon will include an IDPList when it returns a PAOS to the ECP client. To build the IDPList mod_auth_mellon scans it's list of loaded IdP's selecting those which are ECP capable. To support ECP an IdP must advertise the SingleSignOn service utilizing the SOAP binding. ECP specific mod_auth_mellon configuration directives ````````````````````````````````````````````````````` These configuration directives are specific to ECP: MellonECPSendIDPList If `On` mod_auth_mellon will send an IdP list to the ECP client containing only those IdP's capable of ECP flow. The ECP client should select an IdP only from this list. If this option is `Off` no IdP list will be sent and the ECP client is free to select any IdP. Example ECP client `````````````````` To illustrate a simple ECP client based on Lasso we'll use the Lasso Python binding (as opposed to pseudo code, Python is quite readable). All error checking and another necessary ancillary code has been eliminated in order to clearly illustrate only the ECP operations. .. code-block:: python import lasso import requests ecp = lasso.Ecp(server) session = requests.Session() MEDIA_TYPE_PAOS = 'application/vnd.paos+xml' PAOS_HEADER = 'ver="%s";"%s"' % (lasso.PAOS_HREF,lasso.ECP_HREF) # Step 1: Request protected resource, indicate ECP capable response = session.get(protected, headers={'Accept': MEDIA_TYPE_PAOS, 'PAOS': PAOS_HEADER}) # Process returned PAOS wrapped ecp.processAuthnRequestMsg(response.text) # Post SOAP wrapped to IdP, use Digest Auth to authenticate response = session.post(ecp.msgUrl, data=ecp.msgBody, auth=requests.auth.HTTPDigestAuth(user, password) headers={'Content-Type': 'text/xml'}) # Process returned SOAP wrapped from IdP ecp.processResponseMsg(response.text) # Post PASO wrapped to SP, response is protected resource response = session.post(ecp.msgUrl, data=ecp.msgBody, headers={'Content-Type': 'application/vnd.paos+xml'}) mod_auth_mellon internal ECP implementation notes ------------------------------------------------- Notes on ECP vs. Web SSO flow ````````````````````````````` Web SSO (Single Sign-On) flow is by far the most common and what most people are familiar with when they think of SAML. The Web SSO profile is designed so that browsers ignorant of SAML can perform SAML authentication without modification. This is accomplished with existing HTTP paradigms such as redirects, form posts, etc. which a browser will process normally yielding the desired result. ECP (Enhanced Client or Proxy) is a different SAML profile that also accomplishes SSO (Single Sign-On). The distinction is an ECP client is fully SAML aware and actively participates in the SAML conversation. Web SSO and ECP have very different flows, mod_auth_mellon must support both flows. mod_auth_mellon is a SP (Service Provider). IdP Selection Differences ````````````````````````` With Web SSO the SP determines the IdP and redirects there. With ECP the ECP client determines the IdP, the SP has no a prori knowledge of the target IdP, although the SP may provide a suggested list of IdP's when responding to the ECP client. Since with ECP it is the ECP client which selects the IdP the set of IdP's loaded into mod_auth_mellon are not relevant **except** if `MellonECPSendIDPList` is enabled. In this case mod_auth_mellon will filter the set of loaded IdP's and forward those IdP's supporting SingleSignOn with the SOAP binding. Apache request processing pipeline `````````````````````````````````` Apache implements a request processing pipeline composed of stages. An Apache extension module can participate in the pipeline by asking to be called at specific stages (steps) by registering a hook function for that stage. Final content returned to the HTTP client in the HTTP response is generated in the "handler", one of the final stages in the request processing pipeline. One of the stages in the request pipeline is determining authentication and authorization for protected resources. If a resource is protected and the authentication and authorization pipeline stages deny access or fail the request processing pipeline is aborted early, a non-success HTTP response is returned, the content handler is never reached. With Web SSO if authentication needs to be performed a redirect will be returned that redirects to a SAML endpoint (login) on our SP. This in turn generates the SAML with a redirect to the IdP. All of this is very vanilla standard HTTP easily accommodated by Apache's request processing pipeline which is designed to handle these types of flows. ECP requires special handling ````````````````````````````` However ECP has a very different flow. When an ECP client sends a request to the SP it includes a special HTTP headers indicating it is ECP capable. If the SP determines the resource is protected and authentication is needed and the client has signaled it is ECP capable then the SP responds successfully (200) with a SAML wrapped in PAOS. *This is very different than conventional HTTP request processing.* Here we have a case where there is a protected resource that has **not** been authenticated yet the web server will responds with an HTTP 200 success and content! One might normally expect a HTTP 401 or redirect response for a protected resource when there is no authenticated user. *This is clearly contrary to the expectations of Apache's request processing pipeline.* Reaching the Apache content handler ``````````````````````````````````` In order to be able to return a successful (HTTP 200) PAOS response when doing the ECP we have to reach the part of Apache's request processing pipeline that generates the response. In Apache terminology this is called a (content) handler. At an early stage we detect if authentication is required. For the normal Web SSO profile we would redirect the client back to our login endpoint which will be handled by our handler in a different request. But for ECP the current request must proceed. We set a flag on the request indicating ECP authentication is required. The pipeline continues. When the pipeline reaches the authentication and authorization stages we check the ECP flag on the request, if ECP authentication is indicated we lie and tell the pipeline the user is authenticated and authorized. We do this only so we can reach the handler stage (otherwise because the request is for a protected resource the pipeline would terminate with an error). Despite our having forced authentication and authorization to be valid for the protected resource the request processing pipeline *will not return the protected resource* because we will subsequently intercept the request in our handler before the pipeline reaches the point of returning the protected resource. At the handler stage ```````````````````` Once our handler is invoked it has 3 possible actions to perform: 1. The request is for one of our SAML endpoints (e.g. login, logout, metadata, etc.) We dispatch to the handler for the specific action. We detect this case by matching the request URI to our SAML endpoints. We signal to the pipeline that our hook handled the request. 2. The request is for a protected resource and needs ECP authentication performed. We detect this case by examining the ECP flag set on the request by an earlier hook function. The request URI is for the protected resource and has nothing to do with our SAML endpoints. We generate the PAOS and respond with success (200) and signal to the pipeline that our hook handled the request. Note, we have not returned the protected resource, instead we've returned the PAOS request. 3. The request has nothing to do with us, we decline to handle it. The pipeline proceeds to the next handler. .. [1] The means by which a principal authenticates with an identity provider is outside of the scope of SAML. Typically an ECP client will utilize an HTTP authentication method when posting the SOAP message to the IdP. .. [2] Contrary to most HTTP headers the values in the PAOS header must be enclosed in double quotes. A semicolon is used to separate the values. mod_auth_mellon-0.16.0/COPYING0000664002412700241270000006131413367654776015756 0ustar jhrozekjhrozekmod_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.16.0/NEWS0000664002412700241270000004663513614046140015402 0ustar jhrozekjhrozekVersion 0.16.0 --------------------------------------------------------------------------- Enhancements: * The MellonCookieSameSite option accepts a new valid "None". This is intended to be used together with "MellonSecureCookie On". With some newer browsers, only cookies with "SameSite=None; Secure" would be available for cross-site access. * A new option MellonEnabledInvalidateSessionEndpoint was added. When this option is enabled, then a user can invalidate their session locally by calling the "/invalidate" endpoint. Version 0.15.0 --------------------------------------------------------------------------- Security fixes: * [CVE-2019-13038] Redirect URL validation bypass Version 0.14.1 and older of mod_auth_mellon allows the redirect URL validation to be bypassed by specifying an URL formatted as "http:www.hostname.com". In this case, the APR parsing utility would parse the scheme as http, host as NULL and path as www.hostname.com. Browsers, however, interpret the URL differently and redirect to www.hostname.com. This could be reproduced with: https://application.com/mellon/login?ReturnTo=http:www.hostname.com This version fixes that issue by rejecting all URLs with scheme, but no host name. Enhancements: * A XSLT script that allows converting attribute maps from Shibboleth to a set of MellonSetEnvNoPrefix entries was added. The script can be found at doc/mellon-attribute-map.xsl * A new configuration option MellonEnvPrefix was added. This option allows you to configure the variable prefix, which normally defaults to MELLON_ * A new configuration option MellonAuthnContextComparisonType was added. This option allows you to set the "Comparison" attribute within the AuthnRequest Notable bug fixes: * Compilation issues on Solaris were fixed Version 0.14.2 --------------------------------------------------------------------------- Security fixes: * [CVE-2019-3878] Authentication bypass when Apache is used as a reverse proxy If Apache is configured as a reverse proxy with mod_auth_mellon for authentication, the authentication can be bypassed by adding SAML 2.0 ECP headers to the request. This vulnerability affects mod_auth_mellon 0.11.0 and newer. This vulnerability is due to both mod_auth_mellon and mod_proxy registering as handlers for the requests, with the same priority. When mod_auth_mellon handles the request first, it will trigger a ECP authentication request. If mod_proxy handles it first, it will forward it to the backend server. Which module handles it first depends on the order modules are loaded by Apache. This vulnerability is fixes by specifically registering that the mod_auth_mellon handler should run before mod_proxy. Thanks to Jakub Hrozek and John Dennis at RedHat for fixing this vulnerability. * [CVE-2019-3877] Redirect URL validation bypass Version 0.14.1 and older of mod_auth_mellon allows the redirect URL validation to be bypassed by specifying an URL with backslashes instead of forward slashes. Browsers silently convert backslashes to forward slashes, which allows an attacker to bypass the redirect URL validation by using `%5c` in the ReturnTo-parameter. E.g.: https://sp.example.org/mellon/logout?ReturnTo=https:%5c%5cmalicious.example.org/ This version fixes that issue by rejecting all URLs with backslashes. Thanks to Eric Chamberland for discovering this vulnerability. Version 0.14.1 --------------------------------------------------------------------------- Bug fixes: * Fix environment variables in MellonCond * Fix detection of AJAX requests * Fix trailing semi-colon in Set-Cookie header Version 0.14.0 --------------------------------------------------------------------------- Backwards incompatible changes: This version switches the default signature algorithm used when signing messages from rsa-sha1 to rsa-sha256. If your IdP does not allow messages to be signed with that algorithm, you need to add a setting switching back to the old algorithm: MellonSignatureMethod rsa-sha1 Note that this only affects messages sent from mod_auth_mellon to your IdP. It does not affect authentication responses or other messages sent from your IdP to mod_auth_mellon. New features: * Many improvements in what is logged during various errors. * Diagnostics logging, which creates a detailed log during request processing. * Add support for selecting which signature algorithm is used when signing messages, and switch to rsa-sha256 by default. Bug fixes: * Fix segmentation fault in POST replay functionality on empty value. * Fix incorrect error check for many `lasso_*`-functions. * Fix case sensitive match on MellonUser attribute name. Version 0.13.1 --------------------------------------------------------------------------- Security fix: Fix a cross-site session transfer vulnerability. mod_auth_mellon version 0.13.0 and older failed to validate that the session specified in the user's session cookie was created for the web site the user actually accesses. If two different web sites are hosted on the same web server, and both web sites use mod_auth_mellon for authentication, this vulnerability makes it possible for an attacker with access to one of the web sites to copy their session cookie to the other web site, and then use the same session to get access to the other web site. Thanks to François Kooman for reporting this vulnerability. This vulnerability has been assigned CVE-2017-6807. Note: The fix for this vunlerability makes mod_auth_mellon validate that the cookie parameters used when creating the session match the cookie parameters that should be used when accessing the current page. If you currently use mod_auth_mellon across multiple subdomains, you must make sure that you set the `MellonCookie`-option to the same value on all domains. Bug fixes: * Fix segmentation fault if a (trusted) identity provider returns a SAML 2.0 attribute without a Name. * Fix segmentation fault if MellonPostReplay is enabled but MellonPostDirectory is not set. Version 0.13.0 --------------------------------------------------------------------------- Security fix: Fix a denial of service attack in the logout handler, which allows a remote attacker to crash the Apache worker process with a segmentation fault. This is caused by a null-pointer dereference when processing a malformed logout message. New features: * Allow MellonSecureCookie to be configured to enable just one of the "httponly" of "secure" flags, instead of always enabling both flags. * Support per-module log level with Apache 2.4. * Allow disabling the Cache-Control HTTP response header. * Add support for SameSite cookie parameter. Bug fixes: * Fix MellonProbeDiscoveryIdP redirecting to the wrong IdP if no IdPs respond to the probe request. * Fix mod_auth_mellon interfering with other Apache authentication modules even when it is disabled for a path. * Fix wrong HTTP status code being returned in some cases during user permission checks. * Fix default POST size limit to actually be 1 MB. * Fix error if authentication response is missing the optional Conditions-element. * Fix AJAX requests being redirected to the IdP. * Fix wrong content type for ECP authentication request responses. In addition there are various fixes for errors in the documentation, as well as internal code changes that do not have any user visible effects. Version 0.12.0 --------------------------------------------------------------------------- Security fixes: * [CVE-2016-2145] Fix DOS attack (Apache worker process crash) due to incorrect error handling when reading POST data from client. * [CVE-2016-2146] Fix DOS attack (Apache worker process crash / resource exhaustion) due to missing size checks when reading POST data. In addition this release contains the following new features and fixes: * Add MellonRedirectDomains option to limit the sites that mod_auth_mellon can redirect to. This option is enabled by default. * Add support for ECP service options in PAOS requests. * Fix AssertionConsumerService lookup for PAOS requests. Version 0.11.1 --------------------------------------------------------------------------- Security fixes: * [CVE-2016-2145] Fix DOS attack (Apache worker process crash) due to incorrect error handling when reading POST data from client. * [CVE-2016-2146] Fix DOS attack (Apache worker process crash / resource exhaustion) due to missing size checks when reading POST data. Version 0.11.0 --------------------------------------------------------------------------- * Add SAML 2.0 ECP support. * The MellonDecode option has been disabled. It was used to decode attributes in a Feide-specific encoding that is no longer used. * Set max-age=0 in Cache-Control header, to ensure that all browsers verifies the data on each request. * MellonMergeEnvVars On now accepts second optional parameter, the separator to be used instead of the default ';'. * Add option MellonEnvVarsSetCount to specify if the number of values for any attribute should also be stored in environment variable suffixed _N. * Add option MellonEnvVarsIndexStart to specify if environment variables for multi-valued attributes should start indexing with 0 (default) or with 1. * Bugfixes: * Fix error about missing authentication with DirectoryIndex in Apache 2.4. Version 0.10.0 --------------------------------------------------------------------------- * Make sure that we fail in the unlikely case where OpenSSL is not able to provide us with a secure session id. * Increase the number of key-value pairs in the session to 2048. * Add MellonMergeEnvVars-option to store multi-valued attributes in a single environment variable, separated with ';'. * Bugfixes: * Fix the [MAP] option for MellonCond. * Fix cookie deletion for the session cookie. (Logout is not dependent on the cookie being deleted, so this only fixes the cookie showing up after the session is deleted.) Version 0.9.1 --------------------------------------------------------------------------- * Bugfixes: * Fix session offset calculation that prevented us from having active sessions at once. * Run mod_auth_mellon request handler before most other handlers, so that other handlers cannot block it by accident. Version 0.9.0 --------------------------------------------------------------------------- * Set the AssertionConsumerServiceURL attribute in authentication requests. * Bugfixes: * Fix use of uninitialized data during logout. * Fix session entry overflow leading to segmentation faults. * Fix looking up sessions by NameID, which is used during logout. Version 0.8.1 --------------------------------------------------------------------------- This is a security release with fixes backported from version 0.9.1. It turned out that session overflow bugs fixes in version 0.9.0 and 0.9.1 can lead to information disclosure, where data from one session is leaked to another session. Depending on how this data is used by the web application, this may lead to data from one session being disclosed to an user in a different session. (CVE-2014-8566) In addition to the information disclosure, this release contains some fixes for logout processing, where logout requests would crash the Apache web server. (CVE-2014-8567) Version 0.8.0 --------------------------------------------------------------------------- * Add support for receiving HTTP-Artifact identifiers as POST data. * Simplify caching headers. * Map login errors into more appropriate HTTP error codes than 400 Bad Request. * Add MellonNoSuccessErrorPage option to redirect to a error page on login failure. * Turn session storage into a dynamic pool of memory, which means that attribute values (and other items) can have arbitrary sizes as long as they fit in the session as a whole. * Various bugfixes: * Fix for compatibility with recent versions of CURL. * Fix broken option MellonDoNotVerifyLogoutSignature. * Fix deadlock that could occur during logout processing. * Fix some compile warnings. * Fix some NULL derefernce bugs that may lead to segmentation faults. * Fix a minor memory leak during IdP metadata loading. Version 0.7.0 --------------------------------------------------------------------------- * Add MellonSPentityId to control entityId in autogenerated metadata * Fix compatibility with Apache 2.4. * Handle empty RelayState the same as missing RelayState. * Add MellonSetEvnNoPrefix directive to set environment variables without "MELLON_"-prefix. Version 0.6.1 --------------------------------------------------------------------------- * Fix the POST replay functionality when multiple users logging in at once. * Add a fallback for the case where the POST replay data has expired before the user logs in. Version 0.6.0 --------------------------------------------------------------------------- Backwards-incompatible changes: * The POST replay functionality has been disabled by default, and the automatic creation of the MellonPostDirectory target directory has been removed. If you want to use the POST replay functionality, take a look at the README file for instructions for how to enable this. * Start discovery service when accessing the login endpoint. We used to bypass the discovery service in this case, and just pick the first IdP. This has been changed to send a request to the discovery service instead, if one is configured. * The MellonLockFile default path has been changed to: /var/run/mod_auth_mellon.lock This only affects platforms where a lock file is required and where Apache doesn't have write access to that directory during startup. (Apache can normally create files in that directory during startup.) Other changes: * Fix support for SOAP logout. * Local logout when IdP does not support SAML 2.0 Single Logout. * MellonDoNotVerifyLogoutSignature option to disable logout signature validation. * Support for relative file paths in configuration. * The debian build-directory has been removed from the repository. * Various cleanups and bugfixes: * Fix cookie parsing header parsing for some HTTP libraries. * Fix inheritance of MellonAuthnContextClassRef option. * Use ap_set_content_type() instead of accessing request->content_type. * README indentation cleanups. * Support for even older versions of GLib. * Fixes for error handling during session initialization. * Directly link with GLib rather than relying on the Lasso library linking to it for us. * Some code cleanups. Version 0.5.0 --------------------------------------------------------------------------- * Honour MellonProbeDiscoveryIdP order when sending probes. * MellonAuthnContextClassRef configuration directive, to limit authentication to specific authentication methods. * Support for the HTTP-POST binding when sending authentication requests to the IdP. * MellonSubjectConfirmationDataAddressCheck option to disable received address checking. * Various cleanups and bugfixes: * Support for older versions of GLib and APR. * Send the correct SP entityID to the discovery service. * Do not set response headers twice. * Several cleanups in the code that starts authentication. Version 0.4.0 --------------------------------------------------------------------------- * Allow MellonUser variable to be translated through MellonSetEnv * A /mellon/probeDisco endpoint replaces the builtin:get-metadata IdP dicovery URL scheme * New MellonCond directive to enable attribute filtering beyond MellonRequire functionalities. * New MellonIdPMetadataGlob directive to load mulitple IdP metadata using a glob(3) pattern. * Support for running behind reverse proxy. * MellonCookieDomain and MellonCookiePath options to configure cookie settings. * Support for loading federation metadata files. * Several bugfixes. Version 0.3.0 --------------------------------------------------------------------------- * New login-endpoint, which allows easier manual initiation of login requests, and specifying parameters such as IsPassive. * Validation of Conditions and SubjectConfirmation data in the assertion we receive from the IdP. * Various bugfixes. Version 0.2.7 --------------------------------------------------------------------------- * Optionaly save the remote IdP entityId in the environment * Shibboleth 2 interoperability Version 0.2.6 --------------------------------------------------------------------------- * Fix XSS/DOS vulnerability in repost handler. Version 0.2.5 --------------------------------------------------------------------------- * Replay POST requests after been sent to the IdP * Fix HTTP response splitting vulnerability. Version 0.2.4 --------------------------------------------------------------------------- * Fix for downloads of files with Internet Explorer with SSL enabled. * Mark session as disabled as soon as logout starts, in case the IdP doesn't respond. Version 0.2.3 --------------------------------------------------------------------------- * Bugfix for session lifetime. Take the session lifetime from the SessionNotOnOrAfter attribute if it is present. Version 0.2.2 --------------------------------------------------------------------------- * Improve metadata autogeneration: cleanup certificate, allow Organizarion element data to be supplied from Apache configuration Version 0.2.1 --------------------------------------------------------------------------- * Make SAML authentication assertion and Lasso session available in the environment. Version 0.2.0 --------------------------------------------------------------------------- * Autogeneration of SP metadata. (Requires Lasso 2.2.2 or newer.) * Multiple IdP support, with discovery service. * Built in discovery service which tests the availability of each IdP, and uses the first available IdP. * Fix a mutex leak. Version 0.1.1 --------------------------------------------------------------------------- * MellonSecureCookie option, which enables Secure + HttpOnly flags on session cookies. * Better handling of logout request when the user is already logged out. Version 0.1.0 --------------------------------------------------------------------------- * Better support for BSD. * Support for setting a IdP CA certificate and SP certificate. * Support for loading the private key during web server initialization. With this, the private key only needs to be readable by root. This requires a recent version of Lasso to work. * Better DOS resistance, by only allocating a session when the user has authenticated with the IdP. * Support for IdP initiated login. The MellonDefaultLoginPath option can be to configure which page the user should land on after authentication. Version 0.0.7 --------------------------------------------------------------------------- * Renamed the logout endpoint from "logoutRequest" to "logout". "logoutRequest" is now an alias for "logout", and may be removed in the future. * Added SP initiated logout. To initiate a logout from the web site, link the user to the logout endpoint, with a ReturnTo parameter with the url the user should be redirected to after being logged out. Example url: "https://www.example.com/secret/endpoint/logout ?ReturnTo=http://www.example.com/". (Note that this should be on a single line.) * Fixed a memory leak on login. * Increased maximum Lasso session size to 8192 from 3074. This allows us to handle users with more attributes. * Fixed handling of multiple AttributeValue elements in response. mod_auth_mellon-0.16.0/mellon_create_metadata.sh0000775002412700241270000000511713614046140021701 0ustar jhrozekjhrozek#!/usr/bin/env bash set -e PROG="$(basename "$0")" printUsage() { echo "Usage: $PROG ENTITY-ID ENDPOINT-URL" echo "" echo "Example:" echo " $PROG urn:someservice https://sp.example.org/mellon" echo "" } if [ "$#" -lt 2 ]; then printUsage exit 1 fi ENTITYID="$1" if [ -z "$ENTITYID" ]; then echo "$PROG: An entity ID is required." >&2 exit 1 fi BASEURL="$2" if [ -z "$BASEURL" ]; then echo "$PROG: The URL to the MellonEndpointPath is required." >&2 exit 1 fi if ! echo "$BASEURL" | grep -q '^https\?://'; then echo "$PROG: The URL must start with \"http://\" or \"https://\"." >&2 exit 1 fi HOST="$(echo "$BASEURL" | sed 's#^[a-z]*://\([^:/]*\).*#\1#')" BASEURL="$(echo "$BASEURL" | sed 's#/$##')" OUTFILE="$(echo "$ENTITYID" | sed 's/[^0-9A-Za-z.]/_/g' | sed 's/__*/_/g')" echo "Output files:" echo "Private key: $OUTFILE.key" echo "Certificate: $OUTFILE.cert" echo "Metadata: $OUTFILE.xml" echo echo "Host: $HOST" echo echo "Endpoints:" echo "SingleLogoutService: $BASEURL/logout" echo "AssertionConsumerService: $BASEURL/postResponse" echo # No files should not be readable by the rest of the world. umask 0077 TEMPLATEFILE="$(mktemp -t mellon_create_sp.XXXXXXXXXX)" cat >"$TEMPLATEFILE" </dev/null rm -f "$TEMPLATEFILE" CERT="$(grep -v '^-----' "$OUTFILE.cert")" cat >"$OUTFILE.xml" < $CERT EOF umask 0777 chmod go+r "$OUTFILE.xml" chmod go+r "$OUTFILE.cert" mod_auth_mellon-0.16.0/doc/mellon_create_metadata.80000664002412700241270000000226613614046140022202 0ustar jhrozekjhrozek.TH man 8 "25 January 2020" "1.0" "mellon_create_metadata manual page" .SH NAME mellon_create_metadata \- Populate inital SP metadata for mod_auth_mellon .SH SYNOPSIS mellon_create_metadata ENTITY-ID ENDPOINT-URL .SH DESCRIPTION The Apache module mod_auth_mellon provides a SAML 2.0 service provider (SP). This service provider needs metadata to function. You can create the initial configuration for this with mellon_create_metadata. Three files will be created in the current directory. A public and private key pair, and a boilerplate metadata xml file with the public key and the URLs of this installation, that can be further edited at will. You can reference these files in the configuration options MellonSPPrivateKeyFile, MellonSPCertFile and MellonSPMetadataFile, respectively. .SH OPTIONS Specify the desired entity ID of the SP. This needs to be globally unique and is therefore an URL or URN, probably with your own domain in it. The endpoint URL is the full URL to your mellon installation. This normally ends with "/mellon" unless configured otherwise. .SH EXAMPLE mellon_create_metadata urn:someservice https://sp.example.org/mellon .SH AUTHOR Thijs Kinkhorst mod_auth_mellon-0.16.0/doc/user_guide/mellon_user_guide.adoc0000664002412700241270000105017613614046140024130 0ustar jhrozekjhrozek= mod_auth_mellon User Guide John Dennis v1.3, 2018-02-22 :toc: left :toclevels: 3 :numbered: :icons: font :imagesdir: images :data-uri: == Colophon Author: {author} {email} Version: {revnumber} Date: {revdate} == Document Conventions .Example Data used in this document **** This document contains many examples of SAML data. For consistency we will use the following: * The SP is hosted on the node `mellon.example.com`. * The SP `MellonEndpointPath` is `/mellon` * The SP `entityID` is `https://mellon.example.com/mellon/metadata` * Mellon is protecting the URL location `/private` and everything under it. * The protected resource is `/private/info.html` and hence the URL of the protected resource is `https://mellon.example.com/private/info.html`. * The IdP is hosted on the node `rhsso.example.com` * The IdP `entityID` is `https://rhsso.example.com:8443/auth/realms/test` **** == Introduction mod_auth_mellon is an Apache (httpd) authentication module providing authentication and authorization services via SAML. Mellon plays the role of a _Service Provider_ (SP) in SAML. == SAML Overview SAML (_Security Assertion Markup Language_) is a framework for exchanging security information between providers. The nonprofit https://www.oasis-open.org/[OASIS] consortium is responsible for defining and publishing the various SAML specifications. OASIS is an acronym for _Organization for the Advancement of Structured Information Standards_. All SAML specifications and errata can be found at this location: https://docs.oasis-open.org/security/saml/v2.0/ The SAML technical committee has published the https://www.oasis-open.org/committees/download.php/27819/sstc-saml-tech-overview-2.0-cd-02.pdf[Security Assertion Markup Language (SAML) V2.0 Technical Overview]. This is an excellent high-level overview of SAML and worth reading to familiarize yourself with general SAML operation and terminology. SAML is a large complex standard that currently comprises 10 individual specifications whose total content is hundreds of pages of printed material. SAML is much too large to cover in this overview. Instead we will focus on the most common use of SAML, Web Single Sign-On (Web-SSO). This is the target focus of mod_auth_mellon, although Mellon does support other profiles as well. SAML organizes itself into <> and <>. A cursory overview of these two concepts will help you understand SAML better and is especially important if you have to refer to any SAML specifications. === SAML Roles [[saml_roles]] Participants in SAML play different roles. An entity may be capable of playing more than one role, however we typically only consider a single role when discussing entity behavior. The defined SAML roles are: * Identity Provider (IdP) * Service Provider (SP) * Affiliation * Attribute Authority * Attribute Consumer * Policy Decision Point Of these we are only interested in Service Providers (SP) and Identity Providers (IdP). Mellon is a Service Provider because it provides a service to clients. Authentication and user information is provided by an Identity Provider. The SP relies on the IdP for its authentication needs. In SAML literature you will often see the term _attesting party_ or _asserting party_, which in most contexts means an IdP because the IdP attests to or asserts certain claims in its role as an _authority_. On the other hand a Service Provider is often referred to as a _relying party_ because it _relies_ on the _assertions_ provided by an _authority_. === SAML Profiles [[saml_profiles]] A SAML profile defines how SAML data is conveyed using <> on a transport to accomplish a specific task. The _Web Browser SSO Profile_ is the best known and the one Mellon focuses on. Other profiles include _Single Logout_, _Enhanced Client or Proxy (ECP)_, _Identity Provider Discovery_, etc. SAML profiles are defined in the https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf[Profiles for the OASIS Security Assertion Markup Language (SAML) V2.0] specification. === SAML Bindings [[saml_bindings]] SAML bindings define how SAML messages are mapped onto standard messaging or communication protocols. The best way to think of a SAML binding is as a transport mechanism. A key concept is that a given SAML profile may permit the same SAML message to be conveyed using variety of SAML bindings. Or by the same token a SAML profile may prohibit the use of certain SAML bindings. SAML bindings are defined in the https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf[Bindings for the OASIS Security Assertion Markup Language (SAML) V2.0] specification. === SAML Messages All SAML messages are conveyed as XML documents. A SAML XML message may be transported in a variety of mechanisms known as a <>. Examples of SAML bindings include: * query parameters of an HTTP URL. * parameters of an HTML form. * wrapped in a SOAP message. The exact way a SAML interchange operates and the SAML bindings which are utilized in each step define what is called a SAML <>. For example web-sso is defined by the _Web Browser SSO Profile_. SAML data, and its XML schema are defined in the https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf[Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0] _core_ specification. === SAML Web-SSO flow [[web_sso_flow]] The _Web Browser SSO Profile_ is the best known <> and the one Mellon focuses on. Your ability to configure Mellon and diagnose Mellon deployment issues will be greatly enhanced if you understand this flow and the two SAML messages conveyed in the flow, _SAML AuthnRequest_ and _SAML Assertion Response_. image::saml-web-sso.svg[Web Browser SSO Profile] (1) HTTP Request to Service Provider:: A user agent (e.g browser) makes a request on behalf of a user for a protected resource hosted by the Service Provider (e.g. Mellon). The SP asks if there is an existing <> for the user. A session is established by a prior successful SAML authentication. If a valid session exists the SP immediately grants access to the protected resource. A user session is communicated via a HTTP cookie (see <>). If a valid session does not exist the SP begins the authentication process. (2) issued by Service Provider to Identity Provider:: To authenticate the user the SP must send a `` to an IdP. In the _Web Browser SSO Profile_ the SP determines the IdP. The SP uses the _HTTP Redirect Binding_ to convey the `` to the IdP. This binding embeds the `` in the URL query parameters of a HTTP redirect. The browser performs a redirect to the IdP which decodes the `` embedded in the URL query parameters. The IdP also maintains <> information for the user. If there is an existing valid session for the user at the IdP it immediately responds with a `` response unless the `` has enabled `ForceAuthn` which requires the user to be re-authenticated. See the <> to better understand its contents and how it appears as HTTP data. Part of the data communicated along with the `` is an item known as the <>. The `RelayState` is the mechanism which permits the flow to return to the original requested resource. (3) Identity Provider identifies Principal:: If necessary the IdP authenticates the user. How the authentication is performed is *not* defined by SAML. Typically the IdP responds with a login page where the user enters their username and password. After successful authentication the IdP establishes a session for the user. (4) Identity Provider issues a SAML to the Service Provider:: Assuming a valid session now exists on the IdP for the user it responds to the user's browser with an `` using the _HTTP Post Binding_. The HTTP Post binding is a bit magical, you may want to review how this works in <>. If a valid session could not be established for the user a failed status response is issued instead. See the <> to better understand the contents of an ``. (5) SP grants or denies access to the principal:: If the response does not contain a successful `` response the SP denies access by returning a 403 HTTP_FORBIDDEN status response. Otherwise the SP processes the ``. The SP may apply additional constraints on access to the protected resource (see <> for how Mellon can apply additional authorization constraints). If the constraint check passes then the SP establishes a session for the user. Mellon associates the session information with a session ID returned in a cookie (see <>). (6) SP redirects to original resource:: The SP uses the <> which identities the original requested resource and responds with a redirect to that URL. (7) Browser accesses resource again:: The browser upon receiving the redirect to the original resource URL once again tries to access the resource. This time however there is a valid session established for the user as communicated in the session cookie. The SP validates the session ID which should immediately succeed. At this point Mellon informs Apache that the authentication and authorization check has succeeded for the URL. (8) SP responds with resource:: Since the authentication and authorization checks in Apache have now passed the contents of the resource are returned to the user's browser. ==== Sessions [[saml_sessions]] Sessions are maintained at both the SP and the IdP. After an IdP successfully authenticates a user it creates a session for the user. The IdP keeps a list of every SP the user is logged into. When the user logs out the IdP sends a logout request to the _SingleLogoutService_ endpoint of each SP. When an IdP receives a `` from a SP it checks to see if it has an existing valid session for that user, if so it can skip authenticating the user again and instead just issue an `` based on the existing session. However the SP can force the IdP to always re-authenticate if it passes a `ForceAuthn` value of `True` in the ``. The IdP may further be restricted from interacting with the SP if the request contains a `isPassive` value of `True`. The IdP can inform the SP how long it wishes a SP session to be valid by passing the `SessionNotOnOrAfter` attribute in a ``. Mellon respects the `SessionNotOnOrAfter` attribute and will limit its session duration based on it. The SP also maintains a session for the user. The SP session is communicated between the browser and the SP using a cookie containing the session ID. If the SP verifies the user has an existing valid session when it receives a request it can immediately make an access decision based on the cached session information for the user. See <> for more information on the particulars of how Mellon manages it sessions. === HTTP Post and Self-Posting [[http_post]] The _HTTP Post Binding_ is used to convey an assertion back to a SP. Assertions are usually too big to embed in a URL so some other mechanism is needed to transport the `` response data. The data is url-form-encoded as HTTP Form. The form's action attribute specifies the destination URL of the form data. Where does the destination URL come from? The IdP will have loaded the SP's <> which defines among other things the various provider URL <> where SAML communication occurs. The `` needs to be sent to one of the SP's _AssertionConsumerService_ endpoints, specifically the _AssertionConsumerService_ endpoint URL supporting the _HTTP-POST_ binding. The SP's _AssertionConsumerService_ URL as read from its metadata is set to the action attribute of the HTTP form. _The action URL is *not* read from any data in the _, this is one safeguard to prevent SAML messages from being sent to a unintended nefarious party. Note that many SAML bindings define a `Destination` attribute that is embedded in the SAML message. A further check compares the `Destination` attribute to the URL the message was received at, see <> for a common deployment problem. But how does the POST data as received by the user's browser get back to the _AssertionConsumerService_ endpoint of the SP? The form will _self-post_ due to this: [source,html] ---- ---- As long as the user's browser has not disabled scripts it will immediately post the form data to the _AssertionConsumerService_ URL in the forms action attribute. If scripts have been disabled the HTML will instruct the user to click the `Submit` button to post the form data. === entityID [[entityID]] Each SAML provider (e.g. a SP or IdP) is identified by its `entityID`. You should think of the `entityID` as the globally unique name of the provider. The `entityID` appears in most SAML messages and in the provider's metadata. This is the mechanism by which a SAML message consumer associates the SAML message with the message producers configuration properties. When a SAML provider receives a message it extracts the `entityID` from the message and then looks up the metadata belonging to that provider. The information in the provider's metadata is essential in order to operate on the SAML message. WARNING: Any mismatch between the `entityID` the producer is emitting and the consumer has loaded via the producer's metadata will cause failures. A common mistake is to modify a producer's metadata (e.g. update Mellon) but fail to reload Mellon's metadata in the IdP. SAML places two requirements on the `entityID`: * It *must* be an URI * It *must* be unique within the federation A wise administrator will also seek to fulfill this additional requirement when choosing an `entityID`: * It should identify the organization instead of a specific node within the organization. When migration time arrives it is far easier to move resources around when those resources are not tied to a specific node. Thus choosing an `entityID` comprised of the organization's domain name and a generic identifier such as `saml` would be one good approach, for example `https://bigcorp.com/saml`. A common practice and one that recommended in the https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf[Metadata for the OASIS Security Assertion Markup Language (SAML)] specification in section 4, _Metadata Publication and Resolution_ is to use the `entityID` as a _well-known-location_ for retrieving the provider's metadata. In other words the `entityID` is the URL which returns the provider's XML metadata document when a HTTP GET is performed on that URL. _It is not a requirement the `entityID` be the URL for metadata download_ rather it is one common convention. As discussed in the SAML metadata specification other mechanisms can be established for metadata publication. IMPORTANT: SAML requires metadata publication to be integrity protected. A provider's metadata is literally the _keys to the provider's kingdom_ as it contains the cryptographic keys used during SAML authentication as well as other vital SAML properties. _It is essential to establish trust and validate the metadata._ Metadata can be signed but the easiest way to assure metadata integrity and the most common is to make sure metadata is only exchanged via a secure and trusted channel. TLS provides such a mechanism. Therefore if you publish metadata (and Mellon always does regardless of whether Mellon's metadata endpoint matches Mellon's `entityID`) it *_MUST_* occur _only_ over the `https` TLS scheme. Make sure your Apache configuration redirects any `http` for Mellon to `https` and that your https certificate is signed by a trusted CA such that others can properly validate your https cert. _Do not use self-signed certs for your https!_ If you do and you're on a public network, you're opening yourself up to a serious security vulnerability. Note, the certs used _inside_ the metadata can be self-signed, see <> for an explanation of why. The key concept here to take away is that _a provider's metadata provides the trust and is the validation mechanism used by SAML_. Thus the integrity of the metadata is of paramount importance. Mellon's metadata is _always_ published at the URL location `/$MellonEndpointPath/metadata`. See the description of <> for more details on the use of MellonEndpointPath. This is why most of the tools surrounding Mellon generate an `entityID` as the concatenation of the https scheme, the hostname, the MellonEndpointPath and "metadata". Thus for example if the `MellonEndpointPath` for `bigcorp.com` was set to `saml`, the `entityID` (the URL location for downloading its metadata) would be `https://bigcorp.com/saml/metadata`. The only reason why "metadata" appears in the `entityID` is because that is Mellon's URL endpoint for metadata publication. === Username, userid, SAML NameID [[name_id]] ==== Userid vs. Identity (or why userid is so last millennium) Many people struggle with the notion of _userid_ when working with SAML (or any other federated identity system). That's because historically _userid_ has been used to describe _identity_. The two are not the same. _Identity_ identifies who or what something is for the purpose of authentication and authorization as well as binding attributes to that identity. In most of the literature the terms _subject_ and _principal_ are used interchangeably to encapsulate the concept of who or what is being identified. Although a subject is often a person it need not be, it might also be an inanimate object. A good example of a non-human subject would be a computer service needing to be authenticated in order to perform an operation. Userids grew out of the early days of computing when all computing was local and users were given accounts on a local system. The userid was how operating systems tracked who a user was, in most cases it was an integer. Clearly the integer userid only had meaning in the context of the local system. As systems became networked integer userids would be shared between systems but fundamentally nothing had changed, the userid was still meaningful only among a group of cooperating computers. Tools such as Yellow Pages, NIS, LDAP and Active Directory were developed to provide a centralized repository of userids that could be shared between cooperating networked computers. Along the way the integer userid morphed into a string often partitioned into a local part and a domain part. The domain part is used to identify the realm. Realms are nothing other than collections of _unique_ userids often serving the needs of a organizational unit (e.g. company or institution). A key concept is that whoever is providing the userid, whether it be local accounts created by the host operating system or a network provider of userids such as NIS or LDAP, is an *identity provider* (IdP) with the _userid_ being the *key* used by _that_ specific *identity provider* to look up the *identity*. Hence _userids are only meaningful in the context of a specific IdP!_ By definition _federated identity_ is the amalgamation of diverse unrelated identity providers, each of which utilizes its own userid as a key to look up an identity. Therefore while deploying federated identity if you cling to the concept of a single userid you are likely to be frustrated because you are abusing the concept. ==== How SAML identifies a subject [[saml_nameid]] In SAML the user name (principal or subject) is conveyed as part of the `` element in the assertion. The subject identifier can be any one of these elements: * `` * `` * `` The most common is `` and it usually includes a `Format` attribute. If the `Format` attribute is absent then it defaults to the unspecified `Format`. The `Format` attribute tells you how to interpret the `NameID` value. For example if the subject's `NameID` format is `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress` you know the subject is being identified by their email address. The currently defined `NameID` formats are: Unspecified:: This is used when you don't care what the `NameID` `Format` is, you're willing to accept whatever it defaults to by the provider. (`urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified`) Email Address:: The `NameID` is an email address as specified in RFC 2822 as a `addr-spec` in the form `local-part@domain`. No common name or other text is included and it is not enclosed in `<` and `>`. (`urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`) X.509 Subject Name:: The `NameID` is an X.509 subject name in the form specified for the `` element in the XML Signature Recommendation. (`urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName`) Windows Domain Qualified Name:: The `NameID` is a Windows domain qualified name. A Windows domain qualified user name is a string of the form "DomainName\UserName". The domain name and "\" separator MAY be omitted. (`urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName`) Kerberos Principal Name:: The `NameID` is in the form of a Kerberos principal name using the format name[/instance]@REALM. (`urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos`) Persistent Identifier:: The `NameID` is a _persistent_ opaque identifier for a principal that is specific to an identity provider and a service provider or affiliation of service providers. Opaque means you cannot (easily) map the id to a user. In many cases the persistent id is implemented as a random number or random string. Persistent means you'll always get the exact same `NameID` for the same subject. Refer to <> and its `AllowCreate` attribute to understand if the IdP is allowed to create a persistent id for the subject if it has not already done so. (`urn:oasis:names:tc:SAML:2.0:nameid-format:persistent`) Transient Identifier:: The `NameID` is an opaque _temporary_ id. Opaque means you cannot (easily) map the id to a user. In many cases it's implemented as a random number or random string. Temporary means the id is valid _only_ in the context of the assertion response which contains it. Think of a transient id as a one-time id that cannot be used again or referred to again. (`urn:oasis:names:tc:SAML:2.0:nameid-format:transient`) IMPORTANT: The important concept here is that SAML's `NameID` as used to identify a subject is not the traditional userid you are probably used to. Furthermore SAML's `NameID` _may_ only be meaningful to the IdP which issued it. ==== Burden of interpreting NameID falls to the relying party [[nameid_interpretation]] Ultimately the SP needs to provide some sort of _userid_ the application it is hosting can utilize. _Only the application knows what it needs!_ Let's take the example of an application which wishes to identify its users by email address. There are two basic ways you can do this with SAML. 1. Specify a `NameIDPolicy` of `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress` when the SP submits a `` to the IdP. This tells the IdP you want the subject's `NameID` to be their email address. 2. Ignore the `NameID` returned in the assertion entirely, instead use the assertion's `email` attribute. Solution #2 is very important to understand as it illustrates how many organizations utilize SAML: they _build an identity from the attributes bound to a subject_. They can use one or more attributes to build a userid meaningful to the application. They may even require the IdP return an attribute unique to the subject across a federation, in this instance all IdPs in the federation must support that attribute (this is just one approach). The `NameID` is *not* utilized in solution #2 in large part because the `NameID` is likely to be uniquely bound to the given IdP. This is why SAML's _transient_ identifiers are often used: it simply does not matter what the `NameID` is because the SP is not utilizing it therefore it can be any random one-time value. It is also important to understand either the `NameID` _or_ the set of attributes _or both_ can be used to ultimately derive an identity to pass to the application. Another approach is to utilize SAML's _persistent id_ with the observation that the pair (IdP, persistent id) always uniquely and repeatably identifies the subject. The SP can maintain a table that maps the (IdP, persistent id) pair to an application-specific _identity_ using this technique. ==== How Mellon handles the NameID Mellon extracts the `` element from the assertion's `` element and sets this to `NAME_ID` attribute. If `` is absent in the assertion you can change what Mellon considers the user name to be to another value in one of the assertions attributes if you wish. `MellonUser` names the attribute you wish to use instead. If you want to export the username as `REMOTE_USER` so your web app can process this very common CGI variable see <> NOTE: Please be aware that blindly exporting the SAML `NameID` to the application may or may not be appropriate for the application. See the explanation of <> to understand the issues. ==== How do you specify the NameID format in SAML? [[specify_mellon_nameid]] In SAML there are 2 configuration options related to the use of `NameID`: +1.+ A provider declares which `NameID` formats it supports in its <> via the `` element. The following metadata excerpt illustrates a provider which supports the `transient`, `persistent` and `X509SubjectName` formats: [source,xml] ---- urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName ---- +2.+ The SP indicates to the IdP in its `` what `NameID` format it wants returned via the `` element. The `` should be one of the `NameIDFormat` elements enumerated in the IdP's metadata. The IdP is free to substitute another `NameID` format or to return an `InvalidNameIDPolicy` error status response if it can't satisfy the request. [[nameid_policy]] IMPORTANT: Mellon defaults to a `NameIDFormat` of `transient` when it <>. You will need to manually edit the `NameIDFormat` in your Mellon SP metadata if you wish to use a `NameIDFormat` other than `transient`. When Mellon generates its `` it selects the _first_ `NameIDFormat` found in its metadata as the `NameIDPolicy`. === Example [[authentication_request]] Here is an example `` as emitted by Mellon. [source,xml] ---- Version="2.0" IssueInstant="2017-06-28T13:39:14Z" Destination="https://rhsso.example.com:8443/auth/realms/test/protocol/saml" Consent="urn:oasis:names:tc:SAML:2.0:consent:current-implicit" ForceAuthn="false" IsPassive="false" AssertionConsumerServiceURL="https://mellon.example.com/mellon/postResponse" > https://mellon.example.com/mellon/metadata AllowCreate="true"/> ---- <1> `ID`: Unique ID generated by Mellon to identify the SAML request. It will appear in the SAML response in the `InResponseTo` attribute. Used to correlate SAML request and responses. <2> `IssueInstant`: Timestamp of when request was made <3> `Destination`: Where this request was sent to. Used as a protection to prevent malicious forwarding of requests to unintended recipients. The recipient verifies the URL where it received the SAML request matches the `Destination`. <4> `ForceAuthn`: If true the IdP *must* authenticate the principal instead of relying on an existing session for the principal. <5> `IsPassive`: If true neither the user agent (browser) nor the IdP may take control of the user interface. <6> `AssertionConsumerServiceURL`: Where to send the assertion response (see <> to see where this was defined. <7> `Issuer`: The SP which issued the AuthnRequest. See <> to see where this was defined. Also see the general description of <> <8> `NameIDPolicy`: The SP requests that the Subject returned in the assertion be identified by a transient name. See <> for more details. <9> `AllowCreate`: If true then the IdP is allowed to create a new identifier for the principal. The `` is sent using the _HTTP Redirect Binding_. The above `` appears on the wire as a URL with the SAML data embedded as query parameters. You can see the "on the wire" HTTP data for this `` in <>. This illustrates the need for SAML diagnostic tools because you cannot see the `` XML message and its assocated data (e.g. signature, <>) by looking at the HTTP protocol. === Example [[assertion_response]] This is an example of an `` response as generated by a Red Hat SSO server (Keycloak) in response to the above <>. [source,xml] ---- xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="https://mellon.example.com/mellon/postResponse" ID="ID_d06daaaf-64ec-44d3-95a7-08da893aa9d5" InResponseTo="_59126C3306E4679F653022F0C4DA7F04" IssueInstant="2017-06-28T13:39:27.331Z" Version="2.0"> https://rhsso.example.com:8443/auth/realms/test V/3iYohGv2Ot7pzy6q/BfAdXgSxmdCD7K+XEmFIZlUs= ... 1VPndjfABB6S4lb4zwMLjBUhxfzPFnfrvNYvRgcxiUM ... ... AQAB ID="ID_c463a141-d471-40c3-860a-6559ce0a3556" IssueInstant="2017-06-28T13:39:27.331Z" Version="2.0" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"> https://rhsso.example.com:8443/auth/realms/test w8bELRshtX7xHcwZCdglgfpyYBMJmVQJALPAclHHbLA= ... 1VPndjfABB6S4lb4zwMLjBUhxfzPFnfrvNYvRgcxiUM ... ... AQAB xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"> G-803528aa-2f9e-454b-a89c-55ee74e75d1e https://mellon.example.com/mellon/metadata urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified Name="groups" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">ipausers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">openstack-users jdoe@music.com John Doe JD Doe John uma_authorization manage-account view-profile ---- <1> `Response`: The `` element contains the entire SAML response, which includes information concerning who issued the response, when the response was issued, optionally a signature on the response, and the actual response contents which in this case is an ``. <2> `Destination`: The _AssertionConsumerService_ endpoint where this response will be sent. The receiver will verify it arrived at this location or it will reject it. <3> `ID`: Unique ID generated by Mellon to identify the SAML request. It will appear in the SAML response in the `InResponseTo` attribute. Used to correlate SAML request and responses. <4> `InResponseTo`: This identifies the SAML request being responded to. It matches the `ID` attribute in the <>. This is how SAML requests and responses are associated with one another as a pair. <5> `IssueInstant`: Timestamp of when response was made. <6> `Issuer`: The IdP which is issuing this response. It is the <> of the IdP as defined in the <>. <7> `Signature`: This response is signed by the IdP. The signature information is contained in this XML element. <8> `Reference`: Identifies the XML element being signed. In this instance since the signature reference points to the top level `` the entire response is signed. <9> `Status`: The status of the SAML response. Because in this instance the status is `urn:oasis:names:tc:SAML:2.0:status:Success`, the authentication was successful. If it had not been, an error status would have been returned. *The element is where to look for the success or failure of a SAML request.* <10> `Assertion`: This begins the assertion data. It represents the _content_ of the SAML response. <11> `Issuer`: The IdP which is issuing this response. It is the <> of the IdP as defined in the <> <12> `Signature`: The signature on the ``. Note this is independent of the signature on the response. <13> `Reference`: Identifies the XML element being signed. In this instance the signature reference points to the `` element, because that element has the matching `ID` attribute. <14> `Subject`: This begins the `` element which identifies the principal being authenticated in this ``. <15> `NameID`: *This is where Mellon obtains the username in the assertion.* Because the format is `transient` it is a random value assigned by the IdP. Note the `MELLON_NAME_ID` in the Apache environment exactly matches this. See <> for more details. <16> `AttributeStatement`: This begins the *set of attributes* supplied by the IdP. <17> `Attribute`: This is the attribute whose `name` is _groups_. It is a multi-valued attribute because it contains 2 `` elements. This attribute could be written in pseudo-code as `groups=["ipausers","openstack-users"]`. <18> `AttributeValue`: These are the values for the attribute. In this instance there are 2 values, _ipausers_ and _openstack-users_. The above `` response is conveyed back to the SP using the _HTTP Post Binding_. You may wish to review <>. The "on the wire" version of this `` response and its associated parameters can be seen in <>. Once again we see the need for SAML tools because it is impossible to view the XML message and its associated parameters give the contents of the HTTP response. === SAML Endpoints [[endpoints]] When two SAML providers communicate they must know the URL to send a given SAML message to. The set of URLs where a provider receives SAML messages is often referred to as its SAML endpoints. Although a SAML endpoint may appear in a SAML message, this is *not sufficient* to identify where a response should be sent. This is because a nefarious sender could insert a bogus location in the message. To assure SAML messages are only exchanged between the expected parties, the message endpoints are established outside of the message exchange via a trusted mechanism when a relationship is initially established between the two providers. Although not mandated, this is almost always accomplished via the exchange of SAML <> forming a trust relationship between the two providers. The presence of a SAML endpoint in a SAML message is typically there to validate the message against the previously established trust information. Also as you will learn below, an endpoint must be paired with a binding type, which is another reason why an endpoint appearing in a SAML message is not sufficient to establish a communication pathway. A SAML endpoint is defined by a (service,binding) pair. The service component identifies what action is hosted by this endpoint. Examples of common SAML services you are likely to encounter are: SingleSignOnService:: Authenticate a user and establish a session for them. This is an IdP service, it's where a SP sends its `` message. AssertionConsumerService:: This is where SPs receive `` messages from an IdP in response to a `` message. SingleLogoutService:: Terminate a user's session by logging them out. Both SPs and IdPs support this. The binding component of a (service,binding) pair identifies the format of the message. Recall that SAML offers many different ways to encode the XML of a SAML message. The binding component allows the receiver to know how to decode and parse the SAML message back into an XML SAML document received at its service endpoint. It's important to understand there is no requirement for a SAML provider to locate all its (service,binding) pairs on distinct URLs. It is possible to apply heuristics to a SAML message to identify the binding of the message arriving on a given URL. This allows a provider to collapse its set of endpoints into a smaller set of URLs The choice of how a provider maps its endpoints to URLs is entirely up to the provider. A common mistake is to assume that because one provider does it one way all providers follow the same model. The only way for you to know is to examine the providers metadata (see <>) === Relay State (How you return to the original URL) [[relaystate]] If you've ever wondered how after all the redirections, posts, etc. involved in Web-SSO one finally returns back to the original requested resource, you will find the answer in SAML's `RelayState` parameter. The `RelayState` is set by the SP when it first initiates authentication. SAML requires every party that handles a SAML message to preserve the `RelayState` and ultimately return it to the original requester. Officially SAML constrains the `RelayState` to a maximum of 80 bytes and recommends it be integrity protected and not expose sensitive information because it often appears in the URL of SAML messages. This could be achieved by pairing the URL with a random string, using the random string as the `RelayState`, and then obtaining the original URL by performing a look-up. However in practice most SAML clients set the `RelayState` to the resource URL. This is what Mellon currently does. When you are examining SAML messages, the `RelayState` will be the original URL and depending on the SAML binding it may be URL-encoded. _Just be aware that there is no requirement the `RelayState` be the original URL. It can be any string that the client can use to establish the context._ At some future date Mellon may alter its `RelayState` handling. === The Role of Metadata [[metadata]] When a SAML provider needs to interact with another SAML provider they must know various properties of the foreign provider (i.e. its configuration). SAML provider properties are encapsulated in an XML document called SAML metadata. Examples of the provider properties conveyed in metadata include: * <> (the unique name of the provider) * Organizational information * The roles this provider offers (e.g. SP, IdP, etc.) * <> for message exchange * X509 certificates the provider uses and for what purpose * Should messages be signed * Attributes and attribute profiles Typically at start-up a provider loads its own metadata (to configure itself) and then loads the metadata of all providers it interacts with. The SAML metadata specification can be found here: https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf It is vital that SAML metadata be trusted. The SAML specifications do not prescribe how metadata is exchanged in a trusted fashion. Many providers offer a URL where their metadata can be downloaded from (see <>). Metadata can be signed by the provider, which establishes authenticity. Some SAML practitioners do not approve of downloading metadata and instead insist upon the private exchange of metadata as a means to assure the metadata is valid, thus providing a higher level of trust. *Practical field experience has demonstrated that the vast majority of SAML problems are due to invalid metadata. Therefore the ability to diagnose SAML problems demands the ability to read and understand SAML metadata.* To that end let's explore the provider metadata used in our examples. ==== Certs and Keys Inside Metadata [[metadata_keys]] Cryptographic keys are used in SAML to sign pieces of data providing integrity protection and to encrypt data to provide confidentiality. In order for two SAML providers to successfully exchange SAML messages between themselves they must know the public keys of the other party. The provider's public keys are declared in its metadata and are always encapsulated inside a `` element that defines its intended use (signing or encryption). Furthermore one or more of the following representations within a `` element *MUST* be present: * `` * `` (child element of ``) _The (common) use of a `` element is merely a notational convenience to encapsulate a key. SAML never utilizes any PKI information inside an X509 certificate; the only data SAML utilizes from an X509 certificate is the key material._ This has several implications: * Certs are never PKI validated. * Certification extensions that define key usage, etc. are never checked. * The certificate validity period is never checked, thus a cert contained in metadata never expires as a consequence of its certificate validity period. Instead the validity period of the *key* is controlled by the `` or `` metadata attribute associated with the key. * Using self-signed certs used in SAML metadata is fine because the PKI data is never evaluated. In fact extracting the key from an `` element and placing it inside a `` element instead is semantically identical. A `` element is just a container for the key, nothing more than that. Certificates are used only because they are easy to generate and are readily available. IMPORTANT: The consequence of the above is that a provider's metadata *is* the trust mechanism in SAML. *_Any compromise of a provider's metadata is a compromise of SAML security._* IMPORTANT: Even though the keys and certs used inside SAML metadata for signing and encryption are not PKI validated, the key and cert used to establish a TLS secure channel between SAML entities *MUST* be fully PKI validated using a chain all the way up to a trusted CA. Do not confuse the purpose of the keys and certs used for the purpose of signing and encrypting SAML data with those used to establish secure communication: they are entirely distinct. ==== Service Provider Metadata [[sp_metadata]] This is an example of Mellon metadata. It is the SP metadata used in our example authentication. [source,xml] ---- xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://mellon.example.com/mellon/metadata"> AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> ... ... Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://mellon.example.com/mellon/logout" /> Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://mellon.example.com/mellon/logout" /> urn:oasis:names:tc:SAML:2.0:nameid-format:transient index="0" isDefault="true" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://mellon.example.com/mellon/postResponse" /> index="1" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://mellon.example.com/mellon/artifactResponse" /> index="2" Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" Location="https://mellon.example.com/mellon/paosResponse" /> ---- <1> `EntityDescriptor` is a container for all properties belonging to the entity identified by the `entityID` name. SAML metadata allows a single metadata document to describe multiple entities. In that case the top level element will be a `` element which will contain one or more `` elements. If the metadata describes only a single entity, it is permissible to just use a ``. <2> XML namespace declaration. This provides an abbreviated shorthand to identify which namespace an XML element belongs to. The shorthand name is the string preceding the equals sign. If the name is absent it becomes the default namespace for XML elements which are not prefixed with a namespace. Thus for example `xmlns:ds="..."` sets `ds` as the namespace prefix for XML digital signature elements and `` means the `KenInfo` element belongs to the XML digital signature namespace because it is prefixed with `ds:`. *There is no prescribed list of namespace prefixes, rather the document defines the prefix.* By convention certain prefix names are commonly used. _A common mistake is to assume that all SAML XML documents will use the same namespace prefixes_. This is not true and leads to misunderstandings and/or parsing errors. <3> [[sp_metadata_entityid]] entityID. This is the unique name of the SAML provider. It *must* be a URI. See <>. <4> A provider role. In this instance the role is `SPSSODescriptor`, which means it's a Service Provider. A provider may have multiple roles, therefore an entity may have more than one role element. In our example there is only one role. See <>. <5> `AuthnRequestsSigned`. If true then the SP will be sending a signed `` to the IdP. <6> `WantAssertionsSigned`. If true then the SP desires the IdP to sign its assertions. Assertions should always be signed. <7> X509 certificate information. The `use` attribute of `signing` identifies that this certificate will be used for signing data. There may be multiple keys of this type, permitting key rotation. <8> X509 certificate information. The `use` attribute of `encryption` identifies that this certificate will be used for encrypting data. There may be multiple keys of this type, permitting key rotation. <9> SAML endpoint. Logout messages using a SOAP binding are sent to this URL location. <10> SAML endpoint. Logout messages using the HTTP-Redirect binding are sent to this URL location. <11> Zero or more `` elements enumerate the name identifier formats supported by this entity. See <> for details. <12> [[sp_metadata_acs]] SAML endpoint. Assertions using the HTTP-POST binding are delivered to this URL location. <13> For indexed endpoints if `isDefault` is true then this is the default endpoint to select. If no endpoint claims to be the default then the first endpoint in the list is the default. <14> SAML endpoint. Assertions using the HTTP-Artifact binding are delivered to this URL location. <15> SAML endpoint. Assertions using the PAOS binding are delivered to this URL location. PAOS is used the the Enhanced Client or Proxy Profile (a.k.a. ECP). ==== Identity Provider Metadata [[idp_metadata]] This is an example of a IdP metadata as generated by a Red Hat SSO server (Keycloak). It is the IdP metadata used in our example authentication. [source,xml] ---- xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"> entityID="https://rhsso.example.com:8443/auth/realms/test"> WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> 1VPndjfABB6S4lb4zwMLjBUhxfzPFnfrvNYvRgcxiUM ... Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://rhsso.example.com:8443/auth/realms/test/protocol/saml" /> Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://rhsso.example.com:8443/auth/realms/test/protocol/saml" /> urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://rhsso.example.com:8443/auth/realms/test/protocol/saml" /> Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://rhsso.example.com:8443/auth/realms/test/protocol/saml" /> Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://rhsso.example.com:8443/auth/realms/test/protocol/saml" /> ---- <1> `EntitiesDescriptor` is a container for multiple `` elements. <2> XML namespace declaration. This provides an abbreviated shorthand to identify which namespace an XML element belongs to. The shorthand name is the string preceding the equals sign. If the name is absent it becomes the default namespace for XML elements which are not prefixed with a namespace. Thus for example `xmlns:ds="..."` sets `ds` as the namespace prefix for XML digital signature elements and `` means the `KeyInfo` element belongs to the XML digital signature namespace because it is prefixed with `ds:`. *There is no prescribed list of namespace prefixes, rather the document defines the prefix.* By convention certain prefix names are commonly used. _A common mistake is to assume all SAML XML documents will use the same namespace prefixes_. This is not true and leads to misunderstandings and/or parsing errors. <3> `EntityDescriptor` is a container for all properties belonging to the entity identified by the `entityID` name. <4> entityID. This is the unique name of the SAML provider. It *must* be a URI. See <>. <5> A provider role. In this instance the role is `IDPSSODescriptor`, which means it's an Identity Provider. A provider may have multiple roles, therefore an entity may have more than one role element. In our example there is only one role. See <>. <6> `WantAuthnRequestsSigned`. If true, indicates that this IdP requires every `` submitted by an SP to be signed. <7> X509 certificate information. The `use` attribute of `signing` identifies that this certificate will be used for signing data. There may be multiple keys of this type, permitting key rotation. <8> SAML endpoint. Logout messages using the HTTP-POST binding are sent to this URL location. <9> SAML endpoint. Logout messages using the HTTP-Redirect binding are sent to this URL location. <10> Zero or more `` elements enumerate the name identifier formats supported by this entity. See <> for more details. <11> SAML endpoint. `` messages using the HTTP-POST binding are sent by the SP to this URL location to establish a Single Sign-On Session. <12> SAML endpoint. `` messages using the HTTP-Redirect binding are sent by the SP to this URL location to establish a Single Sign-On Session. <13> SAML endpoint. `` messages using the SOAP binding are sent by the SP to this URL location to establish a Single Sign-On Session. == Installing & Configuring Mellon === Installing Mellon Mellon can be built and installed from source code located in the https://github.com/UNINETT/mod_auth_mellon[mod_auth_mellon GitHub repository]. However for most people the best option is to install Mellon using a pre-built package available from the package manager on your operating system. Pre-built packages relieve you of having to know the intricacies of building and installing from source, track and install security fixes, and track and apply bug fixes. Pre-built packages also are tailored to your operating system environment, often including OS specific configuration and support files deemed useful by the packaging authority. .On RHEL (CentOS) ---- sudo yum install mod_auth_mellon ---- .On Fedora ---- sudo dnf install mod_auth_mellon ---- .On Debian (Ubuntu) ---- apt-get install libapache2-mod-auth-mellon ---- .From source ---- ./configure make sudo make install ---- NOTE: If building from source you'll need to have all the necessary dependencies available at build time. Determining the exact set of dependencies and where to locate them is operating system dependent. It is assumed you have the necessary knowledge to correctly perform the requisite operations which is out of scope for this document. === Mellon Configuration [[mellon_config]] Once installed, mod_auth_mellon does not do anything until it's configured to operate on a URL. Mellon is configured in the same way as other Apache modules. See http://httpd.apache.org/docs/current/configuring.html[Apache Configuration Files]. There are two independent steps necessary to enable Mellon. 1. Load the mod_auth_mellon Apache module at Apache start-up. 2. Configure Mellon to operate on specific URLs with specific SAML properties. ==== Load mod_auth_mellon [[load_mod_auth_mellon]] To accomplish the first task of loading the mod_auth_mellon module Apache needs to execute this configuration directive: ---- LoadModule auth_mellon_module modules/mod_auth_mellon.so ---- Different distributions may handle Apache module loading differently, but as of Apache 2.4 the preferred technique is to drop a file in the `conf.modules.d` Apache directory with the above content. Apache automatically processes all `.conf` files in this directory at start-up. [NOTE] .Red Hat Specific ==== Red Hat RPM's add the file `/etc/httpd/conf.modules.d/10-auth_mellon.conf` with the above `LoadModule` directive, so there is no further action needed to load the module after the RPM is installed. ==== ==== Mellon Configuration Files [[mellon_config_files]] To accomplish the second task of configuring Mellon, Apache will need to read Mellon configuration directives when it initializes. The preferred mechanism is to place those directives in a file located in the Apache `conf.d` directory, Apache will read all `.conf` files in this directory at start-up. Although you could place the Mellon directives in any config file, a good practice to follow is keep the Mellon directives in their own file. See <> for more information. Mellon relies on SAML specific files as well, for example: * IdP metadata file(s) * Mellon's SP metadata file * Certificate and key files Although you are free to locate these SAML specific files in the `/etc/httpd/conf.d` Apache configuration directory, they are not strictly speaking Apache configuration files. Many deployments choose to locate the SAML files in a sibling directory, for example `/etc/httpd/saml2`. NOTE: If you are running with SELinux enabled (as you should be) you may run into SELinux file permission problems if you locate files Mellon reads and writes outside the standard Apache directories because externally located files will not automatically receive the proper SELinux labels. === Mellon Configuration Directives Mellon's configuration directives are documented in Mellon's `README` file. The README is the best place to learn and review Mellon configuration directives because it will match the installed version of Mellon. [NOTE] .Red Hat Specific ==== Red Hat RPM's install the README file in `/usr/share/doc/mod_auth_mellon*/README`. ==== Mellon configuration directives are broken into 2 types: * Module level (i.e. global values shared by each virtual server) * Directory level (i.e. applied to directories and URL locations) The README groups all module level directives together at the top of the file, the directory level directory level directives follow. Most users will only need to configure the directory level directives which comprise 2 basic types and are documented in http://httpd.apache.org/docs/current/mod/core.html[Apache Core Features]: Directive:: Enclose a group of directives that apply only to the named file-system directory, sub-directories, and their contents. Directive:: Applies the enclosed directives only to matching URLs. The critical thing to remember when writing and reading Mellon configuration is that like all Apache directory level configuration it is *_hierarchical_*. The path portion of the URL is like a file system directory tree. If a Mellon configuration directive is not _explicitly_ defined for a particular point in the tree, the value is *_inherited_* from the closest ancestor that defines it. If no ancestor defines the value then Mellon's default value is applied. The default value for each Mellon configuration directive is listed in Mellon's `README` file. === Mellon Configuration File [[mellon_config_file]] For our demo example we will place these directives in the file `/etc/httpd/conf.d/demo_mellon.conf`. Let's briefly review what the demo configuration is meant to accomplish and illustrate: * We are protecting with SAML authentication the URL location `https://mellon.example.com/private` and everything below it in URL space. * To eliminate redundant cut-n-paste of shared SAML directives in each protected location we gather the common Mellon directives in a location _above_ any of the protected locations in the URL tree. This permits all protected locations to hierarchically inherit the same values from a common single set of directives. ---- <1> MellonEnable info <2> MellonEndpointPath /mellon/ <3> MellonSPMetadataFile /etc/httpd/saml2/demo_sp_metadata.xml <4> MellonSPPrivateKeyFile /etc/httpd/saml2/demo.key <5> MellonSPCertFile /etc/httpd/saml2/demo.cert <6> MellonIdPMetadataFile /etc/httpd/saml2/demo_keycloak_test_idp_metadata.xml <7> <8> AuthType Mellon <9> MellonEnable auth <10> Require valid-user <11> ---- <1> The first `Location` directive on the `/` _root_ is simply a convenient place to locate common configuration directives that will be shared by all Mellon protected locations. In this instance it defines the metadata files and certificates and keys. It is not necessary to locate this on the `/` _root_ URL, in fact in a real world deployment you probably will want to locate the common shared set of Mellon directives lower in the hierarchy. The only requirement is that _all_ of the protected locations are positioned below it so they may inherit those values. footnote:[When you are protecting just one location with Mellon there isn't much added value in splitting the configuration directives. But when you need to protect many distinct locations, it's repetitive and error prone to cut and paste common values in each `` block. It makes maintaining the configuration that much more difficult because you will need to edit each and every `` directive to change something like the name of a metadata file. This is why Mellon configuration often groups common values in a location that can be shared by all descendants. Using the `/` _root_ location is the most obvious example but the same idea can be applied to complex trees.] <2> Mellon does not process any directives unless it's enabled for that location either explicitly or via inheritance. See <> for more details. <3> Defines where the Mellon endpoints are located in URL space. This is a *critical* value to properly specify and is one of the *_most common Mellon configuration errors_* leading to a failed deployment. Please refer to <> to understand its requirements and what it influences. Also see <> for a discussion of this common error. The important thing to note in this example is the `MellonEndpointPath` is located *inside* the containing location directive of `/` (e.g. a child). <4> The SAML metadata for this provider (i.e. Mellon's metadata). This metadata plays 2 important roles: Mellon reads it at start-up to initialize itself, and you provide the IdP specified in `MellonIdPMetadataFile` with this metadata. Both Mellon (the SP) and your IdP *MUST* have loaded exactly the same Mellon metadata in other to interoperate. Out of sync metadata is a very common deployment error. See <> for how Mellon metadata is created. `MellonSPMetadataFile` is optional, Mellon can create its own metadata from its initial configuration parameters. <5> The private cryptographic key used by Mellon to sign its SAML data. See <> for more detail. <6> The public cryptographic key associated with the private key. This public key is embedded in Mellon's metadata so that an IdP can validate Mellon's signed data. See <> for more detail. <7> The IdP used to authenticate is specified by its metadata file. See <> for how to obtain this data. <8> This is a URL location protected by Mellon. For our example we've used the `/private` URL. Note that this `` block is simple and does not contain many of the necessary Mellon directives, because those other Mellon directives are inherited from an ancestor location, in our example `/`. The only Mellon directives in this location block are those necessary to turn on Mellon authentication. This configuration strategy permits you to define many subordinate protected locations all sharing the same common Mellon directives via inheritance. <9> `AuthType` is an Apache directive specifying which Apache authentication module will perform the authentication for this location. Obviously we want to use Mellon. <10> Instruct Mellon that this location (and all its descendants) will be authenticated. See <>. <11> `Require` is an Apache directive that instructs Apache's authentication and authorization sub-system that it must successfully authenticate the user. ==== Load Your SP metadata into the IdP [[load_sp_metadata_into_idp]] After you have created your SP metadata as described in <>, you must load your metadata into the IdP referenced in your `MellonIdPMetadataFile`. How to perform the SP metadata load is specific to the IdP you're using and you will need to consult your IdP documentation to learn the procedure. WARNING: If you subsequently modify your SP metadata you *MUST* reload it into the IdP. Both your metadata and the IdP metadata must be in sync at all times. Failure to reload any modified metadata is a recipe for problems. ==== Obtaining IdP Metadata [[obtain_idp_metadata]] In order to Mellon to communicate with and interoperate with an IdP it must have the IdP's metadata. You may want to refer to <> for a more comprehensive description. But how do you obtain the metadata belonging to the IdP? There is no fixed rule on how this is accomplished. You will have to refer to your IdP's documentation. It may be published at a well known location (e.g. a URL) for download or there may be some other publication mechanism. IMPORTANT: SAML provider metadata is extremely security sensitive, it contains the cryptographic keys used to secure SAML. If you download metadata from a URL do so only over a secure channel such as https and make sure the download operation properly validates the server cert up to a CA you trust. _Do not trust a server offering a self-signed cert_. If the obtained metadata is signed you *MUST* validate the signature on the metadata. === Mellon Modes [[mellon_modes]] For any given location Mellon can be in one of 3 modes defined by the `MellonEnable` directive: off:: Mellon will not do anything in this location. This is the default state. info:: If the user is authorized to access the resource, then Mellon will populate the environment with information about the user. If the user isn't authorized, then Mellon won't populate the environment, but Mellon won't deny the user access either. auth:: Mellon 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 Mellon will return a 403 Forbidden error. If he isn't authenticated then Mellon will redirect to the login page of the configured IdP. The most common situation is to protect a specific location with Mellon authentication. This requires at a minimum these 3 directives: ---- AuthType Mellon <1> MellonEnable auth <2> Require valid-user <3> ---- <1> This is an Apache directive that says authentication is to be performed with Mellon as opposed to another Apache authentication module. <2> This informs Mellon it is to perform authentication as described above. <3> This is an Apache directive that says an authentication module must have successfully authenticated a user in order to proceed. === How is Mellon metadata created? [[metadata_creation]] The purpose of SAML metadata is described in <>. An annotated example of Mellon metadata is presented in <>. There are multiple ways one can create Mellon metadata: . Use the `mellon_create_metadata.sh` script. The mod_auth_mellon RPM installs this script in `/usr/libexec/mod_auth_mellon/mellon_create_metadata.sh`. . Allow Mellon to dynamically generate its metadata based on its configuration options. The metadata can be downloaded from the `$MellonEndpointPath/metadata` URL. Mellon only self-generates its metadata if the `MellonSPMetadataFile` configuration parameter is not defined, otherwise if the `MellonSPMetadataFile` is defined the `$MellonEndpointPath/metadata` download URL will return the contents of the `MellonSPMetadataFile`. . Use a third-party tool such as `keycloak-http-client-install`. . Write it from scratch. (Not kidding, many provider administrators hand create and hand edit their metadata). IMPORTANT: Before proceeding further with Mellon metadata it is essential you understand the <>. ==== Using `mellon_create_metadata.sh` [[using_mellon_create_metadata_sh]] `mellon_create_metadata.sh` requires two positional parameters * <> * endpoint_url The entityID is the unique name of the Mellon SP. The entityID plays an important role in SAML and you may wish to review its description in <>. The endpoint_url is the concatenation of the `https` scheme, the Mellon hostname, and the <>. Using our example data the entityID will be `https://mellon.example.com/mellon/metadata` and the endpoint_url will be `https://mellon.example.com/mellon` ---- $ /usr/libexec/mod_auth_mellon/mellon_create_metadata.sh "https://mellon.example.com/mellon/metadata" "https://mellon.example.com/mellon" Output files: Private key: https_mellon.example.com_mellon_metadata.key Certificate: https_mellon.example.com_mellon_metadata.cert Metadata: https_mellon.example.com_mellon_metadata.xml Host: mellon.example.com Endpoints: SingleLogoutService (SOAP): https://mellon.example.com/mellon/logout SingleLogoutService (HTTP-Redirect): https://mellon.example.com/mellon/logout AssertionConsumerService (HTTP-POST): https://mellon.example.com/mellon/postResponse AssertionConsumerService (HTTP-Artifact): https://mellon.example.com/mellon/artifactResponse AssertionConsumerService (PAOS): https://mellon.example.com/mellon/paosResponse ---- The script produces 3 files containing the cert, key, and metadata, all prefixed with the entityID. In this example it would be: * https_mellon.example.com_mellon_metadata.cert * https_mellon.example.com_mellon_metadata.key * https_mellon.example.com_mellon_metadata.xml You will need to move these files into the Apache configuration directory and possibly rename them to something more sensible. You will refer to these files inside the Mellon configuration as these Mellon directives: * `MellonSPPrivateKeyFile` * `MellonSPCertFile` * `MellonSPMetadataFile` ==== Using Mellon to generate its own metadata [[using_mellon_to_create_metadata]] Mellon has the built-in capability to generate its own metadata as long as you provide a few necessary Mellon configuration directives. * `MellonSPentityId` * `MellonSPPrivateKeyFile` * `MellonSPCertFile` * `MellonEndpointPath` (not mandatory if you use the default) When Mellon initializes it will check the value of the `MellonSPMetadataFile`. If it does not exist Mellon will generate its own metadata. If `MellonSPMetadataFile` exists, that metadata will always be used. If Mellon generates its own metadata it does not write the metadata back to a file, rather it's held in memory. Irrespective of whether Mellon self generates its metadata or if it loads it from a file specified by `MellonSPMetadataFile`, the metadata is made available for download at the `$MellonEndpointPath/metadata` URL. You can perform a GET on this URL to capture the SP metadata and save it in a file. It is recommended you do this as an initial configuration set-up step and then always subsequently load the metadata via the `MellonSPMetadataFile` directive. The rationale for this is you want to be sure you know what metadata Mellon is initializing with and that it identically matches what you've loaded into the IdP. You may also wish to customize your SP metadata by making edits to it. ==== Where do the keys and certs come from? Please refer to the <> section to understand how keys and certs are utilized inside SAML (TLS connections used for SAML communication is an entirely different matter and it is mandated keys and certs used for TLS be PKI validated). The main point to understand is that even though most SAML implementations use x509 utilities to generate certs and keys, SAML's use of them does not involve PKI. Only the key material is used. The consequence of this is it's okay to generate self-signed certs for use inside a provider's metadata because they are not PKI validated. Many of the metadata creation tools generate a self-signed cert for use in the metadata. However it is perfectly fine to use your own key and cert instead of one generated by an installation tool. You can accomplish this with Mellon by pointing the `MellonSPPrivateKeyFile` and `MellonSPCertFile` directives at your own key and cert files and then downloading the SP metadata as described in <>. ==== Signing metadata [[sign_metadata]] SAML requires provider metadata to be integrity protected. Publishing provider metadata over a secure TLS channel goes a long way to accomplishing this goal and may be considered sufficient depending on the security requirements. SAML metadata can be integrity protected by signing the metadata with an XML signature. Some providers may require any metadata they consume be signed. Unfortunately neither Mellon nor any of the tools currently associated with Mellon have support for signing Mellon metadata. Fortunately there are a variety of tools available to sign an XML document and since SAML metadata is a normal XML document any of these tools can be used to sign the Mellon metadata. ===== Using xmlsec to sign metadata [[xmlsec_metadata_signing]] The `xmlsec` tools are commonly available on most Linux based system. In fact the `Lasso` library which supplies Mellon with its SAML implementation uses the `xmlsec` library to perform all of its XML signing and signature verification. `xmlsec` usually ships with an `xmlsec` command line utility, which can perform XML signing and verification from the command line. NOTE: `xmlsec` may be packaged under the name `xmlsec1` in your distribution. This is the case for all Red Hat distributions. To sign Mellon metadata using `xmlsec` you need to add a signature template to the Mellon metadata. When `xmlsec` reads the input metadata it locates the empty signature template and replaces it with a processed signature. The signature template should be placed near the top of the metadata, ideally just after the `` element. Here is an example of a signature template: [source,xml] ---- ---- Because the ` URI` attribute is the empty string the entire document will be signed. In order for `xmlsec` to generate a signature you will need to supply it with both the private and public key parts. ---- xmlsec \\ <1> --sign \\ <2> --privkey-pem demo.key \\ <3> --pubkey-cert-pem demo.cert \\ <4> --output signed_metadata.xml \\ <5> metadata.xml <6> ---- <1> `xmlsec` command may be named `xmlsec1` on your system <2> Perform signing <3> Private key used for signing <4> Public key used to verify signature (included in signature) <5> Output file containing signed metadata <6> Input unsigned metadata (with signature template) To verify the signature on the command line: ---- xmlsec \\ <1> --verify \\ <2> signed_metadata.xml <3> ---- <1> `xmlsec` command may be named `xmlsec1` on your system <2> Perform verification <3> Input signed metadata === MellonEndpointPath [[mellon_endpoint_path]] Mellon reserves a number of URLs for its use. Some of these URLs are the public SAML <> advertised in the <>. Others are for Mellon's private use. The best way to think of these Mellon endpoints is as a way of binding a URL to a handler. When an HTTP request arrives at one of these Mellon endpoints a dedicated handler processes the request. The way Mellon identifies a URL as being one of its endpoints is by looking at the beginning of the URL path. If everything in the path except the last path component matches the `MellonEndpointPath` then Mellon recognizes the URL as being one of its endpoints. The last path component is used to bind to the handler. Let's use an example. If the `MellonEndpointPath` is `/foo/bar` then any URL with the form `/foo/bar/xxx` will be handled by Mellon's xxx handler. Mellon enforces 2 strict requirements on the `MellonEndpointPath`: * The path *must* be an absolute path from the root of the web server. * The path *must* be a sub-path of the Mellon `` directive that defines it. The reason for this is simple. Mellon ignores locations which are not configured for Mellon. Therefore for Mellon to respond to a request on one of its SAML endpoints, the endpoint has to be inside a path that Mellon is watching. === Mellon Endpoints [[mellon_endpoints]] Mellon endpoints are hung off of the <>. Mellon reserves a number of URLs for its use. Some of these URLs are the public SAML <> advertised in the <>. Others are for Mellon's private use. The best way to think of these Mellon endpoints is a way of binding a URL to a handler. When an HTTP request arrives at one of these Mellon endpoints a dedicated handler processes the request. The current list of Mellon endpoints (handlers) is: postResponse:: The _AssertionConsumerService_ endpoint using the SAML HTTP-POST binding. artifactResponse:: The _AssertionConsumerService_ endpoint for SAML artifacts. SAML artifacts provide an indirect method to convey data. An artifact is an identifier that points to data. Requesting data using the artifact identifier returns the associated data. paosResponse:: The AssertionConsumerService endpoint using the SAML PAOS binding. login:: Mellon internal endpoint used to start the authentication process with an IdP. Any request whose URL needs authentication is redirected here to start the login process. logout:: The SingleLogoutService SAML endpoint. metadata:: A HTTP GET request on this endpoint will return the SP's metadata. repost:: Mellon internal endpoint which replays POST data from the original request. auth:: Mellon internal endpoint retained for backwards compatibility. probeDisco:: IdP probe discovery service endpoint. See "Probe IdP discover" in the Mellon README for more information. === Mellon Session [[mellon_session]] SAML sessions are described in <>. For each successfully authenticated user Mellon maintains a session. Mellon allocates a unique ID for the session when it is created. The Mellon session ID is sent to the user's browser in a <>. The <> is sent back to Mellon in every request the browser makes to the SP. Mellon uses the session ID to look-up the session data for the user. Internally Mellon calls session data _cache data_ (this is subject to change). At the time of this writing Mellon session data is local to one Apache server (which may have multiple worker processes sharing data in shared memory). This has consequences for High Availability (e.g. HA) deployments which may be running multiple Apache servers on different nodes behind a load balancer, see <> for detailed information on this issue. Mellon limits the duration of a valid session by the length of time defined in the `MellonSessionLength` directive. Currently the default is 86400 seconds which is 24 hours. The IdP can inform the SP how long it wishes a SP session to be valid by passing the `SessionNotOnOrAfter` attribute in a ``. Mellon respects the `SessionNotOnOrAfter` attribute and will limit its session duration based on it. Thus the validity period for a Mellon session is the lesser of the `MellonSessionLength` or the optional IdP `SessionNotOnOrAfter` attribute if the IdP supplied it. === Mellon Cookie [[mellon_cookie]] <> information is communicated via a cookie. The cookie name defaults to `mellon-cookie` but may be changed via the Mellon directive `MellonVariable`. Mellon always forms the cookie name by appending the value of `MellonVariable` to the string `mellon-` to prevent name collisions. Thus the actual default value of `MellonVariable` is `cookie`. When Mellon first begins the authentication process it sets the mellon cookie value to `cookietest`. The primary purpose of the `cookietest` value is to confirm cookies are properly returned by the browser, Mellon will not work correctly unless cookies are enabled. The `cookietest` value also serves as a temporary value indicating an authentication flow is in progress but has not yet completed. After Mellon successfully authenticates a user it establishes a session for the user and generates a unique session ID which it sets as the value of the Mellon cookie. When Mellon receives a request for a protected resource it looks for the Mellon cookie in the HTTP request headers. Mellon then uses the Mellon cookie value as a session ID and attempts to look-up that session using that ID. If the session is found and it remains valid, Mellon immediately grants access. A Mellon session will expire, see <> for information concerning session lifetime. == Working with SAML attributes and exporting values to web apps When you receive a SAML assertion authenticating a subject, the assertion will likely include additional attributes provided by the IdP concerning the subject. Examples include the user's email address or the groups they are a member of. You may wish to review the <> and look for `` and `` elements to see how the IdP communicates these attributes. There is no fixed set of attributes returned by an IdP, it is entirely IdP dependent. You will either have to review your IdP's documentation or examine a returned assertion to determine the possible attributes. See <> for the various ways you can examine the contents of a returned assertion. Mellon communicates its results via Apache environment variables. For every attribute received in the assertion Mellon will insert an Apache environment variable. You have some flexibility on how Mellon adds these environment variables which derive from the assertion attributes. * Attributes can be multi-valued. `MellonMergeEnvVars` controls whether each value is added to the environment by appending an index to the attribute name or whether the values are listed together under the bare attribute name with each value separated by a separator character. See <>. * Attribute names can be mapped from the name as it appears in the assertion to a name of your choosing when it is placed in the Apache environment. This is controlled by `MellonSetEnv` and `MellonSetEnvNoPrefix` directives. The distinction is `MellonSetEnv` always prepends a prefix to the environment variable name to help to prevent name collisions. The prefix defaults to `MELLON_` and can be configured using the `MellonEnvPrefix` configuration option. The `MellonSetEnvNoPrefix` directive also remaps the assertion name to a name of your choosing but it omits prepending the environment variable name with the prefix. See <> Using the <> Mellon places these environment variables in the Apache environment. See <> for an explanation of `MellonMergeEnvVars` and its effect. .MellonMergeEnvVars Off ---- MELLON_NAME_ID: G-803528aa-2f9e-454b-a89c-55ee74e75d1e MELLON_NAME_ID_0: G-803528aa-2f9e-454b-a89c-55ee74e75d1e MELLON_groups: ipausers MELLON_groups_0: ipausers MELLON_groups_1: openstack-users MELLON_email: jdoe@music.com MELLON_email_0: jdoe@music.com MELLON_display_name: John Doe MELLON_display_name_0: John Doe MELLON_initials: JD MELLON_initials_0: JD MELLON_last_name: Doe MELLON_last_name_0: Doe MELLON_first_name: John MELLON_first_name_0: John MELLON_Role: uma_authorization MELLON_Role_0: uma_authorization MELLON_Role_1: manage-account MELLON_Role_2: view-profile MELLON_IDP: https://rhsso.example.com:8443/auth/realms/test MELLON_IDP_0: https://rhsso.example.com:8443/auth/realms/test ---- === Handling multiple attribute values [[multiple_attribute_values]] If an attribute has multiple values, then they will be stored as ---- MELLON__0 value0 MELLON__1 value1 MELLON__2 value2 ... ---- Since Mellon doesn't know which attributes may have multiple values, it will store each attribute at least twice. For example: ---- MELLON_ value0 MELLON__0 value0 ---- In the case of multivalued attributes `MELLON_` will contain the first value. If `MellonMergeEnvVars` is enabled multiple values of attributes will be stored in a single environment variable separated by the `MellonMergeEnvVars` separator which defaults to the semicolon. You can override the default separator by supplying it as the second option to the `MellonMergeEnvVars` directive. Thus the above environment variable list would be this if `MellonMergeEnvVars` was on and the separator was set to the semicolon. .MellonMergeEnvVars On ; ---- MELLON_NAME_ID: G-803528aa-2f9e-454b-a89c-55ee74e75d1e MELLON_groups: ipausers;openstack-users MELLON_email: jdoe@music.com MELLON_display_name: John Doe MELLON_initials: JD MELLON_last_name: Doe MELLON_first_name: John MELLON_Role: uma_authorization;manage-account;view-profile MELLON_IDP: https://rhsso.example.com:8443/auth/realms/test ---- === Map assertion attribute name to different Apache environment variable name [[map_assertion_attr_name]] Sometimes the web app is expecting a specific name for a SAML attribute but your IdP has sent that attribute under a different name. You can rename any assertion attribute using the `MellonSetEnv` and `MellonSetEnvNoPrefix` directives. These allow you to rename an assertion attribute to a name of your choosing. The `MellonSetEnv` directive follows the same convention as all other assertion attributes added by Mellon in that it always prefixes the environment variable name with a configurable prefix, which defaults to `MELLON_` to help avoid name collisions in the Apache environment. However sometimes you do not want the `MELLON_` prefix added. In case you simply want the variables prefixed with a different string, use the `MellonEnvPrefix` configuration option. If, instead you want to use exactly the environment variable name as specified., `MellonSetEnvNoPrefix` serves this role. To illustrate let's look at an example. Suppose your web app is expecting an attribute which is the user's last name, specifically it wants this attribute to be called `REMOTE_USER_LASTNAME`. However your IdP sends this attribute as `sn`. `sn` is typically used in LDAP directories as an attribute name for surname, or equivalently the user's last name. To map the `sn` assertion attribute name to the Apache environment variable name of `REMOTE_USER_LASTNAME` you would do this: ---- MellonSetEnvNoPrefix REMOTE_USER_LASTNAME sn ---- Also see <> for an example of setting the `REMOTE_USER` environment variable using `MellonSetEnvNoPrefix`. The `MellonEnvPrefix` variable might be useful e.g. if you are migrating from a different SP which used its own prefix for the variables passed by the IdP. For example, to prefix all variables with `NOLLEM_` you would use: ---- MellonEnvPrefix NOLLEM_ ---- If you recieved an attribute-map.xml from your IDP that uses the `urn:mace:shibboleth:2.0:attribute-map` namespace, it can be converted to `MellonSetEnvNoPrefix` entries with `docs/mellon-attribute-map.xsl` and loaded into your webserver configuration. === Using Mellon to apply constraints [[assertion_constraints]] SAML attributes can be used for more than exporting those values to a web app. You can also utilize SAML attributes to control whether Mellon authentication succeeds (a form of authorization). So even though the IdP may have successfully authenticated the user you can apply additional constraints via the `MellonCond` directive. The basic idea is that each `MellonCond` directive specifies one condition that either evaluates to `True` or `False`. Multiple conditions can be joined by logical operators. You can also specify case insensitive matching, substring matching, regular expression matching, substitute values, and use regular expression back references. All `MellonCond` conditions must evaluate to `True` for the condition check to succeed (logical conjunction) unless you use the `OR` option flag. The directive is specified as: ---- MellonCond attr_name value [options] ---- The 1st `attr_name` parameter is the name of the SAML assertion attribute the condition applies to. If an attribute with this name is found, its value is retrieved and becomes the data the condition is evaluated against. If `attr_name` is not found, the condition evaluates to `False`. The 2nd value parameter is the value applied to the attribute value. For example when matching is performed the `value` parameter is searched for inside the attribute's value. The `value` parameter may contain format specifiers which are substituted prior to performing an operation on the value. Format specifiers always begin with the `%` character. Here are the valid format specifiers: %n:: Regular expression backreference. Regular expressions may contain multiple sub-matches (often referred to as a regular expression group). To refer to a specific sub-match in the regular expression pattern use a digit between 0 and 9 as `%n`. This only works if a prior condition had specified the [`REG,REF]` flags, otherwise there would be no backreference to refer to. %\{num}:: Same as `%n`, but permits a number greater than 9. %\{ENV:x}:: Substitute the Apache environment variable `x`. If the environment variable does not exist substitute the empty string instead. %%:: Quote a `%` to prevent it from being interpreted as a the beginning of a format specifier. The 3rd `[options]` parameter is optional and if specified is a comma separated list of option flags enclosed in square brackets. The set of option flags includes: 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:: Invert the result of the condition check. If the condition evaluated to `True` it becomes `False`, likewise if the condition evaluated to `False` it become `True`. NC:: Case insensitive matching. Ignore case differences when performing any match operation. SUB:: Substring match. If value is included anywhere in the attribute value as a substring the condition evaluates to `True`, otherwise `False`. If `SUB` is not specified then the condition value and attribute value must match in its entirety. REG:: Regular expression match. The value is interpreted as a regular expression. If the regular expression is found in the attribute value the condition evaluates to `True`, `False` otherwise. REF:: Used with REG, track regular expression back references, So that they can be substituted in an upcoming `MellonCond` directive. MAP:: Use mapped name. Instead of looking up the attribute name in the set of attributes returned in the assertion use the mapped name specified by either `MellonSetEnv` or `MellonSetEnvNoPrefix` instead. If the mapped name is not found then fallback to using the name in the assertion's set of attributes. Here is a simple example illustrating how one might utilize `MellonCond`. Suppose we only want to allow members of the group `openstack-users` to have access. Our IdP has provided us with the list of groups the user is a member of in the `groups` SAML attribute. We need to instruct Mellon to only accept an assertion if `openstack-users` appears as one of the `groups` attribute values. This can be accomplished like this: ---- MellonCond groups openstack-users ---- If `openstack-users` does not appear in the as one of the `groups` attribute values the check will fail. The check will also fail if the `groups` attribute is not defined in the assertion. === How to set REMOTE_USER [[set_remote_user]] Mellon stores the authenticated user's name in the attribute `NAME_ID` (see <>). If you want to export the username as `REMOTE_USER` so your web app can process this very common CGI variable this can easily be accomplished with `MellonSetEnvNoPrefix` like this: ---- MellonSetEnvNoPrefix REMOTE_USER NAME_ID ---- == Deployment Considerations [[deployment_considerations]] === Apache Servername [[apache_servername]] When Mellon is running behind a load balancer, SSL terminator, or in a Apache virtual host there is the opportunity for Mellon to identify itself incorrectly. If Mellon does not identify itself identically to what appears in the matching metadata, various SAML security checks will fail as well as the ability to communicate on the defined <>. At run time Mellon asks Apache what scheme, host and port it's running under. Mellon uses this information to build URLs. When Mellon is running in a simple configuration directly connected to the internet, Apache typically gets this information correctly from the environment. However when Apache is behind some type of proxy such as a load balancer, then there is a distinction between what clients see as the front end and what Mellon sees when it's running as a backend server. The trick is to make Mellon believe it's running as the front end so that it matches the client's view. You may wish to refer to <> for more information. Load balancers partition their view between front end and back end. front end:: What the client connects to. It's the public scheme, hostname, and port. back end:: The back end server is where Mellon runs. It will definitely have a different hostname than the front end and will likely also have a different scheme and port as well. When a HTTP request arrives at the front end most load balancers will terminate the SSL connection. This changes the scheme from `https` to `http`. The load balancer will select a backend server to forward the request to. The backend server will have a different hostname and possibly a different port. Mellon needs to see the HTTP request as it appeared on the front end instead of how the request appears to the backend server where Mellon is running. The host and port appear in several contexts: * The host and port in the URL the client used. * The host HTTP header inserted into the HTTP request (derived from the client URL host). * The hostname of the front facing proxy the client connects to (actually the FQDN of the IP address the proxy is listening on). * The host and port of the backend server which actually handled the client request. * The **virtual** host and port of the server that actually handled the client request. It is vital to understand how each of these is utilized, otherwise there is the opportunity for the wrong host and port to be used with the consequence the authentication protocols may fail because they cannot validate who the parties in the transaction are and whether the data is carried in a secure transport. Let's begin with the backend server handling the request, because this is where the host and port are evaluated and most of the problems occur. The backend server needs to know: * The URL of the request (including host & port) * Its own host & port Apache supports virtual name hosting. This allows a single server to host multiple domains. For example a server running on example.com might service requests for both bigcorp.com and littleguy.com. The latter 2 names are virtual host names. Virtual hosts in Apache are configured inside a server configuration block, for example: ---- ServerName bigcorp.com ---- When Apache receives a request it deduces the host from the `HOST` HTTP header. It then tries to match the host to the `ServerName` in its collection of virtual hosts. The Apache `ServerName` directive sets the request scheme, hostname and port that the server uses to identify itself. The behavior of the `ServerName` directive is modified by the Apache `UseCanonicalName` directive. When `UseCanonicalName` is enabled Apache will use the hostname and port specified in the `ServerName` directive to construct the canonical name for the server. This name is used in all self-referential URLs, and for the values of SERVER_NAME and SERVER_PORT in CGIs. If `UseCanonicalName` is `Off`, Apache will form self-referential URLs using the hostname and port supplied by the client, if any are supplied. If no port is specified in the `ServerName`, then the server will use the port from the incoming request. For optimal reliability and predictability, you should specify an explicit hostname and port using the `ServerName` directive. If no `ServerName` is specified, the server attempts to deduce the host by first asking the operating system for the system hostname, and if that fails, performs a reverse lookup on an IP address present on the system. Obviously this will produce the wrong host information when the server is behind a proxy because the backend server is not what is seen on the frontend by clients; therefore use of the `ServerName` directive is essential. NOTE: [[standard_port_issue]] Browsers will strip standard port 80 for HTTP and port 443 for HTTPS from the network location in a URL. For example if you specify a URL like this `https://example.com:443/some/path` the URL which will placed on the wire will be `https://example.com/some/path` without the standard port. Since Mellon and most SAML providers validate URLs by simple string comparison, including a standard port in a URL will cause URL matching to fail because one URL will have the port in it and the other URL won't. The Apache https://httpd.apache.org/docs/current/mod/core.html#servername>[ServerName] doc is very clear concerning the need to fully specify the scheme, host, and port in the `Server` name directive when the server is behind a proxy. It states: ____ Sometimes, the server runs behind a device that processes SSL, such as a reverse proxy, load balancer or SSL offload appliance. When this is the case, specify the https:// scheme and the port number to which the clients connect in the ServerName directive to make sure that the server generates the correct self-referential URLs. ____ === Load Balancer Issues [[load_balancer]] High Availability (HA) deployments often run their services behind a load balancer. By far the most popular load balancer is http://www.haproxy.com/[HAProxy]. As a consequence we will use HAProxy examples in this document. Other load balancers behave in a similar fashion to HAProxy and you can extrapolate the HAProxy information to them. ==== Server Name Because backend servers do not self-identify with the same front end public address, it is vital you force those Apache servers to identify with the public address. This issue is described in <>. The reason for this is because the SAML protocols require URLs to match what is in a SAML provider's metadata. If you allow a backend server to self-identify, the URLs exchanged in the protocols will not match and you will encounter an error; see <>. ==== Load balancer proxy persistence [[load_balancer_persistence]] In an HA deployment, multiple backend servers run on distinct nodes and cooperate to mitigate the load that might be placed on a single (front end) server. Because the backend servers are independent, they do not share state with any other backend server unless something has explicitly been done to share state. HTTP is technically a stateless protocol, which makes web traffic ideally suited for a HA deployment: each backend server can be ignorant of any other HTTP request. However in practice HTTP is stateful by virtue of cookies. Authentication protocols are good examples of HTTP transactions that require saved state, in particular <>. NOTE: At the time of this writing Mellon has no support for sharing session data between independent Apache servers. The consequence of this is Mellon will not work correctly unless the same Apache server consistently handles a users HTTP traffic. HAProxy has two different mechanisms to bind HTTP traffic to one server, _affinity_ and _persistence_. This article provides an excellent overview of the distinction between the two and how to implement it: http://www.haproxy.com/blog/load-balancing-affinity-persistence-sticky-sessions-what-you-need-to-know/["load balancing, affinity, persistence, sticky sessions: what you need to know"]. What is the difference between Persistence and Affinity? Affinity is when information from a layer below the application layer is used to pin a client request to a single server. Persistence is when application layer information binds a client to a single server sticky session. The main advantage of persistence over affinity is it is much more accurate. Persistence is implemented though the use of cookies. The HAProxy `cookie` directive names the cookie which will be used for persistence, along with parameters controlling its use. The HAProxy `server` directive has a `cookie` option that sets the value of the cookie: it should be set to the name of the server. If an incoming request does not have a cookie identifying the backend server, then HAProxy selects a server based on its configured balancing algorithm. HAProxy assures that the cookie is set to the name of the selected server in the response. If the incoming request has a cookie identifying a backend server, then HAProxy automatically selects that server to handle the request. To enable persistence in the backend server block of the `/etc/haproxy/haproxy.cfg` configuration this line must be added: ---- cookie SERVERID insert indirect nocache ---- This says `SERVERID` will be the name of our HAProxy persistence cookie. Then we must edit each `server` line and add `cookie ` as an additional option. For example: ---- server server-0 cookie server-0 server server-1 cookie server-1 ---- Note, the other parts of the server directive have been omitted for clarity. NOTE: The <> and the HAProxy server persistence cookie are entirely separate. Do not confuse them. The HAProxy server persistence cookie identifies the backend server which issued the Mellon cookie. For Mellon to work correctly, all user requests *must* be handled by the same backend server that issued the Mellon cookie in the first place. === Forwarded HTTP Headers [[forwarded_http_headers]] When proxies are in effect the `X-Forwarded-\*` HTTP headers come into play. These are set by proxies and are meant to allow an entity processing a request to recognize that the request was forwarded and what the original values were _before_ being forwarded. A common HAProxy configuration sets the `X-Forwarded-Proto` HTTP header based on whether the front connection utilized SSL/TLS or not via this configuration:: http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Proto http if !{ ssl_fc } To make matters interesting, core Apache *does not* interpret this header; thus responsibility falls to someone else to process it. In the situation where HAProxy terminates SSL prior to the backend server processing the request, the fact that the `X-Forwarded-Proto` HTTP header is set to https is *irrelevant* because Apache does not utilize the header when an extension module such as Mellon asks for the protocol scheme of the request. This is why it is *essential* to have the `ServerName` directive include the `scheme:://host:port` and to have `UseCanonicalName` enabled: otherwise Apache extension modules such as Mellon will not function properly behind a proxy. But what about web apps hosted by Apache behind a proxy? It turns out it's the web app's (or rather the web app framework's) responsibility to process the forwarded header. Thus apps handle the protocol scheme of a forwarded request differently than Apache extension modules do. The critical thing to note is is that Apache extension modules and web apps process the request scheme of a forwarded request differently, demanding that *both* the `ServerName` and `X-Forwarded-Proto` HTTP header techniques be utilized. == When a SAML party responds with an error [[error_response]] SAML is a request/response protocol much like HTTP. In fact the two major SAML datatypes are `Request` and `Response`. A `Response` contains a `Status` element which includes a `StatusCode` indicating if the `Request` succeeded or failed, and if it failed, the reason why. The `StatusCode` element may contain additional nested `StatusCode` elements providing additional details, but typically there is usually only one or two `StatusCode` elements. The outermost `StatusCode` is called the top-level status code, the next nested `StatusCode` is called the second-level status code. `StatusCode` values *must* be a URI. The top-level status codes *must* be one of one of the top-level status codes defined by the SAML specification. The second-level status code must also be a URI and should be one of the second-level status codes defined by SAML but a system entity may define its own non-top-level status codes. In addition to the `StatusCode` elements a `Status` element may also contain an optional `StatusMessage` with greater detail and/or a `StatusDetail` whose format is not defined by SAML. In most scenarios Mellon, acting as a relying party, issues a `Request` to an IdP acting as an asserting party, which then replies with a `Response` containing a `Status`. Occasionally Mellon will receive a `Request` from an IdP for which Mellon will respond with a `Response` and `Status`; a good example of this is IdP-initiated logout. *When diagnosing problems you should examine the `StatusCode` values and any additional information in the `Status` element.* === Top-level status codes Below are top-level status codes as defined by SAML. .Top-level status codes urn:oasis:names:tc:SAML:2.0:status:Success:: The request succeeded. Additional information MAY be returned in the and/or elements. urn:oasis:names:tc:SAML:2.0:status:Requester:: The request could not be performed due to an error on the part of the requester. urn:oasis:names:tc:SAML:2.0:status:Responder:: The request could not be performed due to an error on the part of the SAML responder or SAML authority. urn:oasis:names:tc:SAML:2.0:status:VersionMismatch:: The SAML responder could not process the request because the version of the request message was incorrect. === Second-level status codes Below are second-level status codes as defined by SAML. .Second-level status codes urn:oasis:names:tc:SAML:2.0:status:AuthnFailed:: The responding provider was unable to successfully authenticate the principal. urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue:: Unexpected or invalid content was encountered within a or element. urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy:: The responding provider cannot or will not support the requested name identifier policy. urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext:: The specified authentication context requirements cannot be met by the responder. urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP:: Used by an intermediary to indicate that none of the supported identity provider elements in an can be resolved or that none of the supported identity providers are available. urn:oasis:names:tc:SAML:2.0:status:NoPassive:: Indicates the responding provider cannot authenticate the principal passively, as has been requested. urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP:: Used by an intermediary to indicate that none of the identity providers in an are supported by the intermediary. urn:oasis:names:tc:SAML:2.0:status:PartialLogout:: Used by a session authority to indicate to a session participant that it was not able to propagate logout to all other session participants. urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded:: Indicates that a responding provider cannot authenticate the principal directly and is not permitted to proxy the request further. urn:oasis:names:tc:SAML:2.0:status:RequestDenied:: The SAML responder or SAML authority is able to process the request but has chosen not to respond. This status code MAY be used when there is concern about the security context of the request message or the sequence of request messages received from a particular requester. urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported:: The SAML responder or SAML authority does not support the request. urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated:: The SAML responder cannot process any requests with the protocol version specified in the request. urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh:: The SAML responder cannot process the request because the protocol version specified in the request message is a major upgrade from the highest protocol version supported by the responder. urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooLow:: The SAML responder cannot process the request because the protocol version specified in the request message is too low. urn:oasis:names:tc:SAML:2.0:status:ResourceNotRecognized:: The resource value provided in the request message is invalid or unrecognized. urn:oasis:names:tc:SAML:2.0:status:TooManyResponses:: The response message would contain more elements than the SAML responder is able to return. urn:oasis:names:tc:SAML:2.0:status:UnknownAttrProfile:: An entity that has no knowledge of a particular attribute profile has been presented with an attribute drawn from that profile. urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal:: The responding provider does not recognize the principal specified or implied by the request. urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding:: The SAML responder cannot properly fulfill the request using the protocol binding specified in the request. === Status code examples .Example of a `Status` indicating success. [source,xml] ---- ---- <1> _Top-level status code:_ Because the top-level status code is `Success` no other status information is included. [[invalid_nameid_example]] .Example of a `Status` indicating failure due to invalid NameIDPolicy. [source,xml] ---- ---- <1> _Top-level status code:_ Because the top-level status code is *not* `Success` this top-level status code indicates a *failure* and the _primary_ reason for the failure. In this instance, the requester sent a value the receiver was unable to process. <2> _Second-level status code:_ This second-level status code provides the additional information describing what the requester sent that could not be acted upon. In this case, the requester sent a `NameIDPolicy` the IdP was unable to fulfill. === Finding the `StatusCode` [[find_status_code]] Recent versions of Mellon (>= 0.13.1) will log any non-success status in both the Apache error log and in the Mellon diagnostics log (if enabled). The log message for the above `InvalidNameIDPolicy` error will look like this: ---- "StatusCode1="urn:oasis:names:tc:SAML:2.0:status:Requester", StatusCode2="urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy", "StatusMessage="(null)" ---- Or you have the option to examine the SAML message as described in <>. == Gathering run-time information [[gather_runtime_info]] === Apache log files Mellon writes messages to the Apache server error log file. Depending on your Apache configuration, those messages might appear in either `/var/log/httpd/error_log` or `/var/log/httpd/ssl_error_log`. You can turn up the verbosity of the messages by modifying the Apache `LogLevel`, for example: ---- LogLevel debug ---- NOTE: Mellon's use of standard Apache logging is limited. See <> for a much better way to capture Mellon run time information. === Trace SAML flow [[trace_saml_flow]] Since you're most likely using the SAML Web-SSO profile, which is entirely browser based, you can use any of the browser tools to watch HTTP requests and responses. The Firefox web browser provides the FireBug add-on and the Chrome browser offers Developer Tools. Each of these browsers also has additional add-ons to display SAML messages; see <>. NOTE: The easiest and most complete way to trace HTTP requests and responses during SAML flow, capture SAML messages, and examine how Mellon processes a SAML message is to use <>. === Inspect SAML messages [[inspect_saml_messages]] There are many times when you need to see the content of a SAML message. Perhaps you don't know what attributes your IdP is returning in an assertion. Or the SAML transaction is failing for some reason and you need to diagnose why. In such cases being able to see the contents of the SAML messages can be immensely helpful. Examining the on-the-wire SAML data is seldom useful, even when it's contained in otherwise visible HTTP data elements such as query parameters, post data, etc. This is because the various SAML bindings encode the message in different ways. It may break the message into independent components which need to be reassembled at the receiving end, or it may encode the data in a variety of formats which need to be decoded to recover the original message content. It's best to use SAML-aware tools to examine SAML messages, because they know how to decode and reassemble the raw SAML data into the final SAML message the receiver evaluates. NOTE: The easiest and most complete way to capture SAML messages and examine how Mellon processes a SAML message is to use <>. If your version of Mellon supports diagnostics you may wish to skip to this section. The Web-SSO SAML profile is by far the most commonly used. Because all SAML messages transit though the browser in Web-SSO, it is possible to write a browser extension to capture and decode the SAML messages exchanged between the SP and IdP. ==== Firefox SAML Tracer [[saml_tracer]] The Firefox https://addons.mozilla.org/en-US/firefox/addon/saml-tracer/[SAML Tracer] Add-On will display decoded SAML messages used during single sign-on and single logout. SAML Tracer is not capable of decrypting an encrypted IdP response, because it does not have access to the IdP's public encryption key contained in the IdP's metadata. See <> for how to deal with this issue. To use SAML Tracer you must first install the add-on. Then each time you want to use SAML Tracer you will need to go to the Firefox menu and select the SAML Tracer option. This will bring up a separate Firefox window which looks like this: image::saml-tracer.svg[] The SAML Tracer window is divided into two panes: a list of HTTP requests in the top pane, and detailed information on the selected request in the bottom window. SAML Tracer examines each HTTP request and response, and if it detects it is a SAML message, it flags the request in the request list window at the top with a "SAML" icon. In the detail pane are different tabs which show you the request/response information in different views. In order to view the decoded SAML message you need to make the `SAML` tab active. The `Parameters` tab shows you the query parameters (either URL or POST). SAML messages are usually transported in HTTP parameters, so this is where you can see the raw SAML data before being decoded into a complete SAML message. The `http` tab shows you the HTTP headers associated with the HTTP request/response. ==== Chrome, SAML Chrome Panel The Chrome Web browser offers several add-ons to display SAML messages. The most commonly used is https://chrome.google.com/webstore/detail/saml-chrome-panel/paijfdbeoenhembfhkhllainmocckace[SAML Chrome Panel]. SAML Chrome Panel integrates with the Chrome developer tools. Here is an example of the SAML Chrome Panel in the developer tools panel: image::chrome_SAML_Chrome_Panel.svg[] ==== If the IdP response is encrypted [[encrypted_response]] Data in a SAML response may be encrypted for confidentiality (usually encryption is not needed because SAML transactions should be occurring over a secure TLS channel). Decrypting the data requires access to the IdP's public encryption key contained in its metadata. Most SAML browser tools do not support decryption. If you discover your tool is showing you encrypted data you have a few options: * Disable encryption at the IdP. See your IdP's documentation on how to enable/disable encryption. * Use <>. The diagnostics support in Mellon operates after the SAML message is decoded from its SAML binding transport and after it's been decrypted into a final plaintext SAML XML document. Most people will find Mellon diagnostics to be the easiest and most complete capture of SAML data and Mellon's processing of it. === Inspecting Mellon environment variables [[inspect_mellon_env]] Recall that Mellon communicates with web apps by inserting Apache environment variables into the Apache environment. While diagnosing problems or when initially setting up your Mellon deployment it can be very useful to see the contents of the Apache environment. The typical way this is done is to substitute the resource Mellon is protecting with a script that dumps the environment it received. So instead of getting back the resource, after Mellon successfully authenticates the script runs and returns a page listing the environment variables. Once you've collected this information you need to remove the script from the protected URL so the protected resource will be returned instead of a data dump. If your installed version of Mellon includes support for <>, there is no need to alter your protected resource in order to get an environment variable dump. The diagnostics log includes a dump of the complete Apache environment at the end of each response. This is a much easier and more complete solution than substituting a script for your protected resource. ==== Python WSGI Environment Dump Create a script with the following content: [source,python] ---- import pprint import webob import webob.dec @webob.dec.wsgify def application(req): return webob.Response(pprint.pformat(req.environ), content_type='application/json') ---- Placing the above script in the Apache `cgi-bin` directory is a good idea. We'll name this script 'dump-env'. Add a `WSGIScriptAlias` directive to your Apache configuration so that it runs the above script when the protected resource URL is requested. For example: ---- WSGIScriptAlias "/private/info.html" "/var/www/cgi-bin/dump-env" ---- ==== PHP Environment Dump [source,php] ---- $value) { if(substr($key, 0, 7) == 'MELLON_') { echo($key . '=' . $value . "\r\n"); } } ?> ---- === Mellon Diagnostics [[mellon_diagnostics]] When something goes wrong with your Mellon deployment, experience has shown that it can be frustratingly difficult to gather sufficient information to diagnose the problem. Often you will need access to the following pieces of information: * Mellon configuration * Mellon metadata * IdP metadata * HTTP requests and responses * HTTP headers * SAML message content as decoded pretty XML * Apache environment variables * Session infomation Although Mellon does log some `DEBUG` messages to the Apache error log, that information is often incomplete and mixed in with other irrelevant messages. The SAML message content has to be gathered independently via other tools (<>), configuring Apache to log all HTTP headers is obscure and verbose, there is no easy way to log all Apache environment variables associated with a request, and the SAML metadata and Mellon configuration is independent of the log data. Finally all these indpendent pieces of data gathered from multiple sources need to be correlated to produce a coherent sequence while at the same time removing a lot of extraneous irrelevant non-SAML data. Apache logging also suffers from some serious limitations when trying to record SAML data. Apache enforces a hard limit on the length of a log message, which often results in truncating SAML messages. Apache log messages are reformatted, newlines are removed, and other characters are escaped. This makes trying to read XML documents extremely difficult unless you post-process the log. If you are a support person trying to help an administrator with their Mellon deployment, it is very difficult to get a 3rd party who is not familiar with the various operations to gather the necessary information in a cohesive form amenable for remote diagnostic review. It would be really nice if Mellon could gather all this information in protocol sequence in a single file without other irrelevant Apache messages, and without the need for any post-processing of the log data. Mellon has been extended to gather all the above relevant information in a human readable format. The feature is called _Mellon Diagnostics_. The diagnostics feature is new as of July 2017, and it must be enabled at compile time; thus your version of Mellon may not have it. Because the feature is new the format and content of the diagnostic data is expected to evolve. ==== Using Mellon Diagnostics [[using_mellon_diagnostics]] Currently Mellon diagnostics supports these new Mellon directives. These directives are module level and as such should be declared outside of any location blocks in your Apache configuration. MellonDiagnosticsFile:: If Mellon was built with diagnostic capability then diagnostic is written here, it may be either a filename or a pipe. If it's a filename then the resulting path is relative to the ServerRoot. If the value is preceeded by the pipe character "|" it should be followed by a path to a program to receive the log information on its standard input. Default: `logs/mellon_diagnostics` MellonDiagnosticsEnable:: If Mellon was built with diagnostic capability then this is a list of words controlling diagnostic output. Currently only `On` and `Off` are supported. Default: `Off` To enable diagnostic logging add this line to your Apache configuration file where you keep your Mellon configuration. ---- MellonDiagnosticsEnable On ---- Restart Apache and perform some operation that involves Mellon. In your Apache log directory will be a file called `mellon_diagnostics` (or whatever `MellonDiagnosticsFile` was set to). IMPORTANT: Diagnostic logging may potentially contain security sensitive information. Diagnostic logging is verbose and will generate large files. Therefore _you should enable diagnostic logging only for the minimum duration necessary_. You may wish to review diagnostic output in <> captured when the demo authentication was executed. == Potential Problems === It's the metadata *The vast majority of SAML deployment problems can be traced back to metadata.* * Is your metadata current? * Have you loaded your most recent SP metadata? Did you restart Apache after modifying the SP metadata? * Has your IdP loaded the exactly the same metadata Mellon is reading at Apache start-up? * Have you loaded your most recent IdP metadata? Did you restart Apache after modifying the SP metadata? * Did you make a change to your entityID? * Did you make a change to the `MellonEndpointPath` without regenerating your SP metadata and loading the new metadata into both Mellon and your IdP? Remember the `MellonEndpointPath` establishes all the SAML endpoint URLs that appear in your metadata. See <> and <>. * Did you modify any of the keys or certs without both updating the mellon config _and_ your SP metadata? === Behavior does not change after modifying any SAML file Mellon reads its configuration at Apache start-up. If you make any change to any file Mellon reads, you will not see those changes reflected until after you restart Apache. === Are the Mellon configuration directives syntactically correct? Apache will not start if there is any error in any of the configuration files it reads. An easy way to test the correctness of your Apache configuration directives _without starting the server and examining the error logs_ is to use the the `apachectl` command line tool with the `configtest` option: ---- apachectl configtest ---- === No AuthnRequest sent to IdP During debugging you may discover the entire <> is not executed, so the IdP is never contacted. This is because Mellon implements sessions. The session identifier is communicated in the cookie `mellon-cookie` (or whatever is the current value of the Mellon directive `MellonVariable`). If you had previously successfully authenticated against the IdP, the browser will have been sent the Mellon session ID in its cookie. When Mellon gets a request to authenticate a resource, it first checks to see if it has a valid session based on the identifier passed as the Mellon cookie. If there is a valid session, Mellon will use that cached session information instead of contacting the IdP. Deleting the `mellon-cookie` from the browser will cause Mellon to believe there is no pre-existing session. === Incorrect MellonEndpointPath [[incorrect_mellon_endpoint_path]] . <> [listing,subs="verbatim,quotes"] ------------------------------------------ MellonEndpointPath ##/mellon/## ------------------------------------------ <1> The definition of `MellonEndpointPath` in your Apache Mellon configuration *must* match the path component in each of your Service `Location` declarations in your SP metadata. See <> for more detail. [listing,subs="verbatim,quotes"] ------------------------------------------ ------------------------------------------ <1> Each Service `Location` URL in your SP metadata *must* have a path component that starts with your `MellonEndpointPath` and appends exactly one directory component to it. That final directory component is one of the Mellon endpoints as described in <>. Here the `MellonEndpointPath` is highlighted in the `Location` attributes of the metadata. === HTTP_BAD_REQUEST - Invalid Destination on Response [[invalid_destination]] If after posting the Assertion to your `postResponse` endpoint you get a HTTP_BAD_REQUEST error with status code 400 and a page that says: ---- Your browser sent a request that this server could not understand. ---- and in the Apache log file there is a Mellon message like this: ---- Invalid Destination on Response. Should be: https://xxx/mellon/postResponse ---- Then you have failed one of the SAML security checks. There is a SAML requirement that the recipient of a message verifies that the intended destination of the message is the actual SAML endpoint it was received on. This is to prevent malicious forwarding of messages to unintended recipients. To perform this check, what Mellon does is build a URL by asking Apache what scheme, hostname, and port it is running under, and then appends the <> and the Mellon endpoint to it. This becomes the URL the message was received on. Mellon then does a string comparison to see if this manufactured URL is identical to the `Destination` attribute in the SAML message. If they are not the same string, the test fails and a HTTP_BAD_REQUEST is returned. There are two potential causes for this failure: . Incorrect Apache `ServerName`. See the <> discussion for more details. This problem usually occurs when Mellon is running behind a load balancer or SSL terminator. . Mismatch between the Mellon metadata and the `MellonEndpointPath` in your Mellon configuration. If the scheme, hostname, and port are correct then the problem must be in path component of the URL. The SAML `Destination` attribute is read from the provider's metadata. The `MellonEndpointPath` is read from Mellon's configuration. The two must be in sync. Verify that the location endpoints in Mellon's metadata match the value of `MellonEndpointPath`. See the discussion of <> for more details. The `Destination` check may also fail because one URL has an explicit port but the other does not. This can occur with the standard HTTP port 80 and HTTPS port 443: see <> for more detail. === Mellon metadata out of sync with Mellon configuration Mellon's metadata and its Apache configuration directives have data elements in common but are maintained independently. The Apache configuration directives in common with the metadata are: * `MellonSPentityId` * `MellonSPPrivateKeyFile` * `MellonSPCertFile` * `MellonEndpointPath` This can lead to problems if you: * Generate metadata not consistent with these values in your Apache configuration directives. * Edit the above Apache configuration directives without regenerating and reloading your metadata. * Fail to load the current SP metadata by restarting Apache. * Fail to load the current SP metadata into the IdP. You may wish to review <> and <>. === Microsoft ADFS issues It is common to have problems when using Microsoft ADFS as a SAML IdP. This blog post from Microsoft contains many of the ADFS configuration issues encountered when adding an SP to ADFS: [[adfs_blog,ADFS Deep Dive Blog]]https://blogs.technet.microsoft.com/askpfeplat/2015/03/01/adfs-deep-dive-onboarding-applications/[ADFS Deep Dive Onboarding Applications] NOTE: ADFS calls SPs a "Relying Party" and the SP configuration a "Relying Party Trust". ==== ADFS Signature Algorithm [[adfs_sig_alg]] One of the `Relying Party Trust` options is the "Secure Hash Algorithm" which controls the signature algorithm used to produce an XML signature on the SAML message. This is the signature algorithm ADFS will use to sign the SAML messages it emits. SAML does not require both parties to use the same signature algorithm; in theory, it's fine if Mellon as the SP signs with one algorithm and ADFS as the IdP signs with a different algorithm. But ADFS enforces the requirement that the SP signs with same algorithm as set in the `Relying Party Trust`. If ADFS receives a SAML message signed with a different algorithm than what is specified in the `Relying Party Trust` configuration, you will get a message in the ADFS log like this: ---- SAML request is not signed with expected signature algorithm. SAML request is signed with signature algorithm http://www.w3.org/2001/04/xmldsig-more#rsa-sha1 . Expected signature algorithm is http://www.w3.org/2000/09/xmldsig#rsa-sha256 ---- Since SHA-1 is no longer considered secure, many ADFS administrators set their `Relying Party Trust` secure hash algorithm to SHA-256. This causes problems for Mellon versions earlier than 0.13.1, which always signed its messages with the SHA-1 hash (specifically the RSA-SHA1 algorithm) and there was no mechanism to specify a different signing algorithm. See <> for how to modify the `Relying Party Trust` Secure Hash Algorithm. Mellon versions greater than 0.13.1 added a new configuration option called `MellonSignatureMethod` which allows you to match the signature algorithm emitted by Mellon to the one specified in the ADFS `Relying Party Trust` for the Mellon SP. For example: ---- MellonSignatureMethod rsa-sha256 ---- ==== ADFS NameIDPolicy [[adfs_nameid_policy]] By default ADFS cannot handle many of the SAML NameID formats without additional configuration in the `Relying Party Trust`. Please make sure you are familiar with the material in the section <>. By default, Mellon will generate SP metadata with a <> of `transient`, see <> for how to modify this in Mellon. When ADFS receives a SAML message with a `NameIDPolicy` set to a specific format, it is supposed to respond with a `NameID` matching that format. Because of the architecture of ADFS, it may not have access to the data needed to generate that `NameID`. The necessary data is contained in a _Claim_ controlled by a _Claim Rule_. To get the contents of the _Claim_ being used to populate the SAML `NameID`, you must also add a `Claim Rule Transform` that maps the desired _Claim_ data into a SAML data element, which in this case is the `NameID`. Examples of the `NameID` formats which require this additional configuration in ADFS are `transient`, `persistent`, `email` and possibly others. If the _Claim Rule_ and _Claim Rule Transform_ are not configured for the `NameIDPolicy` in the request, ADFS will respond with a `InvalidNameIDPolicy` error status because it cannot provide the requested `NameID` format. See <> in the <> for an example of this error and how to identify it. This Microsoft blog discusses `NameID` configuration in ADFS: [[adfs_nameid,ADFS NameID Configuration]]https://blogs.msdn.microsoft.com/card/2010/02/17/name-identifiers-in-saml-assertions/[ADFS - Name Identifiers in SAML assertions] === Time Sync SAML, like many authentication protocols (e.g. Kerberos), relies on timestamps to validate messages. If you see one of these errors in the httpd logs: ---- [auth_mellon:error] [pid xxx] [client xxx] NotBefore in Condition was in the future. [auth_mellon:error] [pid xxx] [client xxx] NotOnOrAfter in Condition was in the past. ---- Then it's likely that either the Mellon node or the IdP node are not time synchronized. You can check the status of your time sync with the `chronyc` command line tool, for example: ---- $ chronyc sources 210 Number of sources = 4 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^? tock.no-such-agency.net 0 10 0 10y +0ns[ +0ns] +/- 0ns ^? static-74-104-167-114.bst 0 10 0 10y +0ns[ +0ns] +/- 0ns ^? 69.195.159.158 0 10 0 10y +0ns[ +0ns] +/- 0ns ^? freemont.nerdboy.net.au 0 10 0 10y +0ns[ +0ns] +/- 0ns ---- If the `Reach` column has zeros in it you are not synchronizing your clock. This may be due to a firewall blocking the NTP port. Trying opening your NTP port or using a different server in `/etc/chrony.conf`. == Glossary entityID:: The unique name of a SAML provider. The entityID *must* be a URI. Often entityID's are URLs, however the choice of using a URL as an entityID does not have any meaning in SAML other than it is a convenient way to to have a unique URI. It is best to choose an entityID that will not change over time as SAML services inevitably migrate between hosts in a deployment. Once again, an entityID is *_a unique name for a SAML service_*, it is nothing more than that. Assertion:: Data produced by a SAML authority (e.g. IdP) with respect to a specific subject. The assertion may convey authentication of the subject, attributes associated with the subject, or authorization information for the subject in regards to a specific resource. Identity Provider:: An identity provider is a SAML authority which _provides_ authentication services proving the identity of a principal. The proof of identity is conveyed in a SAML assertion along with additional information about the subject (attributes) which the service provider may choose to utilize when making authorization decisions. IdP:: Acronym for Identity Provider Profile:: A profile is a set of rules for one of several purposes; each set is given a name in the pattern “xxx profile of SAML†or “xxx SAML profileâ€. * Rules for how to embed assertions into and extract them from a protocol or other context of use. * Rules for using SAML protocol messages in a particular context of use. * Rules for mapping attributes expressed in SAML to another attribute representation system. Such a set of rules is known as an “attribute profileâ€. SAML:: Security Assertion Markup Language. Service Provider:: A service provider is a SAML _relying party_ which _provides_ a _service_ to a user who must be authenticated and authorized by the service in order to use the service. A web application is a common example. SP:: Acronym for Service Provider. [appendix] == Appendix === Example On-The-Wire AuthnRequest [[authentication_request_wire]] This is is the "on the wire" HTTP data for the <> using the _HTTP Redirect Binding_. ---- GET https://rhsso.example.com:8443/auth/realms/test/protocol/saml?SAMLRequest=hZJBT8JAEIX%2FSrN32NKWAptCQkASEjQE1IMXs6lj2KS7W3emiv%2FeaRHFi5w2eTsvb76XKVDbqlbzhg5uB28NIEVHWzlU3cdUNMEpr9GgctoCKirVfn67UUk%2FVnXw5EtfiQvL%2Fw6NCIGMdyJaL6fieTgZJPkiTeP8JstHk1U%2BTOMkWcWLbDkfreJMRI8QkOengu1sQmxg7ZC0I5biwagX571kfD9IVTpRg%2BxJREtmME5T5zoQ1aikDAdE34ejtnUF%2FdJbNc6yVGrmlgF0ZVES%2B%2BQZSbYsIlp4h9BG%2FQdVnoZU2YTAb89whikNiWjlQwldt1PxqiuElmDLJZh3%2BFHm507asMZC2EN4NyU87Da%2F%2B1uoKu8uAb4lWXukHWDdLiFmRbu36moKs%2BtmC6RfNOlCXvqK01HcMel6ufUM89miWH2liFYxL73XblRR0A4NF8KIHPax4J6JsSk0IOTsFPn39GZf&RelayState=https%3A%2F%2Fmellon.example.com%2Fprivate%2Finfo.html&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=GJ%2BC%2BEwcPpOmAfYwMdMcAPSkX2y1Da634jrm1oWWs8Ozyb7P%2FumIk9HaNbfJZvaHc6HyOxYXhCpqb6NJrRm%2BrY1NSOJqjtt3kXldNLKecFfhKamzfOfAufKTPGGSZNAuwRTiQCkrLuFt8A%2BezEuCswNDADSRJGLL9aYX8A8G23IcLeVuqobtrCH9bSr2wgO0uy61o1s5bDlA6ceKwrjle%2F6TofFUWBYOB6pzRL40AzmNsvKieHqSOCxo6uNKQdEZQYomF9fi%2FuCPovIn5AdRFC1wcx%2BeGYi%2BDS6R4lbEnrhu8RfEmxhA8PJHDoTMH2fcfD0jyUh%2BejtLdqCUFJ9ppQ%3D%3D HTTP/1.1 ---- === Example On-the-Wire response [[assertion_response_wire]] This is is the "on the wire" HTTP data for the <> using the _HTTP Post Binding_. ---- SAML HTTP Post Binding
---- === Example Mellon Diagnostics [[mellon_diagnostics_example]] Here is the diagnostics output as described in <> for our authentication example. ---- ---------------------------------- New Request --------------------------------- GET - /saml-test/protected.html log_id: (null) server: name=/etc/httpd/conf.d/ssl.conf, scheme=https hostname=mellon.example.com port=443 pid: 21576, tid: 140251630954624 unparsed_uri: /saml-test/protected.html uri: /saml-test/protected.html path_info: filename: /var/www/html/saml-test/protected.html query args: (null) Request Headers: Host: mellon.example.com Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 DNT: 1 Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.8 Cookie: mellon-cookie=aa8aefac8bc813f194b1a8d97e3a4058 Mellon Directory Configuration for URL: /saml-test/protected.html MellonEnable (enable): auth MellonVariable (varname): cookie MellonSecureCookie (secure): Off MellonMergeEnvVars (merge_env_vars): (null) MellonEnvVarsIndexStart (env_vars_index_start): -1 MellonEnvVarsSetCount (env_vars_count_in_n): On MellonCookieDomain (cookie_domain): (null) MellonCookiePath (cookie_path): (null) MellonCond (cond): 0 items MellonSetEnv (envattr): 0 items MellonUser (userattr): NAME_ID MellonIdP (idpattr): IDP MellonSessionDump (dump_session): Off MellonSamlResponseDump (dump_saml_response): Off MellonEndpointPath (endpoint_path): /mellon/ MellonSPMetadataFile (sp_metadata_file): pathname: "/etc/httpd/saml2/demo_sp_metadata.xml" MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= urn:oasis:names:tc:SAML:2.0:nameid-format:transient MellonSPPrivateKeyFile (sp_private_key_file): pathname: "/etc/httpd/saml2/demo.key" -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHUXILDtuMxyFP 6OW81hqfj9z+gC7sxR6U8bRPI4gFiNjkNF+4HMyrKtVtfwA5WJ1tJ4IaZbg4fGap iCcWQ5tjjuZ7CpZ6XLekq63OGrkgZxNm8L+AjvREv78iR4Myc4x6ybN1LwQnV+oD F0i9SAEPnfJCaCA+1+DC3RKy7+xtK3SVimUWwAHmkNU6N0wCVSNYn1KgnxBSCPTx 2DifbyRl21SiokCfLSnktE++WBzRzldSxzMrkSfFoS7/uT232tbdw2NsJmHGLRGk myBrEC6BVhQ746NM1cLDtrOots7bNfZ3yfb3W1tpoKVOhM2se3OTKdidAQsaGlVu 7QIhBqinAgMBAAECggEBAJtU662WfJ9vqJRgCnpp2QG02iM0vl0jGbw1ybFLHXxC s9TUxCv1tcNHdGEK8p++YaFpgskTsMfEmzVPuDZvpa+m9BO7op3ll/CrIp5W0SNh cQtuX6/OuKrDTC9oz+QHjNk8S7DtXS1UJDkYckWg0cLb8qqx/z86eh0isKnmtLg2 H1+6L6mB9fcZldkcrU+kXT+dcDX85skMZAeBsrG4yaoX26AzVl8lEl2rJAQvpxj8 5wGBC4riWY6TzMYiCjcS5JfZIlbhcZe61ej3A48NVBSKCP1XKo0xbKuOHIQuMeeW wSaboBwRzJ9JdTXlq5UWfLvmjXDc/HCwk/N7cj021uECgYEA5KkQr3cSKrMIkYoO H0Vnkw1kYYGUjtTL00Nfdkv7uGMF122nyt1ND0gpdS0jgNx3LSEam/gY35UWEjGH i8DGD04k8pvKDUsl8cuYPcC3oce1lLCGAnw+aHPC5wtA829CLOLtBfxXIhVAI0tp ECosD/A63/m1LC19XolOd10/PC8CgYEA3yZChEYZupDGJFZltiy0ZgUarvD2Ss4N QiRVR+CmpBrOKZdD8q6uUuqWQN9Rw7kXm8LxAPYwiIDVjxjYALF+j7/9Q1oZyKuv eHJdMe4eRUeqeaIKbxnFfKwGZ5rj97jwPrkUCxio75KZhpOcDHMSgBCBtzW0XIZl gTeQYOshZQkCgYB5TK6LRnEesabj/gaL1DejrMEJcMIsGvqdceocSSaZo/4fUA5o 8YjFiJRlkrJ403ttN1h0UOJxCReSQzASlQr8Z4n2IWrILotMf6Kdb7R6YAUVgac1 fk9k/bPw+OlVujmyshbmy/w1GmzRzFlJt/Vz5w50bnULoH4XPmOfspmvBQKBgBcJ rihVzGY0eCBcQxfxuZYmxMB25BaI+1luwtcu3EVo9wvYMA2n9xtcWLLN23UncMaF 87ezswMEugeR+wrnSDezDISdkrfi8bSvqetzt/BTG8h+8DDUKk1avTaJCSwUDcmL 9gPHQfmp2uvH5X5riudpzNqLUtmSjnwurlszKzlxAoGAR8STlDJhNph+p3cF8k25 ydT1kypxnjzVG8CAV5/h3dUmc7j7gyV8NlWZfWacxMZWOBsrdVh0zhMNUPiLJaGd I1isOkmiN9JFYMMhHSnhPnTCIjmu6uBLxf8wotHAvzWOJPV7lUZbw21KIN3DS79F sGZ2QzGYn4inHG4UHClhZxU= -----END PRIVATE KEY----- MellonSPCertFile (sp_cert_file): pathname: "/etc/httpd/saml2/demo.cert" -----BEGIN CERTIFICATE----- MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= -----END CERTIFICATE----- MellonIdPPublicKeyFile (idp_public_key_file): file_data: NULL MellonIdPCAFile (idp_ca_file): file_data: NULL MellonIdPMetadataFile (idp_metadata): 1 items [ 0] Metadata pathname: "/etc/httpd/saml2/demo_keycloak_ipa_idp_metadata.xml" R2OGk9W0luNm_NtZbURWOrPlvFzSTDMimCVK5N1Mj5U MIIClTCCAX0CBgFeFdE9pDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANpcGEwHhcNMTcwODI0MTk1NDQ3WhcNMjcwODI0MTk1NjI3WjAOMQwwCgYDVQQDDANpcGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgIPeag+JJmhtAkIGBwUT/req+jKC6c0Vl1Ngtzbcd07CP9mq1DomBkjuWl59J2urlEfrV4yT8avia0eYE6Dm/TqC74SHt3TVtiUliynAh/z2JvFlLb/EbGePSKrMnuNV8rV75YGcyE12vBRooUPx3hGaygsfaSOg+BijDuCSpbVdWSdVx9VecsWJfxSochOZUj6yvm/qTb8Ptl0x/o7/b/16GgjFRIKSFrdk8pVtMn1wCzpQQoGVHZmp1jrppGcp8KXIK54q7b4pPiTzlW6xhBgrmW2RtWQesCmN8ga1CVeBZKLsaH7argwGH5Ttz31iensqUO0degFu6nwCltgTVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFx8dl6RDle65q3IHIiGaL5fbJK5HxQiMXpk4N5riWQTP4g6xoTNAG4OFFUd4uRxt2ovdEdkbzhEy2lV4x626QdEfK5V9QKppupsTxTGA/4NMW9QCocAvFSpmYErmJIhfy6zzELoBK4Dpfcc3u1peHx2686msx6ExARF116d+5Xaps1dmPPy3yb2cCKzKbLhieqv+aLLrwz657ERUc4OnqEMEmmHFhHvPI7LRlS4AQ1/s1QlKcM9yqcu8WN3yKM/kuvZtZ0YTCSIl9W1b+I5v8wNoVFB22s7rfxs3DfJFaIImaTmRzaDX0MXgibEckrkigpO+anKe9B9z8CJdtlUHco= urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress [ 0] Chain File file_data: NULL MellonIdPIgnore (idp_ignore): MellonSPentityId (sp_entity_id): (null) MellonOrganizationName (sp_org_name): 0 items MellonOrganizationDisplayName (sp_org_display_name): 0 items MellonOrganizationURL (sp_org_url): 0 items MellonSessionLength (session_length): -1 MellonNoCookieErrorPage (no_cookie_error_page): (null) MellonNoSuccessErrorPage (no_success_error_page): (null) MellonDefaultLoginPath (login_path): / MellonDiscoveryURL (discovery_url): (null) MellonProbeDiscoveryTimeout (probe_discovery_timeout): -1 MellonProbeDiscoveryIdP (probe_discovery_idp): 0 items MellonAuthnContextClassRef (authn_context_class_ref): 0 items MellonAuthnContextComparisonType (authn_context_comparison_type): (null) MellonSubjectConfirmationDataAddressCheck (subject_confirmation_data_address_check): On MellonDoNotVerifyLogoutSignature (do_not_verify_logout_signature): 0 items MellonPostReplay (post_replay): On MellonECPSendIDPList (ecp_send_idplist): On enter function am_auth_mellon_user searching for session with key aa8aefac8bc813f194b1a8d97e3a4058 (session) ... not found am_auth_mellon_user am_enable_auth, no valid session [APLOG_DEBUG auth_mellon_util.c:2055] have_paos_media_type=False valid_paos_header=False is_paos=Falseenter function am_start_auth Loading SP Metadata pathname: "/etc/httpd/saml2/demo_sp_metadata.xml" MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= urn:oasis:names:tc:SAML:2.0:nameid-format:transient Loading IdP Metadata pathname: "/etc/httpd/saml2/demo_keycloak_ipa_idp_metadata.xml" R2OGk9W0luNm_NtZbURWOrPlvFzSTDMimCVK5N1Mj5U MIIClTCCAX0CBgFeFdE9pDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANpcGEwHhcNMTcwODI0MTk1NDQ3WhcNMjcwODI0MTk1NjI3WjAOMQwwCgYDVQQDDANpcGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgIPeag+JJmhtAkIGBwUT/req+jKC6c0Vl1Ngtzbcd07CP9mq1DomBkjuWl59J2urlEfrV4yT8avia0eYE6Dm/TqC74SHt3TVtiUliynAh/z2JvFlLb/EbGePSKrMnuNV8rV75YGcyE12vBRooUPx3hGaygsfaSOg+BijDuCSpbVdWSdVx9VecsWJfxSochOZUj6yvm/qTb8Ptl0x/o7/b/16GgjFRIKSFrdk8pVtMn1wCzpQQoGVHZmp1jrppGcp8KXIK54q7b4pPiTzlW6xhBgrmW2RtWQesCmN8ga1CVeBZKLsaH7argwGH5Ttz31iensqUO0degFu6nwCltgTVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFx8dl6RDle65q3IHIiGaL5fbJK5HxQiMXpk4N5riWQTP4g6xoTNAG4OFFUd4uRxt2ovdEdkbzhEy2lV4x626QdEfK5V9QKppupsTxTGA/4NMW9QCocAvFSpmYErmJIhfy6zzELoBK4Dpfcc3u1peHx2686msx6ExARF116d+5Xaps1dmPPy3yb2cCKzKbLhieqv+aLLrwz657ERUc4OnqEMEmmHFhHvPI7LRlS4AQ1/s1QlKcM9yqcu8WN3yKM/kuvZtZ0YTCSIl9W1b+I5v8wNoVFB22s7rfxs3DfJFaIImaTmRzaDX0MXgibEckrkigpO+anKe9B9z8CJdtlUHco= urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress [APLOG_DEBUG auth_mellon_handler.c:3498] Redirecting to login URL: https://mellon.example.com/mellon/login?ReturnTo=https%3A%2F%2Fmellon.example.com%2Fsaml%2Dtest%2Fprotected.html&IdP=https%3A%2F%2Frhsso.example.com%3A8443%2Fauth%2Frealms%2Fipa === Response === Status: 303 See Other(303) user: (null) auth_type=(null) Response Headers: Cache-Control: private, max-age=0, must-revalidate Location: https://mellon.example.com/mellon/login?ReturnTo=https%3A%2F%2Fmellon.example.com%2Fsaml%2Dtest%2Fprotected.html&IdP=https%3A%2F%2Frhsso.example.com%3A8443%2Fauth%2Frealms%2Fipa Content-Length: 388 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html; charset=iso-8859-1 Response Error Headers: Environment: UNIQUE_ID: WabkjcTYa6iga7y800KGZQAAAAA ---------------------------------- New Request --------------------------------- GET - /mellon/login log_id: (null) server: name=/etc/httpd/conf.d/ssl.conf, scheme=https hostname=mellon.example.com port=443 pid: 21576, tid: 140251630954624 unparsed_uri: /mellon/login?ReturnTo=https%3A%2F%2Fmellon.example.com%2Fsaml%2Dtest%2Fprotected.html&IdP=https%3A%2F%2Frhsso.example.com%3A8443%2Fauth%2Frealms%2Fipa uri: /mellon/login path_info: /login filename: /var/www/html/mellon query args: ReturnTo=https%3A%2F%2Fmellon.example.com%2Fsaml%2Dtest%2Fprotected.html&IdP=https%3A%2F%2Frhsso.example.com%3A8443%2Fauth%2Frealms%2Fipa Request Headers: Host: mellon.example.com Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 DNT: 1 Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.8 Cookie: mellon-cookie=aa8aefac8bc813f194b1a8d97e3a4058 Mellon Directory Configuration for URL: /mellon/login MellonEnable (enable): info MellonVariable (varname): cookie MellonSecureCookie (secure): Off MellonMergeEnvVars (merge_env_vars): (null) MellonEnvVarsIndexStart (env_vars_index_start): -1 MellonEnvVarsSetCount (env_vars_count_in_n): On MellonCookieDomain (cookie_domain): (null) MellonCookiePath (cookie_path): (null) MellonCond (cond): 0 items MellonSetEnv (envattr): 0 items MellonUser (userattr): NAME_ID MellonIdP (idpattr): IDP MellonSessionDump (dump_session): Off MellonSamlResponseDump (dump_saml_response): Off MellonEndpointPath (endpoint_path): /mellon/ MellonSPMetadataFile (sp_metadata_file): pathname: "/etc/httpd/saml2/demo_sp_metadata.xml" MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= urn:oasis:names:tc:SAML:2.0:nameid-format:transient MellonSPPrivateKeyFile (sp_private_key_file): pathname: "/etc/httpd/saml2/demo.key" -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHUXILDtuMxyFP 6OW81hqfj9z+gC7sxR6U8bRPI4gFiNjkNF+4HMyrKtVtfwA5WJ1tJ4IaZbg4fGap iCcWQ5tjjuZ7CpZ6XLekq63OGrkgZxNm8L+AjvREv78iR4Myc4x6ybN1LwQnV+oD F0i9SAEPnfJCaCA+1+DC3RKy7+xtK3SVimUWwAHmkNU6N0wCVSNYn1KgnxBSCPTx 2DifbyRl21SiokCfLSnktE++WBzRzldSxzMrkSfFoS7/uT232tbdw2NsJmHGLRGk myBrEC6BVhQ746NM1cLDtrOots7bNfZ3yfb3W1tpoKVOhM2se3OTKdidAQsaGlVu 7QIhBqinAgMBAAECggEBAJtU662WfJ9vqJRgCnpp2QG02iM0vl0jGbw1ybFLHXxC s9TUxCv1tcNHdGEK8p++YaFpgskTsMfEmzVPuDZvpa+m9BO7op3ll/CrIp5W0SNh cQtuX6/OuKrDTC9oz+QHjNk8S7DtXS1UJDkYckWg0cLb8qqx/z86eh0isKnmtLg2 H1+6L6mB9fcZldkcrU+kXT+dcDX85skMZAeBsrG4yaoX26AzVl8lEl2rJAQvpxj8 5wGBC4riWY6TzMYiCjcS5JfZIlbhcZe61ej3A48NVBSKCP1XKo0xbKuOHIQuMeeW wSaboBwRzJ9JdTXlq5UWfLvmjXDc/HCwk/N7cj021uECgYEA5KkQr3cSKrMIkYoO H0Vnkw1kYYGUjtTL00Nfdkv7uGMF122nyt1ND0gpdS0jgNx3LSEam/gY35UWEjGH i8DGD04k8pvKDUsl8cuYPcC3oce1lLCGAnw+aHPC5wtA829CLOLtBfxXIhVAI0tp ECosD/A63/m1LC19XolOd10/PC8CgYEA3yZChEYZupDGJFZltiy0ZgUarvD2Ss4N QiRVR+CmpBrOKZdD8q6uUuqWQN9Rw7kXm8LxAPYwiIDVjxjYALF+j7/9Q1oZyKuv eHJdMe4eRUeqeaIKbxnFfKwGZ5rj97jwPrkUCxio75KZhpOcDHMSgBCBtzW0XIZl gTeQYOshZQkCgYB5TK6LRnEesabj/gaL1DejrMEJcMIsGvqdceocSSaZo/4fUA5o 8YjFiJRlkrJ403ttN1h0UOJxCReSQzASlQr8Z4n2IWrILotMf6Kdb7R6YAUVgac1 fk9k/bPw+OlVujmyshbmy/w1GmzRzFlJt/Vz5w50bnULoH4XPmOfspmvBQKBgBcJ rihVzGY0eCBcQxfxuZYmxMB25BaI+1luwtcu3EVo9wvYMA2n9xtcWLLN23UncMaF 87ezswMEugeR+wrnSDezDISdkrfi8bSvqetzt/BTG8h+8DDUKk1avTaJCSwUDcmL 9gPHQfmp2uvH5X5riudpzNqLUtmSjnwurlszKzlxAoGAR8STlDJhNph+p3cF8k25 ydT1kypxnjzVG8CAV5/h3dUmc7j7gyV8NlWZfWacxMZWOBsrdVh0zhMNUPiLJaGd I1isOkmiN9JFYMMhHSnhPnTCIjmu6uBLxf8wotHAvzWOJPV7lUZbw21KIN3DS79F sGZ2QzGYn4inHG4UHClhZxU= -----END PRIVATE KEY----- MellonSPCertFile (sp_cert_file): pathname: "/etc/httpd/saml2/demo.cert" -----BEGIN CERTIFICATE----- MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= -----END CERTIFICATE----- MellonIdPPublicKeyFile (idp_public_key_file): file_data: NULL MellonIdPCAFile (idp_ca_file): file_data: NULL MellonIdPMetadataFile (idp_metadata): 1 items [ 0] Metadata pathname: "/etc/httpd/saml2/demo_keycloak_ipa_idp_metadata.xml" R2OGk9W0luNm_NtZbURWOrPlvFzSTDMimCVK5N1Mj5U MIIClTCCAX0CBgFeFdE9pDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANpcGEwHhcNMTcwODI0MTk1NDQ3WhcNMjcwODI0MTk1NjI3WjAOMQwwCgYDVQQDDANpcGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgIPeag+JJmhtAkIGBwUT/req+jKC6c0Vl1Ngtzbcd07CP9mq1DomBkjuWl59J2urlEfrV4yT8avia0eYE6Dm/TqC74SHt3TVtiUliynAh/z2JvFlLb/EbGePSKrMnuNV8rV75YGcyE12vBRooUPx3hGaygsfaSOg+BijDuCSpbVdWSdVx9VecsWJfxSochOZUj6yvm/qTb8Ptl0x/o7/b/16GgjFRIKSFrdk8pVtMn1wCzpQQoGVHZmp1jrppGcp8KXIK54q7b4pPiTzlW6xhBgrmW2RtWQesCmN8ga1CVeBZKLsaH7argwGH5Ttz31iensqUO0degFu6nwCltgTVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFx8dl6RDle65q3IHIiGaL5fbJK5HxQiMXpk4N5riWQTP4g6xoTNAG4OFFUd4uRxt2ovdEdkbzhEy2lV4x626QdEfK5V9QKppupsTxTGA/4NMW9QCocAvFSpmYErmJIhfy6zzELoBK4Dpfcc3u1peHx2686msx6ExARF116d+5Xaps1dmPPy3yb2cCKzKbLhieqv+aLLrwz657ERUc4OnqEMEmmHFhHvPI7LRlS4AQ1/s1QlKcM9yqcu8WN3yKM/kuvZtZ0YTCSIl9W1b+I5v8wNoVFB22s7rfxs3DfJFaIImaTmRzaDX0MXgibEckrkigpO+anKe9B9z8CJdtlUHco= urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress [ 0] Chain File file_data: NULL MellonIdPIgnore (idp_ignore): MellonSPentityId (sp_entity_id): (null) MellonOrganizationName (sp_org_name): 0 items MellonOrganizationDisplayName (sp_org_display_name): 0 items MellonOrganizationURL (sp_org_url): 0 items MellonSessionLength (session_length): -1 MellonNoCookieErrorPage (no_cookie_error_page): (null) MellonNoSuccessErrorPage (no_success_error_page): (null) MellonDefaultLoginPath (login_path): / MellonDiscoveryURL (discovery_url): (null) MellonProbeDiscoveryTimeout (probe_discovery_timeout): -1 MellonProbeDiscoveryIdP (probe_discovery_idp): 0 items MellonAuthnContextClassRef (authn_context_class_ref): 0 items MellonSubjectConfirmationDataAddressCheck (subject_confirmation_data_address_check): On MellonDoNotVerifyLogoutSignature (do_not_verify_logout_signature): 0 items MellonPostReplay (post_replay): On MellonECPSendIDPList (ecp_send_idplist): On enter function am_auth_mellon_user enter function am_handle_login Loading SP Metadata pathname: "/etc/httpd/saml2/demo_sp_metadata.xml" MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= urn:oasis:names:tc:SAML:2.0:nameid-format:transient Loading IdP Metadata pathname: "/etc/httpd/saml2/demo_keycloak_ipa_idp_metadata.xml" R2OGk9W0luNm_NtZbURWOrPlvFzSTDMimCVK5N1Mj5U MIIClTCCAX0CBgFeFdE9pDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANpcGEwHhcNMTcwODI0MTk1NDQ3WhcNMjcwODI0MTk1NjI3WjAOMQwwCgYDVQQDDANpcGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgIPeag+JJmhtAkIGBwUT/req+jKC6c0Vl1Ngtzbcd07CP9mq1DomBkjuWl59J2urlEfrV4yT8avia0eYE6Dm/TqC74SHt3TVtiUliynAh/z2JvFlLb/EbGePSKrMnuNV8rV75YGcyE12vBRooUPx3hGaygsfaSOg+BijDuCSpbVdWSdVx9VecsWJfxSochOZUj6yvm/qTb8Ptl0x/o7/b/16GgjFRIKSFrdk8pVtMn1wCzpQQoGVHZmp1jrppGcp8KXIK54q7b4pPiTzlW6xhBgrmW2RtWQesCmN8ga1CVeBZKLsaH7argwGH5Ttz31iensqUO0degFu6nwCltgTVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFx8dl6RDle65q3IHIiGaL5fbJK5HxQiMXpk4N5riWQTP4g6xoTNAG4OFFUd4uRxt2ovdEdkbzhEy2lV4x626QdEfK5V9QKppupsTxTGA/4NMW9QCocAvFSpmYErmJIhfy6zzELoBK4Dpfcc3u1peHx2686msx6ExARF116d+5Xaps1dmPPy3yb2cCKzKbLhieqv+aLLrwz657ERUc4OnqEMEmmHFhHvPI7LRlS4AQ1/s1QlKcM9yqcu8WN3yKM/kuvZtZ0YTCSIl9W1b+I5v8wNoVFB22s7rfxs3DfJFaIImaTmRzaDX0MXgibEckrkigpO+anKe9B9z8CJdtlUHco= urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress SAML AuthnRequest: http_method=LASSO_HTTP_METHOD_REDIRECT https://mellon.example.com/mellon/metadata === Response === Status: 303 See Other(303) user: (null) auth_type=(null) Response Headers: Cache-Control: private, max-age=0, must-revalidate Set-Cookie: mellon-cookie=cookietest; Version=1; Path=/; Domain=mellon.example.com; Location: https://rhsso.example.com:8443/auth/realms/ipa/protocol/saml?SAMLRequest=hZLNbsIwEIRfJfIdnED4syBSSEBCohWCtodeKisswlJsp94NpW9fJ5SWXsrJ0nhHs99opyh1WYm0pqPZwnsNSMFZlwZF%2BzFjtTPCSlQojNSAggqxSx%2FWotcNReUs2cKW7Mbyv0MigiNlDQtW%2BYy9pcPBchmNsnDSi%2FNoMl%2BEy0XWi6N5PFmk8SBjwQs49PMz5u3ehFjDyiBJQ14Ko1EnHHf64VM0FNFAhJNXFuSeQRlJretIVKHg3B0RbRfOUlcldAurxTiO%2B1x6bu5Alhq5qiS%2FEvEGhQWZNQhN0n9MxWVIFLVz%2Fu0oH6EKRSxYWldAW%2B2MHWSJ0ABsfAfqBD9Keq2kCas1uB24kyrgebv%2BXV9DWVpzu%2F%2B3xCuLtAWsmiVYMm32Fm1LLrlv1kByL0lO%2Ba1vermJR0%2B6yjfWw3w2KFreKaJR1L5zaEcFOWlQ%2BUI8og%2F7yHzN5LHJ1cB4con8e3nJFw%3D%3D&RelayState=https%3A%2F%2Fmellon.example.com%2Fsaml-test%2Fprotected.html&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=gBd8iP4CvbWajeMQHKOMgc5NBx7i6Kf5gXcbSa54oehMXgpPJJuwlY8BLTH861vGnl7AxaO%2F2soJPai4D96aNowm8hr9FBokjvI%2FjwdEVtRiFlng18DpEXPTE1SAa4cuxWcLE3BAZD2HZ0sW%2F91sRGnymFH9lC4cDiU1pG9OBBI1pBYxjtrAM%2FHvEjDNZ0xYTwji8S6ltrM0bBFbTdftcn5YCwI31SAFVhopbPRTfiEhanTYChbjy7h%2Fp6BHTwfvcLw4Pud98phEIhXTdK4XIJGSN%2BmCYeXHQZPyGnRGZcmfmPrEcIpptT4a5xRkltfJPHUSLnI%2Ft9QsEuYm02%2F4%2BQ%3D%3D Content-Length: 1318 Keep-Alive: timeout=5, max=99 Connection: Keep-Alive Content-Type: text/html; charset=iso-8859-1 Response Error Headers: Environment: UNIQUE_ID: WabkjcTYa6iga7y800KGZgAAAAA HTTPS: on SSL_TLS_SNI: mellon.example.com ---------------------------------- New Request --------------------------------- POST - /mellon/postResponse log_id: (null) server: name=/etc/httpd/conf.d/ssl.conf, scheme=https hostname=mellon.example.com port=443 pid: 21593, tid: 140251630954624 unparsed_uri: /mellon/postResponse uri: /mellon/postResponse path_info: /postResponse filename: /var/www/html/mellon query args: (null) Request Headers: Host: mellon.example.com Connection: keep-alive Content-Length: 15654 Cache-Control: max-age=0 Origin: https://rhsso.example.com:8443 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Content-Type: application/x-www-form-urlencoded Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 DNT: 1 Referer: https://rhsso.example.com:8443/auth/realms/ipa/login-actions/authenticate?code=qv8kqt2kFiT0YBmG8TIzcNzgxfFp6q_N15M5pS931Eo.caa7c606-3404-4961-8af9-ba27345d1f7b&execution=10aa0b63-d5d9-4960-8ad8-16720df6fc8e Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.8 Cookie: mellon-cookie=cookietest Mellon Directory Configuration for URL: /mellon/postResponse MellonEnable (enable): info MellonVariable (varname): cookie MellonSecureCookie (secure): Off MellonMergeEnvVars (merge_env_vars): (null) MellonEnvVarsIndexStart (env_vars_index_start): -1 MellonEnvVarsSetCount (env_vars_count_in_n): On MellonCookieDomain (cookie_domain): (null) MellonCookiePath (cookie_path): (null) MellonCond (cond): 0 items MellonSetEnv (envattr): 0 items MellonUser (userattr): NAME_ID MellonIdP (idpattr): IDP MellonSessionDump (dump_session): Off MellonSamlResponseDump (dump_saml_response): Off MellonEndpointPath (endpoint_path): /mellon/ MellonSPMetadataFile (sp_metadata_file): pathname: "/etc/httpd/saml2/demo_sp_metadata.xml" MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= urn:oasis:names:tc:SAML:2.0:nameid-format:transient MellonSPPrivateKeyFile (sp_private_key_file): pathname: "/etc/httpd/saml2/demo.key" -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHUXILDtuMxyFP 6OW81hqfj9z+gC7sxR6U8bRPI4gFiNjkNF+4HMyrKtVtfwA5WJ1tJ4IaZbg4fGap iCcWQ5tjjuZ7CpZ6XLekq63OGrkgZxNm8L+AjvREv78iR4Myc4x6ybN1LwQnV+oD F0i9SAEPnfJCaCA+1+DC3RKy7+xtK3SVimUWwAHmkNU6N0wCVSNYn1KgnxBSCPTx 2DifbyRl21SiokCfLSnktE++WBzRzldSxzMrkSfFoS7/uT232tbdw2NsJmHGLRGk myBrEC6BVhQ746NM1cLDtrOots7bNfZ3yfb3W1tpoKVOhM2se3OTKdidAQsaGlVu 7QIhBqinAgMBAAECggEBAJtU662WfJ9vqJRgCnpp2QG02iM0vl0jGbw1ybFLHXxC s9TUxCv1tcNHdGEK8p++YaFpgskTsMfEmzVPuDZvpa+m9BO7op3ll/CrIp5W0SNh cQtuX6/OuKrDTC9oz+QHjNk8S7DtXS1UJDkYckWg0cLb8qqx/z86eh0isKnmtLg2 H1+6L6mB9fcZldkcrU+kXT+dcDX85skMZAeBsrG4yaoX26AzVl8lEl2rJAQvpxj8 5wGBC4riWY6TzMYiCjcS5JfZIlbhcZe61ej3A48NVBSKCP1XKo0xbKuOHIQuMeeW wSaboBwRzJ9JdTXlq5UWfLvmjXDc/HCwk/N7cj021uECgYEA5KkQr3cSKrMIkYoO H0Vnkw1kYYGUjtTL00Nfdkv7uGMF122nyt1ND0gpdS0jgNx3LSEam/gY35UWEjGH i8DGD04k8pvKDUsl8cuYPcC3oce1lLCGAnw+aHPC5wtA829CLOLtBfxXIhVAI0tp ECosD/A63/m1LC19XolOd10/PC8CgYEA3yZChEYZupDGJFZltiy0ZgUarvD2Ss4N QiRVR+CmpBrOKZdD8q6uUuqWQN9Rw7kXm8LxAPYwiIDVjxjYALF+j7/9Q1oZyKuv eHJdMe4eRUeqeaIKbxnFfKwGZ5rj97jwPrkUCxio75KZhpOcDHMSgBCBtzW0XIZl gTeQYOshZQkCgYB5TK6LRnEesabj/gaL1DejrMEJcMIsGvqdceocSSaZo/4fUA5o 8YjFiJRlkrJ403ttN1h0UOJxCReSQzASlQr8Z4n2IWrILotMf6Kdb7R6YAUVgac1 fk9k/bPw+OlVujmyshbmy/w1GmzRzFlJt/Vz5w50bnULoH4XPmOfspmvBQKBgBcJ rihVzGY0eCBcQxfxuZYmxMB25BaI+1luwtcu3EVo9wvYMA2n9xtcWLLN23UncMaF 87ezswMEugeR+wrnSDezDISdkrfi8bSvqetzt/BTG8h+8DDUKk1avTaJCSwUDcmL 9gPHQfmp2uvH5X5riudpzNqLUtmSjnwurlszKzlxAoGAR8STlDJhNph+p3cF8k25 ydT1kypxnjzVG8CAV5/h3dUmc7j7gyV8NlWZfWacxMZWOBsrdVh0zhMNUPiLJaGd I1isOkmiN9JFYMMhHSnhPnTCIjmu6uBLxf8wotHAvzWOJPV7lUZbw21KIN3DS79F sGZ2QzGYn4inHG4UHClhZxU= -----END PRIVATE KEY----- MellonSPCertFile (sp_cert_file): pathname: "/etc/httpd/saml2/demo.cert" -----BEGIN CERTIFICATE----- MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= -----END CERTIFICATE----- MellonIdPPublicKeyFile (idp_public_key_file): file_data: NULL MellonIdPCAFile (idp_ca_file): file_data: NULL MellonIdPMetadataFile (idp_metadata): 1 items [ 0] Metadata pathname: "/etc/httpd/saml2/demo_keycloak_ipa_idp_metadata.xml" R2OGk9W0luNm_NtZbURWOrPlvFzSTDMimCVK5N1Mj5U MIIClTCCAX0CBgFeFdE9pDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANpcGEwHhcNMTcwODI0MTk1NDQ3WhcNMjcwODI0MTk1NjI3WjAOMQwwCgYDVQQDDANpcGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgIPeag+JJmhtAkIGBwUT/req+jKC6c0Vl1Ngtzbcd07CP9mq1DomBkjuWl59J2urlEfrV4yT8avia0eYE6Dm/TqC74SHt3TVtiUliynAh/z2JvFlLb/EbGePSKrMnuNV8rV75YGcyE12vBRooUPx3hGaygsfaSOg+BijDuCSpbVdWSdVx9VecsWJfxSochOZUj6yvm/qTb8Ptl0x/o7/b/16GgjFRIKSFrdk8pVtMn1wCzpQQoGVHZmp1jrppGcp8KXIK54q7b4pPiTzlW6xhBgrmW2RtWQesCmN8ga1CVeBZKLsaH7argwGH5Ttz31iensqUO0degFu6nwCltgTVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFx8dl6RDle65q3IHIiGaL5fbJK5HxQiMXpk4N5riWQTP4g6xoTNAG4OFFUd4uRxt2ovdEdkbzhEy2lV4x626QdEfK5V9QKppupsTxTGA/4NMW9QCocAvFSpmYErmJIhfy6zzELoBK4Dpfcc3u1peHx2686msx6ExARF116d+5Xaps1dmPPy3yb2cCKzKbLhieqv+aLLrwz657ERUc4OnqEMEmmHFhHvPI7LRlS4AQ1/s1QlKcM9yqcu8WN3yKM/kuvZtZ0YTCSIl9W1b+I5v8wNoVFB22s7rfxs3DfJFaIImaTmRzaDX0MXgibEckrkigpO+anKe9B9z8CJdtlUHco= urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress [ 0] Chain File file_data: NULL MellonIdPIgnore (idp_ignore): MellonSPentityId (sp_entity_id): (null) MellonOrganizationName (sp_org_name): 0 items MellonOrganizationDisplayName (sp_org_display_name): 0 items MellonOrganizationURL (sp_org_url): 0 items MellonSessionLength (session_length): -1 MellonNoCookieErrorPage (no_cookie_error_page): (null) MellonNoSuccessErrorPage (no_success_error_page): (null) MellonDefaultLoginPath (login_path): / MellonDiscoveryURL (discovery_url): (null) MellonProbeDiscoveryTimeout (probe_discovery_timeout): -1 MellonProbeDiscoveryIdP (probe_discovery_idp): 0 items MellonAuthnContextClassRef (authn_context_class_ref): 0 items MellonSubjectConfirmationDataAddressCheck (subject_confirmation_data_address_check): On MellonDoNotVerifyLogoutSignature (do_not_verify_logout_signature): 0 items MellonPostReplay (post_replay): On MellonECPSendIDPList (ecp_send_idplist): On enter function am_auth_mellon_user enter function am_handle_post_reply Loading SP Metadata pathname: "/etc/httpd/saml2/demo_sp_metadata.xml" MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= urn:oasis:names:tc:SAML:2.0:nameid-format:transient Loading IdP Metadata pathname: "/etc/httpd/saml2/demo_keycloak_ipa_idp_metadata.xml" R2OGk9W0luNm_NtZbURWOrPlvFzSTDMimCVK5N1Mj5U MIIClTCCAX0CBgFeFdE9pDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANpcGEwHhcNMTcwODI0MTk1NDQ3WhcNMjcwODI0MTk1NjI3WjAOMQwwCgYDVQQDDANpcGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgIPeag+JJmhtAkIGBwUT/req+jKC6c0Vl1Ngtzbcd07CP9mq1DomBkjuWl59J2urlEfrV4yT8avia0eYE6Dm/TqC74SHt3TVtiUliynAh/z2JvFlLb/EbGePSKrMnuNV8rV75YGcyE12vBRooUPx3hGaygsfaSOg+BijDuCSpbVdWSdVx9VecsWJfxSochOZUj6yvm/qTb8Ptl0x/o7/b/16GgjFRIKSFrdk8pVtMn1wCzpQQoGVHZmp1jrppGcp8KXIK54q7b4pPiTzlW6xhBgrmW2RtWQesCmN8ga1CVeBZKLsaH7argwGH5Ttz31iensqUO0degFu6nwCltgTVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFx8dl6RDle65q3IHIiGaL5fbJK5HxQiMXpk4N5riWQTP4g6xoTNAG4OFFUd4uRxt2ovdEdkbzhEy2lV4x626QdEfK5V9QKppupsTxTGA/4NMW9QCocAvFSpmYErmJIhfy6zzELoBK4Dpfcc3u1peHx2686msx6ExARF116d+5Xaps1dmPPy3yb2cCKzKbLhieqv+aLLrwz657ERUc4OnqEMEmmHFhHvPI7LRlS4AQ1/s1QlKcM9yqcu8WN3yKM/kuvZtZ0YTCSIl9W1b+I5v8wNoVFB22s7rfxs3DfJFaIImaTmRzaDX0MXgibEckrkigpO+anKe9B9z8CJdtlUHco= urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress SAMLResponse: https://rhsso.example.com:8443/auth/realms/ipa https://rhsso.example.com:8443/auth/realms/ipa 8OlYuzdYW/pu0OoNp9XFxRi1EETiNMZkgUpWOUJ/dG8= IuBZQ38BqtUc2Qbyy9BFj3x9BCfDQcoEoGFgOa//GFEwzOxunU9OxyZiod6cr1Z/0WgPeabX1GHJUwv//kf22FA8VLC1afErY4Yis9eUQUFUETyFRoFHjpou/rGz0NsEw4y8nTPN6p1je6jDM7Fo5cfSY8og6MY4rUdKOF2/rCCoFRJUC/UIFf8mUmOh8UIUNzPZqDc02Rw8nmGP2eHhHpWjDBalmzt+EL66tVqco3kRtQmPSIomkkJGh9vMGmHnu6n3k7I6SX8/h4bzuPurFB3eGhlxn9WKMQOdGphq4KKssij6yyZq2cM2fFLvZkrZSfLsDKjrpsf2YLC+Cmy5Ng== R2OGk9W0luNm_NtZbURWOrPlvFzSTDMimCVK5N1Mj5U MIIClTCCAX0CBgFeFdE9pDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANpcGEwHhcNMTcwODI0MTk1NDQ3WhcNMjcwODI0MTk1NjI3WjAOMQwwCgYDVQQDDANpcGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgIPeag+JJmhtAkIGBwUT/req+jKC6c0Vl1Ngtzbcd07CP9mq1DomBkjuWl59J2urlEfrV4yT8avia0eYE6Dm/TqC74SHt3TVtiUliynAh/z2JvFlLb/EbGePSKrMnuNV8rV75YGcyE12vBRooUPx3hGaygsfaSOg+BijDuCSpbVdWSdVx9VecsWJfxSochOZUj6yvm/qTb8Ptl0x/o7/b/16GgjFRIKSFrdk8pVtMn1wCzpQQoGVHZmp1jrppGcp8KXIK54q7b4pPiTzlW6xhBgrmW2RtWQesCmN8ga1CVeBZKLsaH7argwGH5Ttz31iensqUO0degFu6nwCltgTVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFx8dl6RDle65q3IHIiGaL5fbJK5HxQiMXpk4N5riWQTP4g6xoTNAG4OFFUd4uRxt2ovdEdkbzhEy2lV4x626QdEfK5V9QKppupsTxTGA/4NMW9QCocAvFSpmYErmJIhfy6zzELoBK4Dpfcc3u1peHx2686msx6ExARF116d+5Xaps1dmPPy3yb2cCKzKbLhieqv+aLLrwz657ERUc4OnqEMEmmHFhHvPI7LRlS4AQ1/s1QlKcM9yqcu8WN3yKM/kuvZtZ0YTCSIl9W1b+I5v8wNoVFB22s7rfxs3DfJFaIImaTmRzaDX0MXgibEckrkigpO+anKe9B9z8CJdtlUHco= oCD3moPiSZobQJCBgcFE/63qvoygunNFZdTYLc23HdOwj/ZqtQ6JgZI7lpefSdrq5RH61eMk/Gr4mtHmBOg5v06gu+Eh7d01bYlJYspwIf89ibxZS2/xGxnj0iqzJ7jVfK1e+WBnMhNdrwUaKFD8d4RmsoLH2kjoPgYow7gkqW1XVknVcfVXnLFiX8UqHITmVI+sr5v6k2/D7ZdMf6O/2/9ehoIxUSCkha3ZPKVbTJ9cAs6UEKBlR2ZqdY66aRnKfClyCueKu2+KT4k85VusYQYK5ltkbVkHrApjfIGtQlXgWSi7Gh+2q4MBh+U7c99Ynp7KlDtHXoBbup8ApbYE1Q== AQAB G-e292fc24-74d9-4979-9f81-2c26d85174de https://mellon.example.com/mellon/metadata urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified ipausers openstack-users view-profile uma_authorization manage-account m5GHJo1Za6Aejg0O7ZpCZleaNS45qzST8/0qFORoGjfLyNm0+lcquA7L2j/puCuVPWJ2jS4Y8P62iWYbhnATo7IPO3srpcfrAZaPtVlDKVp0ra8knx73h7NNIrbkWS63TZZNRh5OU5MgJRShHEpKq8fbRanRlq1iLODveKgw64W3XYr7vXbVT26/2NrrA7bkPLbK7YUyglmYDHN9PUbEKxKwQiBLnKNvaQgvBZaBPTzYTiBidM1Jllp/mj8QzfwiXCIAS/28iVmDCmjkSN6j33nDDLYU/Cq3rIGOWI8uQiw2oh2PlZi49T3400ffz2L9Tucdm/kjkTbhwWB3b5A1YQ== 9agVoiDjgNINUO8G9L+D2vodXqbGgFyWfz2qmHTMEZLqmacSqNsX4bzyS+jE1yA9PR4vZXJsc8JwyQrFAyKcokbJHlIbB+BSUHaqd/5qH6BbA+mt48Er9aExwwEC0IHloIo8pg5WuNZ38+RATP5OVw+du3ZUQ22GWv918YaEOb7i1n36eNYiP9Fv+fCR6jtRaZBqFf/vvROv6ye1dVfDOhhZfflVBbhRKUTgng1eFIxuz/+BG8v+QnN1Hzo+4OMIgDxb4LuS9LSgJBpAyb/nazSJ6Qzmi1DNj3Mp4co/1iMVTdshF1gAOUBm0DWxCnH7pMX48VLMOqbhR1YOQrDUsqPBf/wFFbJKItUXCHi8U29tJnUaDfjfOMA8RRsfZ9wZ8mt3xAmeR+13VcZWgvTYD4Cjw8dXmFqQ5D71HtBGaVJzd9YCxsEDEMAmtkyeLrkBDQmGb6Jl5kR2WV22ZZcz885aIGwUYpG7u7K54O5/Y1s7dtKq9B7rnDv2yhNwm2LSlF59irPPiqfevM7B8uNuMXe8MVDde10f+d+tGyn4MdP1cim94UVThq0IBciiz28NMQBiI+q8tPmgCAqn3cgRx7u1hWZGf8q+v0bh5Db1igKrXzCNbOpD8kCkKv1nQEckilp8/WZjCH9LC7cLDXHfKorAWcy2qr6g/9XMdQcISXFGs9CZksXIt7lQl1ZUGrDKT3ejuWl9+kOD/H/ryCru+CbNl8UvIkPpiXRtPlYoaddhOqdfPA5xDdRpEbxpLjRQEOYFF1Sygub1S300ZJiOKa9py9bbsSSexBLDaK7qCH5f+tJt6fCLUkb9Eq0NCbwNQ1BLgc7Uhvhtb9+V4sSH0FQb7RxO0BSmaG1ZESCmTkwOra1BUgIISBQWI1YauzgeczypP3yFBVRXCIMxuPqi74hwwxg36VUc9JqaiUqXt3BFuHEdVMzkOdYuQ8FK6vxl2iV37oiG8yNbf9a32H2yR3v3Mcpz9ybicUKNTjSCJNP4ixCc9/+O8FdHwVIV6GcrPhlTbxjRdih0tWodcGYNqWt65YBNYsyExpOW7bS/nVaov/hMmWCv9FSg8/KK/ipAQxz2E/9V+SH5fv0KLfTQaaSy13XYBINm3xNcatr8xz6WXNkmf8AeHxQq8bSl/sAOeQnjn5H2S0s014l3ZfZ84RI5rI2bY5k7xWbnoCFr6e1jcA6UeKHB4zqes9azGxZ4A48VND1lNFUKt5XSxADLQjEH0nGxZczJMeYzHkrVqiUgtlPVxgDjm7TxfJDJGPumprmRxU3MYY8fr9E+3CCwAz9WJOxFdu1G/4cEo6zkMHhSmDddmSGcGGALbMFlMD0g0/o+P8GQ3pVEXd88bUH0U1Joanp0mJ9Tu6gS5LatPhAvbD6ucWKDvHXqRKYWy/N7txfSBrYbWkPcZrGesH3bVd8uWMgH61yg30Uq+K4/L9u9Wdtlu92aM/NTjNERGyXdxp+ZYrM3MEthnW8Oygt3/XI8stzml+HmAFvoDgpjTpkbY3prdu5Y+2FMCZVrq/qskpbT3n9aLjLnJVASzZUzwy/SuqzikH45Rstdm1himRksdG9cg2CIpQktXlD5cLfkTCfBuUYlr+MHYjOu8WMBNCXDeNdRgC9v/M/bqalQtOfGYlkK9NPpB8Ky2cKtykWdLDWdefbzXHM1Y2LEl0+ms+b0oZYe//61u8uvsjn5OpQ5/aNTM+ztQ5OsEPzn0skAkJONz6rD3YSZJuEp8pWHHEZkT4pF7+0aXiEJZfOrWSJPM7NgAjXIK93qomjZjHl6s3JodJzdycFSWeLdQjrIatu2PKhm1cSlwfhVhuumh7hMnDOyTdWNTgO2QH+y0ahudAviKYzHRIS9ad2103sWByxpRH4vRtVRcCfWPmQhtygn/3Gd2wHc2owXW9neUBASQJrNUIi3uDklftVAZwhMRCm6nGU5u2R7ywH1K2Ey6JNBp88bh4gn9dybFzYjpj9Ds+sCkqnm4B2O80x307vvKcQ/O5AEzF9ONFD5aypOVXlI1IAMSGraBA0oWeBeE8J6wTB0W3BSvF5PhuJYewu6pxrR9WEykkhJzN59fuOgjz6piJbgTjKSD1LYf4/ev1MxkfPOj611CF7XhhuZW94/3i0SCFTdkJdTXS4oepqBEsVYlaWGCg6Sh4Hznavz+LkeR3Z5aDc4KJfa2jTESxmFkIFgSglDq8TA+1XwCZo2mze2SjpPLEt5ZiIpX4F+RHiPZqikzshzVAeHoKH5rGOUx9/os+Fbvt/EWdpLkeMDRAGC4ptx76wBfrPJlH3k4LDBy8O7iIuwkdQ/XuV74wZSneW+jqpN+oUX+UcCnP6Hu8lZW/7Ip61i2cG+uD1hv0W/YF/9pomBQ2rEQ0hJVElJFpzpz38HriUbRkPiZyn467xec3GFm+8f9XINX8bcf4gmauV8f1hdfESegw0+pMdYFE6a62sWLJixCdOiZbkmkYXRiO7Bow8cCHLj8WKHe8EaUWuVOrJGvw8z+k5cYY0Opg6wG8bKN3Aeg8NFxr5RC9RFKt+3sYza28uCma24G+yHVPl6b4X04+q28oS6EmxW20VTz+uQGjAKxwB64aNr4lj+dqY41ZgLPVtrcrUpNtdtiqMKyIlyMVkNh24xEjCSEJ9RFDs1K4/GJa+1v2eM9QPA5WsN4kMIx5AgS1MIgYIL98tMH3CbXOjQTcGL2RbJDiICUQgXXLNWe+dDWfE1Lc8TGIHRZ6GmrYgF5yQx7PL7y2ZQblMY6nqEKvNPXcbVCSHWu0B15ZfEh/tpcuPWIU04Jq3KuVeEzJ/l3OsE+1jb9j+87ZjON+bezmxv6MHeziSKVoI2GJO0KL/HPTI/5RAMBVDdO0AkgLpDiatnrBvCUFXsvTVZt+h+fqQtjoTxaMmdlsK071RHqk47/8jXx1uFfA8LNzZsdZzqrQJXqh0paLmx9Sngki+TRgPfCzdpU2RNYXqn5qvaEn2dMCWZrIUMCHnHbNNqc4M0YIw869Nq/d35Wc+BDYDdDstkYaVxIV0oQIHaFb0Ewb96383kRJCKC1Gl9NIMwlxC+uJruXN3ApkyP731Mwkq7RX8sv6NI5h3UYhEwrlmvtnNlQ0S01W7qhbMfuEULT2sHc/NvFSMCtZAa2ipU+tSiaBpmElpZGyKJByF2XrxQDly84koxKQIR+vg7tabaRKQL4aIJQclRj1ewVei4iTE8NMML+ys4sqHYuH63e6iO6lBt306j52lDNBIV0ErLpLc5gDKgpfn8PbKcxkMc8v8pqCnZ48MVseNSRgkWKP2y5K6sZ6j2aT1i7/WHl3q0X6y5gTvDKmhKHzdEXR7cAdAfNZouh60dfSpGuN0y4cDn8y7nvzzW5w2TfAFV1SvwaBjIKqKMM3o6ZlEasqvcn7SwWtmViPJtlXvYNnM5idT4mR+r5SqfOqF3AQjKVG6fkaKVrucmZtWxiLp93CHdv4zRafwXr9RePjgeR9ix9cnb3PH+qbGIxYLt13gYpwj7eC7kYWKkHbVw0QLf6zm8loXFvXcQhnYbDb0xZDJdoHkBSBiEyoEQcr4Ps86XQHOJX1ez4GQ5d+fuNTkzoI7mts5sV/HW6kfqd/FMfHYH5D//2hYaluwhHXNhMDqHP0o7vLcg3zgRkhNV/csWcA4Vp8v2e7RE+13oLmREbtRa8mZmqQilAWzTjQAsM8Rp2CcoJGfRrdnZCS3t3ukrSNqJEyHjcreIRee82U+hhrdRsoj6kyVlLuhfv3PiNHHO7ockdwKPW/DNxKGii4qUCpkhaSLnsepXCWDk3hZLsS52WEqmNwA2x1y+KYsMiHUh7kYv5iSYWRyjY/eAWAS/dDcdzg7W0JFiefOb4CiHZvLsfv96GOqu6tQB2pb6nkhVH2CMRsCiZK0OqhII+AmB2RMqpbxvw8l5Vml9G4y+VLTlhwIp92bZ8nHI7j3khqn8dUGf+ZHAInt7XujFlP/p9uy2oV3dEPTPVZGxuQJgfNbGYYF5pUNotp831FL4MA4sUdLXa114JrRo9bWnDOXBrR+04ZUtpfaBehmKxMIVufy81fe2g7heBd/yoeRAkZAw6oUaLcgAWcGAUdR/Ozy121yCdroeeLqbm2nPlsGwDYCSwl0UhZKpYTAs16vvyata55LRPWjsHNs0tPmYtZQk/IOaV5q61Wt2ehsuqXnGmEkgd7Dr5kOokGjV0PRpC1P9NQ88WWeS/MeMGX1e0Ac8FYkJxpNwcdlwBwaKIMrOTrmOfuEibORIzTlh9mJQ99pJoL+FrE0S6Ye6UjAuwixvAgo2rY1JzeXkGv+x9hxBwtYkgY17snKT3lgv6aJxX25rx44EfqLnkAKqI3P/ZCCzixMcdGYILxB/7JAIAaoSNloTqEDyHUdpVpY/4wPMvVajn2O0WPEyk6CQJbzesjuF63Tz1igMqRY0YdJu9J1xRPqOo3m//1fS1rW3pofMCPY1FUCLQShL346t7u0OijOjSq/z3pfdWJ6DuLPAgWO4iLjAew3Qsch/KcDrSayEmwKo0fHQJEQr/EqYSebmRxUDqNkewEoDt85qZcLMu9kofbJRx7yItS/9Gbii1xJV1v9FghE9uAz9KM2DBxlt2oN7JbxbJOMm/h5aLLHTZpw4vm//GpUlZVrZlNJhJNw2QeLgq+zawDOulgUpuU+/G46H0uejnb4UHo/jwdOpEXnY7T5Omm9g1YPS22nzCxA4ile1j0fouRXfPpDcFkKsiIOj8feXoZahJSuxCTSQLrfnPpoacqtPCXx9Ons4V47TN3MsQvv/JBUxXNUhJPTI2d+SfZWHaqg8FoiLWFfRE8h24lsckxwYB6t1o80s4sQfqzUOC3nMl2/t+9d8Wvd8aa/S01lucD1Uweg4X6znzF+uNBm22FPaXWGKJar1OJeWxwDi7gx9Skco+rXWPNr+EWW7gLFzYaFMUdCwS58bGoMR3YCM5UnKUlVgVrlJzIKY1MvRdd+e2li7+LVssNkh2uzLBIlRH329rGLbkdtoRJ+3w67D8enw60xvJuh/unfEAA/81CfN+VH10yV3RvHtCgd+eFeUCh4wB2EKGXHEzufuvhEzzqJuu+nJwaKwAYzJBM4T9QoxZzmM5MUmqfc9zhaWWzYty+oBrca79lp+CL64EB+TLIKxf7WWt12aSHye9rNrSxSnkGR2b+plQWKMxN4lQWX0CgZDqim9BuItWgBNWUwMFwg9U0VZ6v+B3uNcAhkQJVSHurCY/ADkhkqU+j2HnNGupwAYNZv9W5/UuAD8yFpuP+JhtnTz+TGQzuy+B/cZcibZhFG2i5bAqrjxPGJQT/JNxQt8WkipdQfUdCNFhdh4HwD44RxKtBenpR51XXo5cbaQlhETFmaZzED9qf73OI5N6bPFv30WcqZpWEerWVT9hz5OuwTkIqKvtQgzBd/4qaAbMdeYb2F/eTxytKWYGbnarCcrasdkQS5IJfLYb2KyTm3hMc3EM+3/kF5OGszQQ1EaPQcsyI7GVmogRfNbitQ+qPTDjGJnotwwc5aLJpIa8pVeiFFCBfsEdqA5qIbS2NBc0A/Uv8e53L+fZy0uPpZfq4F7GO+qU/efa0+aVvW/aHJcyFJXvQHb2h0XjP7ljbfQB/5ypcP5cXS12at60w4hs7oJDfcQ30JHmeMIiZ9t2+bQwOn+M22KiVIHbjNRGUPfs1jwu5Cid69pqcRtSg2fxfpAH4h5B5PHybmeo2Q5kMMS1FS2ZUirwwfY9fk+q0pSLwq5fMfUooVxPXBgqtPYY4hMWU6A8a9cbUNsJZBSZi5mP1+1xRGAQ62xUCqOgGJt922/5YI2QNf9cWcKuFlsD2+RYLNgFHZK7OzCns8t2VaQZw/6rFURZKW+wfDY+bzYjmn/FgLHOGp5YxBhzwvIcOEAltqC3wWF6c2ehnZGUH3E+JVvaB8mDG1bWf9Gxby9cjKSIUcIS1CTxfboSSx3yYhr8qONLsYKkNZoVnAHks33M5RduRv8XVo51PnqVp7GENpyy2Q3tmQoGAhLz401vI3jhDGhXB7TFqCnQfN4VDnb3z7WuFVGz8P4iMdlIs7eDOM0NF3TgwFxoK+QdQh2DYP1f7hx6kXXON+/Bwo1II8VewMBPyS+/vRbD+zihEoVtuEG0k0h1oyfXuavmkzCgXr17efq+J8FC7FRT4npwUbqSyEOGW1fXtGYqUGeirL3SVGshmehujovVTKkJ0g8Vy/t5i5JKGXxxfhKxH151IrTIZ5oyDbL3ZbJ+b0OrgOS4HDPDBKB5RYqv8N5s8Ylnk0piKrk3nvnYA6j1V7Xu+HvwmfdrdpTcfWFMmy6K5cmxZWQmmS6uNUWBrfa303uMJmAjoZy9U/9qg1H/G+/TcTF7FhUZ+FdAUUGIns94Pjl1D+qeXA61TDRLtV40QHyKgcGXO0GBMBlOjIxdhNDdDMoqt9+xFoAml8Ue89hCYeDXDtPg+/T8a+/C+5CQ5FkD7ZEPAvaEYYeWUGC/HPAdVohtjnhTIigCqsil6+AxOFKaxV1R3Y2H35X3QDnCLow5MNfb0ck+7RtkOhIMqaDLWbF4/OcmNuW91Jchk6INIzLiwZnlSrBIK8He9SrhoE0Tz4+5dAqdvXCZu8IkWg31fxsLDutI9Fa0iFqaSrda/YVIEyp8qlMy2V3GEsOjhX/sPth4YAhTg2SgPQ+ncGrUCHtTCMpNJMqScJbeIWX2NWLsLOiSALc6WCQgALbm+AkpXFxCrxBcX77gENU7bMiSOqW45k1bHj0iPS737CMQ8KRMmdhZb+KNIbgk5HJZRxhPNWIQEub5tjyIxw8TiPN0IXsS+pLcTWFur1I11nplJMdIaho6OOycn/ww6uMsU/yTvBLDPTtHKK4ZVQIr4abDySfX915cjfGx61pTsH5Hs8hDFoqmUh3UUr/8NF9aMpuqZhuOYq2hi5DvIMiS8HQYGzW4GO4wE4fP2/nRZXecYOvVQi+FWTKwXtA3OduMotKj29QgCvRYXMRIyXkz4Mh+UvfUlEYtvy9NZaNboHTtoIJemQ2LCakIFxp0zk69sMsFm5c3hBgCxIXKVmJNCrO565/VA1kuP1qoJhekxBvyo85qSJfjCE4UsX8HcD+HXZqmYDkfbskcLwQkIzYLQus8+oWtRSIrOr1iGR1vTktq1WtRVZ+Af+alslzBZLOBcaNqx0XZRBdjZ4m2pSvlKihSn/9Za0iASX/oQcUyQ2xV6Bw843wONbEqB8ulXnpLI2RVvSdzBpj+yopD+f9TXZJf/qyNNa0sb+q/VTcG60I9xJ+S/pFnS/zIlHajuaS2heekN36nNP9i9q2nrBjMRb6bMF/sL/VBTACIsBxVaI5X1buB+8GqyGhUI0mbohmA5EnM7xOkcwGEdAi9pT9Zku1KWyk2g1nNbml1Q= am_new_request_session id=9cf3ebac4e542827e276dc064ce8c4e4 cookie_token="Name='mellon-cookie' Domain='mellon.example.com' Path='/'" am_cache_new created new session, id=9cf3ebac4e542827e276dc064ce8c4e4 at 2017-08-30T16:15:23Z cookie_token="Name='mellon-cookie' Domain='mellon.example.com' Path='/'" am_handle_session_expire failed to find Assertion.AuthnStatement.SessionNotOnOrAfter add_attributes name=groups value=ipausers add_attributes name=groups value=openstack-users add_attributes name=Role value=view-profile add_attributes name=Role value=uma_authorization add_attributes name=Role value=manage-account [APLOG_DEBUG auth_mellon_handler.c:549] The current LassoProfile object doesn't contain a LassoIdentity object. === Response === Status: 303 See Other(303) user: (null) auth_type=(null) Response Headers: Cache-Control: private, max-age=0, must-revalidate Set-Cookie: mellon-cookie=9cf3ebac4e542827e276dc064ce8c4e4; Version=1; Path=/; Domain=mellon.example.com; Location: https://mellon.example.com/saml-test/protected.html Content-Length: 258 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html; charset=iso-8859-1 Response Error Headers: Environment: UNIQUE_ID: Wabkm2fzHRm5EyVgPZnqEAAAAAU HTTPS: on SSL_TLS_SNI: mellon.example.com ---------------------------------- New Request --------------------------------- GET - /saml-test/protected.html log_id: (null) server: name=/etc/httpd/conf.d/ssl.conf, scheme=https hostname=mellon.example.com port=443 pid: 21593, tid: 140251630954624 unparsed_uri: /saml-test/protected.html uri: /saml-test/protected.html path_info: filename: /var/www/html/saml-test/protected.html query args: (null) Request Headers: Host: mellon.example.com Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 DNT: 1 Referer: https://rhsso.example.com:8443/auth/realms/ipa/login-actions/authenticate?code=qv8kqt2kFiT0YBmG8TIzcNzgxfFp6q_N15M5pS931Eo.caa7c606-3404-4961-8af9-ba27345d1f7b&execution=10aa0b63-d5d9-4960-8ad8-16720df6fc8e Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.8 Cookie: mellon-cookie=9cf3ebac4e542827e276dc064ce8c4e4 Mellon Directory Configuration for URL: /saml-test/protected.html MellonEnable (enable): auth MellonVariable (varname): cookie MellonSecureCookie (secure): Off MellonMergeEnvVars (merge_env_vars): (null) MellonEnvVarsIndexStart (env_vars_index_start): -1 MellonEnvVarsSetCount (env_vars_count_in_n): On MellonCookieDomain (cookie_domain): (null) MellonCookiePath (cookie_path): (null) MellonCond (cond): 0 items MellonSetEnv (envattr): 0 items MellonUser (userattr): NAME_ID MellonIdP (idpattr): IDP MellonSessionDump (dump_session): Off MellonSamlResponseDump (dump_saml_response): Off MellonEndpointPath (endpoint_path): /mellon/ MellonSPMetadataFile (sp_metadata_file): pathname: "/etc/httpd/saml2/demo_sp_metadata.xml" MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= urn:oasis:names:tc:SAML:2.0:nameid-format:transient MellonSPPrivateKeyFile (sp_private_key_file): pathname: "/etc/httpd/saml2/demo.key" -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHUXILDtuMxyFP 6OW81hqfj9z+gC7sxR6U8bRPI4gFiNjkNF+4HMyrKtVtfwA5WJ1tJ4IaZbg4fGap iCcWQ5tjjuZ7CpZ6XLekq63OGrkgZxNm8L+AjvREv78iR4Myc4x6ybN1LwQnV+oD F0i9SAEPnfJCaCA+1+DC3RKy7+xtK3SVimUWwAHmkNU6N0wCVSNYn1KgnxBSCPTx 2DifbyRl21SiokCfLSnktE++WBzRzldSxzMrkSfFoS7/uT232tbdw2NsJmHGLRGk myBrEC6BVhQ746NM1cLDtrOots7bNfZ3yfb3W1tpoKVOhM2se3OTKdidAQsaGlVu 7QIhBqinAgMBAAECggEBAJtU662WfJ9vqJRgCnpp2QG02iM0vl0jGbw1ybFLHXxC s9TUxCv1tcNHdGEK8p++YaFpgskTsMfEmzVPuDZvpa+m9BO7op3ll/CrIp5W0SNh cQtuX6/OuKrDTC9oz+QHjNk8S7DtXS1UJDkYckWg0cLb8qqx/z86eh0isKnmtLg2 H1+6L6mB9fcZldkcrU+kXT+dcDX85skMZAeBsrG4yaoX26AzVl8lEl2rJAQvpxj8 5wGBC4riWY6TzMYiCjcS5JfZIlbhcZe61ej3A48NVBSKCP1XKo0xbKuOHIQuMeeW wSaboBwRzJ9JdTXlq5UWfLvmjXDc/HCwk/N7cj021uECgYEA5KkQr3cSKrMIkYoO H0Vnkw1kYYGUjtTL00Nfdkv7uGMF122nyt1ND0gpdS0jgNx3LSEam/gY35UWEjGH i8DGD04k8pvKDUsl8cuYPcC3oce1lLCGAnw+aHPC5wtA829CLOLtBfxXIhVAI0tp ECosD/A63/m1LC19XolOd10/PC8CgYEA3yZChEYZupDGJFZltiy0ZgUarvD2Ss4N QiRVR+CmpBrOKZdD8q6uUuqWQN9Rw7kXm8LxAPYwiIDVjxjYALF+j7/9Q1oZyKuv eHJdMe4eRUeqeaIKbxnFfKwGZ5rj97jwPrkUCxio75KZhpOcDHMSgBCBtzW0XIZl gTeQYOshZQkCgYB5TK6LRnEesabj/gaL1DejrMEJcMIsGvqdceocSSaZo/4fUA5o 8YjFiJRlkrJ403ttN1h0UOJxCReSQzASlQr8Z4n2IWrILotMf6Kdb7R6YAUVgac1 fk9k/bPw+OlVujmyshbmy/w1GmzRzFlJt/Vz5w50bnULoH4XPmOfspmvBQKBgBcJ rihVzGY0eCBcQxfxuZYmxMB25BaI+1luwtcu3EVo9wvYMA2n9xtcWLLN23UncMaF 87ezswMEugeR+wrnSDezDISdkrfi8bSvqetzt/BTG8h+8DDUKk1avTaJCSwUDcmL 9gPHQfmp2uvH5X5riudpzNqLUtmSjnwurlszKzlxAoGAR8STlDJhNph+p3cF8k25 ydT1kypxnjzVG8CAV5/h3dUmc7j7gyV8NlWZfWacxMZWOBsrdVh0zhMNUPiLJaGd I1isOkmiN9JFYMMhHSnhPnTCIjmu6uBLxf8wotHAvzWOJPV7lUZbw21KIN3DS79F sGZ2QzGYn4inHG4UHClhZxU= -----END PRIVATE KEY----- MellonSPCertFile (sp_cert_file): pathname: "/etc/httpd/saml2/demo.cert" -----BEGIN CERTIFICATE----- MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= -----END CERTIFICATE----- MellonIdPPublicKeyFile (idp_public_key_file): file_data: NULL MellonIdPCAFile (idp_ca_file): file_data: NULL MellonIdPMetadataFile (idp_metadata): 1 items [ 0] Metadata pathname: "/etc/httpd/saml2/demo_keycloak_ipa_idp_metadata.xml" R2OGk9W0luNm_NtZbURWOrPlvFzSTDMimCVK5N1Mj5U MIIClTCCAX0CBgFeFdE9pDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANpcGEwHhcNMTcwODI0MTk1NDQ3WhcNMjcwODI0MTk1NjI3WjAOMQwwCgYDVQQDDANpcGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgIPeag+JJmhtAkIGBwUT/req+jKC6c0Vl1Ngtzbcd07CP9mq1DomBkjuWl59J2urlEfrV4yT8avia0eYE6Dm/TqC74SHt3TVtiUliynAh/z2JvFlLb/EbGePSKrMnuNV8rV75YGcyE12vBRooUPx3hGaygsfaSOg+BijDuCSpbVdWSdVx9VecsWJfxSochOZUj6yvm/qTb8Ptl0x/o7/b/16GgjFRIKSFrdk8pVtMn1wCzpQQoGVHZmp1jrppGcp8KXIK54q7b4pPiTzlW6xhBgrmW2RtWQesCmN8ga1CVeBZKLsaH7argwGH5Ttz31iensqUO0degFu6nwCltgTVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFx8dl6RDle65q3IHIiGaL5fbJK5HxQiMXpk4N5riWQTP4g6xoTNAG4OFFUd4uRxt2ovdEdkbzhEy2lV4x626QdEfK5V9QKppupsTxTGA/4NMW9QCocAvFSpmYErmJIhfy6zzELoBK4Dpfcc3u1peHx2686msx6ExARF116d+5Xaps1dmPPy3yb2cCKzKbLhieqv+aLLrwz657ERUc4OnqEMEmmHFhHvPI7LRlS4AQ1/s1QlKcM9yqcu8WN3yKM/kuvZtZ0YTCSIl9W1b+I5v8wNoVFB22s7rfxs3DfJFaIImaTmRzaDX0MXgibEckrkigpO+anKe9B9z8CJdtlUHco= urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress [ 0] Chain File file_data: NULL MellonIdPIgnore (idp_ignore): MellonSPentityId (sp_entity_id): (null) MellonOrganizationName (sp_org_name): 0 items MellonOrganizationDisplayName (sp_org_display_name): 0 items MellonOrganizationURL (sp_org_url): 0 items MellonSessionLength (session_length): -1 MellonNoCookieErrorPage (no_cookie_error_page): (null) MellonNoSuccessErrorPage (no_success_error_page): (null) MellonDefaultLoginPath (login_path): / MellonDiscoveryURL (discovery_url): (null) MellonProbeDiscoveryTimeout (probe_discovery_timeout): -1 MellonProbeDiscoveryIdP (probe_discovery_idp): 0 items MellonAuthnContextClassRef (authn_context_class_ref): 0 items MellonSubjectConfirmationDataAddressCheck (subject_confirmation_data_address_check): On MellonDoNotVerifyLogoutSignature (do_not_verify_logout_signature): 0 items MellonPostReplay (post_replay): On MellonECPSendIDPList (ecp_send_idplist): On enter function am_auth_mellon_user searching for session with key 9cf3ebac4e542827e276dc064ce8c4e4 (session) ... found. Session Cache Entry key: 9cf3ebac4e542827e276dc064ce8c4e4 name_id: G-e292fc24-74d9-4979-9f81-2c26d85174de expires: 2017-08-31T16:15:23Z access: 2017-08-30T16:15:23Z logged_in: True am_auth_mellon_user am_enable_auth, have valid session am_check_permissions succeeds === Response === Status: 200 OK(200) user: G-e292fc24-74d9-4979-9f81-2c26d85174de auth_type=Mellon Response Headers: Cache-Control: private, max-age=0, must-revalidate Last-Modified: Mon, 28 Aug 2017 15:15:18 GMT ETag: "4a-557d1c33a4519" Accept-Ranges: bytes Content-Length: 74 Keep-Alive: timeout=5, max=99 Connection: Keep-Alive Content-Type: text/html; charset=UTF-8 Response Error Headers: Cache-Control: private, max-age=0, must-revalidate Environment: UNIQUE_ID: Wabkm2fzHRm5EyVgPZnqEQAAAAU MELLON_NAME_ID: G-e292fc24-74d9-4979-9f81-2c26d85174de MELLON_NAME_ID_0: G-e292fc24-74d9-4979-9f81-2c26d85174de MELLON_groups: ipausers MELLON_groups_0: ipausers MELLON_groups_1: openstack-users MELLON_Role: view-profile MELLON_Role_0: view-profile MELLON_Role_1: uma_authorization MELLON_Role_2: manage-account MELLON_IDP: https://rhsso.example.com:8443/auth/realms/ipa MELLON_IDP_0: https://rhsso.example.com:8443/auth/realms/ipa HTTPS: on SSL_TLS_SNI: mellon.example.com ---------------------------------- New Request --------------------------------- GET - /favicon.ico log_id: (null) server: name=/etc/httpd/conf.d/ssl.conf, scheme=https hostname=mellon.example.com port=443 pid: 21593, tid: 140251630954624 unparsed_uri: /favicon.ico uri: /favicon.ico path_info: filename: /var/www/html/favicon.ico query args: (null) Request Headers: Host: mellon.example.com Connection: keep-alive User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Accept: image/webp,image/apng,image/*,*/*;q=0.8 DNT: 1 Referer: https://mellon.example.com/saml-test/protected.html Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.8 Cookie: mellon-cookie=9cf3ebac4e542827e276dc064ce8c4e4 Mellon Directory Configuration for URL: /favicon.ico MellonEnable (enable): info MellonVariable (varname): cookie MellonSecureCookie (secure): Off MellonMergeEnvVars (merge_env_vars): (null) MellonEnvVarsIndexStart (env_vars_index_start): -1 MellonEnvVarsSetCount (env_vars_count_in_n): On MellonCookieDomain (cookie_domain): (null) MellonCookiePath (cookie_path): (null) MellonCond (cond): 0 items MellonSetEnv (envattr): 0 items MellonUser (userattr): NAME_ID MellonIdP (idpattr): IDP MellonSessionDump (dump_session): Off MellonSamlResponseDump (dump_saml_response): Off MellonEndpointPath (endpoint_path): /mellon/ MellonSPMetadataFile (sp_metadata_file): pathname: "/etc/httpd/saml2/demo_sp_metadata.xml" MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= urn:oasis:names:tc:SAML:2.0:nameid-format:transient MellonSPPrivateKeyFile (sp_private_key_file): pathname: "/etc/httpd/saml2/demo.key" -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHUXILDtuMxyFP 6OW81hqfj9z+gC7sxR6U8bRPI4gFiNjkNF+4HMyrKtVtfwA5WJ1tJ4IaZbg4fGap iCcWQ5tjjuZ7CpZ6XLekq63OGrkgZxNm8L+AjvREv78iR4Myc4x6ybN1LwQnV+oD F0i9SAEPnfJCaCA+1+DC3RKy7+xtK3SVimUWwAHmkNU6N0wCVSNYn1KgnxBSCPTx 2DifbyRl21SiokCfLSnktE++WBzRzldSxzMrkSfFoS7/uT232tbdw2NsJmHGLRGk myBrEC6BVhQ746NM1cLDtrOots7bNfZ3yfb3W1tpoKVOhM2se3OTKdidAQsaGlVu 7QIhBqinAgMBAAECggEBAJtU662WfJ9vqJRgCnpp2QG02iM0vl0jGbw1ybFLHXxC s9TUxCv1tcNHdGEK8p++YaFpgskTsMfEmzVPuDZvpa+m9BO7op3ll/CrIp5W0SNh cQtuX6/OuKrDTC9oz+QHjNk8S7DtXS1UJDkYckWg0cLb8qqx/z86eh0isKnmtLg2 H1+6L6mB9fcZldkcrU+kXT+dcDX85skMZAeBsrG4yaoX26AzVl8lEl2rJAQvpxj8 5wGBC4riWY6TzMYiCjcS5JfZIlbhcZe61ej3A48NVBSKCP1XKo0xbKuOHIQuMeeW wSaboBwRzJ9JdTXlq5UWfLvmjXDc/HCwk/N7cj021uECgYEA5KkQr3cSKrMIkYoO H0Vnkw1kYYGUjtTL00Nfdkv7uGMF122nyt1ND0gpdS0jgNx3LSEam/gY35UWEjGH i8DGD04k8pvKDUsl8cuYPcC3oce1lLCGAnw+aHPC5wtA829CLOLtBfxXIhVAI0tp ECosD/A63/m1LC19XolOd10/PC8CgYEA3yZChEYZupDGJFZltiy0ZgUarvD2Ss4N QiRVR+CmpBrOKZdD8q6uUuqWQN9Rw7kXm8LxAPYwiIDVjxjYALF+j7/9Q1oZyKuv eHJdMe4eRUeqeaIKbxnFfKwGZ5rj97jwPrkUCxio75KZhpOcDHMSgBCBtzW0XIZl gTeQYOshZQkCgYB5TK6LRnEesabj/gaL1DejrMEJcMIsGvqdceocSSaZo/4fUA5o 8YjFiJRlkrJ403ttN1h0UOJxCReSQzASlQr8Z4n2IWrILotMf6Kdb7R6YAUVgac1 fk9k/bPw+OlVujmyshbmy/w1GmzRzFlJt/Vz5w50bnULoH4XPmOfspmvBQKBgBcJ rihVzGY0eCBcQxfxuZYmxMB25BaI+1luwtcu3EVo9wvYMA2n9xtcWLLN23UncMaF 87ezswMEugeR+wrnSDezDISdkrfi8bSvqetzt/BTG8h+8DDUKk1avTaJCSwUDcmL 9gPHQfmp2uvH5X5riudpzNqLUtmSjnwurlszKzlxAoGAR8STlDJhNph+p3cF8k25 ydT1kypxnjzVG8CAV5/h3dUmc7j7gyV8NlWZfWacxMZWOBsrdVh0zhMNUPiLJaGd I1isOkmiN9JFYMMhHSnhPnTCIjmu6uBLxf8wotHAvzWOJPV7lUZbw21KIN3DS79F sGZ2QzGYn4inHG4UHClhZxU= -----END PRIVATE KEY----- MellonSPCertFile (sp_cert_file): pathname: "/etc/httpd/saml2/demo.cert" -----BEGIN CERTIFICATE----- MIIDDTCCAfWgAwIBAgIJALnqrR7yvGH5MA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV BAMMEm1lbGxvbi5leGFtcGxlLmNvbTAeFw0xNzA4MjgxNTExNDlaFw0yMjA4Mjcx NTExNDlaMB0xGzAZBgNVBAMMEm1lbGxvbi5leGFtcGxlLmNvbTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMdRcgsO24zHIU/o5bzWGp+P3P6ALuzFHpTx tE8jiAWI2OQ0X7gczKsq1W1/ADlYnW0nghpluDh8ZqmIJxZDm2OO5nsKlnpct6Sr rc4auSBnE2bwv4CO9ES/vyJHgzJzjHrJs3UvBCdX6gMXSL1IAQ+d8kJoID7X4MLd ErLv7G0rdJWKZRbAAeaQ1To3TAJVI1ifUqCfEFII9PHYOJ9vJGXbVKKiQJ8tKeS0 T75YHNHOV1LHMyuRJ8WhLv+5Pbfa1t3DY2wmYcYtEaSbIGsQLoFWFDvjo0zVwsO2 s6i2zts19nfJ9vdbW2mgpU6Ezax7c5Mp2J0BCxoaVW7tAiEGqKcCAwEAAaNQME4w HQYDVR0OBBYEFDBbq0pjLeMFPcBt7A++c90lSM5vMB8GA1UdIwQYMBaAFDBbq0pj LeMFPcBt7A++c90lSM5vMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB AFiIxqhW37Td/rD77N648feciigEk/GW4zsqxdx9MspnvSTfr0/lPPOaVhd/UGAw g+DwGOmqfisvl44wg07y+4T0NTDzvgkrT0ON5hyEBucFhSjPN+lhwWaH422URwUL cKTqkrnAk4Er4bSi1GhsV/2/Xv2ZYyJCcUeiwWQ2fEZXp4ke3IZPN0nYlajKzBTd Bv9YlynXKuO1hxBYDWQrrjpp1UZRKjJD2nLUsTi8oFuLhB/RwUMqXZ0nFuNoOkDQ XotXjsiL1KtqNW1k/oVtLwNP0trqqh9npWV+R3pDTckxIHQhOvs5VqQZANViH6mp YK53b9Bhr0TpIOKetFY68kQ= -----END CERTIFICATE----- MellonIdPPublicKeyFile (idp_public_key_file): file_data: NULL MellonIdPCAFile (idp_ca_file): file_data: NULL MellonIdPMetadataFile (idp_metadata): 1 items [ 0] Metadata pathname: "/etc/httpd/saml2/demo_keycloak_ipa_idp_metadata.xml" R2OGk9W0luNm_NtZbURWOrPlvFzSTDMimCVK5N1Mj5U MIIClTCCAX0CBgFeFdE9pDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANpcGEwHhcNMTcwODI0MTk1NDQ3WhcNMjcwODI0MTk1NjI3WjAOMQwwCgYDVQQDDANpcGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgIPeag+JJmhtAkIGBwUT/req+jKC6c0Vl1Ngtzbcd07CP9mq1DomBkjuWl59J2urlEfrV4yT8avia0eYE6Dm/TqC74SHt3TVtiUliynAh/z2JvFlLb/EbGePSKrMnuNV8rV75YGcyE12vBRooUPx3hGaygsfaSOg+BijDuCSpbVdWSdVx9VecsWJfxSochOZUj6yvm/qTb8Ptl0x/o7/b/16GgjFRIKSFrdk8pVtMn1wCzpQQoGVHZmp1jrppGcp8KXIK54q7b4pPiTzlW6xhBgrmW2RtWQesCmN8ga1CVeBZKLsaH7argwGH5Ttz31iensqUO0degFu6nwCltgTVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFx8dl6RDle65q3IHIiGaL5fbJK5HxQiMXpk4N5riWQTP4g6xoTNAG4OFFUd4uRxt2ovdEdkbzhEy2lV4x626QdEfK5V9QKppupsTxTGA/4NMW9QCocAvFSpmYErmJIhfy6zzELoBK4Dpfcc3u1peHx2686msx6ExARF116d+5Xaps1dmPPy3yb2cCKzKbLhieqv+aLLrwz657ERUc4OnqEMEmmHFhHvPI7LRlS4AQ1/s1QlKcM9yqcu8WN3yKM/kuvZtZ0YTCSIl9W1b+I5v8wNoVFB22s7rfxs3DfJFaIImaTmRzaDX0MXgibEckrkigpO+anKe9B9z8CJdtlUHco= urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress [ 0] Chain File file_data: NULL MellonIdPIgnore (idp_ignore): MellonSPentityId (sp_entity_id): (null) MellonOrganizationName (sp_org_name): 0 items MellonOrganizationDisplayName (sp_org_display_name): 0 items MellonOrganizationURL (sp_org_url): 0 items MellonSessionLength (session_length): -1 MellonNoCookieErrorPage (no_cookie_error_page): (null) MellonNoSuccessErrorPage (no_success_error_page): (null) MellonDefaultLoginPath (login_path): / MellonDiscoveryURL (discovery_url): (null) MellonProbeDiscoveryTimeout (probe_discovery_timeout): -1 MellonProbeDiscoveryIdP (probe_discovery_idp): 0 items MellonAuthnContextClassRef (authn_context_class_ref): 0 items MellonSubjectConfirmationDataAddressCheck (subject_confirmation_data_address_check): On MellonDoNotVerifyLogoutSignature (do_not_verify_logout_signature): 0 items MellonPostReplay (post_replay): On MellonECPSendIDPList (ecp_send_idplist): On enter function am_auth_mellon_user searching for session with key 9cf3ebac4e542827e276dc064ce8c4e4 (session) ... found. Session Cache Entry key: 9cf3ebac4e542827e276dc064ce8c4e4 name_id: G-e292fc24-74d9-4979-9f81-2c26d85174de expires: 2017-08-31T16:15:23Z access: 2017-08-30T16:15:23Z logged_in: True am_check_permissions succeeds am_auth_mellon_user am_enable_info, have valid session === Response === Status: 404 Not Found(404) user: G-e292fc24-74d9-4979-9f81-2c26d85174de auth_type=Mellon Response Headers: Cache-Control: private, max-age=0, must-revalidate Content-Length: 209 Keep-Alive: timeout=5, max=98 Connection: Keep-Alive Content-Type: text/html; charset=iso-8859-1 Response Error Headers: Environment: UNIQUE_ID: WabknGfzHRm5EyVgPZnqEgAAAAU MELLON_NAME_ID: G-e292fc24-74d9-4979-9f81-2c26d85174de MELLON_NAME_ID_0: G-e292fc24-74d9-4979-9f81-2c26d85174de MELLON_groups: ipausers MELLON_groups_0: ipausers MELLON_groups_1: openstack-users MELLON_Role: view-profile MELLON_Role_0: view-profile MELLON_Role_1: uma_authorization MELLON_Role_2: manage-account MELLON_IDP: https://rhsso.example.com:8443/auth/realms/ipa MELLON_IDP_0: https://rhsso.example.com:8443/auth/realms/ipa HTTPS: on SSL_TLS_SNI: mellon.example.com ---- mod_auth_mellon-0.16.0/doc/user_guide/Guardfile0000664002412700241270000000027313367654776021445 0ustar jhrozekjhrozekBundler.require :default guard 'shell' do watch(/^mellon_user_guide\.adoc$/) {|m| Asciidoctor.convert_file m[0] } end guard 'livereload' do watch(%r{^.+\.(css|js|html)$}) end mod_auth_mellon-0.16.0/doc/user_guide/README0000664002412700241270000000165213367654776020502 0ustar jhrozekjhrozekThis is the mod_auth_mellon User Guide. It is written in AsciiDoc which is a popular plaintext markup language much like markdown or reStructuredText. You can find extensive documentation on AsciiDoc and AsciiDoctor on the web. AsciiDoc can be rendered into a variety of formats, but one of it's great advantages is the ability to produece DocBook. There are many ways to render AsciiDoc, but the simplest to produce html is this: % asciidoctor -a data-uri mellon_user_guide.adoc Note: the "-a data-uri" causes the images to be inlined in the HTML output so that everything is contained in one HTML file. If you want to edit the source it can be very useful to be able to see the rendered version as you work. This link is a guide on how to do that. Included in this directory is a Guardfile which can be used in conjuction with the live preview discussed in the link. http://asciidoctor.org/docs/editing-asciidoc-with-live-preview/ mod_auth_mellon-0.16.0/doc/user_guide/images/chrome_SAML_Chrome_Panel.png0000664002412700241270000074537413367654776026322 0ustar jhrozekjhrozek‰PNG  IHDRGd‘() pHYsÃÃÇo¨dtEXtSoftwarewww.inkscape.org›î< IDATxœìÝy|Tõ½ÿñ×™™d&3“}%+Ö6pA\(¨¨¸U­K­Jm«>Ô¶¶¿Z[wo­ŠÞ¶Vª×jµè½¤¢¨(¨¨hED!!$dßg_Îï1!™d&™I|žG3“÷÷;gÎlŸù~¿G¡‡k¯½Vu¹\!„B!„B«œN'kÖ¬Qº×AWQdÑ¢E\~ùåh4TU½ !„B!„B„‘ÇãáÔSOUßzë-Þÿ}E°páB®¸â ¼^/g´û(„B!„B6Š¢pÓM7áõzyÿý÷Q–/_®¾ôÒK(Š"#F„B!„B·6lØÀ²eËðz½£Ý•~½õÖ[œþùýö¯¡¡ÆÆÆ°´›””DrròQÝŽÑhÄh4úò»y<.¹ätv»F3fï|!„B!„b$?~<&“)¤¹6›²²2_Q!\íX,***ÂÖŽªªX­V*++ÉÉÉñm×jµÀá5GŽÆ;^!„B!„ß1™L¨ª²ÏøŠ¢5"íôW u;F£‘””úŒ Ñ…¤!„B!„â8ÑØØÈ‡~ÈÎ;Y°`K—.õ{½÷Þ{¢¢"-ZÄ™gžö¾…rðƒªª(Šâ÷²£±£ÑH}}}ŸË¤8"„B!„B¡±±‘Ï>ûŒûî»ßüæ7~‹#ÕÕÕìØ±ƒ_ÿú×<ðÀ#R-­­­8p€ÖÖÖ¯GVVqqqÃjk÷îÝäääí»¬ªªŠÊÊJ ú´£( V«µO¦G„B!„Bˆ ¸\.À€Å‡ÃÝn ­­mDú6Z8@NN'žxâ€×«««£²²rXőݻwSXXHjj*7n ;;ÛWY¼x1uuuìܹ“ÓN;- L)Ž!„B!„ÃPUUņ ؽ{7N§“˜˜æÍ›Gbb¢oZHkk+7Üpƒ3f ( %%%tvvâõz1 Ì›7óÎ;¤¤¤Q¾EÁkmm´0ššÊÎ;‡ÕVNN;wîdñâÅ,^¼ØW é.Œìܹ³×«ƒ‘âˆB!„B1D.—‹Õ«W£ª*¿ùÍoÐjµX­Vþú׿’˜˜@ll,wß}7&LÀëõòàƒ¢ª*¿úÕ¯hmm%33ÇÃý×Åå—_>Ê·jèöíÛGEEE¿—?žüüüa·Ñ=fãÆ¾ɾ}û|…‘7ö™n3)Ž!„B!„Cäõz©®®æñÇçŽ;î ¹¹™ÂÂBî¹çn½õVbcc±ÙlüéO¢³³“+V°lÙ2^ýuÌf3wÜq^¯—ë®»ŽeË–ñÎ;ïÕÅ‘ŠŠ ¿SY>þøãG «@ã+tçnܸ±ßµF#Å!„B!„"6› ¯× €ÇãaòäÉ|õÕW¸Ýn~ýë_ó‡?üUU1›Íx½^œN'Š¢pÒI'±gÏ.\€ÛíF¯×s 'P\\ÌÒ¥Kiii i_Gr¡Ô£™G„B!„Bˆ466bµZ)..Æh4âp8Ðjµ(Šâ+–èõzßïÝE!!!¡O‘B§ÓƒÕjEUU4MHû|àÀ&OžLllì€×kkkcïÞ½GMq¤çâ«Ð5'??ß·‰L«B!„B!ÂàÓO?åóÏ?§¼¼œë®»Žòòrâââ(..榛n⥗^âþûïgÊ”)x½^, f³y´»=¢ÆÏÇì÷²P8²0Ò] é¹IϳØ"lÅ—ËÅŽ_ÒÜÜ<àõ˜={’+¹’+¹’+¹’+¹’+¹’;†r%S2G+ÓívóÕWÛ=î•Àœ9' Ó ï#pEE—]v………:tˆÇœyóæÑÜÜÌ“O>É=÷Üã+Š<ôÐCLŸ>†† kQÖØØX:;;QÅ—K\\MMM(ŠòbJVV{÷î xZÍPÅÅÅQWWG~~þ€ëŠÔÕÕ {tJee%………@ßÅW» $………ìܹsô‹#••´·w0~|..—»ÿÆuZª«RYYÁ¤Iy’+¹’+¹’+¹’+¹’+¹’;Fr%S2G3³¼¼Œ¶¶vfÍšÙÐßøÓÙÙÁîÝ»)//#/oàÅ@u:n·»Wñ¢§¹sçòꫯòç?ÿ³ÙÌé§ŸÎgœÕjåßÿþ7=ôn·£ÑÈ)§œÂŒ3رc+W®dÙ²eLš4‰÷Þ{uëÖ‘™™Ùk{cc#o½õçw^¿m»\®!wâââFdªLVV•••ƒž¦w¸E€‚‚_;= #= $Ý× TXŠ#.—‹ÊÊr’“Sü>¸Ý’’’©¬,''gü UDÉ•\É•\É•\É•\É•\É™\É”ÌÑÊt»ÝTV–“••=ì€ÙMvv6••åL˜;`!))‰ªª*rrrú½üÔSOåÔSOí÷²[o½µßíS¦Léõû 7ÜÐïõümïVUUErr2ªªx½Ñ2RE˜î¶ü';;;¨µFº…v¥—ÃêëëðzU´ÚÁk/:]^¯J}}äJ®äJ®äJ®äJ®äJ®äŽ¡\É”ÌÑÊTU7nÜ × TZÚ8T•AÛÏÏϧ´´”ªª*\.WÈÚ—ËEEELžTªªú~üIJJ¢¢¢",í'%%õúYYÙˆ´3·§[È‹#夤¤üàõª¤¤¤R]}ÀïƒDr%Wr%Wr%Wr%Wr%WrG>W2%s$3ËËË7.ˆˆÈ€2ƒɸqé ­½çÍó[ýJõJ÷—‘;~<:­Ó/LÇzn¸žwÂÝßp=Ÿ~ LnNv6Ó§LÅd2R[WGm]}¿Å‘`rÝn7MÍÍd÷3e`¸ýP4 §.˜Orb"-­þ?íã,\ûáhËíO¨Žß`Œöãx$÷¯ä†6WUUšš8sáB¢£Í444†$7œïK1”¾ÌðûÁo(¯ÃÓ§NE£ñ?~´o»Íf£¶¾žï¥õýbq¸ýœœŸOYyyH3¡ë˜õWÈ ÇsVBB’ïl-ãÇû_ìHgž±‡ÃÁ7Åžmß3}êTÎ@ÔQÍݶý+¶mÿŠÔ”fÍœÉÔÉù!É ÔXʵÙlTVU1sÆt¿×+÷tWÔóhhlô[ &wÆ´©¤$'ñÉÖÏx{ã{œ¶`A¿ó{‡Òßp쇎ÎNjëêXtúi˜Íþ?¼˹ptíßpõ7Áäf¦÷ÈÊÌäÛ}%~¯hn{G@¿ó“‡Û_€üI“|k5 ô×hßoáÚG[n°ÆJîÑr<Kr‡–;%/ŸŒôqØíþ×0+ïK1”¾~Sü-ûËËû,ö9”Ìî×áÅgž²~*˜Ì†ÆF4̀ū¡öSIÒ£,†ò¹§êÀÁ~d 63˜ç,EÑàv{HîgÙté.LŸ:Õ·-PÉÉÉ”——Q^^F^^þá>)hµ:ÒÒ2hmmCQB0ì,11±ÔÕÕ’–6Îïô&^!(Ž”“œØÜÅæÎpuñž"##^§;WUÕAn &·° €y³gÑÜÒŠÕfðºÁäc¬ä¶¶µ¡ª*Qƒ ó ÷[·Éùy”•W„,7%9™‹/¼€Æ¦¦ßÜ›Žý°¿¬ƒ^ÉhÄf·:<ïXÍ…ð<ïŒDÃõ¸D ¹‡jkùò«ÔÖÕç÷ÍT0¹Ñ‡‡wZ¬ýN¤Óõ]Äo¨¹¡>ε޶ܡ ¹¡~Ëþ ®FQP üÐПw~ë=Þ—*ؾ¦$'‡ìýt÷ë°!Ê€Ûíp¿Žæm·X­DEE4 `(ýô7Zs(™…œtÂ<\.×°3ƒÎR‰‰‰íwqÝpÓjµdgçPYYî+Ž@W¤{í¨ÎÎNL¦è°q:]¸Ý.êëkÉÈl¤ ­aÝÃÕÕq¹\DD„~V\\<^¯«Õðß(Š2è›Ý`r»TËÎÊÄ4ȹ¶‡Òß@Œ•ÜîãÍW ÷[·ÈˆR’ýOjnRb¢ßog†“ÊýPZV†Ýáà¹ÿÁºõoR^Qy\æk,õ7\‹@’ÛÞÑÁk¯¯#3#ƒK.º Ï?oÐ7ýäFDDÇÁêêöw(F37\ûáhËб”ªÇ±ìßðçFl¶ÀÏ1Úýí)\ïK3”L Ç›Ùý:üï7ßbo‰ÿQ‹Cíç`ÍT½þ§¨ 5³§ÁŠ Á~î1FE º®Q¨Ÿ³º=4©ÿúé^cdúÔ©LŸ:•oŠ‹yûÝA礥ÃívSSó]»FhIMMCUUÂ\@£Ñ`6GS__Çã ƒ¢a)-ÝGrrJXî<­V‡Ñh¢½½£1°a¤áÊU…””‡y¥þ†#·{8 Ýa°P4VúÛm°ŠúXëo(rm6Õ55,_¶¬ßEߎ—Ü¡8û®ÜƒÕ5DGG3»pfHsN:anù˜)ùy}ãt,î_ß~8Úrƒu¬æÊþ onjJ2¥eeÌšYЇÙÑîïXÈ­Ì`_‡Gó¶wÝl®ã1úŽÌîç¬iS§ Xkoo%66ލ¨à!ÝüáG¾ÂHÏ©4ߣ×ëƒZwD«Õ0n\:%%{IOÏè±]KBBµµ‡°ÙlDE…î1韛ÍJKK3‰‰I²öÈr ¬ë´K6 †+ÀËÇãÁfëÿ4™Ýº>³Úlƒ+ &·'C?ç&nmv;n·;,ý D0¹F£‘ì¬LjëêC–;÷[ Ëu8¼öú:vó uõõttt ¸c°ý õ~(«¨@9è·HÇzn·P?ÞFª¿cýqa2imk£êÀA,V+{ŠŠCö|69/Ü XýÿbY9ÇØÝáÌ ×~8Úr‡b´sÃñ8çþ ×û’£)wNá,êêëÙ¸is×4•1|üŽÄû’PíÛž}mnnÁét;s(¯ÃḯÉLHˆÇãñÐi l4Èhõ3™ÝÏY﾿‰–ÖþÏVdµZðxKn`¹O=ýW.¾à²23úùë¡ç•äJ®äJ®äJ®äJ®äŽ\É8ó‹/·ÓÜÒÂ’sÎÓýéL‡ÃNcc=“'O ú,5áÒÑÑÁÎ;8ñÄ“IHøîL@‡êêƒTUUGWÙ/¼Ün7z}S§N—âȹ袋†6­¦¢¢œØØ¸°F"#õèõ:;;$w„sUUòp®ci?H®äJ®äJ®äJ®äJ®d%³°`«…ÎÎÎek,fvt´c0D™Â@tt4ññ ”——õÚ®Ñ($%%…í¬ZýÑë#ioo“…YGXÐÅ›ÍJ}}11Ý %³9»Ý†Ç3øÜGÉ ]nCc#еzu(s‡Kr%Wr%Wr%Wr%WrÇN®dúÏÔëõäMœäwÝ¡dÅXÊôxÜ8v22‚™>222hh¨Çnÿî´ÓŠ¢!2RO||<6› ¯7´Éþx…B!„BKECvv6YY٣ݕ€:TCyy9^¯œN÷XUX8Ë÷ÿŸüägè“©©©aÇŽ,\xú¨uN!„B!„mãÆ¥É3êˆÐùè£-Ìž=›´´4`§òB!„B!„8–HqD!„B!„Ç5)Ž!„B!„⸦íˆ.‡ÕP__‡Ûíí®!„BqLÐé"ˆ!##‹ˆˆˆÑîŽb “âÈPWW‹Ýn礓;ÚÝB!„â˜ÐÖÖÆ·ßqà@¹¹G»;Bˆ1L¦ÕŒuuµÌš5[ #B!„B„Pll,S¦L¥½½m´»"„ã¤82¸\. †¨Ñî†B!„ÇœØØ8<ÏhwC1ÆIqD!„B!„Ç5)Ž!„B!„â¸& ²Š£Ò+¯¼Â•W^9ÚÝB!„#Èb±`2™Âׯë¯Ñùâ ¨6{P§D0_w=¦å—„¤ªª¢( ÍÍÍ´´´b±XP£ÑH||ñññ(ŠÒëºÃuNQrw@=ò_õðÕ>Û»ÿÿþìÎô#Xë>´Æ< 4ûáx•šš6¬¿¯«« QOF^XŠ#üò7¿à¯ÿýd8šè£²ê™éhµÚ°´ÑÙÙÉëo¼ÉÖÏ?gY9s'0³`×\yf³€?®|’_Þq{Xú „B!Äñjûöíœ~úéak£ó…¿ƒÝüÚìt¾ð|HŠ#ÝÅŽÖÖVÊÊÊhmmCQEÁét¢Õj©¬T0›Mäå岉êrø ªª‚× ª…®ÂÃám^* hµ«Ï‡ÿpĹ[?ÆënEkÌk;¥¿¼ ké^Æ]{3É˯ k[bä…|ZMwadYyWAq9VׄeÁ¥ïoâÚVðÒêW|…€ýeå¼þïõ\3FV IDAT{à 6¾¿‰?®|’÷6}òö»µÚ;ùÕ»O3í©0í©ð«wŸ[[G;ó}g³·±j´»1jEáÛo¿õûûñHöÁØg±Xxá…xä‘Gø×¿þ5¤Œ{ï½—ÆÆÆ÷läò…Bø·fÍÖ¬YÖ6T»cHñÕÃ;ìö9jkkÙ¹smmí¾ËÜn7ùùùœsÎ9œþùL˜Ëλ8tèoɰÛwÚñ:mxvT§ ´:ÐQ=T§ ÕaCõzP &ÐêPÝ×íúižö­ØªÿBDü¢°¶c)Ú…µt/Mׇµ- $÷åpØívjkk9tèß«Õ:¢}ziõ+,>ÿB^ZýJXòC:r¤ga$wÂ{äÁPÆ(3#ƒÕ5¾I(Gl|=ùßœsÖ"–_p“&æPº¿Œ×ßxƒ÷6}à»N8=üá‹ìªÝÏÛ×=À×=Ê]ïü…G—ü,àŒÚÎ&îyïY6íÿ’6‡…̘Λ<ŸÎYV‘ehÂióæÍ466rùå—¸MÞ±áõzÙ±cDFF†¡wëîû¥—^T?¾øâ ìv;¿üå/ñz½#ÐS!„G‹¢¢"_qº¨¨ˆiÓ¦…¯1U¦kÉá©Ý£2Žü½×ß„`䆢(´µµñí·{ñz½¾#.—‹9sæ0}út¼^/v»„„bcc)-ÝÑh$66vØí{ŽÃÓd¼ ÀòÆ_ðÜGÔ÷~„.-TpW‹íÝ¿£Ë™NÔ—wE”Ãûó°ûЗ›Èˆ2œ®Þ#C® #÷¼ÿ,~øâ ÛB­¨¨ˆéÓ§ºMÞ±aµZùàƒhiiVÞ{ï=>ø øÑpÝ}¶MMMäææ¢ÓéÂRÔêíB1ú¶lÙBvv6ÙÙÙlÙ²%lí¨=‹‡ Š¢tmëç÷îÉ$jˆFnx½^JJJ}„UUÅívÏ´iÓhjjbݺu¬[·ŽÏ>û ½^OTTûö•„ä‹…®‘#vT§¯ÓŽ÷ó7Pßz×Ö£zUTÕ‹óÓ×Qß~ï—ðº\¨.;ªËêr»ýþDé>#VyEíðmó´nÂRþ—CêEai·›³®†¶7™–áÛÖ²ùí°µçp8hoï1¤( ÇW(óz½¸Ýn<ï'”ššš|™999Ìž=Û÷SPP@||<ÐõX©©©áË/¿‘Q$×\uE¯C-$#GF»0Ò­»@Ê$¯¿±žN‹…sÎZÄâ³û¯ þqå“ìÚýÍÛF»ÃBNÜw‹ääÄ¥q ­>à¿·»|~àvÜòy‰™$c9)3Œ•÷1¨ÍnÁ sº-”hmm%//oÀmBÀð ³ÙÌ/~ñ‹a÷Ãn·£Ó÷Rѳ‘AõÃãñ`0‚ífÀ†r{„BŒŽÊÊJß®ââb>þøc~üã°jÕ*ÆONNF£Ñ÷ÿáRÕëEu:Q½^4CWÄfCEE‰¢Óá±Xºþmk#õæŸâ¨(§íƒÍÃn¿µµ•ŽŽ_¡E£Ñàr¹ˆ‹‹ ¤¤„¶¶6¢¢¢p8¸\.t:ÍÍÍ´¶¶’0¬öU§Ý×¶ê°¢( -8 OÑV¼g^êràÝ÷Ú™óÁëÅë° x\ß-ÎbŠÚŽÙþ2н ƒf=6ãUxZ6ÒYñ$«cæUh#†?bf m[?$aÉr&Rõø}tìø[E)Qã'…´-§ÓIUÕìv;™™™€JYY9†I“&°k×nN9e;wîÂd2b4šÈÊÊ Ëȣш¢(:t€èèhfΜÉÖ­[q¹\¾Ÿ‘pÍUW†eÄH·a¿C+…‘n¡.|úÙX~Áý^î5Fz:ù™@×ZGš¿êÇ|öãUƒft‘LLÈà¹/ßàÁsV¡é{êhâ¶7WòŸEÄLüzá5ü p1uÍüêݧù¨ükôºHΘ0›g.ü%û›«)üÓu´Þó.:M×¾^±îQRÍñüþÌ1yå•<~î-Ü»éyâ f>¼ñ϶*üøŸ|~ ˆÚŽ&N_È —ÜÍŠu`cÉ(ŠÂË_oäç§^ÁõÙöóS¯$öŬ8áB6ìýŒV{çL:ÿ>ÿbô&TTþüùþúŸ×i¶¶““ÄãçÞÊüqÓxúé§Y²dI¯ážEEELš4©×·á=·¹Ýn’““Y±b«W¯Æf³ñ£ýˆE‹ñÀ°k×.fΜÉêÕ«?~<555Ü|óÍlݺ•¸¸8î¹ç®»îº€öM]]wÞy'›7w½ˆŸuÖY¬\¹’ääd__n¹å^}õUª««Y°`¯¼ò ÉÉÉýæùë˯~õ+Þ{ï=>ÿüs"##Ù´i—_~9;wî$33“›nº‰ 6ÐÙÙI^^Ï>û,³gÏÒþˆˆˆà§?ý)ëׯ§¥¥…%K–°jÕ*bbb‚êsjkk¹ãŽ;ؼy3ƒ³Î:‹çŸ>à}Ìíèïx¹ÿþû9á„Ø»w/v»I“&±lÙ2ôz=^¯—'žx‚sÏ=—M›6a0¸é¦›¸÷Þ{¹ýöÛÙ°aIII,^Üõøòz½<þøã\zé¥L˜07Þxƒ’’œN' \pÁŒ7ŽW^y…’’Eá믿æ´ÓNãÔSO¥££ƒõë×sàÀ  .dÖ¬Y~ûÞݘ˜}ôQN<ñDöìÙC{{;YYY\z饘L&Þ|óMJJJØ¿?Û¶mcÖ¬YœuÖYtvvòî»ïR^ÞµÞÓ„ X²dÉ€g*زe  ££ƒñãÇs饗òúë¯÷¹= ,àÑGeîܹìÞ½—ËÅìÙ³™0a}ôuuu¤¦¦r饗úÞ” !„¯††žxâ ªªz¯7eÊæÎëûÿK/½Ôëòììlî¼óN¿ïUÕ5rDƒyÁ)hãb騲¯ÝFôi§£¥ã“qwt³p!ºøšÖ¼†&* E¯G ÁÈæææ^Ó(Ün7‡Ã7Z ûÿn·N‡F£Áív£( ÍÍÍÃ/ޏì¨Þ®iE^‡ µ£íÌ3po{ Ï¡2ÔŽf°w¢™v žÝ¢Ú»Î ¤z½a9OŒÑ¶Og v‡»k N£ Kõ+Xm.\Jiá5аv5“ûš(:s îήQÍß cÅ!m«ë¾„É“óikk£µµ¹sçàt:9pàéé¸Ðñz½¾û?TÔSɺÙl6_q¤¡¡ÌÌL C¯¢Hw´vqäÿï·”~ÃZV^ÎÅWü`п™˜;aÈg±©¬:€ÃøBGÝ’œì¬!µ×}ÛzN¥éé—wÜ>bg¦ÙSWæ÷²ÝµûÎùçå¿ç'o<ÆÔ'ÀO¸Ÿx!1ú®**ßõw,ÊÃ?/ÿ=ûšrƳ?£ 5—©¹|ÿÕß1#5—·¾ˆV£¥ªµ. 6ë:›YW´…7®ù‰ÆØÛ™™ºêká¸<î;ëF"´œù?·òÜ—oðW<Àmo>‰AÑk­–þ¶y¼^²ãRy÷ú'Ð(n\û¿xû/üí¢»ØÛXÅ=ï=Ëû?zŠÙéùêh$RIDDS¦Lé3çî›o¾á´ÓNp[kk+©©©ìرƒêêjf͚ž}ûxùå—IJJâ¼óÎãá‡æoûªªrÑEqöÙgóÚk¯±wï^N:é$ {}Píªª\rÉ%äææòí·ßâñxøÑ~Ä•W^É{ï½çëˤI“øôÓOÑjµ,\¸?ýéOÜÿýýæùëËý÷ßÏ;ï¼Ã<ÀÏþs®¿þzž~úéÕpX±b+W®Äh4rë­·rçwú¦=³? ëÅdüøñ|ôÑGh4®¹æn»í6^xá… ú|äþóz½\tÑEÌœ9“’’’Ã+ÄWµ/ƒ¹ý^¯—¸¸8®¿þzEaíÚµlذåË—]…ꢢ"®¹æŒFc¯þ²qãFÎ9çE¡¢¢Fãû†mîܹ,Y²„ˆˆ6lØÀ»ï¾ËøC®¼òJÖ¯_N§céÒ¥¾ÛûÊ+¯››Ë÷¿ÿ}yöÙgIKK#--­ß¾÷Ô=?ú†n@Qþþ÷¿óŸÿü‡3Ï<“óÏ?‹Å¸qã|g!PU•ÿýßÿ%>>ž[n¹UUY·n¯½ö×^{­ßoGÆÇÙgŸV«å¹çžcÛ¶mýÞžî9Ûf³™›o¾™öövžyæššš¸ä’K0üóŸÿdË–-\à§H.„"´’““ùío˃>HUU—\r _|q¯ëÜsÏ=@×­k×®%;;›ßþö·!9ůÇbÁ|ò|rÿú7´z=¿þÖ¢"&>÷< U¿¿‡Î/¿`â3ÿƒ¢Õ h´¸ÛZQ4Ú|ko³Ù€ïÖ—HHH ==ÔÔT ë5Îëõâp8ðz½hµZ:::zýíp¨NGW‘G«Cµ¶£ÚÚQ2òQŠ>Á]ò%jK-šôI(ñãPmT›t:ToèOŠ«Oãë´:8œ*v§[ÃKØ*6‡Jòä«ÑèÂwZg€ÖO6až5ÈÔt–\@ýk/Ðôö:R¯¸]LèF®Ft:MMM¤¦¦òÕW;hnnÆãñàr¹HNNÆd2cµZÑë#ÑjC;"¶¿G||<¾þµ¶¶ÒÙÙ÷”Íá.޼´ú•Ãë\1¶× ÆQ^P:êÍHÍå£ÿÌ—üŒ·ön¥à¿¯á;ºæÌ}S_Á· •ܳè‡Dj#˜‘2ySø ì+Šê+ØSWÆ—þŒ½ S„©É_ü ¯%;6S„aÀvBiñ¤ɈI&ÅÇ¢Ü9”6U)#3&…ôè$î^ôC^ÛóÕ‹^‰ŠJMG×Â`™1)¤˜âÐjµœ{î¹¾0èš·×ÔÔD~~þ€ÛÎ=÷\’’’(,,dÚ´i,_¾œÜÜ\bbbX¶l¥¥¥ìÙ³‡¢¢"î¿ÿ~"##)((àÄOäý÷ßg0{öìaûöíüå/!66–„„ž~úi6mÚÄ|×;餓HII!11‘ï}ï{¾¶ûËó×½^ÏK/½ÄO<ÁE]ÄÂ… ùþ÷¿ïûÛN8³ÙŒF£á‚ .ès6™@÷G·¥K—’••EFF÷Ýw¯¾új¿ó0ƒÙ{öìa×®]<õÔSÄÄÄ`2™|£‚Ý—ÁÜÇF^^±±±ÄÄİhÑ"öìÙÓknñgœA\\\Ÿµ:òóó±Ûí>½^ÏäÉ“ì£BˆÐ3™L<òÈ#œvÚi¬Y³†U«úŽ~æ™gX»v-§v<òHH #ßé}ZZ…¿ª*¾SÚÒõå"!3áñxñz½˜Ífâââ˜;w.‹-bêÔ©@×ëú¢E‹˜3g†ŽŽìv{ȦS¨Nûá5Dœ¨Övp9P̱hÇÏij{ Þ²h&ÏGÕhÀãÄkí躮Óѵ˜k©Í¯ÐÞi¡Ýâ¡Ýâ¡Ãâ¥ÓêÅbõ¢Fä—ñ½¶×ŸÚ>GÜK|¿'œ}~¯ËÛ>û(¤íy½^²²²èè褦¦†ŠŠ š››©««£´´—ËET”ÖÖ6ßû·PŽÚè/ËétÒÚÚê›òe4‰ˆˆôïB­ç‚¬á0ì2Óc<è=21w|8¼Ójâñx|Ójôz=™éCn/wÂÊÊË)Ý_æwôH·ÎÎN{òOLÌÖ¹P¡ Q4,Ÿv:Ë§ÎÆÒ/|#Bê:[°ºìd?Ú»:ÚøB¶×3.:‘(~Øíh­óÛN¸DGFÑhõÿa*Y±)8=.ZíLˆÇ?/ÿ=~ð"¿xûÏÜ:ÿRVœp!zmDŸ¿+**bâĉèõú·õéstt¯'™˜˜߇áîy¸III½þæŒ3ÎôvTUU‘žžNtt´o[÷ï$=½ïc&::šêêþ‹KƒõeæÌ™\qÅ<ÿüó¾Ðõ$úì³Ïò÷¿ÿ¢¢"\.WŸQGöÁßþèOvv6‡ƒÖÖÖ>#y‚Ù]CÓ‰ŠŠêsÙP÷å@·#c#66ǃÝntŽˆˆ¦M›Æž={HOO§¸¸˜ë¯¿èº¾úê+vìØACC§Ï‹]Ommm¸\.þð‡?ôÚÞ=%(¾÷9à i[[ÑÑѽò¢££‰ŒŒ¤½½= Uù»WPÔ‘Å%½^ÔBˆ£ÕÍ7ߌÕjeË–-\}õÕ¾ˆÅbáã?fîܹÜ|óÍ!mSk2aÛ½›²Ÿ¬èšFóñÇxí6Joüº˜:¶~‚»£ƒý7߀6.æµkHûÉÏp75†æTºªJTTÑÑÑX­V¶oߎÁ` ==üü|Š‹‹©­­Åf³ÑÞÞî›Zôû^%X^§½ëL5‘ ^k;( ^¯Š2i|¹%.%k ÞÒí ‚jë@ÕéPÝ¡]wÂëjÄR÷..‡¯.‡Ç…ÅZçBnÁÐhÃ{F>KÑ.œµÕT§kzUÈNåÜO†Åbéõ¥ß©§žJ||÷Í7ß0þüA·cܸqÄÅÅÑÐÐà÷?ò ªû÷ÌÌL:„Åbñ½É¨¯¯§££cH‹™ Ö—ÒÒRÖ¬YìY³¸çž{xñÅ®³mÚ´‰‡~˜õëדŸŸÏ¶mÛ¸è¢ÐÍ-++Ãl6÷š{Û½ÙÝÒÓÓ©««Ãårõ)„z_B`ÇFKK ‘‘‘DEEôBTXXÈš5k|£UºG6•——³eË®ºê*©©©á•WüŸ;Þl6c0¸ë®»|oÄ‚í{0bbbèèèÀétúŠ‹§Ó’Ó•K„bäF’’’0™L0mÚ4’’’üRe¨ºdíܺշ «¢ÕÑñÉPU4‘‘h##iÿhK×t»¯ÍŠêp ôóÚ8”ö n·›öövÜn76› EQÈÏϧ¾¾žÒÒRŒF#Æ7bÄëõ{½ÕuøT¾-j{38¬¨n¤d£‰ÔCêxT½±ëÌ4¶¼Ö60˜P]”Ž ±×¿‰¥ÓŽªK!iÊaiÞCýö?àpATü âÓO Y[þ4½½–Œ›ѧøa)ÚÅþ»oºNëÛ¾ýsbæž’6 ‡“ýû÷“••Eié~víÚÛí&++óðÙ‰ Øl6¢¢Œx•vHr'LðHú›ƒn¡.Œ,¿`&£‘÷6}ÀÆ÷7ù½ÞÆ÷7ùfË£FÚn}s%U|Mƒµ•CM<üÑ?8ÐVÏ ³˜‘šË¸èDîÛü<6·¯êåÛÆJÚf¤æ2!~|ðN ‡ÇÅ—Õ]S Òc’0è"ymχØÝNvÚGEË!¿ý¨‘­7R\_Õe§Ãiõ» `Ç¡¼ª—zK+~ð×Ï9çáÛïUUÆÇ#'.&k^¯—wÞy‡úú®³µ´´P__ßkšAÛ‚5sæLÒÓÓùío‹ÍfÃëõR\\L[[ÐõÄÚsŠJÏß ˜1c·ß~;‹…ŽŽn»í6–-[FFFF¿íõäv»¹ãŽ;سgÏ }ñx<\wÝuÜ~ûíüûßÿfݺu¬]»èuIVV^¯—?þxÈû£ÛöíÛñz½Ô××ó»ßýŽ›nºÉ÷¦¡ç>¨ÏýݾÜÜ\~÷»ßát:q8|ñÅ!Ù—GèØ¨©©AUU, |ðsæÌ xmNNŠ¢ðþûïSXøÝ­¶¶6´Z-±±±¨ªÚkdtš¨¯¯Çåráp8HMM%::šÍ›7ãr¹PU•††ìv{HŽë#¥¦¦’’’Â;ï¼ãÛ÷6l ??ß7â¦çãm0GÞž¡ÐétN=BZ•••äää°jÕ*zè!zè!V­ZENNNŸE[CAUUMTZ“ åpBk4vý~ø¬gZ“ ­^ODrMk^£sû—èzŒ$Fƒ×Û5½F§Ó¡Óé|#EµZ-z½Þwöµî÷111!Y<\u9Q]]Óe0Ç¡[º" ¨n7ʲ[Qæ‹·­%1í÷n]$ªÃŠêv¢ºìÃn@õºi9ø6v·™ø©•I츳Q “°Ú5dM»%Ì_°:ëjhýd3±óö¹Ì4mf¯Óú6½õZÈÚÕétäç瑟Ÿx¡üeœuÖ™,Yò=/^LLL ™™™äåå‘–Ö5½yúôiCzßÙŸ# V«ÇCRRIIIèõzöìÙƒÅbñõ7""â˜X5dGÔhHÂQ®Ûõ“]#{ò¿ùãʧ(Ýÿݨ;wïæ+Ÿâ±'ÿ€_Ü~i=Öš¥©þ§õ tYO.¯‡½‰»Þþ ÓŸ¼š“Ÿ¹‰¯•ðöu““B„FÇ¿®zo*™ºò*r»ŒÖ>BEË!´Š†W¯¸ŸoêÊÉ{âûL{ò<ôá‹8=.ôÚþ|ÁÏùÖ—É}ì2îÛüw2cý¯>P;#áò‚3»FÊ<ùžüôÿünøwѦí ÿoá58NŠŠŠhhhº¦äææöæØß¶`EDD°~ýzŠŠŠÈÉÉ!--«¯¾Ú·öý÷ÞËu×]ç[Œ´çï†uëÖÑÒÒBnn.Ó¦MÃl6óü# ¶;;;yíµ×(..´/O<ñ6›»ï¾›ììlžzê)V¬XAmm-W]usçÎeüøñÌ™3'$O¨kÖ¬!''‡‚‚fΜÉ#<⻬ç>¨ÏGÞ>­V˺uëØ½{7L˜0{ï½§Ó9ì}y¤Ž¢¢"V®\ÉÓO?Mjj*gŸÝ÷ÌUþ(ŠÂÌ™3ihhð-¨]ÅôôtV®\Ùï|î‚‚X¹r¥oqÞ«®ºÊ·íü#k×®¥¥¥%$Çuý¾òÊ+±Ùl<õÔSüå/!22Ò·0ß‘·Áy{†âŒ3Îàõ×_ç믿Òß !„œÅb¡ªªŠíÛ·³mÛ6.¾øb.¾øb¶mÛÆöíÛ©¬¬ô}H EQ¾[$ñð>UU»¶õó;t­8¢¨*„hÝîѪƒÁ·({^^Š¢ÐÚÚê+žtO¥Ðh4äççõ;ª3XªËêv Ú­iDÍ;±ë¦Ú;Q3Q£Ì] µFF¡NšŠÕa§¯+4kŽX[¿¡½µ‰Ä©wcˆÎ@ÑèÈœñcÌÉ'‘V0HÂðØ*J©zì÷¸šû¾Çp··=ûßï;¾ éÝ7p··…¤}EQ|S#""HLL$!!ÁïçÛˆˆˆ>Ó‚‡JUUâââ|ÇRee%;vìðýìÚµË÷¾KQRSS™9sfÀ£™‡ã¥Õ¯°øü yiµÿQÎá,]ºTý׿þEMM ;vì`áÂÓ‡ØÙÙÙk ’¡ž•&XÝg± ea¤§O?ûœÇV>…Åϼu“ÑÈOVÜÈâ³Ï :{ûömœ{î²ávñ¸òÊ+¯på•á¡c¾ïl¶ÿìy&'e9£ªª ƒÁ@JJÊ€ÛÄð)ŠBqq1S¦Lí® ™¿cãÞ{ïå–[né³FÊX"ǵBˆPûòË/Y¹r%sçÎåšk®ñ¦·¡¡—^z‰íÛ·s÷ÝwûJ÷gÆõÌ{€×éVû½À¿|èOÚ»ƒ/Ž?;¾¦³³“èèh ˆÇ`0 ( EEElß¾Ý÷M½¢(h4&MšDjjJ×—a~:ñ‰7Ï5?\òz¿+ú¨^|ëϾLU”ÿv-bûåÿ»lXíTó,Š6‚ô)?<⇭}Tü°Ûð§áõÕúÇ3}¶Ï|} Ð{JMº¯JÝE‡P-º;ØØ®ÑG‡ƒööö ÑÑÑ}¾kkk [ߟ¡ïÿßü÷°ó>úh ³gÏ&--‹/¾xøkŽ©ç$#=ª&\…€SæŸLaÁ ^c=Ÿ~öÊÊË1Lœ8‚–_°lÔÖYc[ôIUÎ IDATvvßÂJÛ„€£ûØ8šû.„bl²Z­ÜqÇÌ›7¯×öäädî¼óN¾üòËOuT z°;]‹®Ï߆â³ÙD[[---466b6›éèè ¬¬Œ²²²^ëŸÅÅÅ’››Ktt4mm]g/îY{ÔÃgœùîC±Úk$M÷¿*jï"JÏˇÉár3~êõý\¢„µ0¼ü*’—_å÷rÓ´™a)€ d$Š"ݺïËÈÈÈ€¾”É©4c~AÖþ˜ÍfžùÓÈŒéÈYl†Ël6‡}!„B!D—ÓOxTû‘E“P0ÿðz:_|lA®ŸeÀ|݇Ý~÷à¬V ¦¦Æ·þHRR‚Ñh"!!žøø®BA÷TˆPðMéUðø®Òó²ï $ >©q(¤L8&,UÅQ,ÜŸÅåˆG¥‘˜R#„B!Ž?¦å—bZ~騵A^^^PÊ‘Ûïñ?jb¤˜ce4êhiѺ)G#)Ž1€ÎßoΨYGû Ù¹÷Þ{G» B!„B³Â{þ#!„B!„Bˆ1NŠ#c€N§Ãf³v7„B!„8æ´µµ†å„ Bˆc‹GÆ€ÄÄ$vïÞ9ÚÝB!„â˜ÒÖÖÊ·ß;Ú]BŒq²æÈ™™Å¡C5lÜø6n·{´»#„B!Ä1A§‹ 66†¬,YàS10)ŽŒãÆ¥3n\úhwC!„B!„8îÈ´!„B!„Bפ8"„B!„BˆãšG„B!„Bq\ëwÍ‘O>Ù2ÒýB!„B!„»SO=½Ï¶~‹#—\ryØ;#D^~ùe.ºè¢Ñî†B!„Bˆc”ÅÒÙg›L«B!„B!ÄqMŠ#B!„B!„8®IqD!„B!„Ç5)Ž!„B!„â¸ÖBˆþ½üòËtÝ©S§rõÕWœ½wï^&Ož<Ô® !„B!DÀäóGoR"ÅÅÅ<ôÐC]÷î»ïso„B!„B„‚GÄQkãÆlݺ—Ë5àõt:óçÏgÉ’%#ԳѳnÝ:Þ~ûmEéµ=??Ÿ;3§Ÿ~š9sæpòÉ'R» LiiiÄÇÇh»µµµ¬]»–}ûöáõzÉÌÌä¼óÎcúôé#ÚÛo¿ßÿþ÷#~ûßyç>ùä¬V+rÙe—1¢ý ëÖ­cÓ¦MÜÿý½öû<ÀøC²²²üû­[·²`Á‚÷k¤ëÖ­ãwÞA«Õ`4™5k—^z)z½>¨¬Ï>ûŒýë_œrÊ)\rÉ%áèîQ§ûy÷Á$99Ù·½µµ•_ÿú×,Y²ä˜?Eý®]»xþùç¹òÊ+9餓†•uàÀV­ZŃ>ÈŸÿügæÍ›7äÇHÏÇîm·ÝÆ}÷Ý7âϽþŒÖsrEEÏ=÷>ø õõõ´··3iÒ¤°¶ÙŸP3ݯ§mmm£~›V¯^Í[o½EGG‘‘‘œrÊ)üô§?íõ»uëV~øan¿ývÎ>ûlßöçž{Ž—_~™Õ«W“žžîÛÞØØÈe—]Æ~ðn¼ñFž{î9š››¹ë®»Fô¶ ˆO>ù„O>ù„… 2þüÑîÎIqDµ¶nÝʽ÷ÞÐu÷»ßÅ€SN9…k¯½v´»á×æÍ›Yºt鈾AmiiáÑGeþüù\vÙe˜L&¾þúkþö·¿qË-·——7b} _|ñ_|ñ?ÿùω§££ƒU«V±~ýz.¾øâÑîÞˆ0›Í¬Y³†o¼1¨¿SU•µk׆¥82’,Xà{^hkkcÕªUlذåË—•³sçN–/_Îi§ŽnµâããÙ¶mÿŸ½ûޝéþ8þºÙ‘½C„ˆØDùÖ(-1K)ªHP¤ö,JÍÖVj«¨Ñ E"¡b6 41b…‰™Ù¿?ò¸ç—››óó|<òxäžsÏ9Ÿsî™ïóù¼?]»v•†ýûï¿èëë¿ÁR½>AAA¸¹¹\îàH^ƒFKK«LÓæ?vüñÇ·æ÷x“çd;;;¦M›ä(233ßHpD•ûÌ›^§€€X¹r%ÄÅÅ1{öl¶mÛÆðáÃ¥ï=z”¯¿þšcÇŽ)G,--9q┆͓'Optt¤_¿~@îMÈHOOÇÌÌŒ¯¿þÖ­[GÕªU¹ÿ>ñññãååÅñãǹví?¦wïÞhii±wï^233QWW§wïÞ4hÐ@åëzüøqìííùâ‹/¤a-[¶D___:Á>zôˆ;v””„††=zô Q£FÒöhР‘‘‘¼xñ‚¾}ûR¯^=ÂÃà ,RR;wî$** 555š7oÎ矮T®Â¶ŸªEGGS£F ) e``Àˆ#¤FE•wÔ¨Q,X°###…Ïñññlß¾*UªÀøñã æÐ¡CdffR»vmˆ¦¦f¡ëYØö«­Zµâܹsܾ}»À›æÂއõëד˜˜Èœ9s°²²¢Aƒ´nÝš„„¦L™Â¸qã¨[·.÷ïßgÛ¶mÌš5‹K—.qèÐ!²³³144dÀ€X[[³víZlllV `†……áëëËÔ©S+dÈËÈȈ:päÈzöì)ÝTܽ{—ììlºtéBëÖ­Ü7³]ºt᯿þâÓO?åúõëDFFòòåKÚµkWè~“wº 0uêTºwïNxx8111|ñÅü÷ßÄÄÄ “É;v,ÚÚÚܹs‡]»vñêÕ+444øòË/©]»v‘ÇaRRÛ¶mãÞ½{èééѧOêÕ«Wäz©š““çÏŸWŽ\¼x'''ésqÛ¹$Û§°óÔƒŽÇ””:uêDÓ¦MÜ Ö¡C‡˜9s¦Ê×=))‰èèhFŒÁ©S§ˆ‹‹“èV¯^M•*Uxøð!ÉÉÉØØØ0`ÀÔÕÕ3f ]»våÖ­[ÄÅÅѺukÚµk§0ï-[¶H5Gä똀……žžžXXXºÏä=vÇŽËœ9s¤š#…£§OŸrýúu:uê„……C† ¡I“&ìÙ³‡¯¾úŠùóçóõ×_Z!ë!ÝðåÕ°aClmmÉÉÉá—_~¡M›6Ì›7!C†ð믿’””$mGGGÆO=8|ø0@¡å?pà:::Ì›7©S§LXX˜Â²‹Ú~ªÖ A.\¸ÀüÁÝ»wÉÊÊÂÀÀ@z‹Z’òæ§®®Î“'OhРãÇçÙ³gìÞ½›ñãdzpáBRSS9qâD‘ëùº~ÈÝ'{õêÅîÝ»ÉÉÉQWÔñ0hÐ 444˜3g 4àÎ;ܺu‹5jHŸoß¾““ÏŸ?ç·ß~ÃËË‹yóæÑ´iS¶nÝ*m³ˆˆæÎKݺu¥åÇÄİsçNFŽYᑼ묡‘û~äÏ?ÿ$''‡¹sç2}útüüü¤› 555’’’X¶lÝ»w§víÚ¸¹¹áêêZä~“w:mmmÔÔÔÈÌÌdìØ±téÒ…Í›7Ó¹sg¾ýö[ÔÕÕ¹zõ*;vì C‡üðÃtîÜ™;vHó+ì8Ü·oæææ,]ºOOO6mÚDFFF‘ë¥jhjjòàÁžªu¿xñ"ÎÎÎÈd2š7oÎùóç¥qêêêܹs‡Ñ£G3}útbbb¸té’4.%%…±cÇ2aÂ<ÈË—/ ]ΦM›èÚµ+K–,¡AƒìÚµ (|ŸÉ{ìšššJó)î½rå îîîLž<{{{Ξ=«êMVì9¹¨}¥°íP˜üû†œ““Íš5ÃÅÅ…^½z½5ûLa²³³Ù¶m}ûöeÞ¼yèëëóêÕ+…ïä_§+W®ÀüùóùñÇ122’ŽQUjÑ¢ÇgÆ „‡‡“™™‰‰‰‰¨‚Ü@GÛ¶m‘Éd¸¸¸püøq…yT©R---"""€Ü6éééå~ .¯KëÖ­™6mZ…5©éׯvvvDEE±mÛ6RSS#vvvÒËÛò(SpdΜ91Êú'‹ާ§g¹W `ëÖ­¬\¹’—/_rãÆ ÜÝÝIKKSɼ sëÖ-† Âljeܸq áÍ)iÕ‚Ö;MI{­yÝ‚‚‚5j”Â_PPÂwbcc‰‰‰‘ÞDÙÛÛcbbÂíÛ·ÜœòãÒÜÜGGG455ÑÔÔÄÈȈøøx¨Zµ*¶¶¶´iÓ†°°0©–NÆ QWWG&“IÕLó322âÂ… <}ú[[Û kôêÕ+ ÿôéSâãã¥7Q¶¶¶ØØØHÛCGGGz˜µ´´”Ö¥°ò‡……I7A•*U¢iÓ¦JûKqÛO•j֬ɤI“HNNæ—_~aâĉlÛ¶¤¤¤—· ÙÙÙÒÍóõëשY³&¨©©ñõ×_Ó¾}û"×óuýþ hÚ´)ZZZüóÏ? ãŠ;äj×®ÍÝ»wÜó}›6m”‚#7nÜ V­ZX[[¹7 ‘‘‘¤¦¦¹EyÛ §¦¦²aà  ÐÖ¼"ÅÅÅ@Æ ¸témÚ´AMM }}}š6mª¨jܸ±R#(~¿É?]ýúõÜsŠ™™–––Òçøøx¾ûî;š7oä>è<{öLš¾°ã0,,Œ-Z “ɰ··çÇDSS³ØõRµ¼—/^”Þ|ËWžâ¶Oq穼ǣü-{JJ 999\½zµÀ±*K9A zÐuvv–®uêÔQ8®ä¿µ¶¶¶Òñ•_ll,‰‰‰Ò[ô:HÍŠÚg RÜ1êàà ]/òîgªTÜ9¹°}¥¨íP”¼ûFaÞ¦}¦ Ož«V­"!!¹sçâêêÊâÅ‹öcǎѱcG€ƒ#íÛ·çĉœ8q‚öíÛ«¼¬‚ð®ÒÕÕÅÃÃC!@’70’·6Iy”«Y±±±t3¬¯¯††êêê>|XºÑ‘ëÙ³'•*U’>gee‘€ŸŸŸ4¬Zµj*KŽ˜ÿ­ç‹/xôè5jÔPÉü rýúu¥›°°0é¢-T¼M›6ajjJõêÕª¬–WAÁ•·µ7š¼¹ “ššJff¦BuÙW¯^‘””„¾¾>:::Òp555…*¨2™Œììl’““¹{÷.ß}÷4N[[[º¹Ë?‚nb† ‚ŸŸK—.¥R¥J¸»»K7>ªdll\äɤ¤$ôôôäôôôHJJÂÂÂBáaVMMììì"ËŸ˜˜¨Ð¶½R¥J<|øPa™Em¿¼o›T¥ZµjÒ~ÃüÁ¯¿þÊØ±cKTÞ‚TªTIÚfÉÉÉèééIãäÛ¬¨õ|]¿^}ûöeõêÕ 7ýEyYXX™™)­S=8vìÙÙÙÜ»wAƒqæÌ…m©©©‰––‰‰‰J9äoˆ+º]yPPô¢§§ÇG}$ݨ§¤¤°iÓ&)akFF†ÂƒTayŠÛoòO'ß'ÔÔÔ =¦®\¹Â‰'ÈÎΖþòOŸš””…û ù~XÜz©ZóæÍY¼x1½{÷æâÅ‹Œ=šsçÎIã‹+OqÛ§¸óTÞãÑØØ{{{BCC±´´ÄÜÜ\!Y¬ª<~ü˜û÷ï³hÑ"iØ«W¯¸ÿ>ÕªUPømtttxòä‰ô9אַ®.))).GþË×O]]]ÚŽEí3É¿ßæ?FKríR…¢ÎÉ…í+Em‡¢ä¦0oÓ>S¤¤$…}Iþ»ÅÁÁ>}úð×_ñ믿ҤIúõëWêDÔ%áèè(%J}ðà6l`Á‚,Y²„ÈÈH"""9r¤ôý””"""ptt”†µoßžQ£F1bÄNž<ÉÂ… 9zô¨ÊË*áu$d•Hò6£Qe`ÊÙµkWI.ëׯ¯Yµj•ôæ0/U·ã”ëܹ3+V¬>;99Q½zõ Y–ÜÇ,½U‡Ü ì§Ÿ~Z¡Ë™ššBHH{±¯Q£†Êƒ%ï:ccctuuY°`Ò¸G•xNNN ûÒ222âË/¿äË/¿äÊ•+x{{óÓO?)´ÓU…Zµj¬”DòÂ… hhh`ggGrr2999 y8ŠkâPXù INN–ÎoÉÉÉJóRÅö+©ÐÐPìíí¥pkkkºtéºuëŠ,oÞ‡ƒŒŒ 233¥ùæ½ÙÖ××—.äóHKK+v=_Ç 6ÄÏÏOáA²°ã!€ÄÑÑ‘k×®¹iBCC111AWW©6 än³ôôti{æ@i×®&&&lݺ•©S§–èA§,Š š3bĈB«oöPUÜ~^ÜÃX~‰‰‰lÙ²…3fP¹rež?^¢ ´|ß“·ß‰‰ÁÌ̬ØõR5ÌÍÍ9{ö,ººº ù øí\ƒ"ÏSù·wóæÍ Á¢ÂjÈ“jæ½  88XzÐÍ{ ÉSÜ1ú:wN.l_yúôiÛÁÚÚºÄçꢼ ûŒüŒœ¼Fžžžô?äTÒÓÓ‹]^Ó¦MiÚ´)IIIlÚ´‰Ó§OÓ©S'®üý÷ßÔ©SGÚ—«V­ÊW_}%Üå‰Xû÷ï/M³{÷nŽ;¦±´´¤råÊ:t===*W®¬Òr BEªè„¬¯K¹î@mllÊ]€ŠªFܱcG)xãééÉÑ£G+ô†þ?Ó´»»»ôF±"kªÊòÀâââ áèÑ£%zþ¡ßÄË«€'&&âíí­pãQœºuër÷î]bbb€Ü„d¾¾¾ÅN§¡¡AJJ iii,Z´HªÑ!?”öª$\\\ˆ‰‰aûöí7näùóç 6ì­ØgÚ·o··7·nÝÂÌÌŒºuë’““ƒºº:ƒ bçÎ@ndJͨò®S¿~ýضmÓ§OGMMêÕ«Wȃۄ Ø´i#GŽ$%%===š5kÆ7ß|ÿÿþK¥J•”Žõ*Uª`ff¦”›°mÛ¶¬Y³†Ï>û¬Àeùùù)4µ©[·.kÖ¬Qù: BiµnݺÂz„åÀˆü¾@>,/6e%ëÒ¥KΞ={ˆŽŽ&44”¶mÛ`ee]äD«V­â—_~)´7ƒúõëKE¹¨¨¨ƒ)S§Nåßÿ•-[¶Œ)S¦(|'>>¾ÈdŠÂûÃÇÇGzÈ(ÎŒ3 ̲téR…ÅvíÚ•+©UaË)lxIÇçwóæMj×®]¦2 ‚ B~>>>ØÚÚ¾±&¾ëÖ­ÃÙÙYJ¾™×øñã™={¶hîú–yÓûŒ ¯×»òü±eËnß¾­”c$oÐÄÁÁ¡T]ù&''qæÌYš4i‚µµ5½zõ*[³š±cÇÛÍcI-^¼X ŒF[[›øøxV®\IÛ¶m133ÃÔÔ”V­ZIÕ ó*i—Àiii¬\¹’–-[bdd„––899ñù矗ª«Önݺ¸Ì(}744ªW¯Ž––ØÙÙÑ¢E  ¤”¯¥8gÏž¥ÿþ4mÚ 444ÉdhkkcggG«V­X´h‘RÛùw¦¦fÃåµGêׯO»ví8yòd±û˜ ‚ ¼O?~Lxx¸H /”˜ØgA(«ÀÀ@-Z¤ˆ\•\\\pppPª"OÒêàà€‹‹K¹—S®f5¯Ë„ رc‡RUË   ‚‚‚ÈÉÉ¡oß¾Òð‹/2oÞ¼"Ûï?þ._¾Œ¹¹9žžž˜˜˜Îáǹyó&•+WfÀ€%*£··7[·neúôéE~oÓ¦Mxyy‘M‡ððð !!cÇŽLpp0 ,(Uo.\`×®]èêêÒ­[7êׯ¦¦&>>>\»v‡Äo¿ýFPPP…ô„ñ&´lÙ’9sæ‘‘ü2Õ«WçÆ MiNž< ºEAÞ{û÷ïçüùóôïß¿Ð$§‚—ØgA(ŠNÈjggWh­]]ÝRÕ)Ê;ÑÓÓãàÁƒ˜˜˜ðçŸ2{öl…®Õ~øá…àH:uhÚ´i‘Á‘)S¦pùòe ·+ż?âÇKˆÊÚÚ—"ƒ#wîÜaÔ¨QdggÓ±cGŽ;¦0~æÌ™¥j†‘—¦¦&wîÜQÈl=|øpªW¯.Õ¹víË–-cþüùeZÆÛ¦cÇŽRW”yÕ¨QC!0¢Š€ÈÛÚm¯ ‚ ä׳gOzöìù¦‹Qdo\+W®|%Šó¶ì3‚ ¼›Z·nM```…&d}Þ‰àÈŒ3¤îÎä­:$ #55µT Xþøãéÿüý¸ÛÚÚÒ½{÷r–ZÙŸþ)Õr(¨ïø#F”)8bbbÂW_}¥Ôå—™™ 6THΙ?QçûÈÄQÌh² IDATÄD© sy$µjÕâÖ­[Ž+*h"Ï:/‚ ‚ ‚ð¾ªè„¬¯Ë;ɯE‹ ÁÈí6«¤Á‘ììl¥ÏC‡å×_¥fÍšÒ°Ÿ~úI d¨JÞœ'{öìá“O>aèСhhäþ vvvÜ¿¿Ôýš:”¡C‡8.¶ò²À …{]Yìß…dH‚ ‚ ‚ ¼Äó‡¢2%d}Ó êž,o3›â¨©©agg'}>{ö,µkׯÅÅ… 6‡¡¡¡Ê»1«^½ºôzz:^^^T«V1cÆ @ÕªUQWW/×rRRRxòä 111¤§§+Œ+ÍvAAA„Á;Q…Aƒ)|ÎÊÊâĉ|óÍ7T®\™iÓ¦‘––¦Òeººº*%CŽŽfÍš5´hÑ‚Æ+õw^·o߯ËË‹š5k¢¥¥…žžVVVT®\™S§N©ªø‚ ‚ ‚ ‚ð^ú`ƒ#3gΤK—.Ž{õê‹/ÆÍÍM¥5-ÌÌÌðññ)°æ À•+WhÙ²%%žgPP7fãÆÜ½{—ŒŒ ú÷ïÏÎ;ñ÷÷/ubYAAAAøÐ¼“9GTA[[???¶oßΊ+¤žkò:zô(¾¾¾|ùå—*[n·nÝgîܹüþûïRO2rYYY :”›7o–h~^^^$''KŸ]]]Ù±c‡ôYdƒAA„ÒÙü~½Cîÿqö›.‚ ¼õ>ØàˆÜ Aƒ4hׯ_g×®]¬_¿žgÏžIãOœ8¡Òàä&^õööfÕªUøûû³iÓ&…n}#""xøð!¶¶¶EÎçáÇü÷ß ÃÜÝÝUZÖ7¡¤!AAA¨;ÎÕ{ÓEP©¦&×ßtá­bkk£4ìƒ ŽtëÖ ooo¬­­¨S§óæÍcòäÉ|òÉ'\½zÈmb£*;wî$&&†‰'P©R%z÷îMïÞ½Ù¶m›B¯(%YîÓ§O•†™ššª¬¼oŠh $‚ðá ÁÙÙùMC%Þåuy—Ë.áÌ{w;úÞ­ ”KllŒÒ°÷«¾X)9r???¥á†††ôîÝ[úìàà ²eFDDÚÌeÀ€R—¾:::ÅÖ¤ÀN^ÏŸ?/_!AAAáóÁGV­ZETT”°ììlNž< €ºººÊ›ÔDEE±fÍ¥D¯§OŸ&33€Þ½{£££Sì¼*W®LåÊ•†íÙ³Gú?33S)§‰ ‚ ‚ ‚ ŠTÒ¬&==K—.Idd$11ÊUT–/_Nƒ ¨^½:7.°ùǽ{÷¤æ,yùä=z¤4¼wïÞ¬_¿///ȉ'”¾3`ÀÆÇ?ü@Æ ,àAƒèÝ»7{÷îeûöíœ={–+W®pöìY:„¦¦&666 <˜Ñ£G—ªímÏž= ȬX±‚íÛ·óìÙ3ú÷ïššçÏŸçæÍ›lÚ´‰´´4ŒŒŒhÞ¼9½{÷føðá%ª5"7xð`ÌÍÍY¾|9¡¡¡¤¦¦òøñcìììØ°aÚÚÚtëÖMú~TT]ºtáøñ㸸¸”x9‚ ‚ ›Ÿä}$r®»éêÕ«lÛ¶¬¬¬ÇËd2lmméß¿¿R­û’ŠŽŽæÄ‰dggÓ®];ìììÊSä"Ý»w}ûö‘““ƒƒƒnnn*_FFFqqqR¥!—J‚#NNNJÍDÊâáÇÅ~§ Úù•¤,dàÀ%*WIüûï¿Å~ÇÑÑ‘Y³f©l™rÝ»w§{÷î…ŽWÅo#‚ ‚ ÷¾Þ×€ |}ºB×ACC[[[*W®Œ¹¹9cÆŒ!;;»B—ù:¹¸¸`aa ÆÆÆtíÚ•ØØØ7]¬wNvv6OŸ>}ÓÅò˜6mnnnRžÆjÕª‘––V¢iÛ´iƒ©©)›7o&<<œ»wïrèÐ!Nž<‰µµ5FFF*)cNN!!!:tH¡gÖ„„:„¿¿¿R/¤¥åèèHÇŽ©W¯ž4L__ŸŽ;òé§Ÿ–ª&‡\ZZIIIeú+i^ÌK—.ñòåK5j¤8räGŽAGG‡:%ËQZQsDx§¨©©Ñ´iSnܸA||<ýû÷'))‰—/_âããÃ…  <<œÖ­[³zõjÒÒÒøòË/‰ˆˆ@]]£GØ•´ ‚ðv3f K–,ÁËË SSSfΜÉܹs™4iÉÉÉŒ1‚èèh²³³Yµjiii,_¾œÔÔT‚ƒƒñóó+ôzÌ”)SPSSC__ŸM›6Q¥Jîß¿ÏÀÉÌÌÄÒÒR%ë’€©©©ô6Rþ–ÎÛÛ[áúõÃ?(]ã Z'ggg°¶¶æèÑ£øúú²eË&NœÈÅ‹ÉÈÈ`üøñåªf\^‰‰‰|õÕWìÙ³‡¶mÛ’™™É¹sçªc8p€Ž;²ÿ~ÜÝݦ?{ö,7nÜÞ>þœ­[·ªì¡¤4äÁ˜/^àææÆ¾}û”ÊûºÍŸ?Ÿ–úoAöìÙçŸ~Jjj*³fÍbþüù¬Y³¦ü…|OdeeÃxøð!ׯ_çæÍ›}úàïïO½zõX¹r%S¦LaÇŽL˜0¡C‡âááÁÍ›7éÛ·o¹×eèСôéÓ‡Î;3yòdéZõõ×_+]¿ ºÆå_§‚DEEñ矚šéééå.wyDEEajjJûöíÜýÏ>ûLŸ““ÃáÇ9|ø0ÿûßÿHOOGKKK?uêTæÌ™ƒ¯¯/›ÈoðàÁlݺõµ®G^¦¦¦´iÓ†k×®½±2Èþøã‚‚‚ÐÓÓcòäÉLžRsÒPWW/q"[{{{BBB áÖ­[9r]]]ÆŽ«¹wï/^¼ jÕªeÊŸ"8"¼ÃÂÂÂhÕªÕ«WçäÉ“Òpuuu¥þ¯A„wß—_~Y`îŒ6mÚàããC›6mÉd$%%¡¯¯_¢ëA­Zµxúô)8::ràÀÚµkä¶oö÷÷ÇÃÃCeMSnܸ••&&&¤¤¤!57É[Þ’^㌠§J•* #///,--ñññy£ÁCCC.\È„ Xºt)ÎÎÎ\¹rccc8@TTK—.•’$®[·Ná!wðàÁ,\¸ÀÀ@®_¿þ¦VƒÈÈHnÞ¼ÉgŸ}Æ€€Üöû)))tëÖ €ÇcllLTTÑÑÑRõrkkëb›ZDFFÃĉÜ· ™™™tèÐyóæ‘@ïÞ½ËÔž¾8ò L½zõ0`€ôöõìÙ³\¿~ýû÷¹Ýp>}ú´Ð2éêêbaa@¯^½1biiiøùù1bÄ©ׄ hÞ¼¹´|CCCd2¦¦¦899ñìÙ37nÌãÇ™6mÆ “ò.V&+++•oÈ=¸ºº²yóf¥ i•*UØ¿?õëׯe %W³fMfÏž­4\&“©¤W9ÒÒÒ¨Y³¦TóÐÊÊŠ5jVâœòšy]ºt‰û÷ïcffFDD„RâÒœœ’““KÙºu+¡¡¡ÒgMMM<<<2d›£eýúõܺuKúNi‚/ÞÞÞRþ’‚Èd2455•†Ë»G.IsÀfÍšñçŸò÷ß“““S``$##Cz‰?ikiˆàˆðΜõë×3räH6nÜXâùäïá&##ooï"§)M𥸖––Ròô¼–/_^â—†††¸¹¹±gÏš7o®ÐtíîÝ»øøøC5®¥%‚#Â[oÑ¢E¯S§§NRîææ¦u9o5±·éQA(^a=eKÝùjiix³X£F …vÏ…]Ú¶mKPPÒôŽŽŽ\»v´´´2·_ίOŸ>ôéÓ§Àqù¯_]ãò¯“««+®®®Jßûûï¿UPZÕˆ%((ˆ®]»¢­­ÍåË— £N:ùä&L˜ÀÝ»w©Q£iii¤¥¥aggGµjÕ8rä={ö$99Y ˆ˜››óßÿ‘˜˜H¥J•xþü9û aaa!55ÊÉÉáÅ‹˜™™ñüùsš5kF³fÍèܹ3ÁÁÁ¸»»£©©©ð°® ½zõbåÊ•lß¾OOO:uêÄš5kX±b2™ŒgÏžann^`™7nÌ«W¯¤Äº»wï¦}ûöhjjÒ©S'~þùgúõëG¥J•X»v-Ÿþy‘eyõê2™ŒQ£Fáìḭ̀aØ9sf¡eR¥þù‡E‹ÆøñãÙ»w/ƒf×®]@îï¸qãF©¶™ðæÕªU‹•+WVør ™0a+W®dݺuØÙÙqÿþ}ìíí¥Ze%acc£Pk£ YYY¬_¿È­U§§§§ÒÞº444=z4+W®d„ Jy½Š¢§§WäøØØX¾ýö[¥á2™ŒJ•*•x9Ÿ}ö©©©>|˜3g΄™™©©©ÄÇǹµ†FŒQ®¦u"8"ÈØØXÚÑòzúô©Ê/<‚ ‚ð¶’Éd* Œ|¨Ù¾};ãÇr›7o¦fÍš8p€)S¦(|ßÅÅ…¡C‡²dÉ’R-§_¿~Òo¥®®Î;wT³077çÛo¿eÒ¤I:tˆ7Ò»woÔÕÕÉÈÈ`Ù²etèÐßÿ¯¿þšü)8Ò¨Q#ÜÜܨY³&Íš5£råÊhhh “ÉØ¹s'#GŽdþüùdggÓ¯_?¦M›ÆôéÓ9wîzzzÔ®]›Î;KëýÕW_áì쌿¿¿JrnÈd2–/_NÏž=éÝ»7sçÎeôèÑ4jÔ---ªV­Ê¾}û ,SLL ¸¹¹‹‰‰‰Ts«OŸ>ܼy“-Z ¥¥E:uŠ} ÇÃÃ]]] ˜7o@¡eR…¼A‘éÓ§³ÿ~)‡A»víØµküôÓOåêUx·™››3iÒ$~ÿýw>|HË–-qww/Õ5£¨€¹*XXX|ÉÌÌ”J+V¬@[[CCÃÍøðáå.cIuíÚ•zõêqüøq®]»FLL 2™ {{{ZµjÅÇ\îóŸ¬K—.9{öì!::šÐÐPÚ¶mƒ••uñS ïµ0oÞ<6oÞ¬0¼¢ƒ#>>>¥Š¶ ‚ ï¶œßt1Tâ]^—Ò”ý]^ÏÂTô:ÉóèëëS¦.&ß·oߦ[·nܸqãM¥ÔY¼x±2dˆRbÇßÿ–-[ÆèÑ£ßPI…¼‚‚‚عsg‰“pfff2lØ05jTÁ%{;¤¦¦*å-)Š––V™š¾Niiihii•¹ Sll gΜ¥I“&X[[Ó«W/QsD(XÕªUER)AA¨ˆä©‚ê2wî\nß¾­TS¤ ³fÍ‘·HË–-©_¿~‰{ÓÔÔ|¯”ùÉ»Â}ßhkk«|ž"8"‚ ‚ Â'009sæp÷î]¦M›V`M‘üúöíûšJ'”FI›BQTßø;æÒ¥K¨««#“ÉX¶lÙ›.Ž‚·¹l‚ ‚ BéÈ»™~Ÿ988¼õMjqqqaРAôéÓ‡ˆˆ†þN4%¡â|Ðg€œœFUhkoÒÛP6QTAAx_2{ölîÝ»Wâš"‚ |8>è³ÁæÍ›9þü›.FÞ†²iii½Ñå ‚ ‚ ByɃ"‘‘‘L:UEA(P™Î ^^^lܸQiø°aÃHMMåâŋͦM› "66–œœìììøì³Ï˜º|ù2ëׯçñãÇxzz²hÑ"ÔÕÕ¹zõê›.š o˜M¥aezú]¼x1®®®tïÞ]¡ÙÇÞ½{qwwgÔ¨Qhkk³~ýz._¾ÌñãÇ9~ü8{÷îeÏž=hjj‡´ÇÊ IDATàî™ëׯçøñãÒ|¸zõ*½zõ*´ŸúÓ§OÓ«W/^¾| @ݺuéÕ«ÇçÂ… \¸pÕ«WsðàAÚ´i#M÷øñcªW¯Îßÿ­0¿æÍ›Ó Aþ÷¿ÿIß?N÷îÝ9wî–––xzzräÈBCC eçΜ;w333…yÓ§O¢¢¢€Ü¦*...ØÚÚríÚ5þþûoþþûoÚ·o““S©Ë–ÍèÑ£Y¿~=›}¹_¿~„‡‡­[·Ø¹s'ýõ-Z´P˜§ŸŸ}ûö%)) uuu>ÿüs¬¬¬ˆ}c5WÄ͉ ‡å}:ï¿Ëëò¾uÏ[òº¿™5k111L›6¯¾ú uuõ7],AÞ"±±1JÃÊ122¢k×®J9)†΢E‹¤Ï}ûöÅÉɉ˜˜Ü=zô !!.\¸€žžÏž=£V­ZÄÅÅG= ÃÆÆ€7Ço¿ý¦0Ï/¾ø‚É“'+-ËÃÃC Œ9r„>úˆ“'OÒ¾}{nݺÅÔ©Sñöö–¾wíÚ5:uê$•Q&“±k×.…,×Ë—/WXfiËöÓO?I€ 0yòd©\¹2ÉÉÉ$%%áááÁ7¤š;áááôìÙ“ôôtiÚÝ»wÓ«W/éóÊ•+™0a‚Ò2+š££ãk_¦ ‚ðfDDD(Õð|W={öì]—gÏž½é"B¹òý÷ßóäɦN*‚"‚ ”J…öVcdd„§§§Â°•+W’‘‘QätÏŸ?ÇÕÕ ¼¼¼øã?˜;w®Âw,X zõê…žžæææôìÙSÇâŋ˴AAA9rDú\½zu>úè#Z·n­ Ú±c¯^½’>9R¡Œ}ûöUêþ+o9K+55•üQaØ_|€7–†ßºuK¡6ÊôéÓ#... AAAxТE FŒÁàÁƒ¹zõ*ƒ AJ¥Â»òÍÛœàéÓ§ÅV9ÍÌÌdêÔ©<|øõë×Ó«W/¦L™‚©©©ô={ö(LS»vm…Ïò&(r¾¾¾e)¾ÒrªU«&ý¯¥¥¥ÐÛ«W¯¸|ù2Û$èÌ™3 Ó2Diþ5jÔ [·nJÍqJ⯿þ"..Nú,“ɨZµªô9ïö¤Ú//_¾Äßß_aœ››[©—/‚ ‚Wdd$cÇŽåðáÃoº( €?þ˜qãÆñÍ7߈ ˆ åRá7íí핆…‡‡+äÍ(ÈÔ©SÑÑÑ‘>ëêêrøðaj֬ɽ{÷¤<#rùƒ ––– ŸŸ>}Êýû÷‚%qéÒ%…ÏòÚ)rÚÚÚRŽ€èèh…fB¸øøã \Æ¡C‡JU¦Âʦ«««P“E[[[a¼¼lÿþû/™™™ ãêׯ_¦2‚ ‚ðaKKKc÷îݬ^½šëׯ0cÆŒ7\*á}ÀÌ™3IJJâÛo¿ÍgAP‰ Ž* {þüy™æ%O(¤4NWWWásþ ä&b-mpDž/EÎÏÏO)×J^òä¦÷îÝSndd„A©–]Ú²¥¤¤”¨l÷ïßWgee¥Ò² ‚ ‚ð~»~ý:Ë—/çÔ©SÄÆÆ’œœŒL&cĈâ¾B¨̘1ƒäädAå*¼YMA'¬¼¹.Ê"%%¥Øå´\yp <˪V­…þÕ¨Q€øøx…éò×âP…üeÓÖÖ.²l-[¶ ÞZZZ*/Ÿ*:u ƒkëÖ­cÅŠÅ+/\]]¥Ï7oÞÄÀÀ@!YoIÊû¾y×5::WWW¬¬¬hÛ¶­R—}>ÄÚÚºÀĉS§NÅÀÀ€Ÿ~ú ÈMšÜ¤I,,,hÛ¶-.\Pø¾··7•+W&55µÔå;v,~~~ÒçyóæÑ¼yóB¿Ÿ••…ƒƒK—.-õ²òëС“&M*÷|@±\ÑÑÑxzzr÷îÝ2Í«¨éóƒåUç Èmj``À†«ºüryÁ°°0ºté‚ ÕªUcúôéR¯r§Nâ³Ï>ÃÒÒ’jժѫW/…`§OŸÆÀÀ€åË—+ÌߨØ ž>}* ;qâRÞ¬îÝ»3jÔ(¥²c``€‘‘µjÕbüøñ ¹¹T%**Š‘#GÒ²eKj×®M‡ؾ};«W¯¦Zµj ßÿ½Ò0ùßÍ›7U^¾’ðññ¡sçÎ899ѱcG)OXNN«W¯æã?æÓO?eïÞ½Ò4‘‘‘ôíÛ— 0tèP¥Zµ!!!Ÿþ™FѦM6oÞÌÝ»w¥úvvv,Y²¤ÂË!|XøßÿþÇ„ 5jW®\ÍgAP¹ ¯9RÐM‘‘Q¹æYP­¬¬¬"? ùAʺ¬† ²uëÖRO—77ˆªä_†––V‰Ê–¿– ü;½+.\¸@rr²BÏ: +¤¤$üýýY¶l™4lïÞ½ØÚÚÛDLxýfÏžÍõë×Y³f ëׯg̘1 9€¦L™BjjªÔ­¸ÜåË—Ù°aƒÔ«€……ãÆÃÊÊŠåË—3fÌ…n®÷îÝK·nÝ <®ŠsäÈjÖ¬YâïÿóÏ?ÄÆÆ¾uÉ“ó–+))‰?þøƒ±cÇ–i^…M_Ð1X^ª>O¥"Ê_SSS¬¬¬X²d ÑÑÑÌ›7>úˆŽ;Ò§O:uêÄøñãyöì>T8vïÞM¥J•صk'NT¨‰˜žžÎºuë˜={6K—.-ñCÉäÉ“iÙ²%aaaüøãT©R…o¿ýVeëüüùs>ÿüsjÕªÅäÉ“Ñ××çÊ•+ÄÆÆJß±²²â矖>›™™ÑµkW–-[†ººº´ØÙÙ©¬l¥ñôéS>ÿüsñõõÅËË‹K—.áïïÏÊ•+™3gÑÑÑLž<™ÚµkS¯^=<==©R¥ .dùòåL™2E¡×\|÷Ýw¤¦¦2eÊ  pAP¥ ?»T¨P{{{Ö®][j?•¥oJR¾|y¶oߎ§§'S§NÅÐÐÔÔTÉÍÍ¥oß¾ôìÙ“#F( ÿ =|ø0sæÌ!$$DÅÚªwïÞlÚ´‰ôôtüýý ÂÃÃCíµ¾L‘%Ç”)S¨W¯áááJ÷ŠweóæÍ±{÷núôéC‡˜6mÓ¦MSœ£««KÓ¦MŸêÕ«+þ733ÃÌÌLñ]__ÿez¦L™Âĉ騱#cÇŽ%33“¸¸8þüóOÚ´iÃàÁƒ™1cåË—çСCüõ×_DDD0kÖ,ºuëÆØ±c9{öìy“žžÎܹs¹~ý:‘‘‘j# _—¼/‹4ÿ6¾¾¾4jÔˆ)S¦0aÂ…¥ˆ ø|ð;Lpp°ÒwMMÍWš—…J•*©˜Ã—|øÕ‡} ¥\ÊJÑB½ˆ°°°2åkÞ¼¹JZ@@À×ÿ*^–M"‘¨Ä:QG½zõTÒ^^ÿS›6mB[[›7*êËÌÌdïÞ½|óÍ7¬X±B±;SGeÅŠüñÇlÙ²…¥K—*”Eøûû3~üxFŽÉ­[·øñÇÑÑÑ᫯¾"==+W®Ð§O† Æ“'OÊ,_É>1b—/_ÆÍÍåË—+ÞRgeeñÍ7ß0fÌêÔ©£$×æÍ›yøð!?üðƒÚqW«V-Ž9Â矮H÷öö¦\¹r´k×±XÌwß}ÇÝ»w™>}:‹-"&&F¥¬">Œ……sæÌ!::š-Zrˮٳg³zõjŽ?®0­/((àðáÃJcîöíÛÌ›7¥K—’••¥v,TªT‰5kÖððáC6lÈ·ß~«Vž˜˜.\ȰaÃØ½{75â·ß~ãúõëøøø”:WJÊ¥£££Pò;vŒèèh444X´hÁÁÁœ;wŽ2zôhž?®2î­¬¬ÔæÕ9¨n>H¥R¤R);wîÄÝݽÔy¤î>qñâEf̘ÁìÙ³9uêþþþ,X°€çÏŸ3`À*W®Ì_ýŶmÛ¨Zµj™æh%åߺu+7nÜÀÏÏ“'OâîîþÊ~z]ߨC"‘0aÂÄb1:tPÌÿ"מ#GŽ()rNž}úÞå166æÒ¥Kܹs‡nݺ©ì†WtÎüùóU,òÞ___ÜÜܘ:u*Ó¦MSì>#(Eþ>øæäÉ“Jß»wï®öGõMùì³Ï”¾?|øPéûÝ»w•¾÷ïß_點žžÊ6==]¥ž’BOž<áìÙ³¯•¯eË–T¯^])í§Ÿ~*õ­âÛÈæáᡲKÏÖ­[_[¾ƒƒƒÊƒáÁƒ_›ïŸ@×®]iÚ´)uëÖ%66===´µµÑÒÒÂÔÔ===µi%󻸸йsg‚‚‚¨T©ZZZ,Z´___6lÈc 2o}|æÌ¥1äííMµjÕpuuU¤µhÑ‚¶mÛÒ¹sçRåÕÕÕÅÞޞݻw³eËêÕ«§6 ä&Ì4lØÎ;ãêêʹsç”ÚãÖ­[äææ²dÉÚ·oObb"÷ïßÇÒÒ’ï¾û޹sçbaaÁ„ yœ”Áƒ3pà@@Y‰Ø«W/úö틉‰ 4 yóæ4iÒDéœZµjѰaC†ŽH$RQª]½z•üü|>ÿüs† ¨*HÏœ9CµjÕ?~<ööö¸»»“››ËÕ«Wáçç‡ _|ñÎÎÎôêÕ ???IIIaÑ¢E888ЪU+LLLÊ4GAõR½zuRSS™5kÑÑÑ899½²Ÿ^×7êøßÿþDZcÇ8xð öööˆD"~ùåN:…±±1C‡¥S§N «Ë½{÷âââ½{÷puueß¾}Jî©b±˜I“&)‚o–´¬{Ë—/§ÿþÌ›7yóæáéé©t¯xW²²²ÐÔ”{ Ò¯_?ŧ(†µµ5{÷îU|LMMß¹ÞÅÑ£GÙ·o?þø#ššš*ë‘H„L&S¤¹?ý-Ë:ãm±µµåÈ‘#4iÒDebggG¿~ýÈÍÍý Ÿ·c$ðÏG&“áãã£PŠLŸ>   ú÷ïÿÊþ½¤§§¿õç]ø Ê‘€€¥Àazzzjßʾ 3gÎTÚ çСC WšÄÄD¥@xÆÆÆ*oKuttTÌy÷îÝ«x›T´Lƒ T1dß¾}Jq:’““ñóóS¼¥‰Düúë¯J>Ù§NÂÓÓ“G)Ò¤R)ÑÑÑJ™e•M[[[a ^ÄÒ¥KYºt©’©uVVׯ_çÙ³gŠ´¢·¬E\¸pmÛ¶)¾çææ~´ teASSSm\™7AKK‹*Uª@ýúõ™7o P9ÏÇÇ---…ÒC*•*\jÊú£]$¯X,ÆÏÏ!C†°wï^5jDFF†JZZZ2™L©|‘H¤²-R4ž>}š¨¨(RSSþæEóÃÀÀ ÒÒÒ-j㢱[Ú\y\E²uéÒ…¨¨(bbbÈÈÈ Zµjjǽ:^žƒê(ímuYæÑëÎ-m̾®ì¢ö}YþrðàAÄb1Ÿ}ö«V­ze?QZß¼\ŸD"!44”U«V©Xj6mÚ”;wrîÜ9®^½Ê•+WHNNæÌ™3üõ×_¸»»³k×.âãã•âô 0€råÊ)ÜËʦM› D[[}}}444”îïJ5¸zõ*yyy´iÓ†¨¨(vìØ¡tÎËn5ÿÔ äþþþL™2… ФI*W®¬ô{‚££#ŽŽŽH$ÅšâáLjÅâ3E&“±`ÁBBBèÔ©“bcffÆŠ+„Y7¢¤RdΜ9‚RD@@à£ó^•#?ÿü3 4 GtéÒ…Ö­[+q¦¦¦øøø(YSpûömîÝ»§R–!!!Šü/S±bE:¤0뉉¡qãÆŒ3†Æ+Ùffføøø¨],¼¬0 ÃÖÖlmmuoÞ¼YiQž˜˜È€Ð××W¼ñµ°° cÇŽJ;ñ´iӆÇ+YwìÙ³‡jÕª¡¯¯‘‘šššØÛÛ«ìPVÙÆÇìÙ³?"‰„Y³faaa‘‘zzzÒ¨Q#îß¿¯(¯OŸ>* ðáÇãêêJÆ ±²²Rr§(búôé* æ¾¾>¡¡¡„„„(Ü0Ô¥ü-whh(>>>´lÙ’„„’““Y°`_|ñ…ÂõiçÎìÚµ ›ÃwéÒE÷ÚµkDGG+¹7”™LÆ­[·=z4óçÏ'!!ððp•´ˆˆÚ¶m‹¯¯/wîÜÁÏÏ;wîàááAbb"S§N%)) WWW,--Y·nOž<áÌ™3„……‘žžÎ·ß~Ëĉ aÇŽÄÅÅ!•J166æéÓ§oµ»JHH¡¡¡lÙ²?hÅç¸}û6îîî$$$°cÇ¢¢¢ðòò"''GIæŽ;ƺuëxöì—.]BWW—fÍš±k×.¢££Ù´i:::jÝÓJ£¨Ï´´´¨U«ëׯçàÁƒ¬X±±XL5ðññQÄ™¬$³¾¾>ׯ_çñãǯÜí'''‡£GªXN”¤´±ðôéStttÐÖÖ&44Tq¾ ¡¡¡Š7ë¯CÝ\Q'W‘¥Ö…  %??ŸöíÛsöìY…I‘K‡ºq¯.ÿËs°ˆ—ç³:J›G zŸh׮Ϟ=cÿþýŠrÛ·oOýúõ±´´dÖ¬Y<}ú”k×®‘––VjÙ˜››sïÞ= Täðà666¬^½šzõêPj?•…’säJš®]»Ò¦MÅ9·nÝbÖ¬Y=z”cÇŽ±iÓ&D"ŽŽŽ:t===ÈÈÈ ##ƒæÍ›«¸Öèèèp÷î]6lØ VŽ„„üýýŸ’ooªV­Ê„ øþûïIJJRºW¼+#GŽ$66–Áƒããワ¯¯JlÜÜ\%w›W¹¼},BBB=z4Í›7§aÆ„„„••Å€ðóócïÞ½¬\¹’˜˜z÷îMãÆ©T©Ë–-ãäÉ“lܸ‘:|0«˜¼¼<<==9}ú4.\`ëÖ­ ¥hµjÕhÛ¶í©WàßÇËJ‘3fpûömA)" ðÑy¯»ÕÔ®] ®\¹Bzz:899ѵkW&Ož¬ò¦zÇŽŒ5JmYEÖ›6mbäÈ‘jÏiÛ¶-aaa¬^½š“'Oòøñc¶nÝŠ‰‰ M›6¥K—.Œ?^á£þ2C‡ÅÜÜœ•+WrëÖ-²³³±¶¶¦nݺôíÛWñ–ÎÈȈãÇãããî]»¸ví ¢¯¯OãÆiÛ¶-žžž*ܺvíJDDÛ·oçÔ©S‘’’BAAåÊ•£Y³ftïÞ]%8dYeX´hýû÷ç—_~áÒ¥KÄÄÄ••…¶¶6µkצeË– 0@%FÉ‚ èÔ©6làÒ¥KÄÅÅq÷î]´´´pqqaðàÁ*»8lÛ¶””Z·n­¶M?C† áâÅ‹´k׎¡C‡²xñb•´öíÛ°råJ"##iÞ¼9ß}÷÷îÝcÚ´iDGGSµjU…EMQœ.\¸ÀîÝ»õy{{S§N·©²œœÌ÷ßOXXVVVŒ1&Ož¬”æììLõêÕ‰¥[·nèêê2{ölzôèÁ½{÷ðññaøðáÔ®]›?þøƒo¾ù†† R±bEæÎË–-[ÐÓÓcÞ¼yT®\YHoܸqÌ™3GñÖöM £sçÎhii±nÝ:‘J¥´k׎Áƒ+ÞV/_¾œ™3gR­Z5êÕ«GNNŽBæF±iÓ&–/_ÎÂ… ©Y³¦ba?iÒ$ÜÜÜA%íììxðàA™d+ê3OOO¶oßΔ)S3f ¶¶¶lÚ´IÉg <<±X¬ˆSRÒZKGGGGG…sÙ²e$&&âààÀÖ­[±··gÿþýôíÛWñæ´wïÞœ>}www\\\o¦È¡C‡ðôô¤nݺJí>iÒ$-ZDÇŽ_¹õåéÓ§ÉÉÉ¡gÏž¥žc`` v,Œ?ž™3gâææFݺuç{zz2fÌŖůãþýû*sE\vvv 8eË–±cǼ½½=z4ááá w®ºuëÒ´iS•¹àì쌦¦¦Rþß~ûMeñò|VÇ”)SÔÎ#Pïøá‡˜;w.™™™tîÜ™¹Ãü¬Ä IDATsçbll̾}û˜9s¦"ðìo¿ýVjÙ"‘ˆQ£F±víZœUä?yò$›7o&55ƇžžžÚ~* 5jÔPšƒ………¬^½š (Æ[vv6—/_fëÖ­Èd2jÖ¬ÉîÝ»©R¥ cÇŽ¥wïÞJ.ˆžžžL›6­Ìã£èºJºÒ¾ì‚:mÚ4öîÝ«°Ê*ºW¼¼åñ›âèèÈX¶lsæÌA*•âèèÈòåË»¦ÄÇÇ+)´¿ùæ&NœøNõ¾oV®\Izz:~~~ŠØ;»ví¢OŸ>ÄÄİjÕ*tuuùé§Ÿ¨]»6Û·ogÆŒL›6F)\úÞ7©©©ôîÝsssΞ=«x€uuu%##ƒµk×~zþ]Èd2Ž=Ê‚ (,,dΜ9od…+ ð¡uîÜYæååEll,´nÝ kk›×çDÕ¤wáÂ…Ì™3çCÉ*ð`çÎJ»½¼/üüüèÑ£‘‘‘o¤ÈÍÍ%99kkk…B*11Qaþ_ÂÃÃWWW+§ÿ"2™ŒØØXLMMÕn-þ¾IKK#''Gi‡\êæàÛÎç:ù” U ²þ©’””ôÉ^KRRõë×WI‰‰¡K—.´iÓ†U«V!‹nÅ[¶láÈ‘#*Vq‚ØØX*W®üÁëxÿ”TŠH$fÏž-(E^É»Ä)zãUÄÇÇqáÂEêÕ«‡ }úôy¿–#%cpüÐÕÕUÙ2Y]¬ÿ"‘He\|HLLLʼ3ÈßÉß!—º9ø)ñ©Ë/ðßæîÝ»tíÚ•‰'*m\ÄÀ_éî÷o$=7?»’SM +'ŽŽ9ÿ±EúÇ"(E>5Þ«rä]d |||}}ùâ‹/X·nJ`ú"^v%~[‰ŠŠ"==V­Záààð^Êýësgf$‡ïîgÓÕuï­Ü»?çàyœ+CºÕîÍ¢n+ÑÕÔ}ouü]H¥RŽ;Æüùó‘ÉdÌš5KPм7Ž9¢p‘~ß¼×€¬rû8w¡mÛ¶dddüãMðÿÉœ9sFp©øG ÌgËöíÛ4hÞÞÞ¥*FÞ'ÆÆÆ4iҤ̦ÐÿV&µžNÂ’ŽŒñãÒc?VŸÿ01d>R©6lÈ’%Køî»ï¸uë–hU@@à½qäÈ¥¿ïÁZ@@@@@@@@€eË–)!רQão©³J•*€êöíoJBf<¿ÿµ‰¸ôXVôÚ@^a÷àl¨§‰\›ÌÏÕ~fùòåJ–"/×YÞ¸¿ö¦¶m2_·€€€À?÷º[€€€€€€€€À?ŸŒ§<Ø3ŒŠ­¾Æ®åÄ*‹T*E"‘ “Éÿ—##†7Ëå¯ï°ªÏ¯ÅÜdꯈ¼ÂƒøûÈd2t4t!Ãà…{IZîsª[ÕD_KŸ¬ü,öî|#™«ZÖàa|0)ÙÉ*Ç6û¯'3?“¿¢ü й©¢é²±%KJÄïPÇ„Vß°ñÊjÒsÓéâÔ“ëOü9|‰TBzn:{oý®ˆ9r"øˆB¢¥¡…Ž¡RðÓÃw¼ˆJ‰ >#¯Û»èTK®ØèT«‡îìãqRé¹ilººVq xyyáääÄ›vbÙÁ+W®Ð½{÷WÖ9°ÁPžœEbfR™”ûÏîp5âⵯ€€€ÀÇàÓzE ðN¤„œ"âäªtÿÓÊ-^ŸáséÒ%¢¢¢¸|ù2—/_¦gÏž˜˜˜¼Q9MZÐÄ¡ù’|2ã˜à5Œ„Œ8LôL™Ñ~>Õ,åf§¹Ï嫽ƒñ 9‘Ž1M[ò(1¤ÌuU³¬Á—FQy %…ÏŽÁXW.oCû&Ô[V]M]V÷ÝLyã Jy£R"¨iíüÊòëÛ¹QËÚ™Íþë˜Úv{‡eöÑ)ŒÛ7MZWug@½ÁÜzz©¿";?‹Šå*±qÀJeuvêAÿ­IÎNbPÃáôpé @ûÛâkzþÖŽÜÂ\:;õ`R›éH¥RŽ:Jòoù¬®¹šü‘*nŽôÞÜ^Qæ«êüÆ}?œY€û:7žg§âhQ•9—¹m>¢Î;˼¼¼ˆ%00Ö­[ammó±åø²sçNÖ7úØbüM¬ï—Éøý†[Œ÷§p-q×'öÚjô߈u-Eúú~™Ô¯_ÿ9‹ÉÍýûãªÄÆÆR¹r忽Þ7Åy‰;‡¢ž]Ã- ßÊ×¥|]&¶úæµçJ¥R¼½½™3g–––̘1CÉ}F@@@àï"==€Û·o—9Oݺu066.Óùññq\¸p‘zõêaccCŸ>}ËO·*ú¬aG§EIËV6·ý¬™)Úšbv^LyeÚ»²h -ÆzLÚ*2ï`©Íž)Œùõ w¢Þ~¡XòÚªÛê”zŸ"c<,hådˆçêÈ- b™Y…ýþ©l?ÿöã¢deæJ¸¼¨:³vÇâw/ó­Ë|_cIIΪútªkÌÂýÅþä—UGC R¤dJ¸œÉêc äÊ^Q’€À?‹ƒÓ+cc*_¾¤fJøëQ+}HÏ‘»ôilB¯F¦Ø™kó$)Ÿýþ©½™®Èß¿©)Ÿ5+‡µ©&ϳ$¿•ÎÙ»ü>±’Úúù´v&ø¯R×Qs# |ïd|lQTøcÉÎL‹¶µ””#Ûϧ‘MU[Fµ· 1½€í~ïO‰( ðwàs# ß;Ø[h3ª½_u”±üP<#Ú™ãÙÊŒí~É„'äQßQŸoûØ`¢¯Á®K©¸UÑgjw+¶ŸOá~t–Ú<Ï’ð49Ÿñ›¢ÓÁ‰6û&›Rð·\Sj–„—RHÎ(äË6æ|ÛdžA«#éXט‘í-X鵩súÚð8.Ø<âž°æx"…ßö¶aX;sV–ø*BZ˜Çã£3(ÈNÅyð4t>­z>,ìÙ³‡Å‹ciiɆ pww}F)‚rDà“bùàòT¶Ö!îyÓÿˆ¥w#Ú»ÊyþKª³Ý/ 1*i·Â³™ÑÛšäŒBª—×%*1Ÿ>#&¥€ÿõ°¢Y  u5ðÍböîXæö³A&ƒEÞòË5 Ñ‹8¯øÁÚÃÕ˜3AÈd°y¬=ÙùR,µÑÖq"0*6:8Ùérá~¦âuh[3ú5)‡X ž³ù¬j ·’4¬¢Ïä®–Ø™k›Ë²Cñ„Çç³y¬=éÙ¬Lµ°6Ñd×ŵÖ/×÷û…vMvàÊÃL~>–ÈÜ~6Ô¨ ËÐu‘¬j‡“.y…2¼®>g›_2Mªé3«¯ ±)T/¯Ëýèž$Ь†³÷Ä˯c*’ž#¥V]´4Døë9¿žNz­y2ª—×åú#ù8“HUÇRim©£%âë®V´­mHŽ\Î&ßd´4D*cØçús¦÷²VÌ;Q9ŒùUþà™O@X6aÙ´¬eDEsíRû¦~e=&t¶¤²•é9æí}†–†Hí\ŠLÌãñjm¢ÉŒÞò~•Ha»_2{.§¾ñ<øï›ZÀ_²ùëQ6µ+éQÉB1CÚ˜ñãáx…¥ÈåYäÈÙÞ‚?¯¦bSN‹B‰|\%¦rùA–¢Ì[9<Ï’ ‘ÿ»ð¹‘€•‰&É…äÈcæ÷hh‚H®Éw¨cLǺƄÄ&²É7 1T´Ð&+OBj¦ªÕHaN!ûÇ¢[®"µlF¤¡õ÷]Ôû³ž~l”øísÕ@³EJ‘E‹aee%(E^ ìV#ðIqôf:ÿÛƒ¹‘&=ÜLØr6™ó÷3¹–Ç÷Øî—¬6  ‚™>7ÒõËt4EŒïdI]G=zº™°âHž«#ùóJ*A‘9E/Œ;Ô1Æ?4“Œ\ùÓlò:Ø[hq&¨ØT[[CÄü?Ÿñ8.ŸÎõŒñöÎRèRß;s-Z;2´9³vÇ2ÿÏgŒp7Ç©¢n©×j¤+f©gyîDåàùs$YyRQ^q\GKÌ’qœÊ`„»:Z"¥üêê«j«ÃòÃñôkZŽ®õå ìÆQ( '“èýc8Ûü’íaŽ¥±\wji¬É©Ûé,9GÃ*úhkŠønß3 %2>kVNQŸ¾¶˜éİý|2CÛ˜Q£¼Îkåyùú=[–£‡› K¼ã´:¯«òþ˜ÛÏC]1CÖDâ{'ï?·U˜Õ[k²îD"‡ÿzN—úÆÜ}’˯g8WÔ¥³\I¦!†vµ8T¬Üz]> # QžýþϺ> {]ú4)Gi¼®¿¬ŒµØv.™•GâiídH}G}@ýXR×–ƒ[›áîjÄÔí1üp0ŽáíÌiïj¤v _œÍšã‰äÈðøþS¶/Ö+YjѬ†ƒZ™QËN‡ÀˆìRûfŒ‡%9y2¬Œ`ιÔÏ¥·¯½›â\Qáë£ûÛ®…e½ñ<øobkªEãjúôojJ³ÜΡŠ\9}5$KéÜó÷3ÐÕQÉB›ó÷2OÈÃëŽ,øÌFå>õ±YòEyϨLgCNÊï ö–Ú„Å[•=ŽËS(5ŽÍªÂž¯07ÒäÜ=e˸¼çÑÜÿc6ÎTéöƒ yÏÔ­Œ¯oúëO|O<.A$ºIá[ºBðûï¿ãììÌo¿ýÆÆ¹|ùò[)FŽ×¯Oòõëo%ÇKŸ}ÆQ¼ÌÌTŽ/_Î1WWö[Y‘xùòGîã#´Á«Û þÜ9޹ºrÐÞžû?üð‘$üo"(G>).gr'*‡Ø<¬Œ5É+”Q(‘2s¥äÊÔ¦qéAâò¸ü0“êåux–R@¡F·7§iu}îGË"Gn¤)ÞâéŠiRÝ€Ó%¬Û»ó$©€ÐgyŠ´°¸<#r¸ø ‰.grú¶|Ñdn¤I=45D,\ž…ŸË}½«Z—¾0¯i§‹¡®˜½WR‰M-ààµ4,µ±2‘+ÂòŽÎåtP:š`f¨lVZ}×esî^súÙpð¯çÜ{’‹H«é³~dE†·5!³†¢¬s÷2ñ½“Af®”àènGæp'*sÃâsÂò~šËÁkϑɠzyÝ2ÉS’¦5 8”A@X6OS ¸‹¶¦ˆºzøÜHãiJ^IES,¢î åBj–„«!Yœxñ š…Hñi…˜¿h“FÕ 0Òsönq¾._M;]t4EŒjoÁ¦¯ì13Ô¤ª6¥ñºþJË–ð×£lަ#‘‚Õ åŽº±¤®-ݪà’EðÓ\®<Ì"ôYªê«Ã)äæK‘É 3WJv~ñÖÖœŸ¾¬ÀøNl<Äñ[é¥öMTb.•tÞΜŒ ÏKÄ2xy.½ÍxJÌÇXOÌä®VX›jŸÿÆóäU¤?¹FlÀf sRß*¿À?—n&¬b‡g+3NÞJgóÙdÅ6¢ZÊŠâ" ­‰ŒŒ\)ÃÖE±ô@¶å´Ø>¡;[þÝâ—Êêc LÚúï€çÌêkƒ±¾ÑKçÈ€’‰ã6E3ýXî>ÉáûÏ‹ãˆdÆÞáÞ±iø%³•3ýK¸q#‘è&bñMll‚><’¤¤Â÷V~ûö¡ìÝ[ºÛá·ßÚR³¦Þ{«@Só&qqê]¹ôõŬ][ 7ëË"¥ˆ“““’R¤]»v¯Ìþ<ç»uC’›«ö!ÿU$p²‘úû¯:öwp®S'žxy©=vwáBnLžüÎu´Ü·óçÕsš>®wî`Tµê;×ó©ò)·A¼Ÿ'ÜÜ@önñÚ^ÕÖíÚÑõÎ* ðNu¼ŽWÍ…÷ÉÇžóo‚àV#ðI"‘ÈÐx'ÕžˆB©Œ§) ZÉ VfŒïdI‹Z†LÝ£tfkgC ¥2.?܉än;Çn¦½V¶Â‹r ¤gKHË–ÐuÉc¥óݪè+ÊUH'*^ÆÝ{eò%±Ê½¸P"SÔQ’Òêù3@Ö‹¿Íj0²½ã6=‘ü› >0¡D*CC,zqmÅÿ—D,!•ËUt]¯’§ˆWýÎ’µ‡TùdÉ‹ïÅm_Ü®FÜŽÌ!1]uÑ\Z¾ôqÇüúDa1¥ôeï/™ìE;ŠD¯K%ÛR$*nƒ¢rd2Ê4†K²`_÷£sØýµ¹ù2¤²ÒûæØ­4<Í¥»› Lr`Ü‹˜ ÊÈçÒیדé¤fÒ¯i9V ©À/§’Ê4NÊŠ®©=÷æéåuè[TÅÆmf5:"ÖügY ¼9¿žIR‰•Ÿ‡D í\ŒØs¹X!Ö´†¹Rb^Ä‘ÊàTP§‚2Ø¢;[ò›oy?0q|Z!ñi…„ÄæÒ·‰)5Êëò$)Ÿ*%„Umtð+a!ŸOx|>b¹å‰–†ˆ„‡¾„ŸCå®K(WµíǸ”¿•œœú„‡ç1sæSºu{D@@Í¿¥ÞJ·&ühk‹˜0ÁªÌç¹Ï,\¸kkk~ûí7Ú¶ý÷‡W‘HÖ“'Øõê¥rL’›KÄï¿ÓæÈ‘ ™À§BèÆT3Fy!ø òª¹ð_F°øäÉ-QÉJKm…«…º4€FUõ±·Ð¢³!·Âs(g¨‰¾˜ §9v+:•äo€ºÖ7¦K}ù6PêséA&¹/Î.öºØ˜jræ {„eaf¨A7lM5ñp5BGSDR†ü½Fy¥ÿÆä’™+¥ÓrXkÒ£¡ ãóÕ>àQÎ@ƒozXaj Qj}µ*èÒÓÍ„ßÎ$ãÙÒŒÊÖÚX›h"•É-m¬ÞîÁÑÁR›J–ÚôilÈ]“’2 17ÒĨtyœ*ê2®£—fÑ¡®1ªêcm¢IÍ :äʸ™C×ú&X›hÒ¯‰)ù‘e‹ £)¢µ“!§ƒÞÌô946ÔL Ÿ·(‡©&MªéSÑBK©$Ry|‚ª¶:„>{óþ*m,©kË¿eÑ´šÕlup«¢Ou[üC³Jù2ô´EÔqÐÃÞBÙ”þir{.§òUG‹WŽ犺\ Ëæ{¯8 %2œK¸·¼<—Þf¼:Zi“”!aÙÁ8ÆäR§’^©²¼ ÚÆ¶XºôFZ˜Kæ³»„ŸKЯx°w8i‘WPV7 |ê¤çHÙu)…ñ,ÕÞœ¦Õ ÞΜQíÍÙx*‰T~oÜÚŒ¦Õ hïjD+'CâÓ ?ºbDSV ©@O7š×4`r+$RˆNÊÇçFÍjÒ£¡ #Û›cm¢ÉÉÀtªÚè°h -®F¸»1°…O“ ˆ¹µˆSßSó³Mÿ Å€ŽŽˆZµtÙ¹³2·og w­*(1sf ööw°µ bþüX%…í²eqT¨p‡rånS¿~0‘‘rEøºu 88ÜåÊ•L&NŒÆÁá.=zSœ{ÿ‡¸òÅŠï#Fp¤zuW®ÌÅ~ýÈ~*wÕÚ·3mÚp´vmFÂèPŽÔ¨AjP×F&`ÄüºtáTÓ¦Üùî»2[[¤r®cG98p¶CÒ>,S>€ðmÛ8Ù¨íí9×±#91òF…YY\Ÿ0ÃU«rÌÕ•G¿ý¦Ès¼~}.}ö>5kòðçŸñ©U‹¿ÆS);ãÑ#R®_ÇaàÀ2µÁ«Úý]ÈŠŒä|rpàt‹$^¹¢t-ÁË—s¶CŽÖ®­47Kòò\xÝü‹?wŽSÍšqÈÁ‹}ú(Æ@fD~]ºpÈÑ‘#5j¼|¹’<¥Íù"o¥yôè"‘Hí§hOb///5j„ŽŽsæÌQ*#--ŋӬY3,,,ÐÒÒÂÜÜœ¶mÛ²eˤR©J½qqqL›6 gggôõõÑÕÕÅÖÖ–ºuëÒ¿n”rƒxøð!'N¤víÚ˜šš¢­­ ;!sÌ IDATvdãÆäç+oÑçëë«öÚ:uê¤8çéÓ§jÏiÓ¦Í{m'€›7o2fÌjÖ¬‰¡¡!zzz”/_úõëÇš5kTòH$vìØA—.]°µµE[[###êׯς HKSÿ¦úSäÈõçhˆ`ó8{ú7-WjÀ6fl›àÀ“¤|6œL¤ªµßö¶áÀ´ÊÔqÐcã‹ ¢õ+ëÓ ²>å 5hPE_éÁº½«1!±yD%¾ÙÖŽcòøñp<ÃÚš³ëkG<[™ai¢Idb>=Êf‰gÒ ÿJdÌÚKƒ*úìûŸ#†ºÌÙûÊ:Ì4iíl„¹‘¦Úú¬M5™ÞËšÓ·ÓÙæ—Ì…à fö¶áÜ=yŒ–Mc+Ñ»‘ ’7¿qW²ÔfèŠôoZŽ¥âˆM-àÊÃLžgIX8°|©×_ÕF‡ÖÎFhjÀžË)HeN?þ˜ì@ïF¦,ÜG^”=SéTϘ9{bIH+›étÓèh‰ßx«ÝܳvÇâ\Q—½S™ÜÍŠª6:Jý¥¯-ÂûÚs>onF5[Ý7î¯ÒÆ’º¶üãB çƒ3X7²"sûÛ°ÉW[§´1|5$‹ûѹü<ÌŽY}lTêÞî—Ln¾”±-Jí›Ïš•có8{6Œ¬ÈõÇÙJ;ý¼<—Òs¤o|ýÍk°bHþœêH~¡Œ½WRK•åm©ä>cyìia.y鱤E\!ôÀd7¸~b.9Éo]¾À?‹§“Xw"‘6ÎF,þ¢<-k²hœ"˜©Ž–˜ MX6¸<Ó{Y““'å;>~Mc= ²ò¤ ogÎRÏò8UÔeþŸÏˆ{^ȉÀt¶žKfd{s:Ö‘oÏú,-ÆúLínÅÜ~6H¤Rº ™N¬ÿ&œ¾øÛÚû²þvŒŒÄT«¦C``6‹?ãâÅ nÞtâÞ=gŽKÃËKnUôøqK–<ãÚµZ¤¤ÔeëVGôõåËâ ¬ˆŒt¡ysCÖ®­Hd¤ GŽ›¾ýµü¸““úxHçáï_““'«3}úSò_¸6J¥°v­=ÉÉu9~¼ß}ËÅ‹òߦ;‰ŒtAC®_¯Ed¤ ³fß»÷î­Ì;Îjë4(77}bchÛö":¸òË/¿²iÓ&ºuÛÏ£GÎ:T•û÷9{6ÿ,µå¼ŒqÔ˜4 ±– þYåxÒµkt¸t‰¦[·rgþ|¤/ÖÓ]nÝ¢éöí::Òõκ޹ƒU«V¯=zë®óçÓáÒ%ôlm¹·d‰âØ)Sp]°€^‘‘´=z{{%y¤ùùd$tÝ:¥kÔ±°ÀãâEª˽¥KËT~"éêU<üüèxõ*ÏNŸ&úÐ!j~ý5…™™„oßNêíÛDüñ K<[˜º¸ÐñÊz„†b\£†’Ò@CW—NÄ?Ž£§'ÕÇŽåÉþýŠã©AA £ø³gˤ@ÊOMåbß¾T9’žSeØ0ü‡G¦æÙðež:ăU«h²u+½""p™?ia¡¢ ²¢¢èrë­¼½ þñG’y«Kå/¿$æØ1ºÜºEì‰*c)lãFFÓÀ Lmðªv®ƒYýúô ÃyæL®LA‰gË̈ÜOŸ¦•—w,PÌÍ"J› ¥Í¿ü”®~ù%.óæÑ#4''®O˜ Èwÿ‡0kЀ^áát¹qCiN¿nÎÿÓx«g¥J•ðõõåàÁƒ¬_¿^éXFF………|ñÅœ:uJmþÀÀ@ºtéB\œ\#îêêJ÷îÝÙ³gçÏŸçüùóœ8q///…ñ7èØ±#)))8991yòdÄb1W¯^åüùóѳgO6l¨TתU«øæ›oÊwwwìííÙ¿?§OŸæôéÓlذ'NP¡BZ·n͹sç:t(OžŠp°”Ç]yy.Éò&ãuçÅTv^TRÚÆyÈ^´ôß,>Ä¿ CC ÒÒä÷¦­[“زÅKKùrwÌKzÎgŸ•C$’[–ç`e¥Iݺï/vÈ_ÈÛßÙY‘ž=+ R%mºv5QœS­šíÚsûv6­Z¾u] …øû?gðà\]áè舽ýrfÏîB›6&ìÜE¦È?uëêšKÓ¦ØØØPX¨ú¢áìÙ³Ô©S=[[ôlåql‡ Q9¯ÊðáhbýÂU'7>ýŠßúZLëÔÁ¸¦Ü-ʾ_?®M½eˉÅd„…Qм9z* ÷bÝ^„aåÊôO)=FÌ£M›¨Ø»7Újâ§ÄŸ;‡¬°[å20tp@ÇÂ-ôííÉMH +2’¤k×h±w/bLœ±i׎8__*—¢|) ±'NP±W/ «T ê¨Q„¬YC­o¾QœS±o_,5"díÚ2•±knkÖ c!·Ô­w¹”H¤27K› ¥Í¿ø 0¨T‰ò/ jNžÌ‘5(ÌÌDÓБXLNl,9±±èU¨ hÓO‘·RŽhiiáîîŽŽŽŽÊCpp0ãÆãÑ£Gjó>þœîÝ»+#ÖÖÖ\¹rCCC*V¬ÈÂ… ðööf×®] 4©TÊÀIIIÁÌÌŒk×®ahXüCræÌ:tè R×Áƒ™:uªâ{Ïž=9ôB[çééIûöí¸{÷.ýúõãòåËhhh ¥¥EÛ¶m±´´,U9"‰hÖ¬666¥*GޥƎË–-[ß›4i©S§06–»{dddP³¦ª_í—_~©PŒ;vŒ† rîÜ9EDò°°0f̘ÁæÍ›K­_ò e*n©Yª[% üsÉÊ“’•÷úó>ÂXú{°ªÛŸ¸¿“¦ö¸$?±–YÏî!ÉÏ”#Ÿ…¹é„zGS×§/vüçÇof¦ d2ˆ‰)`ìØ'h¾Xíæç˨][®©\Y‡_­ÄwßÅÒ§O;³u«&&¯(½l”,CKKDNŽüÝÝ»9ÌœÃ9H¥”TH“&¥óZòóóÙ¼ùwD¢8P‹]»vѸqcÚµ U êZš<7oÞD¦ÆÄßÚÚºLõk—+¶Ìkk#Éy÷í¯µMM•ÊÏM(~QÐdËüôÁ?ý„Q•*¸­[‡‰“S™Ê•ääþûï´-År!tãF¹ÕÈKq$DˆÄbDb¹rI$#+,$û…«†o‰.…ÙÙe–§4r)W§Žâ»®µµRˆµµÑЕÏq‘¦¦Âåæ•ÈdäÄÆróÿCüb2HóóîCú+bÞ¸1)7n`Sr÷"™Œ+Wòdÿ~ ³²I$ÈJ(ÔDŠ¿E™¤xm£ÒŸ‰Ånf¥‘CúÇJ=8qBÁ0"“ÉØ°aƒdÐ××§_¿~„„„HûÙ¿¿´mgg'yÓôèÑ™L&}nݺ•5kÖ ¥õrÿÿvdØ ZÌÝïQ’§èQ ¦e€;KPÓ2¨¥¿@ð|R”“ÌõÓ0hÚ»þŸ‚ìåN%—]ÆÍ›…´o¯ƒLVVêìÞíH»v5{…¼ù¦1o¾iLzz)žž7Y¿>•9sTTdO"=€ÄoÜfÆ Süüš¡¢#FDW_™9‹ŠŠØ°a_}õ͑˗²cÇxŒŒÊÒ’’Š17¯¿ls»víjô9qâíÛ·Wz]5Qù÷æã´U~À+¸¿ü!¬ãNè±}{y‡… ÿòKzlß®”rMHHà½÷ÞSØåFá•,T—/_&**Já˜æÍ›+l·iÓFa{»’°O‚úôT5ˆ»»;-k°4Ž1‚v•,ÌuéWCCCÁ°TPP@XXØ?^Ë‹Àðœªa‚'ÃŒ_â«…þÏ»._Ô{ÉÀº#ºfŠŸ¡2UÔ´ÓlÈra¼pä¥Ü rÓXLZ)/ÕûF å\»VÀ„ ·iß^GòƘ4É„ HN.¡¬ ®\É—r|ÄÆqìX6ÅÅr44ÊóÁéë+>À6i¢Î•+ÿÜâ!™™¥´o¯ƒŠ ܺUX-™k}s–çÈÛCóæÍðööf÷îÝœ<@—.Y¾üÅÅr||2IL,¦gÏúCu._¾LDDDµWëÖ5ç6y´,,È¿w¢Â\êj{põ*Iþþ”pkýz,+=ˆÇíÜIINŽôï¼ZE¸ÃCòîÞ倫kõ„¬r97×­Ãùwj”õÆÏ?cÿÖ[¨êè(½>];; Z¶$béRJóó)+*âþ‰äÄ< ÍU70@ËÜ\ÊQR}gçjm–ƒïëKNt4ÅYYÜúõW)”AjÓÁÃД”äee<ˆŒ”æ.LMåÒGÑyíZ:þð1[·J¹8Š<@ËÜ-ssˉþýw¥e¸»w/¹qqÜ¿Ïoïjk©IVƒ“vñ"‰ /-¥8+‹¸íÛrŽÜõõ倫+…iŠá†v^^Dýô"#‘—•‘Dn\yx´¥‡7×­£$;›ì›7¹»w¯Òº½»oÚ?f9Úºô^—bàäDÚ… MSSŒÜܸ¾z5eÅÅ$üõII˜º»×/P=÷Bm÷ŸY¯^äÆÅ‘xèeÅÅ\_½Ónݤ{0ñàA * a2uõòœ,•Œ†uÝóÏOü›tÚ´i4­âδfͼ¼¼(**"""B¡M·RBMMÅ*ùùùdffbgg§°í򵯯Øàåå…ÅÅŘ™™)ŒUÙ›â!Uók˜U±°]¼x±þ>!êÒSYYGUhs¯å‚6l[·n•¶ƒƒƒÚ+ëªë81ñÙÇV ACà0øk4ô}îëZ¸ oãFÄæqeÕžïA xÞx{ŽkN¢iŸ¹XvÖÐâ48ÚÚ!¼òJFFjüõW3é7ùçŸ7¡];m:uº†¡a(“&Å[ÎXXXÆüùw16ÃÁ!ggM&MRüøÑGfìÛ—‰¥å^{í‘û}ëÖ‘ØÙ…sõjTefëÖúô¯]Û”3âèÓç‹%Ò§~µc–,±dêÔX¬­¯°dIùçRHÈLLaiiPZú-7o®Äظüϱ?þ°çÂ…öÝ7l ;:Zª,s}õêjÇtX¾œ ©SÙcmͽ*9[Ï›Ç]??öXYqeñb,ú÷Çùw81t(\]Ñst¤E¥¼‚õQ›ZÍ‹aëÖô뇯fÍ¢4·<1ï¥>ÀvìXŒ;wFÓÄ·ÿû?ÎϘAqVMGBÝÀÿ^½89bDµ’ú°ôðàôèÑîÖ kOO¬<=ëÕ†‘=·o'jÍšòJ@:‘t옔‡ 8+‹ÜØX…°›áÃiùÑGNš„¯­-W-’¼vZÍ™ƒŽ û;tà䈴øðCL{ôPj7þ÷?œ«$ëU†ºô^—b;f *ššøØÚr¬’¡°ËÏ?“ÌÞf͈øæÜ7nD½Q#꣮{j¿ÿ4qß°ð/¾`Ÿ“™tª”2"=$ÿ>}ð±µ%ò›oèòóÏ ãÖuÏ?oÈ<<<äÞÞÞ$&&JïÞ½07¯^Ù &Μ9CÏž=öûLá¸ÌÌLUºp>úè#VWú ÓÔÔ¤ Rì^ÇŽŒ äP•²a=zôàl¥òI½{÷æÄ‰ Ç<®žRSS1­âB·jÕ*>üð³37oÖW_›6mR7jh¶lÙÂÚ«g•e‰9üÉa;QÓnDK¯Mè˜:‘tq3‰ç£ùȵ/e…†fí¨ÞÝõ÷R>O<‹µ¤„ûrçøw8 _…M§'6îÚQ9¸ºº*ul2yž0‰‰‰888<óy’¢¢"6nÜÈ—_~IË–-Yºt):=¹sþ¼³y3 (*£,§FÂjð`'O®Öv}Õ*2ÃÃéªl¸„@iÎOŸŽa›64ÿý†å‘~é“&ñZX2µ'ž‘â™R×½ð´î¿¿ËÃÊ®åð0°rŠº¸?‰“'OÑ¡C,,,1bÄ“Ï9RyyyÕöuéҥƤ¢Ñ©pqÛ´i ¨1ùirr2'N$!!ùóç×:—j•x¿ªÛ………””” ÖÀ~Mev«z|ÔFÕuÛÚÚ*”®ÊËöCC ¼Ü4í3Ì訨i¢cê€E§‰hXp}ç4ì=¾ÄÈùÕzF†Ä _¹ºVãÿ@ÛX|ÿ[©jñññùWEž&e……˜tîŒÝ¸q5¶kÒjÞ¼g,•àE¢8+ ·•+_xÃH}÷‚ œgz–«†xŒ3F¡¢Lm888påʾûî;~ýõW…¤®Y¸p!^^^ØÚÚÖ8Wi•„6U·555Ü05ë©jŽeû¶mÛ–J&˜z^8·Ô¹¡EÿbÖÚ/Àßß_…Ïg._§']RX\ŵUðô aÇÌGbðÔÔœ§²–ÒÒR.\ȃ¤`NÞ……r¾CjjNý ž*U"¾¾¾RR}ÁßCES“VÿùO­íÿ¤ì®àåÀ¼o߆á‰Pß½ (ç™fï211ACCCa_Mµ¡££Ã¢E‹¸sç'OždÊ”) ãsúôi,--«õϯRb,'Gñ‡@M}SSSÉcæ!)J”¾ª…-=Ž~àe`Ö¬Y5–1o×®çÎcÿþý¼ñÆ : T%//©S§ÇîÝOÇ0"hX Y·nŽŽŽøùùáëë‹¿¿ÿKg±Ÿ8ñ¹qéüsº¬[÷‡ԼLˆû¯œgjQWW§mÛ¶ û”ÉÎÛo¿-m«¨¨Ð«W/Ö¯_Opppµ ,nnnÕÆIMMUؾ_©\QM}dUJsó,PUU¥K—. û‚*²F×GÕ5úˆvíÚ‘––†¡¡!þù§Bmö¡C‡òÝwß¡¢R¾Ì}ûöÑ¿&Ož¬`i×®>>>ժ׼ûî» ¥¡Ü¸cooƒƒöööÜ»wO¡ýÞ½{œ={–ÒÒÒ¤'öìÙèQ£¤})))xyy¡££ƒ¡¡!èééÑ¥K×ïõë×ãáá¡ÐoìØ±R?uuuLLL8p EEE5Î/‚rã²S§NeÍš5 -Žà%àСCL:•+VàååÕÐâ¼ÈTUQÓÓÃqÊJ²³É©¨lXV\Ì•/¾à¯6mðkÑ‚Èo¾¹\êw}Õ*üZµÂ×Îÿ޽ɽs€œÛ·ÙceEè'ŸЯýúñàêU©_nl,'<=ñµ³ãH¤œ=+µpuåê·ßrtÀþrq!zÃrssË"NNìZ¹’ÙÀÔ«WÉ]¾\aq;wr¨sg|íí97yòskä‚+Ë8““ ,¨Ö¶téRÜÜܘ>}z­ý›7oNDDk×®eàÀ4iÒMMM444066føðá¬_¿žC‡å5‹W¯^͘1c077gÏž=,Z´ˆ 6`aaÁ'Ÿ|ÂÕ«Wqww¯6×ܹs cÚ´i8;;ÄÖ­[ÑÓÓcðàÁüöÛo\ºt©Z"S€¦M›rúôi<==±²²B&“¡¢¢‚ªª*¯¿þ:×®]«ŠsãÆ zôèAxxø?Ö“¾¾>ÞÞÞ1kÖ,Úµk‡±±1ªªª”••Ñ¢E fΜɅ ŒMúúú8p€}ûö1vìXìììÐÑÑA]]úöíË7ß|CtttµÄ¯@ P¤{÷îœ9s†Ÿ¨øi IDAT~ú‰3f;ÁScÆ ,Z´ˆ?þøƒW^y¥¡Åy¡(+,$nÇT45ÑiÚ€kÿ÷¤òêñã äÞ‘#Äûúõ￧¿¿?Ãbbè´f jÚÚÒx¥ùù4jÙ’þGÒü½÷8_é÷Úù30ruÅóæMZÏŸO`o•œ˜ú9BÇÍ›Y:gÎNNðó;ïðE«V|xó&¯_¿NóY³¤>)g϶`]û Ϩ(Ô ÿòK©Ý ysšð*êêt\µê©éQ ^fdrooo ¥wï^˜›‹L肆aË–-L˜0¡¡Å‚jdgg3nÜ8JKKÙ±cG5ÏBÁß#$$¤Ö¹/©©©k-r¹œeË–qìØ16mÚÔ ÕóRSSquuUêØ†¨ä”˜˜¨– å9Guí*më6mŠÛ÷ßcÞ§µiC§~¶ooÚDò™3týõWrcc9Ü­Ý·lÁ´GT*U?̹}›ƒ;2<>5]]䥥챲bÐùó¨éè°¯ys†FG£Ñ¸1‡ºv¥Ý_Ðdà@¸ºÒ⫯ð»u‹o¿ýËŒ ~س÷AƒˆÝº•¨µké²n†­[C¥ÄÿfÎDÛÊŠ6ÿýo¹ ÑÑ2„×EÎ#@ð’Uap SºOûöíêyCkãþý$Nž{÷îåã?¦Gøùùagg×Ðb ^pŠŠŠ˜={6©©©ìÞ½[éu‚GŒHL¤4?Ÿs“&‘]n ‘ËÉOL$xÎT*’Ù–aв%ºvv¸}ÿ=‘ß~KæÄ‰XôíK§5kP¯Ð¿Šº:jÕdªª¨PœŒš®.255É0 efFÁýûäææ²7%…)o¿MÇ.]Ø¿?q#GâRaÔi:z49qqMJARŽ“'ÓfáBò¸ò$ ~~@¹ÁL^R‚¼¬ ™Ê3O(/%Â8"’¨©©ñã?²nÝ:ÜÝÝÙ¹s'={ölh±/(™™™L›6 SSS6mÚ„¦¦fC‹ô¢Ѹ1–-ãØàÁØŽz£Fh[ZÒmóf ]\jìc;v,¶cÇR”‘Á™7Þ fófœß{(ÏWR”‘FãÆ”R”™‰–©i¹'II‰Ôqï§ùyþ|lòòؾv-½Ç ®Ò|*¸,X€Ë‚d߸Á±Aƒ°öô¤qûöèXYaõÚk8ÍœùTõ$‚Ú¦h@ “éÓ§³qãFFÅ–-[ZÁ H||<ǧuëÖ¬Y³FFž-[bܹ37ù;//¿úŠÂ”äee<ˆŒ$%0€Ü;wH>uвâbTÔÕ‘Éd¨éé)Œµf eÅÅÜúõWôœÐµµEÓÔ#77®¯^MVF_NŸÎ”ÐP®Ü¿ÏÑ£GYäà€‹³sò¥‘uíÈå¨hj"—Ë%ï»7ÞàæÏ?K‰_ó¸»oßÓR•@ j@xŽ@ð78p Gåõ×_'""‚¥K—JÕÑ‚º¸|ù2S§Nåý÷ßgâĉ -ο çwÞ!hòdœgÍ¢ÕܹD.[F@¿~ef¢goO›Ï?ʸ^ùâ ²oÞDUS“&ƒa÷ÆÒ8ª:: —³×Á={{º®['åqY¹’%^^l]²CCü6l {ÅyŒ©C¶‚äd.¾÷Éɨ7jDëO>AßÉ ³^½h½`AS¦w÷.š&&8¾ýöÓQ’@ jDG@ ø›¸¸¸pñâEFŒÁ¸qãØ´iÚ•*^U9uê³gÏfÙ²eôïß¿¡Åy¡ÑwvftzºÂ>óW^aèíÛÒv›… ¥¼ }œèôhãW훓“ÃÚµkY±b}úô!p×.Z´h¡ÐgpHˆÂöЊ²ÂÖžžX{zÖ:Ÿí˜1ØŽS§L@ xzˆ¿¸@ ø˜˜˜àï簾&}ûö%))©¡E<§ìܹ“?þ˜_~ùEF^ rrrX¾|9ööösæÌvîÜYÍ0"‚aࢩ©ÉæÍ›6l;w&44´¡EÁÁÁ²~ýz<ëp¡¼3oÞÞyç–/_ÞÐâÜÜ\¦NJ^^Û¶m†‘眪ž"ÂSDÀÅwßÅ×Þž½ŽŽÒ¾ýmÛ’ñy > yrïÜaµõóEàúªU\úàƒ†¨ùÚû»\˜5‹¨|Rý{Æ@ ž0]»våÂ… ìØ±ƒiÓ¦Q\\ÜÐ" ž1÷ïßgÔ¨QØÛÛóóÏ?‹D½µ››ËñãÇÙ±cÛ·oçÔ©S>Sj3Š8×R’×Å%™,XáUP çÒ¥M›r¨sg”Ú SR8;~<¾vvøµlÉÕᆱqŒªë|ÞÈ»{—ãƒãkg‡¯'Rœ,µ×u½ûÚÙU».¯|ñ…ÔžˆïÞì¶°à@ûö¤+Ì]ßµwcíZ¼ŒH ’öÅïÞM@¿~ì25%xöìÇZkmŸA/Â8"ÁSÀÊÊŠS§N‘œœÌàÁƒÉÌÌlh‘ψ¨¨(† Æ€X¼x±(ñ\yyy˜™™áááÁ!CÈÏÏçâÅ‹Ïdî‡FGGG"##9wî\F‘‡„„´"?ßUU8{¶ùù®hiɤöü|WŽoNZZ C†ÜúÇrj3"1‘Þ¾¾¨êè0"1‘>û÷+3òÞ=^Ù·¢ôtÎŒ§Ô¸wví"~÷nú;Ækááä'%YËlMÈËÊ[°€F-[*ì™; CC<¯_§éÈ‘œ›4 y‰òF¢šÈ‹çÒûïã¶r%Ãbc1éÚ• ï¼€¡‹ #¥W¿€Ô5¬gÏ4gå¹uíìPÑÐPºOÂþýD.[†ëŠ §Ý’%ÈTU•îïÈ SRö$'cÒµ+ýŽáÕ'(¸w°O?Uj¼ÿüMccÿœAAA B½Q#B?ùDj¯ëz÷ŒŠ’®Ëáwî ad„u…Ñ!?1‘3^^ØÏkaatÛ²-33…¹ëºö ’’ˆ÷ñAMWWa¿†‘­æÎÅfĈ'­Š—ñm-ÁSBOO={öàææFçΉªø§Sðï%00///æÏŸÏÇÜÐâ<÷˜ššÒºuk ÐÓÓÃÉɉÔÔÔ§:gVV–‚QäìÙ³lÞ¼'''¥úkhÈ$cHå÷ÑԔѲ¥[¶8–GPPî?–YUKKz@ªüþ!*šš4oN—_~!3<œ4% Lyññ¹º¢cmš®.Mú÷—¼1 Ü+åØÀøÚÙqtÀ²®_Wè³y3Æ;£kk+í+ÍÏ'ñðaZ~ü1êÑü½÷(LO'õÂé˜Ôsç8úê«øØÚr°cGîŸ8@n\'<=Ë= Zµ"xölJ ÊeMH@UWó>}©¨`5dÈ#Ye2Tµ´¤×oolFŒ@ESS)ÝÆíÜÉ¡Îñµ·çÜäÉgeåûÛ¶%ä“OH9{–ýmÛrÀÕU¡oÂìsvæ¯6mˆÛ¹SÚýûï´š7& ijŠYÏžJËSZP@Ä’%´®bø0rs£ùûï£ç舮­-ö'’VÅÓ 6yRΞ¥Ù”)¨jiaÜ¥ ¦={’xà€RòD|ý5>M›r¤gO2ÃÃËwÊåä'$`ýúëÈTT°xõUd**äÄÆEÿþ¨êè ccCcWW…k¨¶uÖGД)’·Ó©Q£È»{W¡ýöï¿s¨sg|š6åØÀä'$äïÏ‘ž=ñiÚ”ƒnnÜü婼´”°Ï>c¯£#‡ÝÝ¥5hbÚ­Zhš™¡ejJÖåk¨çzWÑÔ”®Ëû'O¢ilŒQEBî¸;1ïÝ›fÓ§£ea¡‹ :66€r×^ØÿKëO>©ft3ïÓKÔ jÔ_NL ‡ºve¯£#—?ÿyYÙcéÿߎ0Ž@ðQUUeÙ²eüç?ÿ¡wïÞœ¨xüûØ¿?ï¿ÿ>?ýôC‡mhq^HÒÒÒ022z*c§§§³xñbš5kö·Œ"‹¾¾ NNš„†*Na5d–4jÙ’æ3ßš¾>zŽŽ`ë éÈ‘äÆÇ“CñƒÜó÷ÇrÀŠ2285r$ͦNeht4Žo¿Í¹É“¥©¢ôtnüü3-«¸íçÆÅ!/)AÏё𯾢(#=;;ÉK!ïî]NK‹?dØíÛôܹS2ô”â0q"C""DÖõëDýðÆ;Ò¨eKî9BYa!w÷î¥I…¬•)+*"ÎÛ;%½gRΞ%lÁºþöžQQ¨þå—h™›óÚ•+´_ºÓîÝyíÊW J»x‘Á¡¡¸oØ@ðÇKê""(LOgÛ¶øµhÁå… •~½þý÷ØŒQÍ‹ *—/Ó¸m[¥ä©†\NvLŒRò”æå\ÎÐèhì¼¼š:ärÉpš9“x__ÊŠŠH½Îª¶iÃÀ³gñ¼qƒæÍ¹0k–Ôv××—kßO× C›E‹(«ðâËå¸~û-C££é¹s'W—/'%0€;ÞÞÜ;r„çÎÑ}Ëþú«Ú¼;uÂÇÆ†ë«WÓlÊ þë½21[¶`çå%mgFD ijJ@Ÿ>øÚÛsv + Ãõ]{)gÎPœ™‰EExÝãxð =wì``` ‰¿gÔö$>ƒ^t„qD ‚gÀÔ©SÙ²e ãÆcãÆ -Žà ³aÃ~ùåvìØA×®]Zœ’””bbbèСÃ7--ŋӢE nß¾M``àS5ŠTFOO•J9þ<&&&Õ^vvvÒ±Û·§q»vh[YÕøà¯,jºº’÷C]h[ZbêîÎA77|ííQÓÕÅ~âD <,DßÙ™¦£G#SUÅÎË‹¢ÌLrnßàÊ_à#õÜ9ÊŠ‹)ÉÍ•Œ)gÎpù¿ÿå?? ]\¸½q#'‡e`P*êêÒ1[·Öè54e ñ>>t\µJ2å%$pÿäIüüÊ×(—#/)A^V†L‰s£ej*½×43“þùWÕÖÆþÍ7Ѳ°Àzøp’OŸÆî7ê”'ìÓOqùôÓ:Cprnß&pÒ$º®_ž½½RòtX¾œKŒ_«Vè;9a=djzzÒ±Ñ62w.MG¢ËºuR›Šº:†† ï SSÑ23ãôر´_²Û±cI ጗ýFßÙ™óÓ§£çà@mÛ(JOçô¸qh››c7~|½ë¬U¹œk+Wrg×.Jrs‘—–*ä³ÉKH@§–ëëÁÕ«\ùâ ²£¢—•Q˜žŽqÇŽ@yòXmsséXm ‹j^7š¦¦hššb/“qvüx<.]ª÷zHÜöí˜t놶••´OM[‹þýi\a n6eŠR Toýò &îî´hQï±5QÙSGËÔ””³gÿÖ8ÿV„qD ‚gH³fÍ däÈ‘Œ5Š?þøÝ*?¤/yyy¼÷Þ{²k×. *ò#ÜÜ\èØ±#Mš4ùÇã=4Ь]»Î;‡ã(ƒù¸dg—qóf!íÛë Aƒª£§§ÇÝÚBþ%ÙÙäDGcèâRï±—/céá¶¥%¶cÇJ! ÚVV˜öèA÷-[ªõ‹Þ°‘‘ìªô žxð ®]£Óš5ÈÔÔȾqöm)+.&7.ý /++…ü#•‰øúkÊŠŠpú4ª::Üüåî?.ÉÚ¸}{Œ*ò.8¼õ¡ó瓃~EÝü{÷¸âDF›®¿ýF×ß~«¶_ÇÊ «×^ÃiæÌzõU•«–&'£Ù£zööÈdrÑÈd²òP”zäI ­–´ÔÛȈAAAè;;“Ÿ˜È©Q£h¿d f½{+-¶¥%=wìÚNŠÍðáÒ¶ãäÉ8Nž\ãËŠ‹)ÊÌDÃÐPz¯ejJNL ¥¹¹ØO˜2&]»¢ïäDò™3è;;“qå ­,@MW5]],$éØ1ìÆ¯wµÉs×Ï;»vÑÛ×-ssDFrtà@©]ÇÊŠ¼;wj\Gдi8NšDmÛ©¨øæ›È+Ή¦™EHÇÕ‘@]¦¢BÎíÛ”æç£kk[çõþ˜?ÿ¤åœ9 ûtíí¥|(Ç•WºFj#=4”x¢+]?ÇÆuÅŠZÏaeòïß—Þ¤¤L£FèÞ½;wjù1'x~IMMeìØ±±iÓ&ô*ý +Pž‚‚hÕª666”––RZZú·Æª>ÄæÍ›Ÿ¸a¤¨HNA¼Úû‡ʹv­€ nÓ¾½]»êâêêJDDDµ×…Z U)-( ¬¨¨Úû‡”’Åù30lÓF©°’ÆmÛ’xè…©©”ïë‹A…¡Ájð`Ò.^$ñÀ䥥ge·};ò²2'Oftzºô²ôðÀ¥"o‡ª¶6–rý‡(ÉÎææO?¡nh(Éc3bɧN‘ð×_ÈKKÉ‹#õܹr]>x€³3ª::”äæ*$5lÛ–ŒË—y r9q;w¢¦««ðO|ì¶m˜º»£cm­”NìÞxƒ›?ÿ̃«WÈOHàî¾}J÷¿µ~=%¹¹¤]¼HÚ¥K4éß¿\¯½Æí-[(ÊÌ,¯*âë«TõϨ(I¯¯ž<‰LE…Ñééè;;S˜–Æ©‘#iþÞ{48°ü:¨Röº6yrccɉ‰¡0%…¨$32ëaÔ^ç5k(+.æÖ¯¿¢gg‡žƒzöö¨jk·cÈåd†‡“uíÍ›å¹Aîx{SVTDaj*IÒõU×:ë¢øÁ´ÌÍÑ27¹œèßWh·óò"ê§Ÿx‰¼¬ŒÔ  rã⤾†mÚHÆû'OJý¬<<¸»oò’J²³’Õ&ùû“ð×_¦¤Ë•/¾ÀÐÅUmíz¯w€´óçÉOL”ªÔ<Äúµ×¸çïOÖõ딽q#fƬºèúÛo ÷Ÿº} #òÒRJ —•=z_é3õîÞ½äÆÅQœLÜöíXzxÔ;çË„ð ÐÐÐà÷ßgõêÕtïÞ:V¸ø žoâââ˜4ižžž¢"Í?$11‘pþüyΟ?€ššoT„(Cjj*kÖ¬‘"?)‰Ãîî”cäæFÇŠ¨FFôܾ°Ï>ã¬Y¨hjbÞ»7¶cÇÖ;®ëwßq~æLö:9¡çà@·¥ÐzlÛÆåE‹¸0kZff¸®X@«yó¸0s&÷P××ÇÔÝì[å¥Ízö¤õ'ŸpÆË‹¢Œ tíìè¶i“BøBl ÿÎׇY¯^´^°€ )SÈ»{Mß~[éþÆ;r CT45é¸zµd¬q~÷]rbb8о=*ššØŽ‹m¥dœ‡¤£GÉŠŠ"dî\)äDUG‡•ŽNkÖ2oûœQQWÇÒÃã'ùl:j÷üýñïÕ ccÌ«xÏØ NQz:“&Q”D£V­$×ï¾#øãÑ41A»IC„ÍÈ‘d\¾Œÿ+¯ Þ¨Û·—Údªª\ýö[²oßFEM ã.]p¯d”©ëz‡r¯kOOI1êØ—O?åäðá”æçcÖ³çßÎ#R™è J Çüñ‡‚W‰¥‡§ÇŽ¥0%»ñã±ÉÃyxxȽ½½ILL$44”Þ½{annÑÐr ^R¶lÙ„ Z @ x¦øøø0mÚ4~üñG¼þáèLLLZ ¥ eÚ´iÌž=»Ú|jjê µ–ʤ¦¦âZ¥Tdm4DøPbb" ûªE/^\í@ ¼xdU$•®)WSm´¯0j(‘s àþý$NžûŒ}ÎÎìupàüôé”dgKí±Û¶q C|š6åøàÁdEEImç&M¯E öXYqlà@Y奥Ï™ƒ ~­Z»u«Rs–øÖ[øµh·‘÷ïKýòâã9;~<{ñµ·'hÚ4Š22È ÇÛȨÚ+)  ^$Të—sûv½s–HÇï21á ›±þ©”²oÝâÔȑ챴dŸ“_].¿?Gzöd•~-ZpyáB…R´u“óÓ§W[KÄ’%Rû½#G8Ò£GùõÕ¡ ~~õÎY—*“†·±1×*ª/Õ§ƒºÕy_ IDATÎuås²»IŽöïOÚÅ‹R{IN—>ü_{{|š6åô˜1ÈKJÊç›=›¿\\ØcmÍñ×^#3"Bê<{v5ýÏž-µG,YBä²e$p¢Jéá—a@ ¨D||<ǧU«V¬]»MM͆é¥ %%¥šQdÍš5XZZÖÚ'$¤ùù®¨ªÂÙ³-ÈÏwEK«þ2¶ÿù9ÅÅ®?ÞœãdzY¾<éI-å¹`Þ< òó]ÉÏwåÕW øê+KiÛÍM§þž½}|xö,Fnnœxýu ’ÊõžŸ˜È//ìÇçµ°0ºmÙ‚–™e……Nš„Ûoòúµk¨hhúé§Ò˜AÓ¦aÑ·/Ãbciÿõל›<™¢ôt ¼²ë·ß28,ŒVsçr~út²oÞàή]ÄïÞMÿcÇx-<œü¤$"¿û((¾ôþû¸­\ɰØXLºvåÂ;ïHs:¼õýüýíØ±œyã äeeÜZ¿žä“'tþ<]þ÷?‚çΕâëšÊËwùå—jz+HNƤkWú9«'NPpïa:0tqaDb¢ôê€z£F˜õìY¯ Û¶Uè¯go_ïœxö,#p[µŠàٳɾq£^”sjäHµjÅkW®ÐÿØ1t+þе·§Óš5¼vå ¯üõɧNq{ãF¥Î @³©SÖÒzþ|²¢¢87y2ÍßÏ7p߸YEÉݺæTFò²2Â, QË– ûëÒA]ç@ÓÔ”QÉÉ ½u ë×_'ð­·¤¶àÙ³É-¿†Â°8¹\޼´55züù'ƒCBhÜ®gßxä ¿öo¾© ×o¿­qþ—a@ ¨àÚµkŒ3†I“&±xñbTTÄO¥§ÍC£H›6mÈÌÌ$44´^£ÈC44d’1¤ò{€©Sãxç;¼úê ™<9V¡¯L&CMMF‹Z Òˆ‹s¥¶K—òèÖí:††a¸»_çêÕGå‹££ éÕ+ #£0,,.óå—÷¤6kë+Ìšuw÷븸D²cÇ£˜ss˘<9SÓËØÙ…óãÉRÛ–-éôésƒ·ßŽ¥eËHzô¸Njj‰Ô~ðàZ¶ŒÄÐ0 GÇpüüHm÷ï—0zômLLÂprŠÀÇ'UUÐÒ*׉Р¨©É¤mY…š¾ûî>¶¶á˜›_fæÌ¸gâ=#SUEÇÚšÖóçcм9±Û··s'æ½{Ólút´,,0tqAÇÆ€Ô  J pš9 CCZ|ð ýEYQÈåä'$`ýúëÈTT°xõUd**äÄÆÐzþ|ÌzõBËÌŒ¦£F¡meEÆåË@ùö‘«+:ÖÖ¨éêÒ¤éá5/!U]]ÌûôA¦¢‚Õ! ¶æ}ú ccƒFãÆèÚØP˜’Bqf¹îïîÝ‹ãäÉh[ZbÖ»7¦îî’·F]sªjiÑlêTÕôÙÈÍæï¿ž£#º¶¶ØOœHÚCo™ U--éuÇÛ›#P©0ìÖ¥ƒòîŠý^ uÎYé|ªhjbÖ³'ZffÒZêÒÁ]»Éd´ûòK4MLб±Á~Âô›5£q»vh£Ý¤ êJŸ“‡òT^‹L­<‹DÔ?`íé‰íر¨и];, ªwNet³y3Æ;Kž‡Ô¥ƒºÎµ´55Ôtu±6Œ‚¤$Š22È‹#ÞÇ‡Ž«W£go†‘ŽS¦ ¢®ŽŠ†¾ýömÑ23£ù»ï’w÷®‚WJmú”#¾ñ@ €Ó§O3~üx>ÿüs&NœØÐâüë©Í(Ò¤I“'6Ç©SÙøú6#2²5Gfsî\nµcîÜ)âÈ‘,Z¶,¯•ž^ÊÀ7x÷]3ÒÒÚ3s¦)cÆDSáÀâʼntî¬KZZ{nÞlCß¾ú ãef–rî\ öïwbÆŒ8îÝ+`É’{ÄÄíÂáÃN|õÕ=Μɑú=›Ãüù\»Öš&M4øõ×Ga#ӧDZ|¹™™í9y²ööRÛ¸q·13S#!¡»w;2}zwîÕ«›Ã‡³ø¿ÿK" À™7\ˆŒ,`Å å¼gdªªt\µ €–³g£ß¬™RýªÒ¸];2®\ 3"MSSúôÁ×Þž³&H¡3Ù·n¡ß¬ù÷î±t)úÎΔ’2N3gïëKYQ‰¢il\í_|€¢ôtò0¬x m:r$¹ññäÄÄPüà÷üý±0ãŽiÔ²%÷Ž¡¬°»{÷Ò¤¢í!ß}_;;NKÓQ£Ð¨5ʾyggnÿþ;i. ïìLö­[õÎù8d\¾Lã¶m«í/+*"ÎÛ»qãjìWUõ»¿m[ü{õâÖ¯¿>öœòÒR’O¢05Êöºtqù2Æ;KF˜ªd]»Æ¾æÍñµ·'óÊl+Ö¢Ì9©KvÓ:*4Õ6g}:(JOçÆÏ?Ó²RxÊCêÒ²”rg÷nôÐ04$3<-33tíìêí›qå Zhšš*5—Õ!XzxШeKšðÁcÉùoA˜Š@ ¼ôìܹ“åË—³nÝ::vìØÐâü«IIIaõêÕüþûïxyyúD "•ñô4DW·ü¿Àöíµ¹q£ww]–/O’Bi¦O7añârO_ß Z´ÐbüøòÝ·Þ2fÁ‚nÝ*ÄÙYwï‘PŒµµ:=zè)Ì9aBy?[[ :uÒáС,Þ~Û˜½{3Y²Ä U T5ª1¾¾™R77š7/7Ðtë¦Ë•+ùÒ˜**2¢¢ yð kku¬­ËÃnß.äìÙþú«šš2Ú¶ÕfÀÊbút“:u³wo&cÆáäTî]ðþûf|ûmŸÞ„‘#GròäÉj}¾üòKfÍš2öDK¥ÎEM¨ééQ\‘ߢ8;›ûÇŽÑk÷nô›5#hút./\HçŸ~¢$75]]²oÝ"nûvš¿÷%¹¹’ fÎäúªU¨éêÒmÓ&Tµµ'“˹ôá‡ØOœˆA‹h[ZbêîÎA77š  ­K¦¦†—çÞ~›Òü|ôèíë«0dû¥Ki9gw¼½%£@I^jzzÄïÝKIn.êzzäU”U®kNeIæÎÎô=r¤Z[âÁƒh4nŒq§NÕ;Ö ýfÍè¶y3º¶¶d„†õœpò$®^å^@€d|SæœDoÜHÜÎÒö”4êÈUÛœõéàÊ_à>žƒ2bÄLªü¨Œˆˆ 00·ß~›°°°'²^'''úôéæM›(,,@SS[[[ºté‚vÕ”‡¥z}||ðööÆÁÁ¡¡Eú×’œœÌ?ü E‚ƒƒ±°°xªs6jôè¡@]]F~~™´ýÉ',]j…Ÿ_&óæÝ¥´´<¤$>¾˜ÈÈZ´x”Ȱ´TNrr1ÎΚ,[fÅgŸ%ЩÓU´´TX¾Üš1c=¬4nüè絑‘÷ï—{Ž$'ca¡.µYX¨sãÆ£pºdݱ￾ǒ%‰8;kñûïv¸¸h_>¶›ÛUéØÜÜ2Ú¶­ÿþLN.ÆÕõÑç©……:IIåãýòË/TëchhXï¸CINŽôP©¦­Eÿþ4îЀfS¦HÉ"Õtu)ÉÍÅü•WxíÊŠ*ÂWÔtu)ÎÊâôر´_²Û±cI ጗ýFßÙYšëòÿKI~>¾ùFÚwmåJÒ.^dHDêúúϙå> óÿþGÊ™3\þïyÅÏCnoÜÈÉ¡C„JE® õFPoÔˆ–óæáײ%ý+BÔtt(ÉÉ‘ÜCçÏGMO¯Þ9•!çöm'M¢ëúõRnÊÄlÝZ«×HM:е³“¼ôÈŒŒä®ŸŸ‚q¤®9¡×¬ÙQQœ?}GG¬‡ «Sš¦¦RN˜š©©¡ea–…YQQ\^¸Ž«V)uNlG¦õ'ŸHciU|Æh™šÖ˜Hµ¾9ëÒAFh(é!!¸­\Yã˜ué >4MMñŒŠ¢¬¨ˆä“' š:•§O£efVç:Jóó9;~<öãÇc=t¨B›µ§'m.”¶ÕŸð=ý¢#Œ#@ x®())ÁÓÓ^yåÌÍÍ æ‹/¾`É’%Ì;Wé±²²²ˆ­ˆ;’¸»»Ó©S'~øá‡'2^~~>·oߦ¤¤¤ÖcÂÃÙ1c:uªf9zô(}ôo¼ñ'Ožä•~d'''SXXˆMEì~MÜ»w¹\®ãaøðáôéÓ‡?þ ¬­­ÉËË#::333¶lÙBïÞ½ÿÁªž’’>ÿüs®]»†ÆÆÆ -Ò¿’»wï²jÕ*¶mÛöÌŒ"Ê¢¢C‡òçŸé¬\yŸÅ‹-±±Q§O}||kìÓ¤‰:6ØàíÁ”)±ŒÝXŠxFóðýÀå?·ÍÌ’’Š17d,©‹®]uñókFq±œyóîòé§ øù5ÃÆF}}®]s©-B¡Vê’gêÔ©œ8q¢ZŸ%K–ð^…ׯ“ ãÊ,Ê“bæ'$Hm2ä‰$õɾu yY2²oÜ@ES\»FinnyÎ ™ “®]Ñwr"ùÌÉ8òÐ ÑkÏ… —/céávÅgŸíر\˜5Kjkܾ=F®®@yÖÐùóɉQ0º@yÎyI YQQèØØ ×¬Y7n`Ñ¿?P^aÒ¥K½sÖG~b"§F¢ý’%˜Õðù›ï÷Oœ¨ña½6TEECCJ,«ÌœP~® Z¶¤Iÿþ$;†õ°auêÀÐÅ¥¼ª‹\^khÍ#Tx (wNÔtukôz0tq!%(H9/JsÖ¥ƒôÐPDF²«RØJâÁƒ<¸v®¿ýV§”EEC‹W_EÛÊŠ”sç0îÔ‰‚û÷É­1´¦¬¸˜ÀI“0lÛ––5ü^ªM?‚rDÎ@ ãœ÷= ù¼ï9ïÖC‡’râ9ááH ‰Þ¶ ³=0íÚeuu¢~þ™ÒÜ\"¾ý«¡CQRSCÇÎeMMnÿï “‘}ý:97o¢çä@ô/¿¿{7Ývì@¢¬Œ´°P¾M«¡‹ ÉGR”‘AYQ ûö¡÷à!ÛÀÅ…¬ààò‡d™ŒÛ»vÉ,óbc‰Ú²…üøxŠÒÓ¹¾bÒû÷åu<šŽI̶m¦¥‘@ú¹sòwðëêÊkL”=˜­WV\,ÿÿ¢»wñ3§?ÄòõבÊÏUˆûãL»vEËÚZáx]c|äÙ¡¡”Ü»Gz@Ñ¿þ*/TÚ>eR)eÅÅäFEqçÌt̾«k šKYi)Á‹St÷.‰‰òmnc~ý•Œ (ÎÊ"óÊnýø£|‰P]_“ú8}ô‰ûöq{×.JssÉ %娱zû¬k ¦Nelf¦ü£ÉàÁ´Y´ˆ.¿üRïÔõµ–mi)Òû÷¹sæ y11èÚÛ£cg‡Õ°a\ž3‡ü¸8г³‰ùõWÊJJ••qéÁ—%K"-,TØ­F&•ÊK )+)AxHÌAž+7nÜ@WW—þ(®`ll\í600uëÖ†……ƒ bîܹ(WZ_[Õ½{÷X»v-ÿüóR©”Ž;òé§ŸbWiª°T*å§Ÿ~ÂÇLJ;wî`ffÆÈ‘#™={6ÿýï¹uë¼õÖ[Ì™3‡îÝ»åµ+¶nÝJRRLš4‰1cÆ(İcÇ6oÞL^^  _¿~5fÏ’µµ5“'OfÉ’%dgg?ñiöÏÂ;wðôô¤mÛ¶¬\¹Q­ÿ‰ª:SäêÕ«˜››?•¾:tãÆòåÝ»‡<Òv¾:uÒ¢uk 6mJgÑ" rdÞ¼<=ãPW—àî®Ç¤Iå¿ ˜93žü|)Íš©³s§â2ƒ.]´±·¿Ž††„-[šaeU>ãóÏ-ùðÃxìí¯£©)aáB úôÑ­KM¼½ï2{vüƒ×¬Å/¿ØÊÏýïöÌ™“@³f×))‘áæ¦Å?6«¥¥‡ Òcî\súõ‹ °PÆðáúüç?剫ª³Óž$¿Q£PÑÑÁ¸sgú<(_ö`äæF//üFBzÿ>f={Òîÿþ%uuºnÛÆå9sY²Ó=xmÃTõôè¶};!K—ruÁÔ iíå…éƒßÉß}G~\+huY¶ §>Âyî\r¬kWÊJJ0êØ·3Ízö¤õÂ…œ?žâ¬,´mmé¶};*ÚÚ(©ª’xà¡+WRVT„ž“Ýv–æÓ¦‘Á77Tttèðå—òdM]}lÕJ¾lâp»vòå©'O’ÁÕùó¹ú`F€²–£å÷Æýþ;-?ù¤Ú˜×5¹‘‘\ùøcŠ33Ѱ° Å¬YòcÒç±îÝA"AÃÌ «¡Ciñ`kݺÆ@I]^»wsÍË‹CmÚ ¢­ÃƒÄ[I^—ÞŸûÉɨêëc5dm>û¬Þ¯I}ô[·¦Ë/¿p}Å .ôš––òﯺúlÈÔ¦®1€Ú¿ÖP^sd·™Jªªh5mJ»åË1zPËí›oZ´ˆ}ú “J1éÒ»I“(LI!aï^öì‘÷ÓÿäIùrµØ;‰Ý¹S~Îrà@z<Ø1JÉàÁƒe>>>$''síÚ5z÷î…¹ùó1ÕQxõx{{3ñÁ/dA^M?ÿü3Ó§OgݺuÌœ9³Ö#ÇgøðáŒ7ŽÁƒ“ššÊÊ•+9r$?ýô?üð‹-"ëÁùùùtëÖÒÒRæÌ™ƒžž[¶láÆÉà<<<Ø»w/Ÿ|ò íÛ·'55•óçÏóÛo¿qàÀæÎ‹­­-ã¬ëvwwÇÁÁ•+W²råJæÍ›G‡¸|ù2ëׯgóæÍòw\½½½™|˜Ó§OKRRR­Û—þý÷ß :”«W¯âúàœ _ý5sçÎ%77*ë™+f}ܼy³Ö10`¥¥¥œ>}ºÚ9###Þ}÷]¾üòKù±ùóç³qãFrss»öÈÕ«WŸêƒXU‘‘‘xzz2vìXæÍ›÷DÛÎÈÈx¦¯åIÊÈȠéêõ©©”×Åùúë¯åI‘ <±¤Hrrò QÆÚ:„}ûšãæ¦ÕØ¡‚ ¼°rrr jð=í”Õ«¡8nMÒÒRñóóÇÕÕ F-fŽ‚ Ï—)S¦pèÐ!æÏŸ——®®®¸»»3yòdœTט={6lݺU~ÌÉɉ¡C‡²xñb¬«L+øþûïILL$<<Ók„GMË–-Ù¼y3‹/Æ××—?þøƒÿýïò™!€|ýðáÃùâ‹/hÓ¦ Ó§O—ŸOIIaÙ²e|ÿý÷L›6 €1cÆ ªªÊ²eËðôô¤¬¬Œÿþ÷¿¼÷Þ{l|PèmôèÑŒ9’ØØØOŸ>}ªÍŒ)*ª ýã(,,$++‹ââbüýýùùçŸéÞ½û W”õüùó|ðÁ,Y²„UŠÔ ÿ^Õ¤ÈÓœ)"‚ O‹HŽ‚ Ï555öïßO`` GŽáüùólܸ‘¯¾úŠ;vðöÛos÷î]"""ðððàŸþ‘ß[VV†L&#$$¤ÆäÈùóçqrr"88Xḽ½½üØñãÇÑ××gl•m ëHII FFF 1éééGNN¹¹¹ÄÅÅU[fóæ›o²¿ÒÖzuùì³Ï–Aù¬’mÛ¶=R¼bÓ¦MlÚ´ eee†Î×_ýÔú{þþûo/^Ì÷ßO—Š­'…ÇR5)ríÚµZw[^ q¿ÿN~-[•š÷郉øÙá9&’#‚ Âs©S§NtzP---òÑGñöÛosûöm>Ì™3gîsww¯µæH\\ééé¬Y³¦Ú9²²²°°°@òˆ[?ܾ}%%%…b*ôïߟÜÜ\ÒÒÒä³V*Tý¼. ¨¶¬&99ù‘b}TÓ¦MÃËË ,--QUmØÏ‹­[·òÓO?±sçNZVZs/ü;ñññ|óÍ7")RƒÄD—Æ¡QÙzx4v‚ ÿšHŽ‚ Ï=sssÞzë->ÿüs222ä…Y,XÀ›o¾ÙàvŒ133ãèÑ£uö•””„T*­³°«L¦¸+†±±1eeelß¾½Öº!…?SRRO;¹ñ¸ ^ˆzUI¥R–.]Ê¥K—Ø»w/–––Ò íöíÛ¬_¿oooÞyçBCC144lì°Aá‰[ù ‚ Ï•   îÝ»§pL&“€žžúúú4kÖŒvíÚñí·ß"}°a…’’Ê*öܬâ7ÞàôéÓÕ–ÕÀúÆ #//-[¶Ô£±±1™™™ Çú÷簾¦¼–HMm›™™ÑªU+vVª/“Éðöö®µ/áß)((`úôéÄÄİgÏ‘y ·oßfΜ9tìØMMMBCCY±bÅIŒäççsôèQþüóOþüóO|}}¹ÿþˆúéiӿɅ»…ðóOÿþ‘üùgfý>‚³gópv}äûÒ|}ñ6 ia!>FFòã2©”+Ì¡6møËÚšÓC‡’ªxþ“OØÛ´)[µ’o -,äÜ”)tvÆÇȈ³ûnýð>FF »ML(ÍË«7Öºú„ò­u»¸°Ç‚½{#-(¨·Í‚„&L`¿ƒûìì¸ðÞ{òNêƒð«½ŽŠ¬k×ꃺú¬OøÆüÝ¡{,-9âæÆíJ;¢¤ž8Áñž=ùËÊŠƒÎÎ/^,ßZøßŽÀÉ^ßÅJuÁêìøñì®RXûîÅ‹íÒ…=––œ;–¢Œ ù¹Œ 8Ñ«5i‰^½Èºvía[ãÆUó=•þ- ]±‚«W“úÏ?øöȱ Ož˜9"‚ ÂqæL<<0íÖ Œ+««#-*ªu êê³>¦Ý»c5t(ê&&äDDpvÜ8 \\ÐoÕ m;;:mÚ„–µ5ÅYY\˜6˜mÛpx÷Ý=ºíØEÿþ ×ÊRާ(=]áXYQç<=qúðCl'L ðƒ¸æåE—Ÿ¦¬¨ˆóžž´öòÂÖÃè-[87e C®]C¢¬L·;’>ç==åÛ= Ï'1sDAx®,\¸~ø®^½JPP¯½öW¯^eΜ9òëÜÜÜ aÈ!pâÄ ”””øé§ŸäÛ¸¹ºº*Ü£ªªÊßÿ¼)ˆ IDATÍ7ß|CRR ..ŽñãÇ3iÒ$ùuëÖ­c÷îÝ”””pàÀv¦™={6{öìÁÙÙCCCÔÔÔ˜:u*/^ÄÆÆ†cÇŽqáš6m*/f å3SŽ=JYYG¥_¿~øøø°páÂjÛðVæààÀÂ… kÜÄÍÍ… Êã¨lذaòÝsjóöÛo3~üøÏÍ™3‡¾}ûÖyÿóäöíÛŒ=šÎ;³nÝ:‘ùâââ˜1c:tÀÐÐèèhV¯^Q¥wëŸuuuÌÍÍÑÒÒBSS j3Çž7jjyò¡òÿDGÑ«WFFAXX³|yŠü\Q‘Œ‰c11 ÂÄ$ˆ¡C£äç¼½34è–üóM›î0n\ŒüóË— èÖ-ƒ ºv ',ìa‚ª®x¦M»Í¬Yñ ‰½ýu¦NSx-kÖ¤be‚¡a:„W,ïßÖö:y|ôQ¶¶×>üa¼kצbo=½kt膯onƒúlÞ<”±c£‰Ž.ÂÖö:¶¶×9uêá½ÿ†’š®k×bà₆™N|@Ab¢|Dâþý8LŠf“&˜õîi×®$>(€­¬¡AóiÓÐoÓ¦Z»ee”54äñ>>Ø>ؾ½>uõý믴Z°ËQ75ŬgO”ÔÕëmÓ¨cGœ>ú´›5Ãnòdî^¹RïT~()!QQyø¹DRçÔÕg…‚ÄDŽvîLÊñã Ç;uB×Ñ5CC´¬­‘¨¨Uþ=¤Û¼9†íÚ¡nlŒ¦¥%ªzzäDD(ܲd g*í×Ðx”TUå¯OéêcI ]±‚Ö^^ Ç3.\@ZXˆãÌ™¨à<{6I‡QV\LnT%¹¹ØO™‚’ª*Ž3gR”™IÖƒíg+Ç’zâY!!´[¾¼Á1 Ïžø‹AAx®3qâD&NœXïµÖÖÖ¬X±¢Öó]ºt©¶3‰ŠŠ žžžxzzÖÙö¨Q£Uå]´ ‰„!C†0dÈjç:vìHÇŽël{À€ 0@áØêÕ«ë¼ÇÉÉ©ÖkºwïN÷îÝk<÷öÛo×Ù.PgòdÉ’%õÞÿ¼¸víÓ§OgîܹL˜0¡±ÃyáT)ýÌjŠìÛ·‚‚JKKyíµ×žIŸOÃÒ¥ÉtŸyye?\.áí}—¸¸"\PQ‘àï_ÿò €ÌL)¯¿É7ߨ0nœÞÞwyë­hBBZ£Ô€·9ýýs¹t©%JJàì|ƒóçóéÚU›èè"V­JáÆ6XY©|-­ò?üÐŒ?4£ÿH¦M3aÜ8ÅĘ¡¡ GŽ8âè¨Á¾}Ù EJJ;´µ•êì3*ª gÏæ1mZááÕÆë¢çä„ÓìÙ(©ªâVÃòÅ Y!!hXX þ Ðuî­[èµhA̯¿¢ßº5º-ZÈÒ*+8˜üøxšÔð;¿&uõy/4c77»¸PV\ŒÍ[oá²t)’†|1«ÄdèRsàªcð¤ÔÔgE’ 4·z’+fûv®ÿßÿQœ•…®£#æ}úÈÏåܼ‰ïÈ‘ge¡¢¥…ËÒ¥ ÷ÞOI!?.î‘㹺`,X€¡«+í–-CÛÖ¶A¯-|ÚŽF•âÒ¹QQè6oÎý”bvì Å¬Y”QPk[y±±Uú; $'‡kŸ~Jǯ¾BõÁ›7VÆD‚†‰ N³g7(NáéÉAA^xÇŽÃËË‹uëÖѯ_¿Æç…’Àúõë9}úô3OŠTxýõ×)(( &&‹xÚ¹’’„ÄÄb’’J°¶V¥G…sÙÙR"#‹pqÑÄÝ]·AmîÛ—…³³&”'(¦L1fÑ¢$¢¢ŠhÑ¢þÇÈ“íÛkYH×®ÚH$PR"#,ì>ff*´o¯Ùà×ùÞ{k2ŒmÀŒJDG—¿®ºú¬‹……¥¥¥ÕŽŸë¢¬¥…–µ5¶&p70ø={äç$**hXX`Þ¯ÚM›¼xñcÇÓdð`ôZ¶Ä M:ÿ=yQQÕ’5 òò¢—WË›T´µ)ÍÏǼO††„ {Pð]E[%uuºnÛFô¶mhÑ‚¬  \\P©”ºHâþý¸®YÓà×'41sDA„’L&cãÆ>|˜¿þú +ñÎ[ƒ$$$ðý÷ßsøða<<<8{ö,ÅÅÅ51~Ö$ ¹¹¹”––¾õb,-UÙºÕŸ,Þ}7ޱc ‘HÊë,_Þ„åË›^H÷îáŒcHÇŽåTKK&rr&š6U¥o_]öîuxâñNšd̤IÆdfJ>ü[¶dðÉ'ë))I¨²k9ii¥Lœ‹¯¯:i`dTíºÚÔÔ&@»víjœ9âëëKûJï°×¤¬¤„sžž¸¸Ðrþ|…s:Í›“)/Ò™{ë&°t+éÐ!Ô 1©²D`x•: éSÇΉäa]‰DBåq˜:‡©Skl÷~r2þo¾Iû+0«R´¸®1xuõù($JJd߸͛oV?©¤Ä½7žh<PR’'3ê’yíZµš)>FF ºpr£¢••!QR"72%uu´š6À¤Køùå³I¶j…AÛ¶@ù×äʼy´_µ õ*;àÏ'1sDA„Nqq1sæÌÁßߟ]»v‰ÄHÄÇÇãååň#033ãìÙ³|úé§òÆ!))‰øøxîß¿Onn.W¯^ÅÈÈè¹NŒËä[åVþ€²II)ŸÑ¡ª*AGG™Šçà€€­†%ÆÛ›âìl SSIØ·³ž=ëm³èî]üÇŒÁéñ|ýu¤……òXëƒúÔ6uõY¡ 1‘¿;t¨–\[»–œ›7)ÎÎ&qÿ~RŽǤS'b~ý•Œ (ÎÊ"óÊnýø#ÆÎUþïñ3¦ÁcP”žNÂÞ½¦¦R˜šJТEhY[£×¢…B§‡ !tÕ*…cÃ#"›™ÉØÌLøù!QRblf&º-Z`Úµ+ÊêêDýü3¥¹¹D|û-VC‡¢ô zVp0…wîÏå¹s1jß^¾D(rÓ&4Ḭ̀zã ù×Dúwûž¼ç÷_AA„Ü»wéÓ§chhÈüF뼪âããùá‡8vì“&MÂ××·Q"•I$BBBÈÉÉAII SSSzõêÕØaÕ©C‡0nÜ(ÀéÞ=€û÷; ¡!!0°€™3ãÉÏ—Ò¬™:;w>œòŸšZÂ;ïÄ‘šZ‚2K–4ÁÉ©ü{·];MÆŒ1¤}û0œœ4èÖM‡´´ò$‹±± ‡92o^žžq¨«Kpw×cÒ$ãzã©KQQŸ~šHxx!J¼ñ†>žžÆ ×ÌkÆÄ‰±lß~WW-nŽƒƒ:óæ™Ó¹óM¬­ÕpsÓ¢iÓ†ï bg§ÆÔ©&´n} Û·Ûáî®û¯œ…))$ìÝ @B¥eýOžÄÐÕ•æÓ¦‘Á77Tttèðå—òm|¶jEqVy2êp»v¨›šÊg„$&rÇߟŽu€­I]}¶øàòbcù»}{”ÔÕiööÛ4k@ò%õäIr""¸:>WÌ QÖÒbtbb½cPŸÚÆ ®>+”“W­ knT~£FQœ•…f“&´]²D^ж$/Kï¿ÏýädTõõ±2„6Ÿ}¦paZZµ¢§uÅ#“J‰øæ?ú‰²2Ænnôøãy£BþíÛè9;×;&*–Î\ž3‡%K0íу×6lŸO?s†°¯¾¢¬¸ wwºüò‹üÜíݻɹy“¿š4Qhó›7Ѩaç9¡ñI,óññ!99™k׮ѻw/ÌÍ_ÜBX‹ÍÛÛ»A;T‚ /‡«W¯bòÓ™2e ={ödñâÅ(=âOSFFÆ#½–§-**Šï¾û???&MšÄ´iÓÐÕ­¹hFF:thP»…ðÎgrr2öööϼ_AáÙËÉÉ èÁ¶È Q±ü®¡Éÿ´´TüüüquuÅ‚ѣG‹™#‚ ‚ ¼ÂÃÃyçwðôôdÆŒÎs«jRÄÏϯ֤ˆ ‚ åDrDA„çÞÙ³g™={6Ë—/gذaÎséÖ­[|ÿý÷øûû3qâD‘A„G ’#‚ ‚ <×|||X½z5›7o¦S•¢}Bõ¤ˆ¯¯¯HŠ‚ Â#ÉAAž[?üð¿ÿþ;>>>¢æD‘‘‘üðà 3Ettt;¬WFûöa¬[gMÿþÏGq[Aáñ‘©$22’yóæ1~üxlllðóócÞ¼y¯\bäÓO“5*ZáØ—_¦Ñ·oä3éßËËggÍgÒWš¯/¾Ã†!-,ÄÇÈH~\&•råã9Ô¦ Y[szèP²CCÜn̯¿rÐÙ™½66\[¸YÅþÀuô)‚ð²3GAAx®äççóÁPVVÆü¶¶vc‡ô\3E ¬ÏO?¥SV›>ÃàÁÏf&ÇÛo>“~ê"“JQRQ¡Çï¿£aaAøÆxx048$uoå›JÐgŸÑs÷n´›6åô°aè·l‰½§ç³ ^á9#fŽ‚ ‚ðܸsço½õffflݺU$F€ˆˆùL‘-ZpöìÙWr¦HUÝ»k#•Ê. ´TÆ™3y ¬@I‰ŒO?MÂÆ&KË`–,IF&{xÿ¶mw2ä~OÓ¦!XXsùry[GŽÜ£eËáàpƒïÉïÛ¸ñ¶¶×QS»Ê?ÿä(ÄSD¿~‘á↟_žüÜ´i·™5+ž"±·¿ÎÔ©q=Jjj¸®]‹‹ ff8}ð‰‰¦¥Õ{oâþý˜÷éƒi·nh5mŠý”)Äÿõ×cÇ$‚ð¢ÉAAž ·nÝbÔ¨Qôïߟµk×¢¢òjOp­HŠxxxÈ“"³fÍBSóÙ,åxÞ©¨Hèß_Ó§s ,@__™¶mËÇgåÊüýs¹r¥¡¡­9|ø>>Y mœ>‹«« .„‡·¡iS5¦O¿Íš5Vdg·ÇÏÏ;;5ù=sçš×–V­4ªÅ4qb,:i‘žÞŽeËš0zt99Rùyÿ\öíkέ9y2—óçóôZõœœpš=%UUÜ6n¬õº¬4,,P75­·Íܨ(tœH?{–¸ßG¯E r£¢¹OA„—…HŽ‚ ‚Ðè®\¹‚‡‡ ,`Þ¼yN£ gÞ¼yL˜0A$Eê1x°¾<9rút®|ÖÀÖ­,[ÖSSŒU˜1Ô}û²î·±QãÝwM00PÆÜ¼|89r„ëׯ7vHÏŒHŠ<}úè"“u¬v\MMªUV¬ZUsâÁÓÓOOãÏ=êXk7nÔ>›ÃÑQÓ§[ÔxnË–f ŸïÞíPk; ¥ieÅØÌÌz¯vãFǦNÅaêÔÇŽCáe –Õ‚ ‚ðLÄÄÄЭ[7:vìȶmÛPU­¿ÃË bùÌÔ©SqssËgAá9$fŽ‚ ‚ðÔ]ºt‰‘#G²xñbfÎœÙØá<3E®]»Æ{ï½Ç† PW¯ywAA—HŽ‚ ‚ðTíß¿Ÿ÷Þ{­[·2lذÆç© cÓ¦M")"‚ /±¬F^±±±üôÓO5nõWÙ¡C‡8sæÌ3ŠªÜ‘#G8}úôiëôéÓ9r䉴õ$âããÓØa gË–-|ðÁ9rä¥OŒ\¿~©S§òî»ïâææ†¯¯/S§N‰AAxˆ™#‚ÐÈZ·nMJJJ×lÞ¼eeef̘Á¤I“PQ©ýGwýúõ8::Ò³gÏ'j­¾ýö[LMMéÛ·ïc·µmÛ6ÒÓÓ|Xž ™9s&ŽŽŽlÞ¼€•+W²téRZµj…––·nÝbÈ!ìØ±£ÖöÃÃÃñõõàܹsôîÝ›¡C‡rèÐ!þùçFŒAzz:ÚÚÚÌž=SSS¶oß.ï3,, ‚‚‚PWW'66–¯¾úŠyóæÉûÚºu+3gÎÄÊÊ MMMTUUåI™ ¹¹¹Œ=???œ‰GKK‹ýû÷Ó©S'”””0`_|ñ3fÌÊgÏüøãx{{3aÂ,XÀ½{÷8xð`¯ûÌ™3 >š4iBJJ ÷îÝãîÝ»òkd2S¦LáèѣɢE‹X¹r¥üšcÇŽ1nÜ8TUU155%""‚±cDzcÇTUU9~ü8sæÌáîÝ»¨ªª’››KÇŽ144$55‰DBzz:®®®;vŒðã?2gΚ7oŽQQQ¸ººrôèц}ÓÂ3’””ÄСCéÝ»76lPHܾ jKŠ'55•ââb¬¬¬;AáörýÕ"¯€sçΑ˜˜È78qâG­µ€iAAK—.eÛ¶msþüyÒÒÒX²dI­í»»»sþüyòóó8yò$vvvøûûSRRÀ©S§puuÅÐаÎ8{ôèAbb"ÑÑÑüç?ÿá¿ÿý/¹¹¹¤¥¥1{ölæÌ™Cll,aaa̘1Cž€©ðùçŸséÒ%.]ºDHH 4oÞœ·Þz‹’’”••éÝ»7'Ož”ßSsÅ1™LÆ©S§èׯ_­ñ®X±‚ÁƒK@@111œ?^ášððpLMMIII!""‚¯¾úеk×’˜˜”¿c>nÜ8úõë'ÿ;vŒÝ»w³qãFùøæææréÒ%üýý166&77—ëׯËÇWMM=z “ÉX´h«W¯æÆššÊ·ß~[ëk„Æpýúuºu놇‡_ýõK•¹|ù2S§NeöìÙôèÑC^hU$F_YY¸¹¹5v(‚ Â+îåùËE^«V­B[[€>}ú`ooOPPP×–”” •JIJJ’'6”••qpp¨µý¾}ûRZZÊÙ³gòDÃܹsQVVæâÅ‹òcîîîuÆieeÅÂ… 埿óÎ;äççsëÖ-víÚ…D"aéÒ¥òkfÍšEóæÍÚñññaêÔ©òå+ººº¬ZµŠ¸¸8y‚ÁÝÝÓ§OSVVF||<ÑÑÑ,^¼Xž #%%¥Î˜ ÉÉÉ!33S~¬E‹ ×hjj²jÕ*ùCß;ï¼Cii)¡¡¡œ8q‚ììlÖ¯_/hrwwgäÈ‘ònlllhÞ¼¹<¶S§N1pà@ºuë&?vòäIºu놦¦&2™Œ¢¢"RSS),,@"‘¼ÔÅ-…ÏÉ“'éß¿?kÖ¬á?ÿùOc‡óÄT$EæÌ™CÿþýñóóI‘',<<sss ;Aá'’#‚ð‚©šØ022"++«ÆkõõõùüóÏñòòÂÒÒ’ &°k×.ÊÊÊjmßÄÄNžœ:uŠôôtBBBêMŽØÙÙ!‘Hâäɇ˜˜ìììä‰(èoÛ¶­üóüü|RSSi—  IDAT×®BÛ‰’¨¨( <‘‘‘App0'OžÄÕÕ•Ñ£G“œœÌ­[·8uêfff mWõù矀¥¥%dÆ dgg+\Ó´iS…‡"CCC$‰ü5EGGc``@³fͪÅ[‘ªˆ·r"ÄÝÝ]áXåY.JJJ¬ZµŠõë×caaÁرcÙ¾}»<Ù%mûöíL˜0Ý»w3nܸÆç‰ ¬–ñððÅBŸ‚ŒŒ ÂÃÃÙ±c;vì ¸¸˜£GÑØ¡ ‚ ¯‘„Œ²²ò#]¿|ùrbccY¹r%ÅÅÅLœ8:ï©xP÷÷÷ÇÜÜ'''ÜÝÝùçŸ8}ú4jjjtïÞý±âTUU¥´´´ÚñÊý***H$’j×U\£ªª @Ë–-iÒ¤ 'Ož”'ôôôèÔ©“üXß¾}’5U 0€øøx~ÿýwY½z5... 3Iê{M***µ¾¦ÊIwww.\¸@\\ׯ_—'Güýý¹uëÑÑÑ É§9sæÀ† PSScÖ¬Y 4™LVg<‚ð´­Y³†eË–áëëûL·Z*’"sçÎI‘g¤W¯^Lžúˆ‚‚JJJX´hwîÜQ¸îÝwßeçÎìß¿Ÿ²²2¢¢¢˜?>:t C‡òë*f¶dffÒ£Gù±pïÞ½:‹±lܸQaéË… €òåA 5pà@lll˜5kiii”––²yófNœ8Á´iÓä×U,]Ú¹s§|,•••éÓ§;wî¤wïÞò‡²¬¬,¾ùæù¸”––rùòe´µµåÛü ³”ŸŸÏˆ#ˆåÔ©S˜››7vHÿZå¤È°aÃðõõI‘F6nÜ8ñ»MAh"9òŠ‘J¥;vŒÁƒÓ¬Y3¾úê«ÆIxŠJJJX¾|9FFFb``À©S§øã?ê¼OGG‡×^{²²2…¤‚»»;R©´ÞDCCèèèðÛo¿qòäIôõõÑÕÕ%""‚1cÆ(\·páBÞ{ï=ÆŒƒŽŽŽŽŽèéé±gÏ…e2ýúõC*•*ÌjéÚµ+êêêØÚÚÖY„ààÁƒ899¡¥¥…¾¾>Ó¦MãË/¿THÀÔGCCƒƒ’™™‰¥¥%ºººÌ™3‡%K–0yòd…k+Ʋr¢©¦ceeelܸsssôõõÑÓÓÃÛÛ›?þøuuõÇ&OBjj*½{÷ÆÜÜœC‡¡««ÛØ!ý+Œ?^$EAA“ ‘‘‘äççãè舎ŽÎcÇ–’’Bjj*:::ØÙÙ‰‡8á™»yó&C† aÊ”) »L=®«W¯bbbòÄÚ«K`` ëׯ'11‘Y³fñÖ[o=ÑŸ¥ŒŒŒgöZž´ŒŒŒ'ƒ+vÎz–’““±··æý ‚ Ï^NN@­;rÖ¤bÃ==½]Ÿ––ŠŸŸ?®®®XXX0zôhÄ_×/±;wîðóÏ?ãííMfffµå fffÏUbDxz¬­­±¶¶nì0j¤¡¡!ÿeV:vìøTc122’ïªó¸ªnü¸,--±´´|¢m BC?žÑ£Góÿ÷ KÄ^|õÕW$%%=•¤ˆ ‚ />ñ—ÁK¦°°Ö¯_OJJ éééµnÛjooOLLÌ3ްnwîÜyîbªªª*M›6mì0AžŠ¿þú‹™3g²cÇ ÔØá<’ФHzz:ï¿ÿ>#GŽ|®fÎ gùò¶nÍ 9¹„ãÇéÓçÅ\"–“#ÅÅ%Œ‚‚2ZµÒÀ×WìòS› //T´µióùçŠ 4*ñ³P;‘yÉ|üñÇüòË/×{í‰'äÅ6ŸyyylÚ´©±Ãx$Íš5{¤MA^_ý5ëÖ­ãØ±c¸ºº6v8 Ⱥuë¸{÷.³fÍI¡šÅ‹-Y¼Ø’6mn4v(EOO™¸¸¶ìÞŦMwê¿¡ŠÐ+¨¨`ìæFøÆô9tè)Dù|°1¥WxÆXÆ… \ž=›A—.=±6³‚‚Z´ˆ¬à`Tõõ±èÛ—Nß}'?/-(`Ÿƒ¦ÝºÑkÏùq##Lºv¥ïáÃ/^Lä¦Mô?yiQþo¾ÉèGÜ‘O&•ruÁRާ8;Ãvíp]³ƒ6mt̯¿rcÍJ °?žö_|DéÅ,ÏysÝ: ’“é¸~}çŸôÏ‚¬´”ÝffŒÍÌä䀸,Y‚éƒÍ^4¯îoˆ—Ô÷ßÏ¢E‹øî»ïسgYYY [¥V6`ÀvïÞýŒ#¬›¨9"‚Ðød2 .äï¿ÿ& ›Æ©ADRD„Ú˜téÒØ!¼Td¥¥œ7ç¹séµ{7Ewï’T%¹–æë‹º±1锿å¡R©[QF…wî afÆ×=V[[þvu%ùèQ…˜ 9Ú¹3)ÇW‹õ¼§'y±±vqá°‹ wüýî ˜0ýÍ›sÀɉÈ*³¸kƒüøxŠ22h>}:ÊZZh5mŠã¬Y ÷%;FÓQ£Ð±³#­Ê,k«aÃH:tˆÌ+WÐoÕ %55ª¦x”ÔÔp]»4ÌÌpúà )LKkP{æ}ú`Ú­ZM›b?e ñýUoŸ_ÍßíÛ³×Æ†½{“~ö¬ü\YQ§Og¿ƒû8óöÛ ÷Öõ½—uí§^}¶¶œ8œðpù¹üøxþ²¶&vçNþvu倣#Q[¶uí‡]\ˆØ´‰xù×ZZPÔÿ³PÛÏ_Å¯¿æD¯^騑ÌË—ëבH޼äÜÜÜøóÏ?¹uë;wî¤gÏžXYY¡ªªJFF/^lìA„çDff&ýû÷G&“qøðaôõõ;¤:U$E–-[ƤI“DRäcl¬Â/¿ØâéK~~3fÜfÆ S:w.3('GÊñãŽ*#•Ê8t¨9?ÿ\ólÚªŠŠd˜šªríZ+æÎ5gñâdù¹¥K“éÜY›»wÛsëV[úõS¬WR\,#"¢ˆœ©Âqoï»ÄÅ‘àBJJ;>þؼRe¼÷ž .„…µæÆBÖ®M•ŸïÞ]‡?ÿ´gíÚTΜqÆÒR?¿ÜG³ª¬† £ÉàÁè·l‰ÓìÙ|ÿí]»è½oo„‡Óú?ÿ‘Ï8wŽîÞÞ¼~þ¬GŒ¨6¶u©ëkR!+$ ÔMMëm/7* ]''ÒÏž%î÷ßÑkтܨ(…kjëSÕÀ€ž>>ŒŒ‹£Õüùœõð 4¿<™z{×.òÊáá8Uúž­ë{¯8+ ÿ1ch>m#¢£qxçÎOª0Æe……ܽr…A—.ñFx8ýúaèêÊМ>ü›±cå_ke-- îŸ…ú~þÊŠ‹Q71a€¿?޳fúÅòseeÜ6n åÇ£Û¼y½cþ¼É‘W„ªª*£FÂßߟV­Z…¡¡!ÖÍ„§aýúõìxð¿ ÏŸØØXºuëF‡øóÏ?ÑÐÐhìjuöìYFŒ!OŠ=zT$E^Qê1x°>={†“’RÂgŸ=ÜÕËÔT==eìíÕiÑB[[uÒÒJÜöøñå»—uë¦Mdäà JJ‹IJ*AWW‰=·koÞ\™¬#ãÆ)î~¦¤$!;[Jdd**ÜÝ&Uœ5?Þ%ôõ•7Έ  ùy{{uìíÕ15UÁÀ@¹A¯åâÅ‹˜˜˜Tû°­ôàkؾ=†íÚ¡ie…åÀ ùk}ï=Ôl_­ëè(?Þdð`T´µQÖÔÄ Mr££îvãFµzÉGŽÐtäHtPÕÓ£ù{ï‘ô &€óܹ¨›š"QQÁ~âD²¯_—Ÿ“()q?9™ûÉɨèê6xÙ€D"AVRBNDe%%´m[íaÚnòdÆff¢an^í~›7ß@¯eKHä³,ĤkW”TUÑqpÀ¬W/y¼ê&&¨êé¡ck‹®ƒZ66¦—ÏhŠÝ¹û)S0hÛ%55œ?úHa $JJäÞºEINšVVè·j¥޽=c33i:fLƒ^?”× ¹Ÿ’B›E‹PÖÔDÍÀ«aÃêe ú>Œ4?ÿ1c8Ôª•|† @Vp0ÅwïbÚ­}û’râ„Âý¦…eÅÅ$9‚yß¾ Ž·¶x*+ÎÊ"hÑ"\¿øIþ](-(@U[›ôsçH:ty‚£¾>í§LA×щ’Vo¼’šùqq@ù׫äÞ=r££QRQÁ¬woù}u}ï%>Œn‹ØŒ‹DYÛñã)ÎÎ&¯Ò¦²²2Ú|ú)JªªH”•ÑyÌ­ÎëûùäßW&;+&$ì&OÊö5*Í:{шš#¯ ###æÏŸÏüùóÉÎÎnìp„WÄáDZ³³còƒ_ž‚ Ô­^mÅgŸ%Ñ©SJ¬YcÍ[oÖÛæ„ FÄÆ1~| ))%ÌšeʪUVdgKùøãΜɣ¤DFÞÿ³wçqQUýÇ?3Ã.»l";ˆ‚€Š;˜˜¥ ¹f¦¦™†ö˜•ÕcÙ£(•-jYiÚfåOÊ ÜwQsEDDP\ØAE``˜ßàÈ&Š>÷ë5/™{î9÷œË½8÷;g)QЫנË6ÜnSÓmñññ!66¶Þvi N2©WƒÛ4 U?K45Q”•5YVyn.&]º¨ÞëXZÞé]¢TráË/¹úÇT•–¢T(Ô¾Á÷ åÜG±'0©¶6^ Ð~Ĉ&ÙÆÞž®K–p><œÂ—^ÂjÀº/_®VÿÆÔÝOª¡¡jçÍ„â.¤8) eu57nж[·šóQ{þ%2™êu»-·ÒÓÉ>xôÈÈÚf+QVU¡¬®F"•Òsõj.|ñ _|“Ý—/¯ ¹_·ÒÓѵ¶Fò7&èÔwrÂoÅ P*É‰ŽæÈøñ˜úøÐÖÏÌ;iëç‡LOó>}rãôiÚvï®Êï8q"7‘µ`0^QVÆ‘ñãq?¾Ù=R4ôô¨,-Å+4 &@r×t÷’Eâ²eªÀXea!Õ•5K»1c(¹r…cS§Rž•…Ó”)xΟ4~í•¥§S”˜È??Õq” yyª^Rmí B4zÿQ3léöïI¢¡¡6ä퉎ü÷vA„V´sçN&OžÌÊ•+:thkW§A‡&<<œòòrBBB1bD‹>à O.…¦MKcÖ, BC3:Ô++Í{+ž ¥%A¡hxß{i×N“5k숈(à•WÒ3Ƥ©9ÑÒ’fMX˜5‰‰åôé“ȨQ&tíªÇ¼yéÈåJΞõ@OOÊW_å°kWQ£åÕmKC툉‰ipùm}}}®7±ˆTG¥BQs‰¤Þ0Ûšó­|s阛«ÍQž­ú&ýzd$Wÿøƒ€M›Ð±´äæùóì}æ™;y­¬è^;àú¦Mœœ5‹öÇ79'@‡±cé0v,ò‚¿ø"©?ÿŒëö¬>6mN“'Ó÷÷ß‘H¥8åÝ_·Óôll° Âeúô÷kÛ½;}×­£º²’¸ùó9FßuëšU'‰DÒ`ôll(ËÈ@YUõ·$µ…cá77/\ ­Ÿ;wRxîœjîeUUMÀ¤Np¤Ã /ü½ãÝCue%G'OÆØË‹Žï¼Óì|NNÜLHP½/¾t©YCCÊsr8BÿÈHL}}Øäà º9¥ZZtž;—ÎsçR|ñ"ûž}ÛaÃ0ñöî}íéÚØ`Þ·/}Ö®½ç±½÷¤ÒúìšÐØý÷O">Y‚ æ«¯¾"&&FõþâÅ‹|úé§jøÆÇÇóù矫ý'›˜˜È¼yó˜1cŸ}öW®\i°üÄÄDæÌ™ÃŒ3X¿~}£ æíåË—STTÄgŸ}FHHáááßõA-%%…¯¿þšY³f1{öl~øáÊø¦êôéÓ¼õÖ[üë_ÿbóæÍ¤¦¦òé§ŸÖÛ÷ÀÌž=›×_ï¾û®ÞñáÁêÕ«™4iþùçc9|ø0Æ #,,ŒI“&©†ÏˆÀˆpÛ¢E™k°lY{¦O7çå—Óîë™ÀÝ]—C‡îïïû–-…dfÖ|3¬©)A__¦ö ~õªgçx¶nUŸõÈ‘Ο/G©mm)J%èë×\Ë…… ÜÝuÐÓ“RZZÍÚµê“>6ÅÍM‡„„2òóïô¦ðõõ%>>¾ÞëD3–rÕ65EÓÀ€gÎÔ´é®É)Ôþ!Cˆ_´Hm›õ³ÏrmÓ&JRR¨,*"yÕ*¬kƒ;•7o¢ciY3¤A©$¥Î¦P3$ <«fމ¦fÍ·þÍŒ”^½JΡCTWVÖ OHÔVTš`Ë6_ßzq6¦òæMŒ==‘H¥”\¾LöÁƒÍÊgÿâ‹\Z±Bõ ^–žÎõ-[TéWÖ¯§ª¤DÕãDÃ@}¾›[ׯ³Í×·Þ„¬P@*ËÌD~ã†ÚvootÛµãÜÇ£(+£òæM2¶mSÛ§¡sPYTŰ0J._FQ^NöÄÆbâåEyv6±± :|˜QYYŒÊʢˇÖ›w¤1ŠòrµWÝ»¡ú(««91}:š††x…†6˜¾ölŸ{ŽìƒÉ?q‚²Œ .ÿü3vwõ}ú¨4&OžÌöíÛ‘Édܺu‹wß}—RYygLöÎ;éÙ³'±±±èèè°páB¦NÊ{ï½Ç­[wÆv¿ýöÛ 4ˆ¬¬,d2Ÿ}ö~~~ü½“*¥RÉ‚ øä“OˆŽŽ¦wïÞ­]%5‡fèС|øá‡„„„¨&ZA¡®'JY¾<‡ï¿ïÀܹVäåU±|yN9ï˜?¿þY@›6g˜3§ñÞ·|˜O?ý¹\άY³2dH³? >òòòTmyÒäååá[Û-¼)å­0Ö<##ÇœpPAx2Õ.aÝмI÷â];\ɰ™seggqðà!|||°²²bäÈ‘¢çˆ ê¹rå ÉÉÉqòäI>þøcÈÊÊ";;›øøxUà ::š²²2&NœHAA2|øpNœ8Au.€½zõRF&MšD›6mØÑŒn–uWV²··'88˜¨¨(Õ¶6mÚœœÌ† X¹r%»víÂÔÔ”„Ú®©×¯_'!!ÕÖ––S¦LQ;ÎöíÛqwwÇ××WÕžÊÊJž}öYŽ;ö7Ψ <>Š‹‹6l%%%ìß¿¿Õ#PLݳgÁÁÁ|ôÑGLŸ>;vôDFAAx²‰ YAPãë닱±1{÷îÅÆÆkkkúõ뇇‡ûj»ôikk«ºá'''S]]ƒƒCƒååææbY»ìY‡ÔÒ444°µµ½çü$·ab¢¾€ƒƒgjÇB¼þúë¬ZµŠnݺѮ];$ åååäÕvLOOÀ¦NwGë™5o IDATÛ»ºæ&''“€©©úŒ.u–)„'MFFAAAøûû³dÉ’V¢¢T*‰ŠŠbΜ9@Í=ü¤õAᇎ‚ F&“Ñ¿öìÙƒ­­­ª‡H`` {öìA*•Ò»wotuušÞZZZ¡­­ÝhÙ7oÞ¬·­  €¶mÛ6𝤤„ªª*4êÌ¢^7ßñãÇY¾|9G¥W¯^ª}ìììT?ßÐäÝ5Î477Wí½––½{÷æÈ‘#ÖIž$ññññòË/³`Á‚V­Ëí È‚ ¨ªªbòäÉŒ;VEAAhUbX õ 0€ýû÷³{÷nµàȾ}ûØ»w¯j€¿¿?r¹œ76YîáÇÕV}‰%33³Éqî …‚]uf^¯ªªb÷îݪ|iiiH¥Rµ!;±±±jKÚÙÙѾ}{~ÿýwÕ6¥R©ö ÿþœ>}šäää&Û#O‚}ûöÈâÅ‹[50¢T*‰ŒŒ¤[·nÌ›7÷Þ{ØØX(#‚ ‚ ´:ÑsD„z™5k7nÜPBHOO§ªªJ-8âããÃäÉ“ áúõëôë×ŠŠ Îœ9CBBß}÷j_MMMFͼyó(..æ­·Þ¢[·n <¸ÑúèêêòÖ[oQQQA»víXºt)YYY¼ûî»tëÖ ™LÆo¼AHH©©©¼ÿþûjCq444øøã™4iåååtëÖ;vu{é¿Ú‡³—_~™U«VñÌ3ϰ`Á<<<ÈÏÏçÈ‘#5¹ºŽ Œñå—_òå—_Þw=¡µ) fΜÉÑ£G‰ŽŽ®7 ñÃV7(¢­­ÍÂ… A¡Q2™ ™LÖÚÕAþÁDpD„ŒãÇóÇàï簾&QQQüøã|ÿý÷­]5Ah¥¥¥¼ð TTT¡¡á#;öí ÈüùóÑÑÑaáÂ… :ô‘_x²EEE!‘H°±±ÁÛÛ[Õ‹DAá±fll\o à¿ËÜÜœ«W¯2wî\ªªªprrâ÷ßçùçŸo‘ò¡5eee1tèP:wîÌÊ•+ÙÃeÝ ˆ®®.aaa"("4›D"ÁßßSSSnݺÅÉ“'9uê”ÚÊc‚ ‚ð(ˆàˆ µ)S¦0eÊ”)ËÑÑ‘ÿû¿ÿk‘²áq’œœÌ!CxñÅÙŠ4ÕÕÕüùçŸÌŸ?þ©Tн½=P3¿T×®]‰ŽŽÁAá‘ÁAAx‚;vŒ#FÆ´iÓúñêELMM A¡ÅH¥R”JekWCAøÁAAxBmܸ‘~úé§&—Ä~P"("< yyyTUUabbByy9±±±ØÚÚ¶vµA„ A„'вeËøì³Ïرc¾¾¾í8·ƒ"óæÍÃÌÌLE„%—Ë9~ü8¥¥¥hiiakk[o¹xAAxDpDAž J¥’÷ߟÈÈHŽ=ŠÝC9ÎÝA‘Ï>ûLE„gmm­¶¼º ‚ ´A„'DEE“'O&;;›#GŽ`llÜâǸùÏþƒ¹¹¹Š‚ ‚ð ‚#‚ ‚ð(((à¹çžÃÒÒ’mÛ¶¡££Ó¢åßùæ›o8p`‹CAáq%‚#‚ ‚ð˜KMMeÈ! 4ˆ%K– •J[¬ì»ƒ"ß~û--V¾ ‚ “ å>] ‚ ‚ÐâN:Eß¾}™9s&Ë–-k±ÀHee%?ÿü3îîî,[¶Œo¿ý–Ç‹Àˆð–‰½ý9´´b8p ¸µ«ó·)°·?‡…ÅYú÷Ojíê<±Ò·neßC^ñKžTÙûö±ÕË‹vvœ_¼¸µ«óP‰àˆ ‚ <¦víÚÅ!CXºt)ÿú׿Z¤ÌÛA‘N:±råJV¬X!‚"Â?ÎüùíHKóÄÕU»µ«ò@ e¤¥yòí·-?1söFQ^N„©i‹—ÿ81îÜ·×^kíj´ª?ÌÌ(ÏÎn±ò*òò8öÊ+lrp`“ƒ‡Fޤ,3SmŸýC†°ÑÎŽj¹\µí@p0¬­©*- s×."LMIúúkU= Μ¹ïú$¯ZÅŽž=Ù`mÍŽž=¹¾ys³óæ?ÎŽž=ù³];¢ÇŒ¡"/ï¾ßÒ.|þ9§gÏ~$Dz0€ ¸8:ŒÛ`º²ªJõ7bïÓO“{øð#©×à ‚#‚ ‚ðZ³f /½ô7nd̘1\Þí ˆ‡‡‡ZPdÀ€-P[A„'W›°O·¨Óo¿D&cð©S >uŠcÇ¢T(Téò‚òOBÓÀ Þô® Y»wpmãFôœ¸>ùùø†‡3$6wÞáø«¯R|éR“ùª+*8:y2Ž'2ô¤ZZœyÿý®ðxÁAAxŒ(•J,XÀ¢E‹8tè}úôy òîЬ\¹RE„ÿ ùùU´oÇÑ£5ß0çæVaclj¥\¿^‰…ÅYzöLÄË+¹sÓ17?Ëwßå6Yîõë•èèÄž…O..ñ?^ªJOI©Àß? SÓX¬¬Î¦þmøÕ«rÜÝãÙ¶í¦ÚöŠ %&¤bf‹™Y,AAɪ´ÔT9\ÄÄ$›8¦O¿By¹€©S¯Ð¿––g™??OÏóx{'P\\Ýd[&LHå….ãïŸD§Nçyï½t”Ê&³5i›¯/ù'OªÞovr¢øâEÕû¯½FÜ‚9’¨NØÚ¥ ÊêjNÍšÅé·ßæàˆlóöæäÌ™õÊŽ %úùçëmÏÞ·½{³ÉÞžC#GrëúuUZÒ²elóöf£»Ô¶«+*8þê«lvrb³“Ñw}û½ÕË‹-.. «ÉÚ½›=z°ÉÞžm>>dìØ¡–~ëúuvøù‘¹k—ÚöÆÚYzå † c“½=‘œž=Ey¹ê¼F?ÿ<‘îî$.]JdÇŽœ¨Ó£¥àÌö=ó ›ìíÙ;hE‰‰ª´’ÔTöÂ&¶¸¹‘^¯=‚ƒë ‹8þê«lõòBY]ÍžÀ@¶zyqáË/U銲2bçÎ%ÊÓ“Mööü5eJ³ÎÁS§pœ4 í¶mÑnÛ–cÇ¢gk{çÜîÙƒq§NXLÆÎjym‡ ãÚ–-TËåž;‡©O½¶Ü˽êÓé½÷°ð÷GÇ»ѣѵ±¡àìÙ&ËË;v Ey9.Ó§£elŒû¬Y¤GE©õv¹×1¿ú*Ç^y…ýC†°³W/â.¤î ˜öÛoD?ÿ<1ï¾KTçÎlqsSõй×õ^pæ [½¼HZ¾œ«lõòb«—Š[·(MKS]_»úö%÷Èõöüõ{Ÿ~š:°½[7²P¥%}õUÍýàêªv]þ]'¯câ/#9¼eKüáyDpDAUUU„„„°}ûvŽ=Š««ëß.ë^A‘§žzªk,­§m[ V¯¶gòäTJK« ¹BHˆ9~~m€šù8vírÁÄD†B¡$*Ê™U«š×¾¢B‰¹¹&gÎxðæ›–ÌŸŸ¡J[° ?¿6äç{sé’'¨å•Ë•$%UPT¤PÛ¾vm>ii\»æEfff϶¬s¼j¦M3ãÚ5/:qþ|9ááYªô>}ôY·Î‘ðð,¢£Ýi×N“ƒ›7WJLÌ-¶ms!&ƃ;o²qca³òº¹á6kRMMº-]Ú¬Ã.^ÄÈÃC-à ilL¿ˆ†§¥áñÎ;~ñEÕ°Œ+ë×SzíÁññ ML¬7|&(.Ž®÷hß©·ÞÂkÁ†§¥ñTTmìÔ‡/UËå''SU\ÿwq¯vVWTàøÒKÇÇóì±c%&’ôÕWª|®3fà8ié[·2$&†ŒíÛ©*.F^PÀ¡Q£pž:•çRRpzùeþš2euMìüâŘvíÊðË—rêþþõêT’šJyNŽÚ¶+W‡D*eàÞ½ÅÅѱÎp³óæQ”˜ÈÓûö141›!CšuL¼½9¿x19¢(+«W—Œ;±èßËþýëŒ=<(¹|™ŒmÛ°¨—·1ýNn“߸AYz:Æ;7Y^qr2ÎΔef¿h®®TWTpëÚµf³àìYú­_ÏÓ½w/éQQjé9ÑјxyÏà'е±iôz7ññ!(.·™3±3† ¸8‚ââéép<$S__†]ºD§÷ÞãèĉT5Aœè±cqã †_¾L¿õë‘jiµw/Iß|CÀÆ >uŠ¢ÄD.._ÞäùÈdª¿gÏÆÀÙ+Oú:öç?Q³éñEGþ{x)7Ëš÷÷§µˆàˆ ‚ <JJJ:t(999ìß¿ ‹¿UÎí HÇŽY¹r%«V­AáÖ A† lD¿~‰dfVòÁíTiææÊptÔÆÕU{{m²³+›]ö¸q5cè{÷nÃÅ‹w¾A•J%\¿.'=½)}ûê«åsvÖF©ìÊ /¨ÏÓ!•J(,TpñbïUÜÝu7Î}})FF2^xÁ”ØØ[ªtGGmµ17×ÀØXv_m>Ü}})ÚÚF6aóæš‡+++ÌÌÌê½ÎÖ~›®Û®í B"“áðÒKÍ>o·ÙaÒ¥ @ͰˆÚàˆõàÁh´iƒLWãÎ)NIQË×cåJž½+`’}ð m:tÀúÙg‘jiáþÆä:DUIIÍù™4 $R)6C‡"ÕÒRX$R)•7oRœ’‚TCã¾¶%R)Å—.QYT„® FjéúŽŽŒ¹qƒö£FÕË{¯v¸ºb7z4úúhÒ~äH ÏSåkcoO{{ô‘éê¢caAy^é[·Öä3‰L†ý¸qÈ )¹|YUײŒ Ê22Ð00À¬gÏzu >ž®uz…4I©$í÷ßñZ¸mssd::Øݬsà÷ßÿbÚ­§Þz‹ÍÎΜ~ûmUD©Pµw/VýûcáïOYz:E.¨å· $ö?ÿ¡ý°aͯo#õ©Û¦So¼ÃK/aèîÞdyU¥¥h´iCqr2WÖ­C"“©¶7ç˜6AAhèë#ÕÖÆö¹çHß¶M-]ÏÖ‡‰Ð42BÇ¢Éëý^*rsÉ?y÷7Þ@ª©‰Mp0Úäýõ×þü‹~ý° F"“¡ïèˆyïÞdlßNûáÃÑwrBÓÐçiÓHߺµÉó€D¢úa=x0:VV´ÑjCHŸY~3ŽoÇþDböyü¾pçí3šWn+ÁAAheøûûckkËü^í7@÷C.—«‚"¿üò k×®åðáÃôïß¿å+,‘sΜ)cÊ3jŸ[É$ªk^PUÕ¼®ÝÚÚttjòkjJ(+»3„eñbôô¤tƒÃ9Ö¯/hV™ãÇ›2r¤ ãÆ]ÆÔ4–¹sÓUi…… ¦LIÃÅ%{ûs„†¦SYy§®wÚp»MÍo‹‰‰†êgSS UPåôéÓÄÆÆÖ{yÜø»ÚtèÐàvMCCÕÏMÍ{Ü­"/:cmssHT½ Ò£¢ØûôÓª!•……TWÖ´ÓnÌl‚ƒ96u*›8Öì6ô\½šÜ£G‰òôdÏ€ÜLHhvÞ{µ³òæMNΜÉö®]ÙêåÅùO>QÕj¾…¿ýºý^YUU@HLd‡ŸŸê¥T(T“ƒz…†"ÓÕ­Ó¥ ×6nlv]ïE^P€¢¬¬^™æÐ44Ä+4”!11 :tˆüãǹ´bpg¨JÛ=Ð44ÄÄǧÞлQ£0tu¥­Ÿß·£®³óæQUV†Ï'Ÿ4k6m¨*-Ų‚ââT=u4Ú´iV~-cã;?›˜Pž«>´OßÞ¾^ž¦®÷{)ÏÍE¢¡–‰‰j›Ž……j¢Ý[éé÷ü]–çæ¢cy§7›Ž¥e“Ç»Îf®tn×…¶mÌHÌiþ}ô¨i4½‹ ‚ Ëùóç bòäÉ,X°à¾óËårÖ­[ÇÂ… qttä×_¥G-_QAx )0mZ³fYšÁСFXYiÞsÿ»‡½kiIP(Þ÷^ÚµÓdÍ{"" xå•4ÆŒ1¹Ý1âž´´$„…YfMbb9}ú$2j” ]»ê1o^:r¹’³g=ÐÓ“òÕW9ìÚUÔhyuÛÒX;23+Õ~¶°¨ùøß¥KªªªêíàÀ¼½½=¶LGçÎäšJeƒßhKêFª¶™™ÚƒZEn.(•蘛Sž“ÃñúGFbêë À&Õ ’jiÑyî\:ÏKñÅ‹ì{öYl‡ ä‰6´íÞ¾ëÖQ]YIÜüùœ £ïºuÔ–ø?¦Z.gPt42==.}÷Ùû÷7žI©Dׯó¾}é³vmƒ»èXYѽvÄõM›89kí‡§É ó6©´Þ ¢eb‚LW—Ò«W1öôl^9 ÐwrÂvèPnÖöÉØ¹“j¹œMµeU Üß|S•ÇÈÃÿ þö1ráË/É?yÿ h4ï1Xßɉâäd”ÕÕ5=‰.^Dª­^ûöÍÊ_–•¥ö³Ž™™ZzC÷Ic×»J¿/ss”UUÈ T’òœ´k-z66äÝÕ+«nÞº«•gg×eêji©MªÛ¥RÉÁ”½üzr û.í"¸ÓVŒý/ëæÏ!󨉞#‚ ‚ÐJöïßÏ€X´hÑ}Fîî)òÛo¿±{÷nþQ-ÊÄØXƒeËÚ3}º9/¿œv_Žº»ërèPóæî¸mË–BUÀASS‚¾¾LíùóêU9ÎÎñlݪ>!ë‘#%œ?_ŽR ÚÚR”JÐׯù(^X¨ÀÝ]==)¥¥Õ¬]›_ursÓ!!¡ŒüüúÁŽˆˆ¤¦ÊÉʪä×_ó:´æ›ì³gÏ_ïÕ©S§&§ï訚Cãzd¤ZχuvÞ<Ý5<ÁÂߟÒ+WÈØ±ƒêÊJ—-üwo4 ¨*-Eª©‰aíM×6n¤òæsŸwìXÍ ¥©¶6J¥²Ùßú_Y¿žª’UO õùen]¿Î6_ßzq6F~ó&†®®Èôô¨*-åÊúõÍÊg3dù'O’±mJ…‚Ê¢"®¬[§êɱ};åµâMÍš6ÞÙ?dñ‹5X¾®•…çÏ«o”H°7ޏÐP*rs©®¨àÚŸªír¯sp~ñbnÄÄ (+£øâE®mÞŒ‰—™;wâûÙgŒÊÊbTV{÷’âò7šu.ªårååªWÝö{Õ'eõj®þñ½þ‰LV/4|í™÷ê…L[›äU«¨*.&é믱 RÍÕÑØ1®oÞLé•+”ggs5"ëgŸm²}]ï·éZZR””¤ÖmssL»v%qÙ2ª++IŠ¢<+ ó^½h?r$9‡‘…R¡ ôÊÕëgŸåÚ¦M”¤¤PYTDòªUõêjèâBþ‰Í¿r”÷·¼A÷½8óï–ZõXF@ôA„VÁ¬Y³X¿~=÷1^.—óã?òá‡âîîÎï¿ÿŽ_ w;„'Á‰¥,_žCllÍ0¹s­èÕ+‘åËs1¤‰Ü5æÏoÇèÑ)|þy63gšóé§¶Mæ9yòÓ§_¥´TA‡Úüò‹ƒZº\®$%¥‚âbõˆ¬¬J^~9¬¬JŒe„†Zãæ¦À¼yÖLœx™íÛobh(£_?’’š¿R„››Ó¦™ãäOU•’ôt/ŒŒj¾‘6̘Áƒ/’—WÅ”)fŒUsnlllš]þÝ<Þ}—ãÓ¦‘±c–O=¥ÖÿA•gg«Mv  Ý¶-½Ö¬áìüùœ˜>ooºó ú¸Î˜Ážѳ¶ÆÄÛÝ:m+ÏÉáäÌ™”çä idD§9s0pqàòO?qá‹/P”•QY\ÌV//´Í̸oP93g&]ºÐýë¯ÕêU-—Sš–ÖèäŸwóx÷]NLŸNæž=h`Þ«ÅÉÉMæÓ25¥ßºuÄ~ð'^{ ©¶6–t¨]}çFL §gϦêÖ-Ú´oOÚ!,u•^¹rÏy6:ð§Þx”Jœ^~™Žï¼@—°0â.dwÿþ5ÃK P›[ã^ç@"“qlêTÊ22Ð25¥ýðá8‡„Pš–Fñ¥KX×YȤKôllȬ]¾·)w¯,äóé§8O›Öh}’¾ù†Ò´4";vTmóZ¸·×_W½oèÚ“jkÓëÇ9õÆÄ…†bÞ·/=–,QÛ§±ëÀzð`¢ÇŒ¡"?‡ °iÆ*]ï·Ù Ê•õë‰ôð@ª¡Áà“'‘ééÑcÅ N½ù&›Ñ³µ¥×?¢id€^ûöôýýwΆ†râµ×б°À÷óϰ8×38ðÜs(Ê˱<÷Y³ÔŽÙáùçIß¶:`Ü©î u·®íýøköùF÷yÜH¬Œˆˆ ##ƒ3gÎ॥Uk×Kø‡Z»v-&Lhíj‚ |¸Éo6îŠlܸQEAAA„‡,??ŸaÆáââªU«ÐÔ¼÷jwE6mÚD·nÝamAø_±v­CÓ; ‚ðPôX¹²µ« Ü'±Z ‚ ß÷ïlz €Ôü†¬ðÇa¡)nY¾7Lµïî¤íô[æÝ|Cº~æÂwG¾R¥ù†;óüCpÿ¨K,¦ãÇÖ¼¶~r³ëq½ð*~Ÿ»³+q›Úv ©::H%Rdu~¾méOñøØû&|åËÕ‚4µúöø¢#ö Œñ wbÇ…Hµúž¼zLõÞ)ÌŒ‹¹‰T**Y¸ý=ó¬*RYMø°¯éfד«i<ýMO<­½éíàÀŒ¾oq,í0[Ïo"æß)x,²¡¸¢mƒ&ë#WÈIÎK¢¸¢¨ÙmHÍOaÉþE}kCâ3Ï¢«©§JëÏWùlø7 öFúÍëÜ,+lV¹_ìû˜£©‡Ø?ë4R‰”Q«ŸÁݲ#¼žWíóÛ©‰˜².6¾¤ä]B‚¤ÙõAxXDpDAZ@EE/¿ü2iii}úpáÂ<Ȉ#H¿æÃ—0góëhkè0Ño*KG­R ^H%R2n^'£(#[zÚ÷U¥ rRýìdæ‚¿ÓÎeĪ‚#ö¦Ždeàhæ‚®¦.ú–ä•ä4+8âØÖ™‹•MîW—D"¡RQIRNæúxZ{«¥K¥R.å&ѧü&6F¶ØÙ6«Ü_O­á«Ñ«1kcÀä!l;¿I-8Ôi8]ljþ:™¹ÜW½A1¬FAPAAƒ B.—³wï^ÌÌÌ(--eÙ²e¸ºº²gÏ¢¢¢D`Dî"“ÉÐÕÕÅÅÅ <<<())¡´´´µ«Ö Ü’²‹³ð´ö¦s».õ¡ƒ£«©Gà×Ýéò©ãÖ«Ò²Î1ö‡ ¼?uÄk±={’¶S©¨T¥Ë¤²š—D¦z_U]õÐÚboêÈ’‘ß¾g!Ž Lyé—Q•ßT¥¯~ñÿ8šzÏOìðuw²â›,S©T’Q”ÎÛgà÷¹;~Ÿ»³dÿ'”ÈKÔöë`êØâíAxP"8"‚  -->}úàííÍúõë©®®n0(âããÓÚU„ÇŽAý"êÎQñ8áõAAxÂ‰àˆ ‚ 4S`` ÑÑÑŒ1‚>úWW×Ö®’ ‚ ‚ÐDpDAšiܸq,Y²„.]º´vUAA„$V«A„fš6mšŒÂÿ€°°LìíÏ¡¥ÃÅMgxL)°·?‡…ÅYú÷Ojíê<Öbߟø>jíjB«÷½‰àˆ ‚ ‚ð2~;ÒÒ÷ÖÏ>ÛÚÕh5yÇŽ±ÃϯEË,ˆeÿ!l°±!ÒÓÿú—ZºâÖ-þl׎C£F©m05ePêýÙùó‰05¥àÌòŽcƒ­í}×E©Ppzöl¢:wfƒ­-ûƒ‚(ŒovþË?ü@¤»;íì83gÊêêû®ÃãâÂçŸszöì{¦·ô½ ¬ª"ÂÔ€½O?MîáÃ-Vö£&‚#‚ ‚ ‚ üO3ëÙÓnÝZ»ÿ3”UU~ál‡ ã¹K—ܹãÎÕöÉ>pí¶mÉ=r„ª’µ´Š¼<ÊsrÈ©Ýïê£P ÕРïo¿1$&“.]8òâ‹ T6™·0>žØ> çš5[½¼HY½š]}ûéîαiÓ85k§ß~›ƒ#F°ÍÛ›“3gÖ+ÿ@p0ç/®·=髯ØêåÅWWNÏž¢Î²ÑÇ^y…-®®lvtäÐèÑܺ~]•V’šÊþ!CØäàÀ77ÂÃUiù'O²ÕË‹vv %H\º”H6ÙÛ³; €Ò«WÕÒ¯oÞÌ??*òóÕ¶oóõ%!<œ½ƒÕ¹3)kÖ¨Ò²vïfW¿~l´³c{×®\úî;ÕyÝââÂÞ§ŸfWß¾œ c³³3—øA•÷Êúõìðóc“ƒM™Be%M³vïfGl²·g›;v¨ÕéÖõëìðó#s×®zuýkòdJRSÙêåÅV//rRËwdüx6;;³Å͋˗7yJ¯^¥"/çW_E¦§‡^ûö¸Ì˜¡–/cçNÚ¾ƒÙû÷«¥Ù“ÅÓ§1òð@ª¥Es5T©–>áá{y¡caÛ¿þÅ­ë×)ÏÎnVy–ýûcÞ»7zíÛã8iW7lhò˜IË–±ÍÛ›vvìPë5Q]QÁñW_e³“›œˆ;V-oc×^Á™3ì{æ6ÙÛ³wÐ ŠUi¥W¯²ÁÖ–Ô_~a›[\\Hþþ{ Μa«—IË—s5"Bõ»Vܺ4}/Üëþ»ý÷ iÙ2vûû³½kWnœ:Õäy=yõÉä=(›¤zˆàˆ ‚ æôéÓtëÖ­É@XX .lå7îòåËL˜0: §§‡­­-AAAlÛ¶­Áý¿ûî;ºuëÆ?þØ`úÀéÖ­ëÖ­«—vüøqÕù)((PmïÝ»7+V¬¸¯zÿôÓOœ?þ¾òÜöÒK/ñ믿þ­¼‚ð¤hÛVƒÕ«í™<9•ÒÒjBB®bŽŸ_ f>Ž]»\01‘¡P(‰ŠrfÕª¼f•]Q¡ÄÜ\“3gú¬[çHxxÑÑî´k§ÉÁƒ>WŠMp0ÖƒcÔ±#n³fÝwþ+ë×°iCéôï«¶ç=JŸµky毿È9tˆü'Ôò•¤¦ªz Ü–µw/Iß|CÀÆ >uŠ¢ÄDµ‡tcOOž9r„a/bèæÆ‰×^S¥_¼Ó®]~ù2CNÂÂß_•Ö¶{w‚ââp?¾^ýKRS¹°d wïfxj*Ý—/GCWWmŸÊ›7)NNFYUÕ`þÀ]»ðˆ nÁªår fÙwßðpžKI¡ßúõ$|ú)¹GÖ”W\ŒÿŸ¢id„²ºš~ëÖq¹¶—Bî‘#ÄÎKÏÕ«–”„¦¡!çÂÂTÇ;õÖ[x-XÀð´4žŠŠ¢úpªj¹œâädªŠÕ¯!11ôúñGôŠ‹#(.NuŽ” ‡_xC77‚ÏãÙãÇicoßä9е¶FÛÌŒ˜wߥàìÙúÃP”J2wïÆ2 ‹€€zÛaøɵM›°}î¹zç¶1ýNn+ˆ‹CÇÊ msó&Ë+NNÆÀÍÜÇIûí7 ]])NNVÛç^ÇÔ46¦_DÃÓÒðxç¿ø"U¥5ÁÔ+ë×SzíÁññ MLÄ­Î5ÛØµ'/(àШQ8OÊs))8½ü2M™¢vŽ«ËËÉ?}šgOœ`hb"V`âãCP\n3gb7fŒêw-ÓÓ¿šºÿªår´ÍÌxúÐ!\fÌ þ“OTi™ŒnK—Ðqöl œð°ò¤¯cþ5›_t俇—r³¬°ÉßGkÁAA¹¹9cÆŒQ½œœœ8}ú4O?ý´Úö'A~~>ýû÷çܹs|òÉ'ìܹ“/¾øŽ;Vo¥RÉ’%KHLLdiíôw;{ö,.\`ù]߬¬ZµŠ .púôiªê|€Š‰‰!33³Þþ™={6ûöí»¯<‚ðO3h!ƒѯ_"™™•|ðA;U𹹆†2µquÕÁÞ^›ììÊf—=n\ÍúÞ½Ûpñâ R©„ë×大Wb` ¥o_}µ|ÎÎÚ(•]yáSµíR©„ÂB/V ¡!!0ðNPÅÝ]‡qãLÑ×—bd$ã…L‰½¥JwtÔÆÑQss ŒeÍjËñãÇ133«÷²¯óàkâíI—.èÚØÐnРfŸU[§MCÛÌ ÕvëÁƒÑhÓ™®.Æ;Sœ’¢–/øüyº~ù¥Ú¶ŒíÛi?|8úNNhâuÉ ˆ;ŸO>A"“5Y^Õ­[h¶iCîÑ£¤GE¡¡¯¯ p4uLÇI“0pqA"•b3t(R--JÓÒ€šßWåÍ›§¤ ÕÐÀ" @•¯±k/}ëV \]±3‰L†ý¸qÈ )¹|Y•_Y]Mç÷ÞCª©‰D&CßѱYçî^šºÿÕueæç§<’Hpxé% æÞשíuÖF« !}fqøÍ8¾û‰Ùçñû·7ª÷0zœˆ¥|A5vvvÌ™3Gõþ§Ÿ~býúõ̘1;»{OúwùòerrrèØ±#FFFõÒ«««¹páEEExxx4¸Off&)))èèèàââRo¥RIbb"xxx`llÜh[vîÜɵkר¿?NNNªícïêÚzÛÑ£GIJJbõêÕ¼òÊ+ÄÄÄàëë[o¿#Fðûï¿séÒ%\jnݺEDD#GŽdíÚµÖ«)………(•JÊÊÊT=P ‘Õ~ÈËÉÉ!99###œÑÖnxRÉêêjΞ=KEE¾¾¾hÝÕm¹ªªŠ¤¤$nܸAÛ¶mqssSCž!!æ|õU+Wv îå+“ITÿÖ¼ ªªy]»µµ%èèÔä×Ô”PVvç¡nñb>ø îÝÐÑ‘òé§¶<ÿ¼I“eŽoJjjãÆ]&3³’3ÌY´È€ÂB³g_#:º„ÊJ%%% zõºt¹Ó†Ûmjº->>>ÄÆÆÖÛ.•¶Üw£úwõ0¸MÓÐPõ³DSEYY“e•çæbRg50KË;½K”J.|ù%WÿøƒªÒR” …Ú7ø^¡¡œûè#ö"ÕÖÆkÁÚÑä1ÛØÛÓuÉ·‡SøÒKX @÷åËÕêߘºûI54Tí¼™@ÜÂ…'%¡¬®¦âÆ ÚÖö¸”Ôž‰L¦zÝnË­ôt²$=2²¶ÙJ”UU(««‘H¥ô\½š _|AÂ_`àäD÷åËëHî×­ôtt­­‘hÜÿc¡¾“~+V€RINt4GÆÇÔLJ¶~~dîÜI[??dzz˜÷郼°§OÓ¶{wU~lj¹™˜ˆLGçÚP—¢¬Œ#ãÇã0~|³{¤hèéQYZŠWh(@M€¤M›fåMŠ"qÙ2U`¬²°êÊšÀ¥Ý˜1”\¹Â±©S)ÏÊÂiÊ<çÏ¿öÊÒÓ)JLT›@W©PP‘—§ê•!ÕÖV!ZB£÷5Önÿž$jCÞšÃÙÌ•Îíºpòê_$æ$´L¥ÑsDAx UUUŒ?wwwhß¾=QQQjûœ8q777ºtéBPP¦¦¦¼õÖ[ª1¨ÕÕÕL™2…öíÛóüóÏóôÓOÓ¶m[6oÞ¬*#..ŽÎ;ãééÉСC155%$$…B½ëz]Eµcµ›û0°fÍ|}}™2e ;vä‡:ãÀërpp €Ÿ~úIµíÏ?ÿÄÀÀ€§Ÿ~ºYÇjŒ««+Ì™3SSSLMM9sæ }ûöÅÒÒ’áÇãëë‹••üñG½2²²²ðññ!00~ýúáìì¬ö tîÜ9ÜÜÜèÖ­ãÇÇÇÇçÚ]‚ð¤P(`Ú´4fͲ 44ƒ¬¬Æ{SÜ=ì]KKB#BÔ®&kÖØ“™Ù…ðp[¦NMkΜhiI ³æÿÙ»ï°(ŽÿãoŽ®€Ò» MTDÀì½ÄØb—XbùZcK –[Š5¿ÄX##Kl±WTš iR¤s¿?NŽ’¨1&ózž{’Û™™ÝÛ=¼Ù™Ïܺ՘˗ٸñ ×®•Žùä“ÇÈ oDLL|}-ª-³|º²ã ÁÅť«e V,‘ii!/.–*y~šF™š<•¯)-cc…øyIIÒ“ô¸_%vÏÚïßOïˆÚíÞ­Ã@ËÌŒ6Ð72’¦‹suÆŒâ°yç:;FŸë×É{òä¥â œ0ó.]èBïˆÌ:v¬2æBYZ-KKœgÌ Gp0=‚ƒéyå ýîÝ“:T [´ í®]¼…Q«V Snª£¢¢¢´ µ,-ɯrŠJ ÇÄÛ}772oßJ㤲×̌ȋŠHøí7…Ýl†ÅuÑ¢¿^ïsJ ðñ¡®«+ ?ø ÆûéÚÛ“u祰³îÝ“:!ª’—œLÐĉ¸¯\)MaQÓÕ•®=™†.óçÓ#0ÎÇsÿ‡H/÷w¸²kOÛÒã¶m¥ë Gp0ýîÜQUå½'“Õøú/SÕý÷WÉårÎFdÂOÃiö¹#7Âùöížxî…Ê}•Dçˆ ‚ðBŽ=ŠÙÙÙdffÒµkW¦L™BÉïÃg“““éÕ«^^^ddd––Æ™3gøî»ïؼy3ÇÇÏÏk×®Ozz:wîÜÁå÷È÷™™™ôìÙ“F‘’’Bjj*—/_æçŸfíÚµ•¶­K—.hkkÓ½{w¾üòK‚‚‚(,Tþã);;Fÿ>4tôèÑìܹ“üü|¥ùÇŒÃ?þ(ç?üÀèÑ£_ÊSÙääd X·n]é“C¹\Šó2uêTž>}Jrr2YYYŒ7Ž1cÆ––¦PÆÆ™5kiii$''ckk˘1c¤öΛ7gggÒÒÒˆ%''Gú<áM±lYu몱v­5“&óî»5ë¨(ãì¬Íùó.vÇÁƒ$$”~¨«« ££ŠŠÊé±±88ÜàðaÅ€¬—.esófr9hjÊËAG§ôû"#£gg-jÕ’‘“S‚ŸŸbÐÇê4h Å­[¹¤¦þñ×ÃÃ7nTx?ÿCMÔuuIû½Söùà”/êL¯^ÜX¶La›E<Ú¿Ÿìèh Ÿ>%jÓ&i¹ÑÂÌL´LMK§4ÈåD?×qô(y‰¥1ZTÔÕKŸú—ÿP*‘Kòùó”–NOPQAMGqšTÜþýñ𨈳*…™™ÔmÒ™Œìû÷I:W³ƒ¶Ã‡sïÛoɼUúd=÷ñcâ”ÒîÞMQv¶4âDMW1Þͳ¸8ŽxxTÈ ¥H¹ <÷·BßÍ mss®öʹffÿ\L.eç ðéS®/YBöýûçå‘tö,éaa軺’—”DzXÝ.^d`b"iúé§âŽT¥8/OáUþÆVÖyI Á“&¡®§‡ëÂ…J÷åמÕ[o‘tî©ÁÁäÆÇsÿÇ©÷ÜÈ#euåä SWGÏÉ €GûöQ˜ùÇ}ŸÈÓÛ·A.G¦©‰\.—F¤TuíYöêEê•+Ä9‚¼¸˜Â§Oy¸kW—Ö65åé;¥œ5TÕý÷W= `ÞÁ´°ñ"ô£hÖÜ„«…û •ùª‰ÎAá…ØØØ°dÉ444ÐÒÒbÆŒ>œ{ûm´LLþT{ª“óð!ù)ŠÁqͺtÁiòdξõG<<б·Çù÷ ±õ B]OÞÞœ0€ZVV û¦…„p¢cGöÙØpsùrZ• €}eÚ4»ºò`Ç¢6oæ°«+¡¿-ÉÏ'bñbØÛsÄÝ{{l‡W(»ðéSrbbþÔÈ Ï?çÚ¬YœíÛ—›+V`Ò¶mö3ñö¦ñüùŽÇ>kkÎôé#ů€ÒΑCMšpÀÞžŒ7pý}ŠF™’‚rbb”Žô©mcCý#8æåÅ¡ÆIþ½ÃFEU•6;wò42’C..mÑ¢BŒeç@¦®NΣG¥«ÙÚòᇸ}öúîî$œ8®ƒzåV8²ìÓ‡Ì[·VªLñ³güba¡ð*‘RY{òx´oöîåKKi¿ô禖)»öê6iBÓO?åÒ¨Qóôļ[7ê?÷÷\Y:õëã4y2'»táü€d\¿Ž¶¥åmJNæÒ¨Qì³±áLŸ>4ž3GŠÍSÕµ§a`@»]»¸³aìí9Ú¢‰§O£Rƒ? 4ö‰º:¿6jġƥÕjªºªºÿþªfÖ-¹üþMÞk= =­ŠS©ÿ‰Tzöì)÷÷÷'>>žÐÐPÚ·÷ÆÔôåÍ_„?ÃÏÏ‘#G¾îf‚PζmÛðññááÇbŽ 4ˆ‚‚–{²IÆ ÁÝÝI“&±{÷nºt颰ï;wÈÌÌ$&&†ŒŒ <==‰ŠŠ¢S§NtïÞÁƒKõ}øá‡lܸ‘Ï=ÅˆŽŽ&&&†Ô<ÑKMM%((ˆ°uëVÚ¶mËérÿØðööF___¡3¤k×®¨ªªr¬ÜÓ.ccc&MšÄ§Ÿ~ÊØ±c)((ÀÉɉcÇŽ€ŸŸ£F"99ã߇¤jii1gΜ?µ²¡¡!‹-bÚ´i ÛýýýY±b·nÝ"¯Üœß-[¶0vìX ´ciÀ€|þùçRzqq1ÚÚÚ|ýõ×L˜0Ÿ~ú‰1cÆ```@ß¾}éÑ£ýúõC]]½Æm|BBB0ú= ã›.%%å=–””¥ñu”Éû“sÍ_†øøxì^0à  ‚ðfxZn ë?K¯†1ƒ’’9wî<îî™1`ÀUAx1Ïÿ˜.ël(›Ûœ™™‰‘‘‘Ònʉ֭[—°°0>ÌñãÇY·n ,Àßߟ>}ú™™Iݺu•–QÓ¢†††ôêÕ‹^½zallÌgŸ}Fll,666Ü»w‹/R·n]…À­iiidee‡ÕsO*|||èÙ³'FFF|üñÇ5jÇ‹ døðá¬\¹’wÞyCCC444ÐÖÖVX*ÆYQQQAEEEŠÑ2lØ0<==Ù·o§OŸfĈ4nܘ .Pë÷eÿAAþ+Dçˆ ‚ðJ9;;säÈzõêEí*¢¿kii1pà@HQQ]ºtaýúõôéÓggg~üñGºvíZí 55Q¿~é0øôôtllløá‡XØ*> IDAT¨S§Ë-C¥<ãÇgÛ¶mJ;?Úµk‡¹¹9ñññ•®€óW©©©U6{æÌ¬­­yÿý÷¥m7nÜ    Âþ ïoß¾MAABÐÕúõëóþûïóþû»;'Ož¤_¿~/õXAAþéDÌAá•;v,r¹œ÷Þ{ììli{LL §OŸJGDܺõÇÒnÅÅÅäååISFEíÚµ;v¬B¬¸¸8Ž+ (гfÍÌÌÌhܸ1ÅÅÅlÛ¶0xð`…×!Cxûí·ùᇔFùWQQ!<<œÄÄD¥K——žžÎýû÷^qU̽®W¯ÁÁÁ aŒŒHHHàÞ½{@é°Ó™3g*‡|üøqŽ= @~~>sçÎÅÑÑ‘:Hç¦üçQöÿoê´AA„!:GA„WÊÚÚšƒráÂLMMquuÅÒÒGGG¸rå ...XZZÒ¼ysÌÌÌHOOgéÒ¥@iœÃ‡޹¹9Mš4ÁÊÊŠúõëK,Êhhhðå—_bnnŽ¡¡!8::"—ËÙ»w/êêê?~œÇ3bÄ¥eŒ1‚¨¨(.\¸ 4½víÚ5šßº~ýz)ÈlÙëù*å}òÉ';v ---TTT¸zõ*#FŒ I“&¸ººâêꊕ•­[·–Ý–çããÃäÉ“iРæææ°eËÔÔÔ¤òŒŒhذ!...têÔ‰éÓ§ÓºuëjEþ) |ÝÍx­Ä9¨Z¯^½¨W¯^ƒY ¯W\\ZZZ¯»’ãÇckk‹žž‹^â²ÃÂ?“˜V#‚ TiÀ€´k×Né _ýu…mvvvDGGcY.b{‡ˆŽŽæêÕ«ÄÅÅallLãÆ¥€¥Ó¦McÈ!ܸqƒÌÌL¬¬¬hÞ¼¹BÜ OOOî޽˵k×xøð!†††4nÜSSÓJÛþÖ[oÑ·o_nݺEÌïÿmllpqq‘þ¡ìééItt4¶¶¶JËèܹ3ÑÑÑÒJ:W¯^•V‹Q¦ÿþ ù¡tJ‹²‘'e+Å(Ó¯_?RRRHLL$//KKK455¹|ù2¤¤¤àêꊽ½=cÇŽU¨ïìÙ³èêꢩ©Éùóç)(( ]»v S’nݺÅ7xøð!ªªª4iÒkkëJÛ#o¢‹/2~üx"##•¦«©©‡™Ùß¿A›6m˜2eŠÒŽÙ?þ˜ääd6mÚô··ëßæÈ‘#¤¤¤HoÞdÛ¶mcÉ’%}ª´½ñññ:tˆuë”ÿHX»v-«V­ª°=//   "##‘Éd¬Zµ ___ tÒÂ… éÙ³'‰‰‰ 6ŒsçÎѧO222¸|ù2>¤iÓ¦x{{cnn^e[Ï;ǬY³8qâ5bêÔ©Ì›7ÿû¿ÿ«ö8ýýýY¶lÄÅÅ…K—.QXXÀÒ¥KyðàÑÑÑ$$$Ю];ÜÝÝiÛ¶-³fÍââÅ‹ìß¿_š†š••%ÒŒçþýû õµjÕ Ž9BçÎñ÷÷Wø±ššÊš5k dôèÑÚ;uêTV­Z¥´£áæÍ›tîÜ™´´4jÕª%u`•·fÍÆ§°êœ¾¾>GÅÑÑ‘ýû÷Ó¯_?ª\™ª¿ÿ ÈÊÊ"!!O?ý___~ûí·jÛS¾üS§NallÌ;w€ª¯ËꮽªhiiqûömLMMÙ³gÍ›7gçÎxxxTyÿ•÷ü=?uêT¦NZé´š¡C‡Ò¨Q#?~Ì;wèܹ3Íš5£^½zU~?Uõþ,W¿çû o©­¡Ãè–ãq0n çÛ‹kÕbµ5þ8ï+N.¢™uKOéééÒ¨ sss¶nÝ ”N­7nƒ®Ñ›ü5jT!íÎ;±gÏ¥û*[=¤:,Y²„%K–I›6m8p Íš5`Ô¨QŒ5Š´´4úõëÇæÍ›™={6&&& ?æ“““)))©6h¶ŠŠ –––ìÝ»—¦M›*ͳ`ÁZµjÅåË—9qâ„4Úå“O>¡  @šÞ³nÝ:Ž?^e}å?»ò#†‚Q—­ V\\¬p­­­éß¿?3f̨²e¬­­b´”÷üùKLL¬öÜUu„„„мysZ´hÀ{ï½ÇôéÓ¥•àÂÃÃ¥¥Ú¡tôÊ7X´h÷ï߯Бð|‡M™LFDD„ô¾¨¨ˆ 6Tˆã’””ÄÈ‘#9{ö¬Ô&é8´´´:|”M3«îþT¦²ö”÷ü½RÝuY“kOCCƒââ⵱옪»ÿª»ç•kkktuu¹}û¶Òïœê¾Ÿ*;ç'þHHܶ^þ†ÏŽB—=Õbmí: ¢¢BQI›6°exÅón¦kΆÁ¥uîðgúžqôoRZ焟†ãÓj"?ùüŠLEƨíóç>ó¿“9"‚ üë-Z´ˆÀÀÀ×ÝŒ?­¤¤„¹sçrýúõ×ÝAx#™››óøñcRSS+M/ÿC°¼/¿ü’ÂÂB6lØ@ƒ ¤)”~ˆ«««££££ð#%66>¬Pž\.gݺuÌš5Ki}kÖ¬a„ RÜ—áÒ¥Kܼy¹\ަ¦&r¹\z"ÃéÓ§),,DCC)æE§Nxðà¿þú+………¬\¹’öíÛ+¬\VÙqúøø0þ|©C%""BеpîÜ9~úé'6mÚÄÖ­[;v¬ôÙdddàììL­ZµÈÉÉ‘âRÔÔ×_Mvv6—/_æÚµk #zzz˜››WˆùàããÃÚµk¥ïظ¸8öîÝ«ÇßßRRR*ìûÕW_AII —.]’¦^õë×õëד••Å;wسg}ûö­ñ±|ðÁVswwçÚµkDDD —ËñóóCGGkkk&Mš$½•Ëå¼õÖ[|úé§ìÚµ ggg…´Õ«WÓ¹sg©cdãÆ\¼x‘´´4‚ƒƒY»v­Âô›½{÷baaQaJNvv6êêêÒR»wï–F/8::Jw=zD@@@…ã¬êþ«Leí©NU×eM®=gggjÜAUßPý=¯ìüØÙÙáâ₯¯/¹¹¹pòäIi:SußOU}'zXµ`Ãà­„|…›e3Þß7‰ðøÒಯïÅLÏ‚–6ÏûÑ[IÌú½NUujkþQgfnM,ܩȸŸŹ{ÕÇ-zDçˆ ‚ðFÚºu+ ,¨QÞ5kÖHÑãÿ©Ê–h,¯¤¤„•+W* œ&Bõê×¯ÏØ±ciܸ1VVVœ:uJ!}éÒ¥Œ?+++–.]*m¯]»6r¹üüüرc‡ôý+W®Ð¬Y3êÔ©ƒ¯¯/Û·oW(³  €èèhi%•2¿ýö%%%J—×NKK«2†Â_•˜˜ÈÛo¿M:uhß¾= .”b0äçç3wî\ ±³³ÃÉÉIZùÃÈȈݻw3wî\Œ çû￯Ñq.X°€¦M›Ò¢E êÖ­‹999dff2zôh6mÚ„¾¾>:t`РA¼÷Þ{@éÓû_ý•6mÚ0hР K–WÇÓÓ;;;Þyç6oÞ\aÉÚõë×Kq%:”v-Y²„¡C‡¢««Kûöí+BÍÌÌ$::ºÂ´—!C†0wî\L:uøðÃ¥‘ ,ÀÆÆ;;;ºvíÊœ9sèСC%11‘‡*lëØ±#‹-¢_¿~èéé±~ýzöîÝ[mðÓêdeeáãヅ…ýúõ£K—. ÷ÂêÕ«•N³··gÖ¬Y´lÙ’nݺ¦°ŒüèÑ£ÉÉÉÁÝÝO>ùoïŠ8+»ÿªRY{ªSÙu 5»öFމ¦¦&uêÔ¡uëÖ5ª³ªû¯&÷üÌ™39xð  ÁwþùgîÞ½‹ ¦¦¦ S¦ªû~ªî; ®¶>SÚÍ"xv$Í\øæâj&µU~ÞCâ®Ðq]3lÖaù _¾}ç:?ïÿ5³~™Hßï:²âÄBÚÚw¬Á™{}Tzöì)÷÷÷'>>žÐÐPÚ·÷ÆÔÔ¬ú=áðóó“j ‚ TeÚ´i\»vMéÓ¨çÕ­[—eË–1eÊ”¿¡e‘‘¾¾¾LŸ>]Ú&—Ë ÁÎÎNa>ý¿IHHFFF¯»/EJJÊ{,)))xxxÔ(oM–¸|Ùâãã+ÄxU¢¢¢pss«QðÑ?£GôïߟI“&UH[±baaaìÚµë¥Öù_aeeÅþýûiÞ¼ùënÊ¿JPPƒ âÁƒ SvD{^Ü›tÏ_}„ÏöA„Í}€šìï9ï•­öUÊ‚+“””ȹsçqwwÇÌÌŒˆ˜#‚ Â_Fjj*­[·Æßߟ¸¸8<==éÔ©P<.00kkk† ‚ºº:P:çû—_~¡M›6XXXHåݸqƒÇ+}еµ5Ý»wÇÔÔ”ƒRPP@XX˜t<]ºt¡nݺܿSSSôõõ9sæ ÚÚÚÚôøñcèÓ§ÚÚ¥‘å9pàiii899Ñ£G~Â(Â_“ŸŸOëÖ­•.± ¥±j²l¨ ü233Ù¸qã?¦#âŸÖžñ&ÝóOó2ùjÀÆ¿­cäuÓjA„¿¤lÃöíÛ³yófŽ;FçÎÙ°a}ô'NäÂ… Lœ8‘·ß~[Ú/??Ÿ!C†påÊ…ò~þùgfÏž-½ÿî»ïpwwçСCܾ}›;v0pà@¸yó&III|÷Ýw|÷Ýw\¾|¹Êöžû ''§ sÛW­ZEÆ Ùºu+¡¡¡¬]»VZ*tûöíäåå Ï“'O(..fÈ!Ò±:uŠV˜ó¼jÕ*>þøc´´´8tèlذ«W¯JËMÆÇÇWy,‚ ¼šššøúúV[à½÷ÞS¤U^§nݺѫW¯×Ý É?­=/âMºç;9v£›ó¿ã¼Wçßßý#‚ ¼2ׯ_gÛ¶mÒÓЩS§2gÎúöíË­[·PUUåĉtëÖÐÐPÜÝÝk\öÚµk™?>‹-’¶• ³œ2e ·oßæÚµkœ8q¢Få:uŠÐÐPììì())aèСL›6Û·o£®®Î7ß|þ}û–F œ?žŽ;2`À:vT>O¶yóæÄÇÇKAÖÊÊž7o—.]àÂ… Ì;—åË—3gÎißääd 4àŸ‘‘S¦LQ˜Vóü÷1cưlÙ2Nž<)°)((à§Ÿ~’–|òä ÇgÚ´i,[¶ rrrhß¾=~ø!;vì¨Ñù„ÿ*‡—>¥Fxµâââ^wAø#GA„¿ÌÂÂBa˜x·nÝxöì³gÏ––ÒëÔ©ªªªÜ¾}ûO•­¡¡App0wïÞ•¶Õt©2Æ “bÈd2æÍ›'-¥ƒ R˜šãííM³fÍ8{öl¥åêë룣£CTTçÎãôéÓ899)D˜ß¾};õêÕãÃ?TØ·liКrtt¤uëÖlÛ¶MÚväÈRSS¥Ïá·ß~#//O>ùD Y»vm&L˜Påq¿Íï{Ñdy= æV¿Ä® ‚ ˆ‘#‚ Â_fee¥ð¾lôDùíªªªÔªUëO×Z»v-#GޤAƒ4jÔˆ^½z1kÖ,¥±}Jvv6ÙÙÙèèèC£FÉ^ü™„Ó§O'33“:uê°mÛ6:wî,­p÷î]Š‹‹+|6………ää䟟¦¦æ ·Cþév¿{„Ôœ?5þSû)Û§™u+NüïÍ[\A¨9Ñ9"‚ üee#jº:JJJ¶çææ*¼÷öö&&&†àà`NŸ>ÍÖ­[Ù±c·oߦN:º­eKö=ÿ¾¬CG.—3jÔ(æÎ[aß²<Ê”’IJJBWW(]ykÔ¨QÈårŒ‰ýÓmVfÈ!̘1ƒÝ»wÓ¿>¬°Ä¦\.GWWWó< —ÒAø·2¬mDüÒ\‚0ü‡~D-LA¦"[ ‚ üÛ‰ÎAáo¥¥¥Eݺuyôè‘Âö    ye2žžžxzzÒ¯_?š4iBxx8ÞÞÞhkk“ŸŸ_ãzŸzîÜ9TTTpqqÀËË‹   lll¤)A5qóæMÞ~ûm©càÌ™3 y\]]9pà‰‰‰˜™™)-G[[›‚‚‚jëÓÓÓãí·ßfÛ¶mäææ¢­­­ðÖÓÓ“¥K—’””„——WCÞ4»Cýø¿ «IÉNÆ«~; ‹ ¹sÝï¡©eÍ–%^xä#n'Ý`÷»G¶k©i¡¡ª!ýyR£ùŸÿ»ÜNº†ª㼦ðQgßjÓ¦ï‘3;”vÀŽñD{‡.Œõœ$ϧ–ò$'™ŽŽ]Y3à;ô´J;‚cÓchý• Ëû­å«3ËÈÎÏbN—…”ÈKˆzr—q^Sh`Rù7A¡z¢\AøÛuéÒ…µk×Htt4}ô‘BŒ€É“'sîÜ9žø€JqH,XÀÝ»w1bááᤦ¦ÁòåË+t¬”çââÂîÝ» '11‘Õ«WóË/¿(ä7nzzzôïߟ˗/óäÉ®\¹ÂÖ­[¥<7fÏž=;vŒ“'OV9 ÉÇLJ€€¾üòK† ¢°FÏž=iÛ¶-ÇçÀ$%%qÿþ}öîÝËòåË«|{q-·¯ð83Žƒ7öV{|ß\\ÍΫ?0¡õTÂçÆ0·ë"Ñ1"‚ð‰‘#‚ ‚ ¿ÖÊ·Ö3þ§aääg³eø.º7ì#¥%e%ò(ý¡BþÔœ?5–Þ[,Ц™u+Nü¯´c#$î ïï›Ä³‚¬õmøöíRÞªÒ†zŒæðÍýx¯uÇż)­í¼¥4oûNÌï¶„q;‡—‹‘Ž ï¶šTí±Mõþ€÷;Îÿó'EA¨@¥gÏžrâãã ¥}{oLM•/3(¯šŸŸ#GŽ|ÝÍAþ&!!!½îf¼)))o챤¤¤àáQ³åoóòò^qk*Š—V–AþݪZ±¯:5Ž””ȹsçqwwÇÌÌŒˆi5‚ ‚ ‚ ‚ ü·‰ÎAAAAþÓDçˆ ‚ ‚ ‚ ÿi¢sDAA^³µxœWãü‡oî§ç7m_a‹þu ‚ ü‰ÕjAA„×âÖ­[\½zUa›ŠŠ C‡E]]ý5µêõq1oÊ”vï¿´òRrž0çÀ4Nß;€»es¾òæz¯¬ÎêÆ\dúžñù·Õ)‚P¢sDAAx-6lHƒ ¤÷‘‘‘$&&þ';Fl êccPÿ¥•7{ßd4Õ´¸úÁ]NÞ9JqIÑ+­SáM%¦Õ‚ ‚ ¯…ŠŠ ªªªÒëÁƒØÛÛ¿îfÕXrv_œ^Êû§þ8„vkݨç«ÇŠ‹òæåóÞ®‘Ø/1Â~‰ï|ß[!Ýu…-ŽŸš(â⺗eÖ8/5Ç÷ȇx¬rPHˈ¥åÎ<¢°ýjl cZNÀ°¶†µxÇcVuë½p«Xuj ݾöÂe™5[¿•ÒNÜ9*ƒfŸ;²ñÒ:…ý|üó -×¶¸®°å|ôiv‡ú1hK)呂 ŒÛ9Tz›ƒÕ':l¿²÷Uö8~jÂæË_KŸƒß`ì—ÑìsGÝÜWáxAª#:GAA„×.55•ììl¬­­_wSªðà<ãv¥Õ— y˜ö€Q-ÆS"/aÜΡŒhþ.Q SÈ)Ȧ 8_Úgw¨Òc¸1ï‘'T˜Ê17†5¾«PW\F,³~™È–?sí£(B]©§ ¸€¨”;då?UØîfÕœ'q.ê¹…¹ö{‘:¤Fsü—ñwŒEG>¢ ¸¹¼„UýÖ½0•ÝïaåÉÅ<8@ÈGQü0ÒŸúöDÌ!bn Þö”b¥òŠò¸ö(ˆàÙ‘D~œ@'§îŒÛ9#nÍÌ#÷2sï{ÄeÄÖ¸\A#‚ ‚ Â?@tt4666¨ªª¾î¦Têâý³´YÝ„‡fÓÖ¾#s²~КZzp79’„§ïõ?4T5˜êý¾2™yD§ÜEM¦F{‡Î5ªóxäaZÙ¶¡•MkjkÔfœ×” yì H[!g`Ó¡ Û¿ò#Í­[1ë—‰8,1bö¾ÉJ;IþJƒÜ†Ðд1***$=M ›so¼ê·C]U{#G¼í;q=>¬FÇZy1s».F]UU™*v†ĤÝ'(æ‹{­BSM“Ææ®trêÆÉ;Ç^J‚ üwˆ˜#‚ ‚ ÂkU\\Ìýû÷éÔ©æ£^‡'ÙÉ$e%Ò»q\Ì›¢«©+¥¥ä$cXÛ5Yé?¯MuÍPQùã9ä`÷Õæ??hwï®yù™™ø /*ª>³ðRˆÎAAá?Å×ל˜˜&89i¾î¦ÔX­Z2Ö¯·FUU奕ycéRn®XAâÉ“5úqWÞí/¾àÚûï+MK äXË–/£‰J%9ÃÑ-@^±£¨03“ý¶¶<{ôè•Õÿ""׬ሇ{ÍÍ9Ú¼9wízÝMRpýÓO¹:cÏ=âÒˆ°·gýúN˜@Azº”7/)‰so¿Í^ss~kÓ†´)M^\̵ٳÙgmͯ³c‡B=÷¿ÿž_ÙW¯¡sæ /)©¶=©AAóôd¯¹9&?%EJK ä„·7¿XXpÂÛ›ôÐÐJótÄúûÿ¹“óu==zGDàñÅ/TNeTµµq_¹UÕ?µ_qn.¿6l@àøñ$ž8!¥îÙ“'/±x17W¬x©íý7#‚ ‚ ‚ð§¡¡ÂÔ©&¨¼¼¾‘7ÖÝo¿ÅqâD”ŒûÛ¶aÚ¡µ¬­_C˪gܦ í~þ™¾·nÑbÃBçÎ%óÖ­×Ý,Šóòxðã8Mš@^r2Fžžt>~œ®gÏ’—@ؼyRþ>@£n]úEFRoà@.ûøH£¢6o&ùÜ9zÑê›o¸öÁ<½s€Œ7ûøc<·n¥û¥KÄ;ƃ¬¶=%ùùøø`7j}oßF¦¡Aèïí)ÉÏç²öãÆÑÿáCl‡ #`ÌäÅÅÊM %'6«þý_î |Éd8L˜ ô:¯JÆõëÔmÒ(=V}w÷WѼ%Ñ9"‚ ‚ ¼qRS‹°¶Ž €'Oа´Œ 88‡¸¸BLLÂñôŒÄÕõóç?ÆØ8œŸT[n\\!ZZ!¬Z•ˆ»û-o”#¥GGçãí}ƒ0ÌÌÂY²$AaÿØØœopäHf…ööé…ž^(ýúE‘—§ø¤|åÊD,-#Ð×ÃÃã11RÚС÷©W/Bé´šðð\ÜÜn¡§ʼyÑÒ !.®°f'±÷¾ù†vvnÚ”´«WÒ[‡]]¹³a±þþvuå°«+ÅÏžpÄÃË>>d?x ¥%Ÿ/]8è½÷7Ž3½zñ›—‹Wý±p!† ©´MYQQ¤]¹‚í°aÒäÅÅDmÞŒÓäÉR}Ñ[·VÈwqØ0®N›Æå±c+¤Ýýúk.¿û.œ*¤¥‡‡s´Y³JÛV]±þþ¶h®£#úúÔ²²BEM¬¨¨*Ë,“tú4¿µnÍ~[[ÎÀ³¸8…ôî®DoÙÂñ¶mùÕÙ™À (ÈÈàâСì«W ƒs¦wo¥Ó;þü3u›4Aï÷QÍšÑ`Ú4tìí©mcCýÑ£I½v (ÿÛo4œ5 õ:uh0u*ùii¤wàöcÇ¢maIûö{ywà€”fÚ¡Æ­[SËÚ»1cˆýå—jÛ“Hq^Ž“&¡Q·.ÎÓ§óøÐ!J ÈŠŠ¢0+ »1c©«ã8iùii¤‡…U(÷î7ßà0~<2uunõ•Â5wsÅ . ÀÕéÓ9Û§œ¸±l¿µiÃñví(ʪ~:^AZštÞ/FÉsË G®Yï±ßÖ–íÛ“+¥ŽÇ¡&M”N«‰õ÷ç‹ ¿:;îëË‘ç–aO C¿iS 22£Yƒ%æ{~Ó–]!?’Wô÷/ÕþO":GAA„7Ž¡¡[¶Øâã󀜜&N|ÈĉƴlY€§O‹9~Ü}}UŠ‹å:äÀ¦M)Õ”Z*?_ޱ±:¡¡˜9Ó_ßx)mÑ¢xZ¶¬Mjª÷î5¡S']…} äܹ“ÏÓ§ŠO¬?ü0ŽÚµe$'»1v¬çÎeKiÑÑù,[–@PPCÒÒÜØºµ>µjýñÏô]»ìˆˆh\¡ryiÇɘ1†¤¦º!——¶½¦,ûôÁ¢gOê4lHƒéÓÒJ (ÌΦod$6C†pcùrôÝÝéAƒ©S©7x0½#"èj­Zô Áë‡Щ__J3ñö–ÊM§ÝîÝt={–¤S§x|èB½¹ äÄÄTÚæ{ß~KýQ£P«]»BZÜÁƒhcت†Í›“¦djEzX–}û*v‘†Y§NÈKJ*t>¤‡†bмy¥m«®Î²}ïoÛÆ7mЦ‘¦:TY&”þÐ3†&¾¾ô»{—:qeêÔ ùîÞMûýûéIã>àúâŨjiÑïî]Æ'åòe¥uÜÛ¸ÇßGi(“޾«+9"/*BÇÞžëŸ~JAz::¶¶dÝ»@Ö½{è99qÿûïI F×ÉIêÊŠŠB·Až\¼HÌÎè•K«ª=YQQè:8›ÀeËÐur¢$?¿Ê)TÙ(¼ÏMH þ·ß°÷ñ‘¶9ÏœIQv6÷øô°0lßNóuë¤tÃV­ðܲ…;ëÖÑéÈ´ÍÌxPieÂ}}Q­U‹~wïRäH…}²<àöêÕt9q‚þÐbÃÔ´µ¥tÏ-[è~ñb…2ŸÅÅquÖ,¼¶l¡çµk¤—›Ê”pü8ûmm [°€»ß|Ã!žÅdzßÖ–Œë×h8s&ºXõí‹EÏžÒ¾“ÚÎÄ?tM–×cᑈI»_íñý‰ÎAAáÔ­›={Ö¡]»H ùøcs)ÍØX ==Uìì4qrÒÂÖV“¤¤š¨6Ì€Ö­ks÷îOSe2ââ xü¸]]mÛê(ìçà ‰\ÞŒ¡C ¶:”ÉŒ¦hi©Ð¿]4ø#Þ‰Š ʹu+—ÂB9nnÚ˜˜T¿¨ddd±±L›f‚ºº ï¿o*¥addTáekk+åÑwsC¿iS´--1ïÖ­BùN“'#ÓÐÀ²wïn¨ŽeïÞ¨éè ÓÔÄê­·x|äˆBz«ï¾£ÇW‘Aìž=¥S ”¸÷í·Ò¨ƒæÍ¥‘‡%zËò)),ĬKòÓÒ(ÈÈàÎÚµŽ”vb6oŽA³f¤‡‡“Ì¡&M@.'=<Ã-ª<¾ªêÔ©_›Áƒézö,-Ö­ÃvèPÔuu«*€¤sç¨mcƒEÈ44pž1ƒäóç)ÊÎVÈç0a‚4R@×рLJã4e ªZZ˜w’Q1I§O#/*¼kW¥õ§]»Fìîݸ,X@ѳg¨ÈdÈ‹Šx¸k911¨éêR”“#¥«éèðèÀRƒƒQ×Ñ‘ÚZôìêµkó$ €Ç‡¡¦£#íWU{ŠrrP«]›¬¨(îÚ%Åâ(ÊÉA×Áu]]îÿ=ÅyyÜýæJòó¥MÿÏÞ}ÇUYýÿÜËž"D½²Dá6Üš˜+µœ¹KË_ÓÊ‘£r”9Ë•Z©¹GŽRGŠDQD!"K@†ì{€\ve™uÞ¯×}½î}Îóœsžû\ôÞïsÎ÷<µ~=  @ÛôÉß§L.§ÍÚµÜX²„óãÇã½t©Úh C…C…ss´êÕC¿Q#òSRª»T’ûGâžKÓ§cڲ哠‹JÅíuëÔ¦]Iç™–Æ¥éÓi½z5>+V³u+içÏ×ú¾>fäèHVDD¥Ï»mïÞÒ¨ ‡áájA°ÜøxR‚‚P!×ÒB&“I#kjbÓ£içÏ“~ù2%yyDoÞ¬Vžqõ*õ==I Á´eË:ŸÃÀ ÝÑÖÐæøçÙ1ö0=›½ˆ\öß ü÷ÎXAAxîç²jU 64࣬IK+fÕªÚs<6{¶ {öd``Â$Ô~pñâ#¼½oR¯^³g'òÃêwV UDG­4X´ÈŽ›7óiÕê&³fÝÃËëÉ¿‚%3f$`fнý5œu3Æ €k×òP(®áî^úÃÊÑñ: Å5¢£ ÉJ“µnܘ†™Y(2ttdÈå ­­]¥‡­­mߟš4è×¹–]]9Ô¼¹ZnƒÆiòê«üÒ®‡š7'åÔ)©Ì¶woNÂÑöí±ó÷§¿¿Z½ùÉÉU&ØŒüæ›jGÜÞ°Fƒ¡]¿~¥2³V­(ÉËÃÔÛk??òSR0-[ÝÃÌLJüäd¬|}1ozjÜ IDATÛ–¢ìléǽ–±1º––X´o†žMšTJƪ,,$76¶ÒÊ%5µ™Å©8Ø´)aŸ|‚Ûœ9ØöéSë{ cfF»¹6w.?99‘yý:­V¯®òý¨ÈmÎJòò8àäDÔúõX´o/M)ÌÈ ~÷nœÊr®”÷8råÝwÙkkË^[[ö;8Hå^K–P–Æ''bwì ýæÍÒÈÇñã±èØ‘Ÿ}|8?~<^K–`Ü´)P:õÈcþ|ÎŽÉ/mÛbÓ£MF®µ?ÒÔ™9àäDq^žåFy¤ž>Í/mÚp´];”´ýö[µsQ)••¦Ì@éŠ4‡ŬuktÌÍñþòK.LšT§i.PšÛ¥ÉèÑñòb¯tœû'Ÿuë¿úùqýÓOÕr|( ›;—ñôÄÐÁEÙ 9oÜà°»;G;vJW€:ìîNNL úvvø,[ƹѣùÙÇS//éZªJJx‘ƒéW®PÿwG®ÎˆeNï…4ª¯¨ó1ÿF²Þ½{«víÚEbb"!!!tîÜ ++ëÚ„¿À–-[1bÄ³î† ‚ð7¹rå æuVÿO—––öÜžKZZ^–ƒ¬NþSÊ=ñ{$&&booÿ··û¼ÉÍUblB~¾ZZ•sGƒEàêª'M3ú7±òó«¶ÌÈÑQ-™§PYQV¿½öEYYèZZÒvýz´Êî®W•§åYú+ú#×ÑÁõý÷Ÿz½ÏʽC‡¸0izzXué‚óo<ë.ýkˆàˆ ‚ ‚ <§&N4gâÄçc:W›uëžuþ“ê{zÒ'$äYwCxJ|V¬ÀgÅŠgÝ%1­FAAþ%¬?ÖåÞú%—8¾ŸÞßtü {TÙ‡§³àèÌ¿µÍÇÛ‹![žZ}ϯÁi¾%ŠOL¸_÷NAøç#GAA„g&::š«W¯RPP@ýúõi×®õêÕ{ÖÝúÏhaãÁë/¼ýÔêKËMåƒÓ¼]º¼­gV¿¼ã'«äôwŒ¦†ÖSkó:pm7c·QÛöVç˜Ó{a5G¨+Q–0ëð;œûßu›6©ýAþÑDpDAAx&rss9wî]»vÅÆÆ†Μ9Cß¾}Ÿu×þ3›6yª?ìßÙ7M].½ À¯·~¦DY¬¶O[Åß;R¥:ýZ $qAËN,äJB0ßÜ‹¦¼î?ÒrS)Q–ˆÀˆ üKˆi5‚ ‚ Â3‘››‹¦¦&¶¶¶Èd25jÄÇŸu·þÑn$]çÝý¯³éÂZ®Þ»Â Ë[Òh¶1 ¢¶oAq·Àaž9ó̺I=èä¾PÓ|Ë*§Õì ÙJ‹Ïâ²À†ÙGÞÃk±zÒÓ„ÌxZá±õ%D/ÅŸgtë ˜˜cf`ÎP¯‘Ø™4àbüyÜ*h4Û¸Ò´¥JɬÃïb?׌vK›óúÎ1¼¹{<÷&`ý±.ËO-¦ÓrO¼—8qéî騔œdÆl‚Ãk:1dc/LôLéÕ¬J•’qÛ†ñªÏX¢æ¤‘[˜CaItÜÎ-Ü͈åú‡w‰øø~¥é3a3bY6°r’Ô„Ìxþ·wß¾ºƒËïGqåîÅJû–•v‹ì‚,µí-í|Xøë'œŠ ¯(O­¬U£¶„͈åÕV¯UªoïÕü|ã'ν}Çä×[?Wh¯s ‚Þ aJÇé|~l¶T6nÛ0Ì -¹ñÑ=¾±‡é{&’ÀìÃm@äìFø¼Æ¹˜SÕ½ÍUœcõmšt’£oü†¶†a3b ›‹§_. .#†+ïG³gÜQ–Ìç|ì&wœÎ®­¸}Þˆ9GÞ'6ýNû#Â_KGAA„gB.—ãàà@PP[·n%>>ŸgÝ­” ¿­¦å¢&ìÝÆ›ß#lF3{.ÀÆØ–È”îgÝc|»7ÐÖÐfj§wÕŽ•Ëä<ÌÏ$:-M¹&»Ö©Íc‡i£è@›Æí1Ð6`\»×+ícoæHúBƒ<†©mÿæåïñi؆ÿí„ãö,7“ÃQ©Tèhè B…¶a­íõpéËùØ3\¾L^Q›Ë’¿–—×bGŽEVÛ¾ðø'\I¸H^Q‘©¸¶ w[¯ZÛìãúÛ.o&%'™„Ìx]ß[ë1 S{šY·à³ã³É+Ê£°¤“Q¿ó €Þ®þì ý€ðûa„'…IÇ*UJò‹ó).)~ò¼ÂÊ:DoWÖ[IvA6·SoqàÚnz5ëÀÀ ÝÑÖÐæøçÙ1ö0=›½(#‚ð!FŽ‚ ‚ Ï„µµ5RPP€‘‘]ºtASS|E­Š–†Ü_f€ûË–"—ÉÙøÊ¦ïÈóñlØ - miÿ”œ$¦îKJvõôLø Ûœ,Jsn|¼ž/?%¯èÙùY¸/T`n`Aà´‹Ø™4bÙÀuŒþa*T õ)%8}¬°¤Øôh² ²Õ¶kÈ5¿m8‰0Õ7ã%÷—™ÔáM¦íÇ©¨2¥#—ËÙ²…^Íú±¸ÿJz åê½Ë´[Úc[z¸ôE.¯[Ð`ã+;øðà[¸/lLQIžv>,°€Oz/bÊÎÑø­l…µ±-å5¯ïeìÖ!ÒkÛ™z¼Õùæô^ø;®JeïtÉûû§â¹È=­Ò:;ÚwàêŒX´Ë]#Aþ9d½{÷VíÚµ‹ÄÄDBBBèܹVVuŸW(OÓ–-[1bÄ³î† ‚ð7¹råŠ4¥ây—––öÜžKZZ^^µßÝÈÏÿû— MLLÄÞÞþooW(u,â0ËN.âÈä ¿­ÍÙGÞCWSzÌûÛÚáŸ!++«öªall\§ý’““8u*OOO¬­­8p ˜V#‚ ‚ ‚ºc‡yTôˆbe1[.n¤‹S·¿´½Â’BŽÞ<$%Q=x}ï_Þ¦ BybÌ¢ ‚ ‚ j]ßǤ#ÑÓÔ£‹SwÞxá¿´=•JÅgÇg3yç(ŒtŒßî Ú7éô—¶)‚PžŽ‚ ‚ ‚ fÅà ¬¼áokOGS‡So^ùÛÚA¨HL«AAþõÛ‹![ê¼ÿüLLgÈžÊê%ÿd‡Ã÷Óû›ŽO­¾—7õÁíóF˜Î¨¼¬îcœÎ‚£3ŸZ›uñ,Úü½v†la𷽞J]ÖërïaÂS©Kþ+DpDAA*ÐÓÖg‘ÿJ4dO­ÎcGè¸Ìëuñ\ìÀÁ:.WûWjaãÁë/¼ýÔêÛ9ö'kÒßm0½\û=µ6¾\ÀÛû&ÿ-m.<þ ¦3dœŽ>”Ò,?ÒfØæë\‡ù‡š$g'=•þ‚ðtˆi5‚ ‚ ‚P¶†6ÚO}jõÝJ¹Ék[_æËßÐÛµ?1¢¸Ÿuï©ÕÿG56mBcÓ&k›mOo¤Ê³jÓÁÜ™×vó‚ƒ/‡Â÷ýíï¡ OŸ9"‚ ‚ <÷Rr’ù"pïî€ôG¶ùEÍ6føwþä«/A¼ìä"\?m€â“út^áE|F¬T6nÛ0ijHÅi5×ï_å…å-i4Û˜y¿|XiúBBf<­¿páXĵãVœZŒ¿Û`†zÄX×^ôjV:’!.=ÿu~(>©ë§ x{ßd©¿÷&`ý±.ËO-¦ÓrO¼—8qéî©ÞØô;eÇšÐq™;gÊÞÜ=žwöMaÀ†î´\dÏÔ]¯©õÉ}¡§ù–UN«É+Ê㣃ÿÃíóF(>©ÏkÛ†JeËO-¦å"{Í6¦ó /ÎÜ9Yíu)ïbüyÜ*h4Û¸Ò—Û©·hþ™ô°›e¨6dܶa8Ï·Â~®ƒ¿íEBf<! —p_¨`UЗì ÙŠûBî <*zTk›¥×e î ã<ßêw½ïí›tâ|ì”*%®í¢_‹ARYQIsžÛçpY`ÃçÇç R©˜¸}î (U%t]Ù ÷… –žøL:¶XYÌ䣰›eHïo:ò 7M* ¼}Œö_µ@ñ‰ 7ô !ó®TvõÞés¹ðø'uº‚ ¨ÁAAá¹u.&ˆq󠄾ËfÄ¥Ç0²Õxf~}m"g§0Âç5ÎÅ< Ä<ˆæ«Ÿq|êb椳jðFô´ô¥òo_ÙΙéa•ÚR©TŒÛ6Œá^£‰žó•JEaIÚ>…%…D¥Ý"» KmûÕ{Wª½PPRÀ¨Ö¸þÑ]οsƒˆäpVœZ\®ÎÌ ,z+„)§óù±ÙRÙ¤í#ðjØŠÛ³R™Ñ}.#HVþ“¶ÏűeÔ~~{;œ è‚ã~“ÊÂfIJlàº*û4ëð»D$‡8í3ïÓǵ¿Tf¢WŸ]¯ýLìÜLÞõ›É+›ýÉ-Ì­²žòZ5jKØŒX^mõZ¥2'‹¦„”@øG N»D==y —ÊÝl=9û¿kDÎJ¦©•+¯ï€§a3b™Úé†x¾JØŒXÂfÄ¢_v=kj3 ò(«Oɾñ¿ré½H"’ÃYô…T^Óû®!× U£¶½yˆŒGé(Lí¥²/?å\L'޼̹·¯s,â0û¯í`ݰ-„͈E.Ó `ÚEÂfÄò¶ïGÒ±çb‚ê5’Û³SÑÖÔáûàõ@i°oôƒ˜Ýës"g¥àjãÆÔ]cPª”ŒÛ6ŒW}Æ5'ÜœJŸKAj'‚#‚ ‚ ÂsçÌ“tøÊ™‡Þ¡£ƒ/a3âX9ø[<xp4â“:¼…®¦.}›¿„£ySéX™LFQI·RnP¤,ÂͶ%†–µ¶™ABf<;LCKC«Ê\öfޤ/T1Èc˜ÚöÔœdêë›VY¯³… ƒ[ÇPÛcÝz ôƵÄPµ}µ, ´nÜž¨´È²:S¸ÿouþ- -^l>KC+~‹ ’Žëíê¶zZz´°iItÙ±5Q©Tüxy3sû,ÆÂÐ]M]·|E*ÝzNM‘Ëäôk1mMbD×Zo](UJ&mÁ˜6“è`ßYÚ>½ËXZ¢)×dD«q\»ZC-uóó¼äþ2æNëÖcBûi߯¶OUïûcþnƒyoÿôvõWÛ¾õÒFftŸ‹¹¦úfŒi3‰#ê­N |º£§¥G÷¦}¤6OEÐØ´ ½šõC[C›·:@Pt9…9D¦Dp?ëãÛ½¶†6S;½ûGßAøO9GAA„çNjN ÉÙIômþ-l<0Ò1’Ê”*%rÓ°6²‘¶YÛJϦö|5p-‹Ë¨Äø9÷dÕëÖ«¥ÍdÌ ÌÑ”—~…¶0´D&«Û½F C+2¥WYö0/“½Ío±§)*)"·0‡VÚIåÚ:èjê )×"¿(Oz4åZjAK#kµDŸåÏIKC‹¼²ck’‘—N^Ñ#ÕWTY~(|ËO.’ÚÉÌË HYTk½uñeà§¼ã÷±´M¥R±ôÄgìÝFna.%ÊbŠKþ|{©9)R0 ÀÊÈš”rï]uïûc/8øboîD·!Òt&•JEbÖ=ÞÙ7Eúœ–Ò̺Eúdªo&=×ÑÔ‘ÚLËIÁÒÐZ*{üÙKÉN"-7Eísied]çÏ¥ Oˆàˆ ‚ ‚ðÜàþ2ÝšöfWÈþ·w"%ÊF´ÇP¯‘˜X`n`ÁÃüLiÿ̼ µã‡zd¨×H2¥óÊwþ|¼©Þ©±M C+ä¦Q¬,FS®IZn**•²NýmaãÁùØ3Œj=¾RÙ§ÇfQXRÈééWÑ×ÒgíÙœ¸}¬Ö:- -)V‘ñ(] ¤d'aidU§>U§¾ž)zZúÄgÄâ¦×R­,%'™IÛGppÒI¼ìZÐd®©”S@KC€e òº¯ös.&ˆMç×pò­+ÈËý¸?¾—Ý¡ÛØ?!+#kÂï‡ÑóëöjÇÊerµ>Ô……¡¥Z )9; ú¿wšrM~š Gd2¶Æ ø~äZØxT{¬\&GEÝûknhIJΓ¾¦æ¤ R)±0´¢¨¤ˆ¬ü‡RYVþÃ:.AxB„AA„ç’‘Ž¯µÂ™éa|5p-Wï]æí½¥I<{»ú³'ôGÂô$‡H|F,AÑ•¡¥©L&ðÜÈ“ê8[¸`gÒˆ ¿­¦DYš3Ë+í“×bGŽEVÛ>­Ó»ìÛÉÎ-ddsýþUŽÞ<ÀÃüLœ-]Ð×Ò'·0—![êtþ†–x7lÃòS‹(*)âPø>’²i§x¡NÇWG&“1Ü{4s޼OjN Å칺€Ü‚´4´p¶lÀ¾°<¬x2Ö5ÆÊȆså¦÷ÔæAn“¶àë¡ßaY!@ñ0/+#¬Œ¬Q©Tlº°¶ÒñVF6ÜJ¹A‰²¤ÎmöjæÏþ°D§Ý&+ÿ!ëÏ­”’äþýÇ0ÿ—HÍIA©R~?¬Ò{amlCøýÊymªÓÉÁ¸ô~¹y¢’"–ŸZDû&1Ò1ÂÉ¢)†VRàW~øÓç ÿEbäˆ ‚ ‚ðÜk«èH[EG K ø¤÷"¦ìßÊVXÛâaûdúDAqsžÁí”t´téÕ¬¯øŒàFÒ5†mî'Ýy÷ZìÀ 41sàÛW¶3yÇ(ŸÃ¤o¡­¡£6Ê¡°¤Øôh² ²Õú×ÜÆo_Ù΂£3m÷8lŒ0¿oiòÏ÷ºÎbòö‘üzëgŒtŒi×ä¢RoÕé¼× ýé{'â8Ï;“Fl±›zz&µ÷]ðz¾ ü”¼¢Gdçgá¾P¹Ó.0¯ïÌýy]Vx‘[˜ƒŸsOy £‰™S:þn+[c[ÏŽ–v>4¨×°Rý‹ú¯düÃÉ-ÈáÛW¶Ó³Ù‹LÛ=ŽSQd>JG.—³3d ½šõcqÿ•œ=CbÖ=Æm{’«åï1Ìïûƒ[¾Âñˆ#tZ9»Uj¯_‹Aì Ù‚ëg Дkrñ½Hôµôkl³[Ó^Lé8þëüÈ/Χ·«?ovy¿Nï{MÞí:“…Ç?¡ëªVd>Ê ‰¹#3{~ª¶ÏÇ=ðÖîñ¨P1¶ídÞõ«¼’Nyfæl|u'³¿Ëä#iÙÀ‡ÕC6¥£P6¾²ƒé{'òEÀ|<¶’Fï‚Pw²Þ½{«víÚEbb"!!!tîÜ ++ëÚ„¿À–-[1bÄ³î† ‚ð7¹rå æææÏºOEZZÚs{.iiixyyÕ¾#ŸŸ_ûNOYbb"öööµïø7Ë-Ì¥Ñc’䣥¡õ¬»#‚𯕕UûNÕ066®Ó~ÉÉIœ:„§§'ÖÖÖ 8PL«AA„º:{†¤ìû|waštA„1­FAAê("ù£~@S+WV ÚðŒ{$‚ < "8"‚ ‚ u4¦ÍDÆ´™ø¬»!‚ ÿˆàˆ Ï\@@»víªñAxx8>>>Ü­å?ºÙ³góV5_¨þ* ,`Ê”)O¥®Õ«W3lذÚwü›íÞ½›Î;?ën‚ ¨9t(“† µ8x0óYwE2th}ììD’Ö?ª$?Ÿ˜ï¿Çyòä*Ë#׬ÁiÒ$ÉþæžÕE‡¼°cýnÜ ÕªU„̘ÁÃ7žu·È !7>»—^àѽ{8M˜@ïà`Úÿ=1ßOÌÖ­( 87f ö#GÒïæMäÚÚ„|ø¡TוwßEÛÄÿˆ ÄocÆ *.@®¥…bØ0š—Û¿*U]k#GG¼—.ÅÐÁAmßÚúSÞï¾ÃªKôV^fZª#‚#‚ •º¹ñùU^jj*¡¡¡Ïº‚ j~Èœ9¶ç’šZ\§c”Jx÷ÝÌÌBiÞ<œ1cb?>N*?v,‹-Â11 ¥GÛܽ[¨v¼Bq¯¿NÅÝý66Wyå•–-KA¡¸†¶ö•JÓj¯3dH4ÑÑ(×P(®˜-•«5*CÃ:vŒ -­ô\Æ£K—[XY]eöìDÜÜÂiÙòÙÙÊ?ô~•§*.&xòdöÚÙØ»7µ~=‡ÝÝI»p+|ÀawwÎ ”þ¸>ìîέU«ˆßµ‹Ãîîvw§äÑ#.LœÈùqã8ѧGÛµ#lîÜJ#œKÓ¦ñÛk¯U*‹\½ºÚ+ÕÔf^b"f­Zaää„výúèÛÙ!ÓÔ$;*ªÆ:»µb‡ÝÝùÉÙ™Ëo¿MI¹ï<¹ññìµ³#æ‡8âéÉONNDm(Mœyý:Ç^x}qmÞ<öX[“wï^åóûæÇG®U¼S NÃAƒÐ³µÅÔÛ›=H¿\:-íüyJòóqš<m\Þ|“{‡¡,,¤$/Ä£Giö¿ÿ¡U¯M§N¥ =´à`Œœií‚§NeWj£*ͦOÇÈÑ»~ý°íÝ[­,õÜ9JÿÛ·ÑÐÖ&æûïpœ0¾aa˜·iƒ×¢Eô £ã?PßÓ“¾aa4:•FC†Ð7,Œ¾aahèëKõf\½Ê ;wÒýäI’¸wèZ»911䧤TÛçÛk×âTͨ‘ÛkÖÐdäH4 0õö&£Bà>;:MCC,_xôJud„†Ò _?2ª(K ÅÌǧھÕÖ¦ž­-P:rဣ#‡=<Ð17ǪK—ëH àÖêÕtÞ·Þ—.‘AäªUjû(óóypù2½‚ƒéµŸ¨Tœ7ÅðáôŽF¥RU äÝ¿OâÑ£8ŒSm2ÂÂ0qs+=§¨(ŒÉ»ŸëŸ}†‘³3Ê‚ݽKn\ªâb ¸6>…*dßþ}¹sjºÖÕÔŸò~ú  ÌÚ´)Ý “Ñzõjn¯[ÇÃ7¸½v-2™ìIðpž2ûÑ£¹wø0}®\!ñçŸ)ήÛßXun~ù%içÎÑýÄ zž;ÇýcǸ»?.Ó§Sœ“ÃÍ›É %æ‡ðY±B:öê¬YdEDÐ=0~4èÓçOõE¨á¹SPPÀ§Ÿ~J·nÝ6lgÏžU+ß´i+W®”^çääðùçŸÓ¯_?|}}1b»wï®¶þmÛ¶ñÅ_H¯‹ŠŠ˜°& ›œõ‘Ó¦YbaQºðcÓ¦•ƒ!¿—‡‡Ý»£§'§OŸzDF>1`o¯ƒ½½š˜˜h Pèœ\ÄÒ¥K177¯ôxõÕW¥cmzöD×ÚS//ê{x¨µiÒ¢V¾¾hèéaÓ½{G7Ô¦Aß¾h"×ÑÁ®î•ý[þØ‹ááx—û?³¼äÀ@TÅÅØtï^©¬03“øÝ»qœ0AÚfæãCFh(Ê¢"~rrâád”8LËÊÎ Fô·ß’Ÿ”„²¨ënÝ(HO§03“[Ë—s~üxéØšÔÔæc‡ ¡ûÉ“´Z±ŰahÕPc©ÄŸ¦áK/aèà€–±1Ž&pïðaµ}TJ%-fÌ@®¥…LCC{{²"#y”€Óĉȵ´hZͨ‡¨õëi8`Ú¦¦U–ß^³€&#FPœ›‹¦ÙQQÄmߎLCCÚ^üè2¹Uq1qÛ·“‹¦‘ʹµžçc5]ëªÔÔŸŠçá\aª³®µ5^K–ðÛØ±D¬XA믿V›–e P` P`ho†žº––äW“Ϥ®b¶n¥ùŒ蘛£mjŠý˜1Òß‚L.§ÍÚµÜX²„óãÇã½t):ææ¥ªTÄþø#îsç¢ca†®.Dòä¿…Ž‚ðÜ3f ¼øâ‹dffâëëKdd¤T~âÄ ~ùåéõèÑ£Ù¸q#~~~Œ5 …BÁ±cǪ­¿  €yóæQTTÀ… X·n ,~П>}š 6`ee%½>\î ÌåË—Y·n={öÄÄÄ„>}úpüøqºuëFII‰´ß›o¾ÉÌ™3iß¾=ݺucæÌ™•F DGGãããÙ3gð÷÷ÇÙÙ™7ÞxƒÉåî´„‡‡óÝwßI¯·lÙÂúõëÙ¶m›´mÓ¦MDDDT{Þ›6mbôèѸ¸¸0jÔ(:vìÈÉ“'Õ¦1Ñ£Gd2/¾ø"/^¤cÇŽäååIûÌ›7aÆaeeÅ€¸uë­[·&¸l¨m½zõXµjaaa@i iÁ‚|õÕWܹs(¦ôù矣©YúÃcúôé,Y²„^xQ£FáêêªvAøo:tè!ݺЭ›1G>¤¨¨ö$ÉÉEX[?²_þyJŠz™¥¥&r9$%©Õao¯óg»¯ÆÌLSz®£##/ïI0FCC&=J_—NÙ0a¡¡¡•+ÊÝ®IùÉrµ)†¶‰É“çõ듟šZçc#׬)IPE>‘˜ï¾ÃÊÏ};;i›Y«V¤‡†’~éºÖÖ$Ÿ8!*LÜÜÈ‹£(+‹‚´4’Ož”F†ÈärL[¶$#4””³gy”@af&©©5mZckjó1 }}ôíìP¼ú*.^$¾šÄ¶å姦¢[ö½@×ʪÒ¹ŽºÖÖjÛ RSÑ13CVö¦Ž…E¥÷¯$/;5äq¹wø0·×¬¡ã¶mȵµÐ40 87«.]膪즌¦šúú¨”Jä::¼ŽY›6ggK#zꢦk]•šúóXúåËmÞ¼™6mÚðÁ`kk‹··7?ÿüsµç"¿ßáÙôïoBù›üÔçÐ!õ%}W®lÈðáw04 ‘r’ jJ¿~&4o~_Œ¢oß'õ˜›k²s§=3f$`aq•«W±iS“:õ©yópŠkܸ‘ψ1(רºõɰ&M´yí5sš7ÇÎ.Œ€€?—ðÑÈÈ;;»J33³?UïcÎS¦øóÏlÖŒÓF_6è×¹–]]9Ô¼¹´Z €mïÞœ2„£íÛcçïOµcsãâJG7”S˜‘AüîÝ8•åþ¨(ò›oªbÖªFŽŽÈµ´0oÝ… ]]©¬$/Soo¬ýüÈOIÁÔË«´ÌLJüäd¬|}1oÛ–¢ìì'?îËee‘ŠEMmfGEqjÀ6mJØ'Ÿà6g¶n|ä''WJ$jÝ­ÎS¦p²Žxyaèà€Ë›oVyÎjd2Ú~û-1[¶pÀÁ™†FiÀ ìC€J©T›ºñØï¿'?9™Àž=ÙkkË^[[Î ”Nái·y3Ñ7rÀɉâ¼<<Ë­2äµd iipr"vÇÚoÞ,Øxxó&{mm¹0q"9ÑÑìµµ%¸ìúÕv­ÏÏ^[[²nÝ"xÊöÚÚ’}ûv­ý¹½a B»~}µúв²ž2ŸeËÐ61Á¢cGìüý¹4}zíïm™Æ/¿Œ\G‡}سgŽq}÷]Lš7ç×®]Ù¯Püúë””åG¹ôæ›4:³Ö­Ñ17ÇûË/¹0iEY¥«\yÌ›‡‘“Ç»t᧦MI¨{FøkÈz÷î­Úµk‰‰‰„„„йs'¬¬¬k?Rþ[¶laDY(á¿ËÆÆ†Aƒ±ªB†öàà`Ú´iCdd$Nå†m0iñ¨Q£xðàZ(Í—ñÛo¿1þ|.^¼HBB†††Uöa×®]Œ1‚­[·2}út¦k×®¬]»–±cÇ’‘‘!l˜8q"ÑÑÑÒÈ’¹sç²eËnWÈÚ®««Ëš5k3f ›6mbâĉäååI¹5FŒAXX˜”“ÃÙÙ™~ýúñå—_Jû¢¯¯ÏªU«¤Ü# @KK ]]]¬¬¬X²d Ó§O'++‹ŒŒ ´µµë¼ŠÏÍ›7Ù´iK–,áÈ‘#ôîÝ›o¾ù†3fðð¡zÂÃF1uêTÞÿ}Ž9Bß¾}‰‹‹£Q¹¹²_}õü1¹¹¹Èd2âââP(ìß¿Ÿ‡¹¹9¦¦¦ìرΞ=KûöíÕÚ*..æÒ¥K,^¼˜#GŽpçÎJ#áùråÊÌë8äþŸ.--í¹=—´´4¼Ê~0Ö¦¶åÔÿ ‰‰‰ØÛÛÿeõ¿÷^zzræÍÿžüY&NÄÄͦ¿sdIJed^»FÛ Óf ttù1cè*åÕªWœ›Ë¾F””„\K‹ ÁƒiЧÿå`kºÖTÉ£GrsÃ÷ÈŒkÉ#üóeeeÕ¾S5Œë´_rr§Náé鉵µ5#GAøw+?WZKK‹N:1sæL>|HJ ËúúúR\\̼yó¤ ÞÞÞhjj²páBÚµk'Fþ¨¶mÛ¢T*Ù·oŸ´-;;»ÒˆoooöîÝKa¹eùvìØAII ­ÊÝáz<ò" €nݺЭ[7~ýõWN:UëH—òïU³fÍX¼x1FFF•<5iÙ²%šššüX¶ä#€R©äÇÄÛÛYYÒµÇS—fÍš…““vvvèêêÒ¡C>þøcŒŒŒÔÎíqß455iÛ¶-Ÿ~ú)ÄÇÇ×¹o‚ ª8tè!J%¤¥³wo†”§Dx6´MLp}ï½*ËŠ²²ð^ºTFjvþ<ùe¹^î|÷: ×ÒBYP€yëÖ(Êr…ýÔt­ÿ¨G‰‰´øè#þñ/Œ ÿj< sçÎ >gggòóóY±bžžž4iRý0éÇSPBCCy¯ì?p ºtéÂþýû™;wîŸî[³fÍ9r$ãÇ'>> V¯^]i:ÎìÙ³iÛ¶-ݺuãµ×^ãîÝ»,\¸áÇãíí-í×µkW¦M›†ŽŽ/¼ð:uâþýûך#¥_¿~ØÛÛãíí¡¡!?ýôJ¥’žu> ¥S‚þ÷¿ÿ1kÖ,>|ˆ‹‹ ;vì ,,¬Ò A~~~¬[·Ž7ÞxCí>üðCúöí«6ݨiÓ¦ 6 WWWJJJX¿~=öööxTX–R¡.T*˜=û£FÅ`l¬ÁoXÒ©SÕ# …¿GUy"³ª%¸/@VDçÊ’»7mŠOÙŠEr\ßÿYv­’š®õeä舑£ãS¯WøoÁAþqFŒQåðnKKK&NœH½zõÔ¶÷èÑCmZН¯/9eK¥1räHÎ;Çþýû100 G¼ýöÛÒ(†ê¼óÎ;œ>}šîÝ»KÛ&Nœˆ¥¥%ƒ*$Q{á…p)—ÞÛÛ›â*–€7nMËÝÕX»v- …‚;w¢§§Ç믿N½zõ¤)5PD¹pá‹-bÕªU²`Á‚J+·4kÖŒ7ß|KKKiT‹±±13gÎ$33ÇZ¾4Œ3†}ûö±víZT*M›6åôéÓR]]]SÅšW^yE-H±hÑ"š6mÊ?þÈÑ£GqrrâôÿÙ»ïøš¯ÿã¯{3É‘-!‘%‘Ø{• VP«jGŒ¢(E5ÚÒj¿Z5‹¢©ŸšQIJÔ,+!B‘EdI"‘}sïïÄåJ$WKµzžÇ}<’Ïç¬Ïç¦u?ï{Îûüþ»ÊL€ÑkšG¥<æããC||<ýû÷W);eÊ~ÿýw>ŒŽŽ­ZµÂßߟZµjU{M‚ ü5‹-¢~ýú¼õÖ[¼êá¼0::š¼êa¼–ÚlÚôª‡ðŸÔhܸ—t„ÿ‘sDøG9GAþ[DΑ†gåÙ¾};S§NÅÄÄGGGfÍšE·nÝTÒ‡—säubñ¡.—ß¿Eý:Öj•ºÀw§WñëÔê•¿H ÍBO[ŸzUÞ†X¨ÚÝwhñ…©Ÿþõœ?onéͰæo3ÌS|æþ™DÎAAáe̘1XYY‘œœÌñãÇ5j®®®Œ=šK—.½êáUëÁõëœ4ˆ66~jû×'9’½ÏÔ*+,ä‹ P¾³FêÑ£(d2öT,<Ö³'ÕìŒÖ{}ö„ÿô\ý¾ n–͘ÖiÎ k/3?ƒ‰;G`·Ä»%Æ ÞìŽÜ•2›¾Iï&ýŸÑ‹šp†Ö«×\ð8sû$Æ $|~t±òX÷5­°þXýe[& 5IËK}á"8"‚ ‚ TI"‘Ю];åïyyy$%%±wï^|||puuåý÷ß'>>þ޲jR--lGŒÀuáÂg–¹BqFÆs·síFM›N]OÏçª~çIY ø¸{î¾_´†ÆvôwüÂÚ{ïÀT4¤š\šË¥¹± o>š2¹ê2Ó¶¶iiÓæ…õùOcehÍÑè`³âÉ+þóß‚ ‚ð÷9GAAþ¤üü|e~¡ÜÜ\•DÂOÊËËC.—ר^nn®ÊÎQUQ(jM9–ËåäååÕXN&“‘––öÌ™ Ï÷ýû÷¹ÿ>kÖ¬aË–-XYY1uêT¦M›VcŸ/Òí~ výz SS©ãâB»­[©U¿>NN89qçàÁ*땹|9M/æÌðáÊã…wïÜ¢n ’´o²ü|Úl܈qË–Ê2ÙW®P·Y3JrrP”•¡óœ3OÖŸù¾íßAK£üï¥ÏúŽŒmã‡û0t5u«­{#5’­¡ëpµlÆø6“¹z7Œé{'xÿv¥ ŲbfìȱØÃ´´iÃÏãosï¾Â–ÂÒLœ*-«ÙþK~]€L.cXó· Œ<@Øû·”çïä$1x³Ëû}WcoåñKI¡lñõôÊïÉðæ£•ç.&…2qçr ²ðë0SeY\!gqðûütéÌ ,ð´n…¦T“oßܬ\R²Ðk)û®üù%Ù8Â_`ùæÔüº¬üLìLø´ß×tlÔ€æ_8PP’Ïý‚LÜWØðÝЭt¶ïÎîpv‡ù³wbùýùþÜw„&œaË[»HÊN ý×n|>à¾>ñ‹ó˜ßc1¾íÞ!ýaïLç÷Û'0ªU—%Þ_ÐÏuµµõ°6j@Lz‡£Ñßm›Î~«¼ÖÝáþ¬:¶œŒütº9ödõàMêÖÁo×Û„&œA®(ã5­J5ׯ9Ý>xâog5_[†®![ÞÚ¥¼ Y·™¹×—ˆ”0¬°rÀ:4ê@VÁ}¦í˹ۧé`ß•"Ùß¿· üˆàˆ ‚ðŸQ\\LAAA劊Š(,,¬±\AAÅÅÅ5–ËÏÏWÙŠùYòòòªLäû´ÜÜ\ÊÊÊj,—““£ÖƒvNNNmÉår$//’’µÞ‹ââbŠŠjþÀ_XX¨Ö{ñdУ:zzzÊü …♉ž Jkž°khhXc²h‰D¢Öšj©TªVUMMM444ÈxÆ Š»wïÖØ†D"A*•¢££ScÙéN@Qÿûv‹ ™çÏ#Wã}ˆþßÿ°<]3³Jçä%%蘘ÐóôinmÞLäçŸÓyß>î…„pÞÏYARmmbׯG^RB€­-]ÂÈÍ–«Wà2gN•»gÜËMáHT +<~XžÒq;.|ÏÇAsy«Å8Æ·‚­ñã+¥e¥ü¹-¬#1ë6#[Œ£·Kä 9wŽÀ·ý;Lh;•e‡? ¤ìñÿ“v‡û“œ@äÂd4¥šœ‹?­2–ˆ Êœ#*÷5'‰Ùû'³Ï77Ëf ÿ¡o¥ë()+áVfL¥YÖ-YñÛ'Ìíþ­¶§–ÖãÄÙ­´%bA ͪÔÞþ«?óë_87'’¢ÒB¼Ö¶£·Ëã¥7%eŘè™rúÝp6ÿ±–ÏC±oâŒjÕeÏ„_±7q$èzom@ÔG÷ÐÓÖ#ìý[„&œaæ^_.Ì®ÔoMŠdE\N>Ï…÷¢‘J¤$f—Ï’š¸sÎfM¸ñÁ]neÄ0ðû7ð¨ÿx¹Mßä—k{9ÌgV+ƒ#goŸâƒC³9à{gó&¼p:K/d•Ï:6ðÊ—Õ›qsÕ<%e%<,Î#ú£{|yl™Ê=˜¼ëmÚÙubßÄ#‰dôƒ¹2?C]CÍ£¶¶øõ@< IDAT±‹Ò9s˜Ñ?bLkß羂ðºÁAá_%,,Œ=z¨{ô ]åN>ÕÑÕÕUk'œÚµk«õ0¨§§‡¶¶vå ÔJtihhˆ††F匌Œj|Ш[·ne¤Ri;Aùƒ¶:äZZZèëësóæÍJ;P=I[[[­÷BGG]Ýê¿q¨U«–ZïEíÚµ« ÚTåuLÈ 0kÖ,N:U鸡¡!ÆÆÆxyy1sæLìíí_ö0+‰ß¹§©S1rsÀ´}{µê=Œ'ùàA¼N"76¶Ê26»’™´nMÌš5XzyÑ7"‚c=zÐóôi.Ï™ƒu¿~Xy?ž9aW±ªUŸ>U¶ûý¹ïÔl8Ƶë) lú&›¾Irv"?^Ü€MÝhlîÊš7·r(rÿ;ñ9îõ›3³ËëÔ‚©f£­¡M_Wv‡—S2¦s1év DKC‹~®ƒX~øCþˆ?M/—~‰ÄLºšºôuõÁÁņ~á¿IGA„wwwâââTŽ=zÐþ}Œÿµ…ÿŠ'N(ÖÖÖÆÌÌ gggfÏžM·nÝÔšó²ܽKí ž»Þ•… q[¸é3‚›Rmm4*‚mMMÊ*f%´·§¬¨…LÆ¡Æ)ÍË#%8§©Si2~ý–²ãÂ÷N®l°0´ÂͪŽÉ‰I¢¨´Ä¬x M-=p1wSF2óÓ©§g‚¦´ü#½¹Éã÷c¨ç(³âñÝ9’Ô¼{Lh;•E½?«qœéyi˜=1káé Õ1Ô5dqŸ,¸Ì›Œõ“ gV3»Û³s¿(ûtzÜ™w²“”¿kkè(—iJµ(*}<£,ðú¾9¹R™Ä4§0›Ry©Úc®ŽŽ¦®2`óÈÝœdº}ûx¦HAI>M,côµõñjÜ—V Ú©Ö}Ì©[¿qèÚ>(ÉeÈr¤’êÿ[ÒÖÐA_[_ùó£{ñ0M©uk+ËšX–—Š\!ç~~¦Ê5XZ©}ý‚ð_"‚#‚ ¼6’““Y»v-QQQÈd2,--éСC‡­òÁyõêÕ¤¦¦òñÇ£§§§r.66–­[·"‘HX²dI¥oš ÅÑÑ‘‰'píÚ5~úé'æÎ«öÃÞ;wøî»ïX¸pa¥oÐCBB8~ü8õë×gÆŒjߋי¦¦¦Z3AøëBCC¹}û6˜™™áëëËðáÃÕÞ*ñe«]¿>II5|JVx8÷BBTŽí16¦wh(šOý{ð¤qq„Í›‡IëÖ88píÓOé¼w¯Úýî ÛA³úÍilÞDåxTÚuü/naï•xÔo_ûôlìT"eYßULîð.ÛÎo¤÷ú8›5áíVéç6S}sr‹/wË-z€Bñ8GŒ¶†6x-寥ÄfDÓ{]4¢²ü£*fæ\LúCù{z^šÚ×ø${Gú» &*-²Æ²fæ¤?±C‹º}¦?Lcò®·94ù$Í­[`·ÄXeI¡D"EAåY.:šºÈžH[UâT IåYzõlÐ×1àü{Q•fç¹}RùóçýË—Xݾÿ8WKý:6ôuõaJ‡wŸyMÒgŒ÷YLõÍÉKÉ.ÈRHÒóR130G*‘b¢gʃ¢ÇË's ³Õn[þKÄn5‚ ¼"""pqq!00¦M›Ò¦MŠŠŠ˜={6QQQ•Ê'$$ðÞ{ïñÅ_°oß¾JçãããY¹r%_|ñ*çär9³gÏæË/¿d÷îÝÊã111¬\¹’¬¬,µÇššÊÊ•++µõÈÂ… ùòË/Ù¾}»Úm ‚ ¼({öìáí·ßæôéÓ\½z•3füc#¶#G³n®_G!—“J~bbùI…‚²¢"䥥(žø`@L C³²š•EÏS§H¥ ÍÊÂÀÉ©Æ>³¯^¥®§'Yáá{x¨=V…BÁ¦³ß2µãìJço†6Gß åçñAôré§2‹ÀÚȆz-'bA"cÛø±íüF6Ÿ[‹£©3¦úæ„TìŒòsØ*í†&œ%*í: … (ÐÓ®y–W㾄&œárò K Ùv~c¥2wr’hþ…!ÑA*ÇWý„°;),-$6#šƒ×öànUõ’­'y7ñaçåm¤?LãNN‘ûk¬_ü- -œÌÊ·V>±›O=ü[XrïÁ]² î«·7qäFê5òKò)–y@­>mábáÆgGQXZHIY 'oýFüý¸ë¾ÕrÎ|ÃÔkÜ}p‡_"U?‡XZrý^„ZcòàH ›6|sj%¥e¥^?@j^ íl;ЧÉö]ù?®ß‹àzªúm ‰˜9"ÂkaùòåXYYqéÒ%•¼………Uî´°mÛ6¬¬¬h×®?üðc*Öˆ?­GlÛ¶ÁƒosxêÔ)222T¶·ü«õ3iÒãuÓDDDеkWµa ‚ ¼h_}õUÍ…^!›Aƒ(ÉÊâܸq¥¦R§IÚnÙÀƒ¨(B:vT–Ýoe…Í AÊ󆢬Œ‚¤$ ìí‰^½«Þ½Õ®{,ör…œ7œzU:wuAÚ5çÂÑÒÐbû0¹£¤¬©DÊÖ·~fÖ~?V[†§M+´žh'ýa*Ó÷Œ'=/•:µŒ˜ßc1ަåù&¶_øž¯ŽJaiyE¹¸¯°ÅDÏ”ã3.bmÔ€Õƒ71öÇ!(P0¼ùhîä¨ÎÐ))+!!+޼bÕÌR |wŽ$åÁŒk×ÃÇ}“;Ì`ÆÞ‰œºuŒœ‚,¤R)»ÃýéíÒŸ/®ap³á\½{™v_»bih…Wã¾j-Ù²«gÏÔŽ³é±¦5Vu¬ñ°nIý:6*eÛ1ªÕÚ}튦T“uöÓÅá Ü,›1 é:㣩3­¶'ã¡z3V¶¾õ3 ½‹ûІ”–•âiÝ’¯m¨±^gûî|ൔ‰;Gp'' }3Æ·™¢RæC¯å¼»× Æ·ÂÜîÕØî†á?2k¿KM°6jÀ¶·÷R§–ŸôYÉÔÝc龦†V4S#X%ÿE’>}ú(öìÙCJJ ááátéÒssõ× ‹äïïÏÛo¿ýª‡!ü µiÓ†zõê\cY¹\޽½=#FŒ sçÎôíÛ—[·nѨÑãŽ9BïÞ½Ù¹s'cÇŽ%99sssÆŒƒD"!==¹\Α#å™â÷îÝËСC‰‰‰ÁIo.]ºD«V­ð÷÷g̘1DEE)ëΞ=››7obddDtt´Ê6›×¯_gÞ¼y?~---Z´hÁªU«hY±ÕdYY}ô;vì %%<<<Ø»w/fff̬Y³ˆ‰‰ÁÎÎŽ+V°{÷nzöìÉäÉ“•å6lØÀ×_ÍÍ›7±±±aÀ€|ùå—j%Ú^oaaa¯MΑ×5!ëÓÔÙèEKIIQùô¿Ñ›[zãíêĶSj.üÄê“+ žrºæÂ/È¢àyèjÖ⯥[Ÿ‚ ü3¨³]ý³¨;»1--•S§Nãé鉅…ƒËjAx=´jÕŠ£G²jÕ*ª-{âÄ 3f ={öÄ‚~ø¡Ê²žžž¸¸¸ðÓO?å[­îß¿Ÿ±cǾÐñÛÚÚÒ¥Kåò™ÒÒRe`æiñññtêÔ‰ZµjqòäI~ÿýw6lHÏž=•[nnÚ´‰7²~ýzâââ8yò$>>>Êí_>ˆ‹‹ã—_~¡E‹ÇkÐ333éÚµ+\¿~Å‹³nÝ:V¬X¡,“€mÚ´áÊ•+lÛ¶•+W½{÷”åV®\Éܹs™9s&7nÜ`ݺuáçç÷Bï¡ Â?U±¬˜Ö¶íÑ¢êYŠÿD!ÑA” “Ë𿸕®Ž=j®ô””•p$*P™@ôPäþ—Þ§ “DpD„×ÂòåËéׯóæÍÃÎÎ+++&NœHDDåuµ[·n¥U«V¸¸¸ ©©É¨Q£Ø¾}{•ËoÆǶmÛØ½{7¦¦¦tíÚõ…_øqãøñÇ‘Ëå!“É0`@¥r_}õæææüüóÏ´mÛ¶lÙ‚±±1;vì 22www @£FhÞ¼9sæÌÁÒÒRy¾GtêÔ‰FѾ}{>þøceݺucîܹ´iÓgggÆŽËœ9sTò³¬^½KKK¶lÙB³fÍèܹ3?üðyy§X±|ùr>ùä¦OŸŽ‹‹ ýúõcíÚµìܹ“û÷U× ‚ ¼Žt4uxÿEÔÖªy+ñŠÀȸ~fûç Ñ×1àNï½Ôþ Ÿ]„ýRº­iÉø6Sho×ù¥ö)‚ð$‘sD„ׂ‘‘ 99™£GröìY8€¿¿?!!!téÒ€œœ8À_|¡¬;vìXV­ZÅo¿ý†——W¥¶GÅüùó cÛ¶mŒ3æ¥l]9dȦOŸÎ±cÇØ¶m#GŽD§Šm&ÃÃÃÑ×ׯ” @WWW™|ÖËË‹õë×Ó©S'†JïÞ½U–úxyy±dÉRRR(..æþýû*Éf###騱#ššÿ)qwwWYVpóæM>|HBB+W®TÏËËC.—M‡þÂ]A^†oßÜÌ·onþÛúÓÑÔáÔ̰¿­?A„§‰™#‚ ¼Vlll˜0a[¶láÆÔ©SGe)È®]»(,,äСC 6ŒaƱtéR´µµŸ¹´ÆÌÌ ooo>þøcÎ;÷—Ô<¢§§ÇСCYµjÁÁÁŒ7®Êr)))”””pûöm•WÇŽiݺ5äĉ8::²jÕ*œéÑ£‡2±ë‡~ÈÎ;ÑÑÑaÞ¼yX[[3qâDåì™åË—3eÊš7o΂ X±bƒVÉ5ŸŸ_eÎ'¥¤¤åËtžkFF~~~ÿ¨]/Ax½x{ߤAƒ$’Ë{ß7øí7õ×ÌûûgÑ»÷Í—8¢Êwïæô›oþ­} ¿MÚñã¹»s A®?ñyòYöYXPX±ÄYwƒ‚8Þ§Ï_¢ð‰àˆ ¯- <<dœ9CÄ’%j=Ü=é”ÉUlwµj—çÌyC¬Òµe˸ôî»UžK;q‚_[µ…â¥õÿWD¯^Mpóæì³´ä×–-IܵëUIÅñÞ½IÚ³€ôÓ§9Þ»7û­­ tu%ò³ÏTîëýóç9ܶ-û,-ù}èPŠ33•çŠÒÒ85hû,-9Ò¡Yagþ<¸~SƒqÀƆÃ_Ü<ËÓïuu}>»v-{ŒÉ ­²ÍÒ°µ¥ 9Y½›ò æÝ»Ó7"‚†Ã‡ÿ¥vžÅÈÍ çiÓ^JÛÂóÁA^ ?þø£ÊC9@\\/^ÄÍÍ (_ráÂ/^\é!Ýßß™LÆÎ;«l¿oß¾=z”­[·¾ÔëèØ±#¿ýö{*>´Tå­·Þ"88˜“'Oª/**"5µüáà餴­[·¦nݺ”””T:/•JéÙ³'ÅÅÅ@ùÚïÌ'>Œ¤¦¦²å©í/ÇŒÙ3g8xð P¾çƒ>@&“)Ë4iÒ„fÍš±téR>|¨R¿¦Ä¹‚ ê ÌÁÆF‹C‡r^õP”†¯‹µµVÍ…*•¿cNSªÞÙ'vÃ'O‰äo™zL;t ÓÏ?ÓÿÆ Z}÷á ðàÆW=,²ÃÃÉOJÂÚÇ€‚»wqœ4‰>.Ð~Çâwì ¾" ½¼¸˜sãÆÑhôhúGE!ÕÖ&|áBe[asç¢mdÄ€èh ÂãÆ¡¨ø ÕÒÂvÄ\Ÿ(_•§ßëšú(JM%ùÀ4õôžÙîííÛ1ïÚ•Ú66Ï,óO ×°!õû÷ÕÃ*ˆàˆ ¯…M›6akkKëÖ­9r$ÞÞÞ¸¹¹addħŸ~ ”Ï144¤ÿ™™™Ñ£Gg.­ÑÒÒ¢GØÛÛ×8–Gåž|=š±Q‰DÂo¼AãÆŸYfòäɼõÖ[ôèÑooo¦M›†VVVÊ­ŒgÏž³³3cÆŒáÝwߥuëÖÈd2ÆŒ£cëÖ­ñõõåwÞ¡mÛ¶xzz*s®øùù±f͆ʤI“hÑ¢mÚ´QGïÞ½ñóócðàÁ¸ººÒ Aîß¿½½=ÚÚÚÊëÙ¹s')))ÊñLœ8‘:T{‚ ê zÀâÅV\¸OF†¬æ €\sçÞ¡^½+¸º^gܸ|}•çCBrqs»Ž‘Ѽ¼n’œ\¢RßÖöëÖeàî~KË«¼õV<«W§ck{ mí°JËj":4ޏ¸blm¯ak{ãÇ'°–ÉŒ¾~8;F“™Y~-¾¾‰t탹ùU-J¡iÓëxxÜ /¯ê$âÏC!“qaÊö[[s¼OŠ+’dßúþ{‚ÜÝÉ<ž°ùó rwçÌÈ‘@ùÃu»;1ß}GÒž=¹»äîNYAçýü8‘ÞÞi׎ˆ%K*Íð8Ù¯_µ³Xþ£¦M1tq©t.ïÖ-².^Ķb<®®äÆÄâ¬,2/\ÀÀɉ†Ã‡S»~ýj¯÷é÷ºº>¹òñǸΟDC£Ê6eeÜÚ¼§©S+(8=dÑ«W+?9`±ëÖܼ9¿ơƉ^½šC..\Ps6GöÕ«Êûþôß´¼¸˜ó~~´·ç ½=¿?5û$ÈÝ_«\V“´gnnjܘ«‹¬æVëÂ_#² ‚ðZ8vìgΜáòåËÜ»w&NœÈ€ЪøÐ®];ºwï^ež €Ï?ÿœ .P\\Œ««+7nÄÂÂâ™}Μ9ÅøZ´hÁÆ«,ÛúSJ-©.è2aÂrr#*•JÙ±c“'Oæ·ß~#''‡víÚ1wî\Ú·oÀš5k8vì7oÞ¤°°É“'3bÄêÔ©@`` 'Nœ >> –/_Λo¾© jÌŸ?Ÿ¦M›rêÔ)tuu ¢víÚ•vÏÙ¸q#S¦L!&&;;;Z´h¡¡! 6T–iÒ¤ QQQìÝ»—ˆˆ´µµiÓ¦ »þaÓŒAø÷‰-&>¾˜aÃŒùöÛt‚ƒ0vl½ëýüs¿ü’Cd¤+……rÚµ‹¦#îß—1dH;w6¢W/C.¼Ëøñ üö›“Jþþ÷9vÌ SSMbbÊ`gÍ2cÖ,3<<*ϸuË3gâë›@t´[¥ó§O?$(ÈÒ¯ß-¾ÿ>“… Ëÿ êÐAŸÅ‹­èÓç&©©Í9ò6§Nåѯ_¯ÕeÖ, Ш]»ÒÃdƹstúùgZüïœ1‚ø;h<{6“&á0i§||h4z46C†(ëÔõô¤oDQ«VQ’B‹¯¿®ÔgöÕ«ô~¥ö³®\Á¼sõ»äT×g­Šäæ··oçÚ²e”dgcà船»Ü¥;FÌÚµtFÇÔ”3#FûÝw¸Ì«,#/*âþåËô¾p‰TJ~b"(„NœH£±cq˜4‰ÈÏ>S &ûì³JmõêÕ‹Ÿ*–FXöê€nÁ~#77Ì»u+/׳'žÊ#õgÕïÛM}}¬änp°Jp¤ßõëϬ›vü8 ™ Ë*~KrrHÚ»¯3g”ÇêµlIö•+Ø L`“&t9x7nP¯eKŒ[¶äö?pfÄ,{ö¤~ß¾ÈKK±èуâ¬,Jrrˆß¾ìk×h»y3ÙW®Ð¤†\*ÕõùHáC±xã ÒO¢(#­j²IùõWl||ЯøÂÄaÒ$b¾ýV%8¢Ëq[°@9óC¿Q#rcb(¸sG??$šš8O›FÌ7ßTjÿÖ÷ßc3hÚÆÆUö³b–«ÝÛo ËÏGSO¼[·Hܵ 犼f²ü|ä2©…LFâ®]Xyy¡i`€,?¿Æë|¤ª÷ºÚ>KK _°€¶›«ßE鿆 *(ÿûoþå—ü1~<¥yyô8zTeY–ž­-…©©è7j„F­Zèš™Q”™‰~5ï[^l,…÷îáàë[~ß§O'víZåy‰TJéƒäÅÅaäêŠYÅΉ5¹‚I›6ÊàŽýĉ\[¶L­ºÂ_#‚#‚ Ÿ֥KêÖ­‹ 7oÞäòåË,[¶L­åG‚ U`àzô((ôèaȰaq”–*ÐÒª>EZZ)s‚XXh‘”Tþozºê933M¤RHM-ÅÁáqbêF*oµþWÔ«÷øc¹ŽŽ„ÂÂÇËf44$ÊWùïåËp&MšÄ°aÃ*µõ¬’O{ò!Yª££²„ã¯Ð62züsݺed¨]7vçL©2ŸHüöí˜wïNíŠdßõZµ"béR².]Bׂ´'(¼wz-[bÔ´)ù‰‰”ææRœ™IÚɓԪ_Ÿz-["‘J1öð ûÊÒÏž¥47—’œŠ320xbFHUªëóÚµ©]»6¶£FqvÔ(’öí£A _ÒedP·Y3åïºææ¥§«”‘êèT tgd S¯Íò¿!SÓJ÷¯¬°Û;vÐ-0°Ê¾ïqsú#­˜Eª©§‡,?ó®]éAIÅ,VM=½ò™#r9Re°K–—Wm§Uõ^W×ç­1i×Ãj¾Üɺ|™‚;w°8°Ò9K//ÂçÏǬcGj=µÜG¢¡¡|=ú]!«~™^qf¦Ê}×57W¹–C‡ò01‘P__ŠRS±Ÿ0¦‹ÕpW (=]33åïºææ*çóbc9\ñ%T-KËjƒÂó9GA„?íÀL˜0fÍš1mÚ4®_¿Î‡~øª‡%ÂÀƒeœ=ûÏ>»‡®n>>·ÈË“sêÔã<ÚÚå*eeªuÍ͵HM-Uþž–öøg3…Lê} IDAT3Õséé2äòò:Oz¨xR©ä…m°¢P”/mtss«ôzçw^H©Teù¨ ©ô™»Å¦¦ªü¬kb¢Vy7o’ué¶#FT:§É¸¹ys¥$­u=<ÈŠ"õÄ š~ü1çΑŽqË–Hµ´¨Ó¤ q[·bÙ³'²‚².]¢^«V·lÉýóç0i׎øíÛ©ëé‰DZý#Ru}VE"•’£Æ¬®©)EiiÊß‹ÒÒÊO¶UEž SSŠïßW>ÌgfVzowí¢n³fUÒOŸ&|Á:íÙ£\ ooOÞ­[(ä庼ØX¤::Ô¶±A¯aÃò\*±±ÈKKÉOLÄÀѱÆë„g¿×Õõ™NÜ–-ì16f±1¥¹¹œðöVÉ+ótN•'E~ú)Æ-ZpÿÂÒNœ¨yOÜC©¶6ЧþG¢cjJiîãÜB¥¹¹•ê¸}ð½CCy#$„ÛÛ¶‘}åJÝêš™©ÅŠŸ 8914+‹¡YY"0ò‚‰àˆ ‚ð§9::2~üxæÏŸÏøñãq©"yž ÂËpøp.õêiPXØœ¢¢òWÿþu8t貌¡¡––Zœ>§R×ÇLjmÛ2IK“‘”TÂþý·qïÞÝ€øøbz@i©‚•+SéÒ¥|éÎ_ei©ÅÝ»¥Ü¿¯^âØšLž<™ÈÈÈJ¯µOLíÿ+tÍÍŸ¹ËJ-ssrcb*=0Ü9xüÄDŠÒÒHÚ³«Þ½UΟðö.ß2ö)±6`7v,µkWnó—_¨eaA½§rxiÔª…ž­-Éû÷cÞ­r™ŒÜ¨(êV,“­×²%77nļ[7Œ[´ þ§Ÿ”3<êµjEܶm˜¶k‡E·nÄnØ 2ûàN@ÁÍ›+“ÕªÓç/¾ 7*Š’œîZ~ó ­¾ûŽ‹3fP’•Uã½UÞcGGî_¸ ò÷nà舎©)÷BB€òä²OÊ %7*  ¤::( µfÖXzy‘JÖåË”·m›ÚãþAAþu‚‚r8Ј'¿ä4¨.ª[ú®YcÃÈ‘·Ñ×'0°}ú(öìÙCJJ ááátéÒsógïÎ /“¿¿?oW$A^aaa˜¨9åþŸ.33ó_{-™™™4Ws«È¢”—ây¤¤¤Ð¨Q£—Öþ¼yw¨UKÊÒ¥V5ªuÞÏ£¦Mqž1ã¹êE¯^Mεk´Ý²¥Ò¹¬K—87n}¯\QæwžM–ŸÏ ’šŠTK‹Óo¾I}ooå ‹W­º÷úÏ*+( °iSº«ì"ô:¸BôêÕt ~ÕCùÛäææÖ\è Õ*—––Ê©S§ñôôÄ‚Áƒ‹™#‚ ‚ ÂKI‰‚ÀÀÈå™)cÿþlebWáÕÐ62¢É¼yUž+ÍÍ¥Å×_‹ÀH52CC)ªÈõr{ûvL;t@ª¥…¼¸“Ö­«ÌãòªT÷^ÿY))¸}ðÁk¹BYA ™Œxµ¶‚þ:ñAAá?E¡€E‹î2fL<††¼óŽ;ë¿êaý§U•'âógli/<–͹1c0tv¦å·ßåËTš¼ÿþ«Z%Õ½×–ƒ/¼ÝWån` ç'OF£V-Ì»vÅé%Yª'‚#‚ ‚ ÂŠŽŽ„°°&¯z¯¥6›6½ê!ü'57W£å·ß*\ÂßG,«AA„×Ð¥™3‰^½úUCá_AGAAá9”r¨bëòP__R}Å#z± ’“9;jíí °³#tÒ$J²ow|¬gOö+_çýüTêÇmÙB»;û,,8Ú¥‹ÊN6Çûô!ãÌ"–,áz5»,;ò!ïî›ôâ/NáIJAAAx9×®aÔ´)Ùáájo5úoQ”žŽIÛ¶4[º‰¦&ßy‡+ ÒzÃe™ö;v`Ñ£ åñ»AA\_±‚Vk×bìéɃèh•ójõ/+bÇ…ïùÅïÄ‹¹ A5ˆ™#‚ ‚ ¿NâîÝíÚ•@77B'MâqãøÅÙ™ì«WòíL/NŸÎA‚ÜݹõT.Œ„;ù}Ø0ÂæÍ#ÐÍ­¼nx8Pð8Þ«¶¶óò"7:Z¥nö•+ÔmÖŒ’œeeè<±…s»;q[¶Ò±#‡7&tRùìyq1çýü8hoÏA{{~>\Y'?1‘“`kË¡&M¸žÞÞØÙñ‹³37¾øBå|ÁÝ»ïÕ‹66\œ>]­>[´ÀyÆ ôííÑkØ»1c¸ù²J»R--4tuÑÐÕEª¥¥<÷Ã4™7K//tLM1ëÔ ©ŽŽZ×òÈÏa?ÒÔÒsWfîõå'>'3?ã¹ÚAx"8"‚ ‚ ü+ièêÒ;4””à`ìFÂiêT’öî ꫯÈOLÄ;,ŒÎûöqãË/É U©ŸþûïÔuw§_d$}.\ Výú”dgszÈ|}‡ýøñü1a ¹œ{!!ØÚr壈]¿ž@77 RR°µ%çÚ5e»‰»wÓ% €þÑѸVì’¸{7ùÉÉô‹Œ¤t4ÎÓ¦)ËË‹‹i4f ý"#éJnt41j&c¼¾bÆ-Zàsû6Þ—.aÖ¹³Êù{!!´ùþ{zŸ?Ͻ#G¸áÂs÷™}õ*uÝÝUŽ…Í›G»;çÆŽ%?!AyüAd$ÅYY¹»s¨qc®.Z„B.Wžw™5 ¬û÷ǪOŸ*ûÛxö¦tœ¥ü}\ÛÉÜ̈¡Õ*güv"4á¬Z÷Fáyˆàˆ ‚ ‚ð¯¤×°!šúú蘘 ïà€^ƒ¥§p78G??´ 1ptÄzà@î©Ô¯mmÝèÑhÕ©ƒ®™wƒ‚0pr¢ÁСH44°9’’œÞ¾¥—}#"зµeà­[X@ûmÛðIHP.³p˜4I9›ÄÀщTJéƒäÅÅ!ÕÔĬKey''¼ù&šúúhb3x°J°¥:©”” SRÐ40À¤m[•óÖ¢× µ¬¬0nÑ‚¼¸¸çê3ëòe’vïÆí£”Ç\æÌ¡ÓÏ?ÓaçN$ ¿B& 4/Ôß~㺇„p÷—_HܵKYײW/t-,0nÞœºÍšUêïøÍde2z:?œ4·nźaÛ¸:?¶¶YðËL:}ã!‚$‚ ¼P"çˆ ‚ ‚ð¯ô(—…DCCùR”•Pœ‘®™™²¬®™y·n©Ô×·µ­ÔfáÝ»äFGs¸ukå1EYÅ™™ïÕ‹²¢"2‡7¦4/”à`œ¦N¥ÉüùÕ¶Û`èP&&êëKQj*ö&ÐtÑ"J<àʇ’ùÇÈKK‘åçS¯U+eݼØXW=jYZÒïúuå9÷Å‹¹¶|9¿½ñRÜ?ù›Aƒ”çuŒ•?Kut(+,T«O€‡·osnÜ8ÚnÞŒ¾òø“3>Z¯[Ç È¥N“&hÔª…ÝèÑèZX`=hé¿ÿŽí[oUº'UÙpf5S:¾‹D"©tÎP×WËf4µòàpÔ!² 2ÕjSA"8"‚ ‚ ¼> tLM•³H <Éè“Á ÊD¡µê×Ç´cG:øûW:70.ްyó0iÝ®}ú)+–ñÔÔ®T[·>ÀíƒÈ‹åxïÞX@]"?ýyI ^¿ÿŽFíÚÜܸ‘´“‘8914+«ÊËÕµ° Õwßp' €‹3gbããUžTSŸ…))œ~óM<–/W™åRéZ55A*U.Ñ·³S lH$å{R“›1\J:϶·UïiúÃ4v]ÞÿÅ-èjÕbBÛ©¬ð-zÚzjµ+‚ ±¬FAAxíXõéÃÍM›åå‘wó&wĪwïëÕ÷öæþÅ‹¤£(+£47—Ä]»”ÿÙW¯R×Ó“¬ðpŒ=<ÔOfh(¹QQ P ÕÑA¡P ©Wþp_òà†NNhÔ®,?ŸÄÝ»Õn7å×_)JM@¢¥UÞf ‘šú,¾ŸÓC†à<}:–³eäÅÅåç22H>p€¢ÔTŠRS¹òÁÔ¶¶ÆÐÉ €ú}ûrÛߟ’œŠRSIÀ¬S'µ®eÙՌm=‰ÚZµUŽÏÜëËÔk|7ôN¿θ6~"0" 'fŽ‚ ‚ ¯&ï½GØûïäé‰F­Z4~÷]L;v¬±ž¶±1víâʇraÚ4¤::˜wéBÃáÃQ”•Q”„½=Ñ«W«ly¤(=‹Ó§S”žŽV:¸ÎŸ¯ÌGÒdÞ<.L™Â½ß~CËÀÓví*-z–¬°0.Ï™ƒ¬ =Ú<±Ýnuªë3õØ1rcb›;—°¹sШ]›Áwî (+#æÛo¹8c êµlIÇÿû?¤ÚÚ8½óãã öð@ª£CÃáÃi8rdãÉ.Èb›s½Ò¹£÷¡­¡­Öu ‚ üY’>}ú(öìÙCJJ ááátéÒss‹W=.á?Êßߟ·ß~ûUCAø›„……aòÄ6¨ÿf™™™ÿÚkÉÌ̤yóæj•-Rs‹Ù)%%…Fýíý ŸÕ'Wp-å [ÞÚUsaA^k¹¹¹º®¡¡¡ZåÒÒR9uê4žžžXXX0xð`±¬FAA„W˨–1óÞXôª‡!˜XV#‚ ‚ Â+5®ß«‚ ÿqbæˆ ‚ ‚ ‚ ÿi"8"‚ ‚ Ÿfñ¡.wÜQ»üÂC³X~䣗8¢ç“™ŸAŸõ±ýĈQ;|^õpá•)ÎÌäxŸ>ØÚrvÔ(ò“’ØomýŠGö÷ÁAAᕉeß¾}ìܹ“ãÇ¿’„¯Âßk`Ó7éݤÿ i+6#ãŒH°ú¨6Ý×´âtÜñçjãÇ ›±«ç@Â'9ü4&à…ŒëŸÄÚ:‚ÀÀÊß§OOÂ×7€#n#‘\F"¹Œ¥åU¦MK¢¨H@f¦LyîÑ«mÛhµû=Þ§gαd ×W¬¨tþÚÒ¥ÄmÝJÞÍ›ëÑ€;²ÇØXå±d 1kÖ°ÇØ˜½¦¦7oÎ/¿DQV¦ÖXŠ328;j¶¶rqáÆ—_ª}Ä®]Ëcc2CC•ÇŠÒÒ85hû,-9Ò¡YaaÏÝnUþïÿöôä@ƒœðö&7&€›ë×Wº?{ML=|øBúÿñGôíìðIH ÃO?©UG^\LؼylÔˆýÖÖ\œ>ý…ŒåUÁAAá•ÈÊÊâÒ¥KtíÚ•aÆ¡¡¡ÁÅ‹_õ°„—¬­mGZÚ´y¡m&/}Hì¢t|܇1zÇ r‹Ôßí"9''³Æ/t<ÿ&ï¿oNiisNœpæÄ‰íüÂú;z•ºÍš‘}å u==¨ß¿?ƒSRhòþûXôèÁà”š~ø¡²ŽI»vøÄÇÓzíZ’öîåÚ²ejõumÙ2Ê éão„„pkÓ&î…„¨=Ö¢ÔT’@SOOåxØÜ¹h1 :šC†ðǸq(d2µÛ­JAr2—fÌ Å×_ã“€IÛ¶\˜:??§¤(_îK–`Þ½;šúú©ÏGò““1trz®:Ÿ|BVX]ð¾|Û·Þz!cyDpDAAx%ÒÒÒ077§^½zhjjâêêJrr2 …âUMxÂÔHæLã‡ó¸z7ŒNßxÐ`‘!+Ž~¢R¶XVŒß®·±_j‚ýR†ÿÐWyîbR(î+li°È°Ò²¹BÎÇAsi´¤í¾veÚîqÌÜë«Ræàµ½´^Õ˜ûù™•ƨ!Õ@_[Ÿ‰í¦‘WœK\f,¥e¥,ùuM?o@ãå–|~t±òï+èzî+lÙþ«O®Ä}…-~»ÞV¶~ç½ÖµÇö#¼Ö¶#:í†JŸî+lÙòÇ::®v§ñrK&ýß[5ö¹;ÜŸþ›ºñΞñ´ùÊ…>ë;ª\Ïœ$FíðÁa©)ÎË-øîôWjçÏ’H$hjJhÜX—~ýêpñb¾òœ®®mmI¥Ÿ_„ׯSÇÕ•¬°0Œ+¶—H¥hèê"ÕÔTþ,Ñ|¼‡ˆD"AS_“víðüüsâ¶lA^ZZc_ùÉÉXôèFíÚÔ¶±¡nóæäF?ž“¸{7‡[·&ÀÎŽ?&L ô©­d¯|ü1®óç#ÑÐP++,$åÈ\fÏF«Nœ§O§8+‹Ì ”e2ÿøƒc={r aC~mÙ’´“'ËÇ“˜ÈÉÊg²4iÂå9s(«˜5Wp÷.zz˜wë†D*¥~¿~Ê™# 4tu•¯¤={°1B­û-/-%bÉ›6åPãÆ\ÿüs¨ø»¼D»;I{ö½z5Aîîœ÷SM’ùé§hЀNȹvMÙf¼¿?­Ö¬ÁÈÝ]ssLÛ·Wk<ÿD"8"‚ ‚ ü#( d2………¯z(ÿy¥e¥ì»º ï ºµ7FµŒéíÒ¹BÎÄ#Õr<·g’_ò’²be½Ýáþ$g'¹0™èï1­Óå¹V Ú± Q­&TêoÿÕŸùõÆ/œ›Éÿ;Äo1¿V*ó 0‡[™1ÈäU3_,+æç°èhêÒ ®-_ÿ”sñ§91ó2çæDDÀµ=ôuõ!bAƒÜ‡3«ë|"$°i„?ÙY ÙÒ ßvï·è>ãÛNaÂÎaÈr•>w‡û0éÑÝãý‹kìà|ÂYfu]Àù÷¢07°dÇ…ï(“—1b[œÍ\¸¶0‰óïEa[¯Ñsç¯HJ*!$$ÝҞˬY88`Ý¿?V}ú(Ÿðöæ@ƒgfr¨qcn}ÿ=áóç:qâsµ_×ÃY~>oß®±¬ƒ¯/i'NPš›Ëø8DFbÞ­gÏråƒh»e bbÐ24äÚҥʺgÎPš“ƒEÏž*mæ'&¢Éз·çÚ²e”dg£okKÞÍ›ܹÃïÃ‡ÓøÝwñ¹}›N»w#ÕÖÊ—¢43†~‘‘ô %7:š˜o¿ ^Ë–Ôqqá^Hòâbî<ˆ¥—W¥kʾz•ü¤$¬¼½Õº_Q_}Eæ¹sô¯‰·JQE{ûŽMÞ/­à:±™1tûìÖ^$ •‚œ’,Ü­oíÉpsfÇMÍ©ÓÌ •æ½L*£¢Z=S*­ð:Ž­œ5åî%ž†Hø9¼îµyóìùøc'öî-àÍ7SQ(š^Ö6kÖ,žxâ‰z× -w嫯¸üùçT£kb¢Ùlu—\ÎÈ+Wèë7Y÷M7!dffMæ=9{6&®®ôþåªòòøsâD íìOžLYZ™aa¤íÝ ¨ûPUSƒJ©$aõj¬1kWO]##TJ%}}†ÇĨc*.Ö Ú”¥¥aܺuƒñTrîÿþœ'PVWSSZŠU@ ž©r~áBٻ󎹺~=a£F18"‰L¨gž¤lÛFï_~©wï=^^Tfgtæ &®® RQžžÎÙ×_GRû﫲ª ³öí›l;‰L†ž¹¹ÖûÊœôZµB¥PÐî•WÐ56ƶo_Ì}|ȉˆÀÄÍ €ˆ3¸¾s']—/§íÓO7«Î– þŸGAAh1^^^xy©EÏÊÊÂÈȨÉ/XƒõÁ°Ïx¶×+¬?¹š!ßöÂ˶OÌ`xÇDZ1±£¨âÖÉ'E…¨ê,íГê±`Ðû,ô>qÙ± ù¦#}ÆÒÙ©K£uÚšÚ‘Uœ¡ùœUœy×q[Y²dä †®êÃøÎ“iehŽ£™§l§£C§¦oP‡“¹ ½]û³ééæ“êHµ>ëèèÜ{­\H/L¥FYSo€¤¹ñ4ÄØXª5à¡P€‰‰öˆ‰D£F™óóÏy|ñEf“³GV¯^͇~Xïú!CؼyóËyÍ™ƒüÉ'9Ëŋ뽾þúë&ãÉ?w‹Ú/þ.h}YW)•(**PÖÎÜPTThþ¢R©¨))!çÄ ¢æÏÇmÆ ÍlŠÆ˜ûø²u+ʪ**srÈ8xPs*‹üÉ'‰_µŠÂKê nËÓÒHݳP±Ÿ—§yÉÌÌèŠÛôéH q<˜Ø+¨).&þ›o™›kz\ÆŒ!ëèQÒ‚ƒQ)”&'“sâU……˜yz"52¢¦´TkŸs__òÏŸ§0&T*’·lAרC''Mžk?ý„|Ò¤&Ÿ».ù¤I\øà*³³Q)•ÆÄÞìòq+W¢¬®&áûï1‘Ë1quE·vãØ¸o¿EY]Mî©SäGGcxW±ýSˆ™#‚ ‚ B‹¹|ù2©©©Èd2ÜÜÜð©Ý@øgIe<îûû>A•¢ ‰Ž„uOþÊܳùìÐø¹ “êiòg•dðÒÖgÈ*Π•¡9ó,Öì¯1gÛ ÂQP–‡D"aKÔ&†´ÁÒQ_1¦ÓΧ%ð oÌÔn’ÛÖƒU’”—xÇ Yoz¾Ï«Lÿi/ôy7{‡%¿¿Ëc+((˧­µ;ï þ¨Éç¶4²bó´`þ/øU^Ø2 }]}ú¹?Æ¿)M–½×:¥)?OÝü=/Óñ¤:Ræô{ó/ÇóÉ'NÌžLÛ¶04”0c†5O>iÙ`Þ€#¼½ X¹2›Ù³­±±9¯I34Œ¤{wc""ÚajjŠ©©i“u7$ÿüy,;w¦,5= ¤ufŠ¥íÝˉgžÑ|Þáèˆ×+¯à»X½ÙmΉìjÛ#ggä“&Ñþõ×›UgÀÊ•D¾ù&{<=‘Èd8áõòËØöí‹÷‚D̘AYj*úÖÖ¸Õ‰¡1þË–qò¹çØíቫ+=×¯× Ö¹¸Ðû—_8¿x1§^x[[ü?û €o¾É©çžãÆÁƒÈLM± ¤8A½ŒmŸ>xϛDZI“¨ÊÏÇX.§ç† ·–뤦’uô(]–/oVŒ7uxã b–,áàcQUP€IÛ¶ø¼óNÓ©‘»ÝÜ0‘Ëéþý÷šÙ"]þ÷?N¿ø"»är lm X±B³¤æa£¤Úºu+éééDEEѯ__ììêor$‡M›6ñÔSO5QAøWˆŒŒÄÚÚº¥Ã¸/rrrÚgÉÉÉÁ¿ö—ܦTÔ7ùwJOOǵSç…E¡ob kÈ‚Aï7Y„•¢fl²{'fÍØƒ 33ƒ°°£øùùaooϘ1cIJAAAZV•¢Šý—ƒQª”ä–æ°÷âñÐÒa ‚ð"–Õ‚ ‚ ‚ТT*ÿ¾ˆç¶<©¾3_¤gÛ¾-–ðˆY²äŽi®S¦hí×!w"GAAAhQúºú„½ÙÒa)ïùó[:á_@,«AAþS’~þ™c'Þ÷û†øú’ußïûO·ÝÞžò´´–ƒÄuëØãáÁ.¹œÜÓ§ÿÒ½JSRØáì|Ÿ"áa GAA„‡Zþ¹slµ²âríI ßÏo=z°ÃÑ‘ßzô u÷îfßïê?âëË''ŽMœHenî_ޱ2;›ã“'³K.goûö\Z¶L“¦R*9÷ÿÇOOv»ºrröljŠ‹ëÝ#îë¯ÙjiINDÄ_ŽçA‰]¾œP¶;8°¯kW’7oÖ¤© ξþ:;]\ØÛ¡I?ý¤I;>y2[--µ^‡Ô¤—¥¦rlâDv8:²ÛÝ«?ü U¯J¡àüÂ…@Q^ΈK—¨ÌÍåà£bÑ©ƒ‘²m×·ogÀáÃèYXplÒ$b–-£Óû·Nj©ÈÈàúΚã<ÿ©lzõÂiØ0ô­­)ºr…c'bîëK«HX³†¬°0†œìlÝš}]º¿zµVzYj*Ç'Of·»;{¼¼ˆ[¹R“1c†f&ËÑqã(KMÕ¤åŸ?¯¹ïíaZ`êáž…FÎÎèèêRœ@êîݸMŸŽ¡£#¶ýúa¨™Ñ#‘É 50 ¦¤„Ì#GhýÄäž9CyF]¾øã6m0nÓ‹N4u>œƒ=†²ªŠ_ßzË›Š._&ïìY\ƌъUj`€ûÌ™´êØñŽmxñ£ØÙº5úô¡àÂ…&ÛütJS~Ñ„ƒ¨Tª&ó ‚ðÏ!GAA„‡RU^q«VÑþµ×šÌWž–†y/ÁYþ‰ûÌ™ŒŠGj`À…:³4´¨T(ÊÊš½ä"÷ôi†FE¸ng_}U3¨à>s&™üAuQ%‰‰^¼ˆ]ÿþ´;–Òë×)¹vêÂBnüþ;Žƒiî™}ìÕØ×YfÒ*• ÿ¥K•˜HŸ-[¸ôé§d‡‡«Ó ŽMœˆ™—Ã/\`ÈÉ“Ëåš²æ>> >~œ‘qq˜yyqê…Ôå”J"fÌ íäÉŒJH ¦´eU•V½W7l`·»;!:¡omÝ#P™§'WøÜS§0õôÔ œÔ•üë¯Øô꥙éQxñ"­Ú·çä¬YìlÓ†Cîœ&ÿ#ÁÁ<¶?==†EG3,: ??Mzä[oáûî»èèèÜUû)ÊÊ@¥bTb"òI“ˆ˜9jôv}„w‚_£ûçíùöØr Ë îT… ÿ bpDAAx(E¿÷žÏ=‡ÌÌìΙT*μò mŸ~³ví4—[y{c?`}}<ž}–ôß~À¦gO²ÃÃÉŠ¢¦¤„¸o¾ååÍŠÉ}Ö,t± ÀªkW2ÀÒÏEe%»äröà6cæ>>::bȾ.]ØÕ¶-ºÆÆ´}úi”ÕÕDÍŸO§>¸ëöq4ëÀ@$2&nnØöí«™ýîå7nÐqÁ¤††è™›ã4|¸¦l»¹sÑ·±AGW×§žÒ”+Ž‹£üÆ ÜgÎD¢§‡×K/Õ«·Íøñ r ÂÀÞc=cžíõ2ÇæFóÍ„ ÄfÆÐíóv¼¾óù{ª_„¿AA:ùQQäEFÒvÊ”Fó_¸šòrü>ùD뺭­æ½¾ •99€zÆD§÷ß'|Ú4B|}Õ³MT*Í|€=^^šMCK®^Õ¾¯Í­ûÚÚjî{rölLär¿~açÏ“²}»fCÒË_|AîéÓ ¿x‘Ç““‘™™qæå—HX½ëÀ@­ÛÝ)žÂK—øsÂB;w&Ä×—¢ª® ,- CGGttØ‚P¥âò石?0__ŽŽ‡ª¦Pïí¡oe¥)g`g·ÍÈaäìŒ|òdrOŸ&eûvtŒ¨))¡ß®]x¾ôÕ%%蚘h•Í;{–²ÔTœ† ÓºŸ¾ m§LAjhˆç /Pšœ¬µÔ§!5%%\üøc|/n4ßHd2ôÌ͵ÞßìÏæp·ö¤£C'¬Œ­‰ÍºtO1‚ð÷² ‚ ‚ ¼¨( cbØVg0"}ß> /_¦ÇÚµÀ­A‡¾;vÔ¨»L¦2;ý:÷q›>·éÓÈ9q;;­ô‘·í¢u߬¬[÷ÍÊB¿woò££ñ^°]cctq<˜ŒÃ‡‘OžLþùó8aèè@› 4ËXò¢¢¸¾s'‰µÏðÇСøö™&Æ;Å1knÓ¦Ñû—_БHŸ2E³†‘“åéé¨jjêµMêÞ½¤lÛF¿]»0°³£0&†Cƒê¤ê¢"MÞê¢"ÍR“†èH$ÄÄÐzÜ8LÜÝ)Š‹Ã~À@½Ìƺ{w­ü×~ú —Q£j®™Èå /‰ibOòôtJ““ ñõÕº¾ÇË«Ñ>¼IY]MUAzææš÷u¿¢R©K<ÄO§×q8þýgÕ„ñuôk´œ -OÌAA:nÓ§3>/Oór ¢ã‚š‘ĵkIÙ¶ž7¢#•¢¨¨ÐZ†Qx颬¬$~õjœ†Õ¤åDDPUP@At4‘o¾©„hŽ„5k¨)-%÷ôirÏœÁ¡v ÀÜLJ”­[QVUQ™“CÆÁƒ˜yz`áëKúo¿Q™“ƒ²²’ë»viÒz¬]«õœ233ú‡†6+¦êÂBÌ}|БH(¹z•̰0MšEçÎ:8pá£P”—S]XHzh¨¦œzVˆJEbcsM=<з±áÆ€zº.-]JÑåËTº{77ÀºöX]—Ñ£¹º~=™™d?Nvx8ΣFiÊ*Ê˹¾còI“´îiÓ³'ŠŠ ®oߎJ¡ qÍŒ[·ÆÈÅ¥Ñç7õôÔj»Î}„mß¾Z#ÊÊJ”••ê÷UUš÷7Å­\‰²ºš„ï¿ÇD.ÇÄÕµÑ:O&‡óöžWhHÔ[‰|9ö{10" 1sDAAø×¹òõ×”&%±·Î¿¾ï½‡×œ9€úׄ5k81}:6½zÑñw4ùâW­âÆHŒh;yr“¾ÖeÕµ+¡~~Hôõéúå—:9°r%‘o¾ÉOO$2ŽAAxÕ.i7w.åì DY]e—.t]±â/·ÿ²eœ}õUô­­1tpÀ¶v ¨7íõóÏDÍ›GpÇŽèH¥š¶i=n7~ÿßûöEÏÊ »~ýn•“H\·Ž3sçré³Ï°ôóC"“iÒ‹{üqªòó1ttÄgñbkžÜgΤèÊöu튮‰ þË–aæå¥)›Œž¹9Ö7ð­%52¢ç† œ}ã N¿òŠæèß»9¹çNövè 9©(¤S'ôml4ƒ'R##v»¹a"—Óýûïë-!º]—nœx-æ/Ç%ÂßO'((HµuëVÒÓÓ‰ŠŠ¢_¿¾ØÙÙ·t\ÂÔ¦M›xê©§Z: Aáo‰µµuK‡q_äää<´Ï’““ƒ¿¿³òVTT<àhêKOOǵ‰_ìA„‡¢:K÷î–Yctב™™AXØQüüü°··g̘1bY ‚ ‚ ‚ ÿmbpDAAA„ÿ418"‚ ‚ ‚ šAAá¡q:%ÿ¥îwUæí½sùpÿ;Mg|È­‹X…ǶÈß5çtJDK‡ó—-?²„—·Í¼«2}¾ìÌ‘„ƒ(¢Ž·>CÛ÷,q{ÿáÜëêŸHœV#‚ ‚ <0 …‚cÇŽ‘••Eyy9ãÆÃ¨ö€ììlÂÃÃ)))ÁÎÎŽÞ½{c``Ђ ÿF£|Æ¡+•5±Æ­Âáøýõ®ÉŒIý ä¾Ôq/J C^'üÕ‹´±lÛbq´´Wy›v÷å^öÿg@•¢²ÞõA톱yZð}©ã^œJ>Áɤã\y'=©^‹Åño#GAA„ÊÎÎOOO~ÿýw­ë …‚°°0:t耛›áááœ:uо}û¶P¤Â¿Uyï¦35Ó/Óö¢P)/6gÇÌø»tC‡Æù}ÐrJ³Q(ÿé€1&Ü·{%¿Wˆ ‘×O1fÍ ’Þ+@ªóבþ+®$#·t#÷™XV#‚ ‚ <0R©”víÚaaaQ/-++‹ššÚ·o¾¾>;väúõë(•ʈTø§P©TŠÛÏ“Fq!ý›#7âóIk¼>´gÿeí_ì¯å&2tU_Ú¾g‰×‡ö,=ô¾&ítJ¾Kä´^dVoYR¥daȸ¾gEàÞ¼°eZ½%»/l£ÛgíÈ-ÍÑ\“Ieè` kPûY]ôuõ5y^Ø2w÷ÍcÌšAxìL§OÛ¢T)IλÆÈïEþ®>râµÏQQ£>{KÔ&F|ן·>C÷ÏÛômo­z¿²îŸ·Gþ®9~KÝøíò^MÚðÕðØWT)*ñ]"Çw‰œ¨Ô3”V•òÒÖ鸿oƒï9ß…¥õŒ?Ÿ]Ï? åÍÝ/Ññc¼>´'*õ /o›ÉðÕàùXD¯ÿùÐçËÎW7Úî§S"˜òãŽ$D¥RÝU_+” þ/ø5ÜÞ·&ð o’ò®jåo¬ ¾=¶ß%rlèÕ[VSPžÏÄõ#h½ÈŒñë‚¶º[¢6iåYúOü0Tëš¾®>ºÈj!nö»¬Î,$ß%rÖžø†ÞË}i÷¡³~yRkŸ/;Óz‘]–y°úø M™{íëÌâ |—È™·{ǯ…á»D®µÄ,)ïjíß—9½—ûrüj˜ÖóÜéïÒ©;Oü0”v:°üÈÚäÈ [¦5ÚwÿFbæˆ ‚ ‚Ð" iÕªeeeÄÇÇÓ¾}{ %%%˜™™µtxÂß,¿,ŸÎüÀ'Wa¬gÂÓÝfânãErÞ5^ßù<»gÆË¶nû»äà»tqéFȳa”T•sã¼&- u¢ç'ñöÞ¹õêÛqþWö]ÚCøk©¨.gÐ× i?B+Oay 9W¨QÖÜõóü|f=[§ï£““?‰9ñè C¥¢’§»ÍbH‡(• &®Ί°¥¼õØ"N&ç‹ÇWáaãÅ´MãÙxê{^íÿ6¯nŸÍ²Ñ_Ôa$i…©–hê ~öi…©tYêNôü$­8>?ü!Éù׈|+‘Ìâ ]Õ_G?­Ù4&þÁˆŽcY6j%…åTÖ.%é.ïż‹¿.ˆ+ïd0ó—I„_ cpûáwl÷ö>ôv}„w‚_£ª¦Šgz<Ç“]¦ÑÊÐ\Sßúz빟8‰×b(®(bð7=Úa”¦\cmð|ï¹<ß{.}¾ì\¯/ÞÛ7]âeIF0µÛ,­<7ŠÒIʽZ¯lsl‰ÚÄ®Y‡°6¶!>û *•’¥#¿¢kë¤ä'1ðëø8v¦g[õ̸{ék;S{¢ç'±%j["7±mÆoZq<»ù)ÛöaûŒýì fÊc87ïf·þ=mèïRÝ~¯‘tŒ˜]D¾•H‡(®,ÆTßôžÚäa$fŽ‚ ‚ -B¡P •J)**"11õ¤WWW·pdÂßmζtÿ¼=‰9q¬™ô G_‰bfà‹Ê 9xeÝÛô¢«KwLõM™ø¢VY‰Ž„ôÂTÒ‹Ò0Õ7möšÐK»˜ÜõìM[º2Ògl½oÇë;Ÿïëý—ƒ™Ôe*¶&v¸Y{Ôë“{mƒ˜]¼Ðç5 t Ü~8ž6íëåùnâ&N½Û¬ûÝnVÏ9XÛhúoP»a¶íƒL*ÃÍÚƒ¾n>оÎ.ÉâtÊ ^é7™TÆpïDZ5±ãĵ£Zùú»„[}íjí¡Ì[;rJ²î©=VbpDAAhR©…BƒƒcÇŽÕLÁ—ÉîÏÆ™ÂÃ#63[::tÂÕJû$šì’,ìÌ4ŸÌµÒ-ÁPfÄc_ÐéÓ¶ìŒÞÒ¬:³Š3±­3èa{ icéZïZay/mN—eø.‘óÉï‹©VÜ 43h¥y/“ʨ¨.×|^û䯄_;ŠÏ'­yô«.e\lVÙ¥Yõž3³8C+¼X¤)’Ú€D"ÕÌ¢iN»»[{ÒÑ¡VÆÖÄf]šÑצ·úÚþ¶¾¾—6Pª”ä”fk pÝï¾–[Õo¿K˜ðÃ0:êŠï9¯ì{ }]’…®D†…‘¥æZC}ÝÐß%¨ûZ*‘jöS‘Öéëÿ 18"‚ ‚ ´ˆV­ZQXX¨)**B*•bbbÒ‘ ·ß_Œà«ñëˆJ=ƒßR7fo~Š?ÿ@¥Rakj§µ|¢ <_«¬½©+ǯ#ö¼´”W¶Ílr¯ [S;²ê|qÌ*μ„úËåí>:°*EÎ=Oôü¤Úå4MÇ ê%B›§í%aQÝå½xÿ··›UÎÆØö¶çÌÀÖÔ®ÉXïäfÛÞ©ÝU*G2ë—'é²Ìƒ‹7γj„<«Þÿâ®úºL»¯ï¥ $:¬m´ ²Kîs_7°Aë¬_žd€W‘o%=?‰þžƒP=À¾¶1±¥FYM~YžæÚ_îëfÆûo!GAA„J¡P P¨O÷P©Tš÷¶¶¶H¥Rbcc©®®&&&$ñŸ¨ÿEþά¿ŽÈ·èìÔ…×v>ÇùôHz %"é˜æËí–Hí4÷]ÚCFñ @ý ¼±¾‰f©@c†vÍÏgדU’IjA ÁwÔ˳+z+þKݵ6Ëü+ + ð´m‡‘̈ҪÒz›‚6fKÔ&JªJ4¿ð7w/ˆ #ù.ü+Š+‹‰Ï¾Âî Ûêí­r/îÔî'“Ãy{Ï+´ $ê­D¾û=¾Ž~ZeïÔ×AíG²çâ6j”5Wzi÷}iƒaÞ£ùöØÿ¨RTq(n?W².×˳0ä Æ®|­Q_ay>Ž‘èH¸š›@XüÁ¦ Õº—ç´1±¥‹Kw¾ û”jE5Á1;É(N'PÞç¯<ÆŠØUAAx ¶mÛFe¥zƒÇíÛ·chhÈøñã‘J¥ôë×'NpöìYìííéÝûþ¹*<œÌ -x¡Ï«<ß{.5ÊdRKF~ɘ5150£½öþ ‘©§ymçs”U•âbцU~Ô¤ÍÙ6ƒ°„C”å!‘Hص‰!íG°tÔWŒé4óig üÂ3GµVo`®¨¢¤¼Äû¶¼àÍÇòÜæ)¼²S}3Ûö!¡vϦl‰Üļ=/ÐÉÑŸ¯Æ­mV¹×{‡·v½„ß§®Ê y¥ßrOpËÚ½‹K7N¼Ó¬{ÜÞ×¾Ž~œOä‘]hehNg§.ZùkƒÀ/¼)­*%£(g7?…¾® Äx¿É,ZÂs¿NÁã}Ûö¥gÛ¾Ht´û:³8ƒëùÉ¥I´,ý5¯îxk[ÌéíÖ¿ÙeﵯWMø‘¹;fãþ¾5Îæ­YÿÔ6­p…Æé©¶nÝJzz:QQQôë×;»û»KškÓ¦M<õÔS-† ‚ð7‰ŒŒÄÚÚº¥Ã¸/rrrÚgÉÉÉÁßß¿Yy+**p4õ¥§§ãêÚð:yA¸_…¾‰®! ½ßtfá¡öèW¼;ôSúº=ÚÒ¡ (**ºç²Í=é,33ƒ°°£øùùaooϘ1cIJAAAþ{ªU쿌R¥$·4‡½wðˆÇ€–Kxsâ5'ÅD¦ž&9ÿZ½Y)‚ –Õ‚ ‚ ‚ðŸ£R©øø÷E<·åiLõ͘ø"=Ûömé°„ ¨¢é?O ¨¢[;¾Ÿô³Öi1‚bpDAA„ÿ }]}Â^Žlé0„¿ŸsW¢ÞJlé0„8±¬FAAø×:4žÖ­£ÑÑ9û·×ݹó%lþšùM›ò2$þFT_ò–-7îo­S6™‡âëËÎÖ­‰Y²¤ÉüÛíí)OKköýÓBB8ôWBlQÕEE„øú²ÇÃ#Ç·t8™AAZ‡££s–O>ÉкêAdd‡‰éí·h×ΰÞõcÇJh×îâßËá  ²#ú½÷šõå®®°Ñ£¹¾}{ƒi—?ûŒ³¯½v?BlÐ…>àÌ+¯4˜–ùÇì •êÕÿWÄ._N¨¿?ÛØ×µ+É›7·tHZBÊÖ­d=Êá!CØáìL°·7?þX«]sOžä·=ØîàÀŸãÇS™sëHãŠÌLœíìïÕ‹¼êù÷K IDATÈ[³pT g_..ìíФŸ~ºc<·÷ucu^ýáB|}Ùáäı‰©ÌÍmðžÕ……ì’Ë)»~ýGeXt4m&LøK÷¹óŽñzá…».—yø0'¦O ØÛ›šÒÒûZ³ÈÌÌÿgŸµHý÷›AAZÁÁ¸¸ÈØ»· ¥Cј0ÁggYK‡ñÐRTTpmãF<Ÿ{®Áô¸U«ðxöYÐÑù›#k›^½èó믌¸t‰€•+‰š?ŸÂK—Z:,ò£¢(MIÁyôhÊÒÒð˜5‹ S§è¹q#×6näZí@†²²’ðiÓp2…—/#ÑÓ#êí·5÷Š|ã ôÌÍKë±c91mªõ‘Ç kÖÆ“'éþí·œ}ã Š®Ô?²øö¾n¬Î‚ 8·p!=ü‘QññH 9W'žº®nØ€Ý#`äârÿï0nÓ§#îº\þùóXvêDyz:²V­Ð56~Ñý÷ˆÁAAá¡RÈâÅŽœ:UJvvM³Ê(•ðÆ©XYÃÛ;†iÓ’˜93Y“~à@;Æ`n~ŽAƒâ¹~½J«¼\~o¾ÉÆ×÷çyòÉk,_ž…\~=½ÈzËjÜÝ/2~|"‰‰•ÈåË/pøp±&½¦FÅÓO_ÃÄ$ŠÞ½cÉÉQ?ËÌ™É<òÈììγhQ:>>1tî|‰âbå=µW]ªšN=÷;œ9¤ù>áûï ñõ%çäI"çÍ#Ä×—c“&ê/×!¾¾\Y¹’”­[ ñõ%Ä×EY'gÏ&bÆ þ:”ýD¿÷^½G†otKò¯¿bîãƒYûöõÒŠÈ;}ym<ÁÞÞõ¿t«Tì’Ë Ÿ2…ÄuëêÝãØ¤Iœ™3GóË{]q_}Ç+75Vgyz:V˜zx ga‘³3:ºº'$4zÏ›®¬X¡^¦àéÉÙ×^CQçèìÒ”v8;síÇ õóc‡ kÖPpñ"úôagëÖ\xÿý;.ïˆûö[ÜgÎD"SÞÉ'MÂeìX ±ìÒ‡AƒÈ;«^Š–¢¢çžCÏÜœv/¿LZp0ʪ*åå¤ïßOûW_EÖª^/½De^9§Nº{7nÓ§cèèˆm¿~Ø’º{w½xnïëÆêÌǦgO,:uBjd„ל9¤…†¢RjÿoA¥P°f žÏ?¯é›£cÇ»|¹æó‘‘#‰ûæBýýùó‰'ØÛ®±Ë—³·}{N5s6Gþùóšv¿ýoZYYÉÉÙ³ÙíæÆn77þ¼möÉÍå( -«IÙº•àŽÙÛ®ç-"ô¶£Öó¢¢°èÜ™¼¨(,ýü4×+²²81m»Ýܨץ iÁÁš´3/¿ÌÙ×_'ìñÇ íÜ™Ó/½¤I;2r$É¿þªù\’˜ÈÎ6mPVVªŸ3*ŠÃƒ³K.çРAÅÆ6«}jJK9ýÒKìvw'Ä×—„ï¾kV¹–"GAA„‡R\\%×®UòÄ–x{Zجr¿þšÇž=\¼èÍÞ½îìÛw«\nn cÇ&òÉ'ÎdeuÂÇÇgžIªwM›r9tÈ“7:±x±sçÚ’”äC‡õò'$tdëV7ÜÜôIJò!)ɇG5Õ¤=Z”)VdgwB__Â÷ßßZJЫ— ›7»²tiþÙaaÅõêhHû¹s1uwÇyÄoû–N› Ô¿ÂëéqmãFÜgÍbXt4ÖÝ»ãÿé§ ‹Ž¦÷/¿`áçǰèh¼^z‰ÖãÇ3,:šaÑÑHŒ4÷Í?ž>[¶0ðÈ2Òú‚PríYYwŒ9~õj<î0k$~Õ*ÚN™¢ù¥Ü²KòÏÓÊSœ˜ˆ®‰ ¶}úUïùçÎá4bù ¤å;‡U×®wŒ­©: õÌ…Ýîî„tꄾµ5v<Òè=2âÊ×_ÓoçN‚Μ¡(6–¸•+µò(+*È={–!§N1"6ûG•Šˆ3OšÄ¨ÄDT*ʪªz÷/¿qƒôýûq›6íŽ1äGGcîã£~¦„LÝÝ)¿qƒ‹Œ©§'ÊÊJÊ®_§49UM &nn\øàªòó1‘Ë)ŽWïS™§'WøÜS§0õôlp€èö¾n¬ÎzT*eeTdfj]Nݳ}¬ºwW_ÐÑ¡Û×_ÿÝw^ºDüêÕèèèÜ<<ŸשSI ahd$éûöQSÜøÿÆTJ%3fÐvòdF%$PSZªÕîÉ[¶Pzý:Ã/^dDll½å3â£érsÀ¦Ž²ÔTμú*k×tö,ùu–+]xÿ}vÉ夅„pü駉˜1ƒë;wìí @ÄŒè[[3âÒ%znÜÈ™¹s)KMÕ”Ï §×¦M >q‚¬£GÉ­Ìr=šÔ={´ÚÐqÈ$úúTåçstìXÜgÎdTb"nÏ<ÉéÓë J5äòçŸSšœÌÐÈHúnßÎ¥eËȉˆh²\Kƒ#‚ ‚ ÂCiïÞzô0ÁÔTÂÀf7opd×®žyÆ®®úŒk¡I;t¨˜¶mõ1¢zz:Ì›gÏ¡CÅ””h˜3ÇõÁ^^õCîV§N† h†¡¡„¡C[wkÆ€««>®®úØØèbn.E.×'3³š/¾økkëz¯É“'kÊ: Œ½=–þþXtê¤U§yÇŽØõïÔЇ›=»¡)NÆ¡kb‚D_çQ£H ÕJC—/¾h°læáèjjp8°^ZUA)Û¶á>k–æšU×®äŸ;‡²ºš=^ºD~í‡emÀ±‰I\»–ŠŒ ”ÕÕØ@e^U\ùòK"fÎДmLcuÞÔfüx9BÀŠÈ'NDfjÚÈÕÒ÷íÃeôhLÜÜ™™á>ki!!ZyTJ%çÏG"“¡#•bâêJQ\e©©xÌžD&»ã ßËã£giÙ`züªU´}ê)@ý«¿®±1Å $oÞŒŽTª¹^SV†ŽD‚ª¦†äÍ›)MJB×ÔT³÷EMYº&&\ß½›ÜS§™˜PSR¢U_C}ÝX6={’N~T5%%ê™::(ÊËë=GÝ{{ü—-ãÄ3Ï»bÝjËÞd,—c,—câêŠÔÐ[[*êìuÒâ¸8ÊoÜPÏÄÑÓëÎL ‰„êÂBŠ‘èêbÛ¯_£÷»éÆXwïŽU÷îèã6c†&ÍgÑ"zmÚ„cP£¯^ÅÄÕ•¡QQ ‰¡4)‰œ“'ñ}ï=$úú´òöÆþÑGÉ8xPSÞ1(]cc¤††˜wìHq¢úç#È ÓôQêž=¸Ô.½J ÁÔÓ“ÖãÇ£#•"Ÿ4‰ª‚J®^mòYÒBCñ˜=™™¦ênû›þ'Gù ‚ ‚ ¥ààB 0`À3žx"‘êj2Yã{QdfVcokO{{))ê_|³²´Ólmu‘H #£ww}ÍuW×[ïï+«[ÿY®¯¯Cyù­Á©TGóRV/Ù5kO<ñD½{Öß ¶!u¿$Kôõµ–püzææ·Þ[XP‘Ýì²q«V©g4°ŸÈµ °{ôQŒœ5׬ˆ~ÿ}òΜÁÀÞžÌ?þ üÆ ¬ºvÅÜLJÒädª‹Š¨ÌÉ!óÈ œ°êÚ‰ËÎÉ?wެãÇ©.*¢ª €ÊìlL½¼±±:o’add„|òdŽOžLÊöí´nât Šìl­,;»z3l$úúØÛk]«ÌÎFßÊ ]õß¾M½öS”—suãFúß6‹ç¦´âW­¢h(==t©)-Åî‘GMUA溲º•R‰D_Ÿá11ÔkfôèQSRB¿]»ˆš?]­:êëÆê4qs£Óûï>m5ÅÅxÍ™Ãõ;µžòΞ¥,5çQ£ê=£Ã ADÍ›‡mïÞ:9i¥éH¥š×ÍÏ7÷O¹“Êœ­v7°³Óz–ÖãÇS’œLÄÌ™Tddà6}:>‹5zOP/1°µÕ|6°³ ÷ôiþ?Ey9:ººì’ËQTTð[÷îôܰAÇÁþý5ekÊÊhÕáÖÆÔ23³[Ï,“i–ôml°ô÷çÆï¿cÕµ+ÅW¯ªg%åiiÅÆò[·nš²*…‚ÊœLÝÝo£ìlíg±µ½oƒ°‚AA:…… Ž/!<¼„O>¹@e¥Š°°bÍ€‰žžú‹ŠB¡P¸ÉÎNFFFµæsfæ­÷¶¶ÚiYY5(•ê2uݨ¸‰Î};`E¥‚Õ«Wóá‡ÖK2d›ïà ): ª;,‘Üñ´˜òŒ ­÷ÖÖͪ¯8>ž¼3gè¹~}½4UM ñkÖ¸v­Öu‹Î)º|™Œ?þÀgáB®nØ@UA> "‘ÉhÕ¡‰ëÖá0p 9§N‘wæ VXvíJîÉ“XrmÃ,üüБ4>¹¾±:¢#‘PÓäàˆÖ‘ŠÌLõ@GÝ{ÕýC®¥ocCen.ªšttuÕ§»ÜÖ7É›7cÑ©fíÚÕ+Ÿuô(QóçÓoÇͲ 77ŠP)•èH$ÇÅ!Ñ×ÇÈÅ•B¡ÞK%.s__”ÕÕ”&'cêá¡.ëîNQ\öê¾µ¾¹Ì…;÷ucu¸MŸŽ[í^19'N``g§ÕF·ï©R×Å>²KrO"ó?°«3ˆÐ :m(ÑÓC¥Ph%ëÛØP]tko¡ê¢¢ze:.X@Ç (Ž‹ãð!8‰EçÎVk`kKîéӚϕµdVŒNJbÏžôݶ”íÛQ)´›;€Ò¤$d&& 9yòž6+v=šÔ½{)OOÇqð`$úê`C''lz÷¦×¦Mw,+ÑÓkp™¾Ößí?ÿ4bY ‚ ‚ðÐùí·"¬¬¤”—ûSQ¡~ÑŠ½{o-­13“âà ãèQí½F6gýú23kHI©bÇŽ|MÚ£šríZ%{÷R]­âÓO3è×O½tç¯rp‘–VMnnó6ŽmʳÏ>Ëŋ뽾þúëûr;»;ž²bhgGÑ•+õ¾0‚z3ÎÒäd*23IÙºÇ!C´Òÿ:T}dìmâV­¢íÔ©Zû—hî¹g†ööXÕùõ@jhˆ±\Îõ;°ëßeM E—/k¾€ZuíJüêÕØõïe—.\ûé'Í «€ׯÇ&0ûþý‰[µªÞ’šÔ]»õ÷×:2¶©:/-]JÑåËTº{·z™Dí€ÌMç.äèØ±Zׇ áú®]”$&R]TDÂ÷ß×k»†˜yzbäìLš5¨ Íò •Šøï¾«·Ô ïÌN>û,ëÖaäâ‚¢¢eµzpÐ&0©¾> ßOMq1W¾ú §aÃèé!54Äqð`bW¬ ¦¸˜øo¾Afn®xr=š«ë×S‘™Iöñãd‡‡kÍæ¸S_7V'¨7l­*(  :šÈ7ßÔ ”€z†Ãßǵ=U²'eûvº~ù%+WrzΪòòšl[M{x{ê”Öß»©‡ú66Ü8p@kCÓ›±]¾ *}}T*U³N•q4ˆœˆòΞEQ^Nb$EYÕEE::j6e½ÉX.Ǭ}{.~ü1Šòr”UUd9BɵkÍzF§áÃÉ<|˜”mÛ4Kjœ†%÷ôiÒCCQ)T‘¼y³Ö`ˆ©»;E±±õÚÔ1(ˆøï¾£¦¸˜âøxRwïnÖßtKƒ#‚ ‚ ÂC'$¤€Q£Ì©û#ÿã[¬}¤ïW_¹0iÒULL¢4{’L˜`Ɉæx{_døð† »ukk]¶lqeþüTllÎsþ|?üжY1y{Ç —_àÒ¥ žzêrù~úéÖ—…¶mõ˜>Ýo9t¨y›ªÞ‰©©)ÎÎÎõ^VVVé¾7y>ÿ<éûö±·}ûz'm8D&co‡{{kN«õ¢?ÇgÏž8‰ÓÈ‘ZeK““Õ³ê¨ÊÏ'eÛ6ÿe  ^¸~=‰ëÖ±ÛÚòrüêœÈâ¿l•99ìöð é×_é¹~½fƆûÌ™Øôî;®]‰˜9ÿeË0«]®ÔX_7UgüªUwè@ؘ1Ø?öí_{íVÚš5´;= ­{Vqêùçéº|9zææØôîóÈ‘œ©qÑmžx‰¾>;Û´áðàÁêf—H\·Ž˜O?åРA'$hÍX©ÈÊâø”)ìlÓ†?†Ç{Þ<ÍÌš«6âëËÙ¹s5'@¬]ÆbäìL×åË Ÿ:•}]»béﯙÍTpá‚fÃÜü¨¨zû®[Gqb¢æÄ£Ø/¿lö3ØÚbîãCqb"vµ±€zù]ŸÍ›¹²r¥úœ€2F§ÎìSÚNJ¨¿?;œ53j:¼þ:F..„øù6f í^y›Þ½›ÓßM'((HµuëVÒÓÓ‰ŠŠ¢_¿¾ØÙÙ7]R€M›6ñTí&P‚ ¿_dd$ÖÍœrÿO—““óÐ>KNNþ·y'÷i_Š»‘žžŽ««ë»ÿ›o¦bh(áý÷›Î,4êäìÙ˜ûøà5gÎ]•‹]¾œ‚ èqÛ²PÏnŸ6açÎiöU´”­[36#‰LÆÑqãp:Tk–EKj¬¯ï•¢¬Œ`ú‡†jaþ-n8@ìòåô¿mcã³¢¢¢¦3ÝY}U“™™AXØQüüü°··g̘1bæˆ ‚ ‚ðßRU¥"8¸¥rrjر#_³O‰Ð2ôÌÍéðæ› ¦UÑå‹/ÄÀH#r""¨¨Ýëåê† Øôê…D&CYY‰u·nÈ'Nláoi¬¯ïUYz:,ø× ŒÜ8pEYªš®mÚÔ¬£ …¿Nü #‚ ‚ ü§¨T°hQO?} 33)/¾hKß¾&M˜†ö‰¸©î¡aE±±„?ý4f^^t]±P/SéðÖ[-Z=õõ½2uwoò䔇IZp0'Ÿ}©¡!v<‚ç‹/¶tHÿ bpDAAøOÑ××!2²CÓ…»Öý»ïZ:„ÿ$×iÓÈ ƒÐ2º®X¡àþ>bY ‚ ‚ðŸ»|9gš³ñe3èÓ‡Ì#GîÛým§S"ð_zw³ÞÞ;—÷¿ó€"úçX± l‘¿kÎ锈–G*bæˆ ‚ ‚ ÜGí_}³ÚS)þ—?ûŒ²ôtº|ñÅßRŸª¦†m¶¶ŒÏËãÐÀø.^|Ç(†|Û‹=^`¼ßä¿%¶;å3]©¬éŒÍ0níÇï¯wÝHfLê%÷¥Ž{¡P*Xò:á¯^¤eóNXá18"‚ ‚ ÷‘˘1-Â?BTêRò’íûDK‡Bùý;>ô—i{Q¨È›³cæü]º¡ƒN%¬œÒlJ…„{$GAA„‡R°·7ŽAAäGGSSRB‡7ÞÐ L(««¹øñǤlÛ†ªºשSñž?ttP)œ_´ˆäÍ›1°µÅª{÷f×Yrí§_|‘ÂË—‘èéá>c†fÃËøo¿%îÛo)ÏÈ Ï–-Z'LTpê¹çÈǺ{wjÊÊp:•6OŽÙ¹‘ö¿C•¢Š)3µò_ËMäÅ­Ïp9ó"zR=f¾À[-ÔKpfü<‘‚²»ž]Ñ[hcéʾ˜ÝT+«Ù<-˜"Vq5÷ÿٻ︪«ÿã¯{/pÙ²7xe‰2Ü{”÷ÈÔ̲ÔÔ†™eY}S3¿eý¾™™–æHÍúº%œ¸s ¢ 2bÈ’){ÜßÈÅëE@Ó̾çùxÜÇã~>çsÆgàÃϹç¼O<±YטÒeAQ{JeÏ<…‘ܨÙÏ <-DÌAAá©UQPÀ3‡ÑkÛ6.Í«ZÎôÚ×_“sæ ŽcЙ3Üý™®.#bcq6œ³gÕÒsΟÇcΟ?žµ5‰›7«ÒL¼½ôûŸukB_S__†FFÒúÍ7q7Ž¡‘‘ ŒD¦¯ßìóÉ:u S†]½Šh(zööTäåqrìX\§McdB.¯¼ÂÙW_EYS€D&£ãòå´™;·ÁUBn¦sðZ SºÌ ­7=ûò¯À¹tùº ?œ^NAi¾Zž¼’\VžüšŽÿqçÓýóéï>WËÖ$ç&òîžYüôâ.΋ãLâ µ|K,¢ƒcgþXp‹‹óâèíR¿ºM'§®DÎObR§W5Ú¸ûò6öGïåÌÜ«üwÊ>Ž\߯qLAi>ñ9שª©jö5­óëÅ,ô_JÔG©ìžz Ê«Ëy©ót®~tƒsïF“ÅŠ_©òœOú9}çsþÝkXÙ²9t­*í]¯±ÈÿK’å8ãN¦õ#DgçàgёɉœŸDäü$|:ðõÑ%$ç%ö~»¦äÿB>ã\Òiµ¶žJ8†/W?ºAè»1Ø›8ÐEуõ/leʼn¯žy #[Îü¡~ýáŸBtŽ‚ ‚ O­–ãÆ ï舩¯/!!$þò žóç#·°@ÇÌ ç)SH  ýàA'¢ke…¡‹ #F4»>‰TJiz:¥ééhaѵk³ò¥áþúëÈtu±4#wwµtÓöí1º§Ä¼sgŠâãUisæ ·´D¢¥…ó‹/’åJ³ÛÛ}ZMž €v‹èZY‘„‘»;NãÆ!‘ÉPLœHE~>·ÿø£6“DB«;ËÆÚùû£kc£QîÚ3+Ýn;VíEmIeHï|¤RÙC¢„§èAAžZe™õ¿b—ed ïß$ôììè¾y3&^š/r++* TÛùêÓ;ŒÜÝ—›Û`}º66tZ¹€Ô€.Ìžã¨QÐH ‰TŠÜ‚²ÌL Z¶j;hš#uß>Rvî¤O@ºÖÖDE2húAR)(•y¥r¹ZGJUQ‘f>Ëök IDATÛd2}zööXöìI-[šÕÆ{m ÛL;{?<¬Ûªö)•JN$„ðË… ;Ä0ÏѬÿ3>v¾~ãa©Øpöþ}èžmíÏäNSééÜ·6vGz¸ª¬üÒ<µúlŒlY9n‘;˜½s*£¼Ç5×ÀÊÈš¬¢ ÕvVÑ£!“j^Ûú„Šê N͹Œ¾¶>k~_Á±¸CÍ*¯“SW¶NÙGeu% ‚ç±øÀ‡l²¯É|–V÷œgVFÖjÇ4ÔÖûQ6ð¬ Â?˜V#‚ ‚ <µâ×­£êömn…†’wù26ýkãM(&NäÊgŸQž²¦†‚¨(²ÏœÀÞߟԽ{QVUQUTDúé6Í‘¾¿*®‰D[-ƒF;FêØJì?PSQAFH…ׯ7«¾Ê‚t­­kG(•$üô“Æ1zÖÖ^¿Ž²ºZm¿‘‹ ·.^ $5•œÐÐfÕi?d·.\ =8eu5•……$oݪŠ9Ò¥RÉ¿¯`VÏwÔöŸO>Ç{ߦSËn„¿ŸÀ·cת:Fêø9tbå¸ „½O{ûÌÝ3“Ëéa h=„sI§É¼ó‚¿=L½Ófô^2Šnµ£- ä†MvŒ i;Š_/m$ëv&©ù)^Ý­qL@äü¾råVqN“å5GAY>îVèkëS\QÌöðæw@mßÂíŠÛȤ2dRY³ƒ¢ú·Ág¾£¨¼ˆ¸ìëüve§*« õÄÈAAá©eÖ±#Á¾¾Huuéøí·èÙÕN¹hûÞ{D-]Ê‘gž¡"?ÃV­ðþ׿p;–¼Ë—9Ü·/Ú-Z`Ú¾}³ëË ãÒܹT•”`àèH—Õ«Ui»u£ª¸˜ÒŒ ÎϘL.Çû“Op7ï… 9“ßÜܰìÖ ËîÝ‘H›þÒé¹ç¸yø0‡{÷FÇÜë>}4ޱ>œäíÛÙ×¶-R--ü/\@¦¯OË H âpïÞ´ðò²{÷f£Ž™½¶n%âã }ýu¤r9Ö}úÐrüø&ó†Ä¤FYÃ3îê£[:8væìܨûäRg¢gÊë½ÞaVÏ9TÕT¡-Óféˆo³nFºÆ´±V –z¹{fRRQŒ£iKVÿY•öÖΩœˆ!¿$©TÊöð- n3œ¯F~ǘv㹜v‰nË<±5¶c ÇP¤÷ܓ²’rÙT’yÏ|ÂÌ­“9r}?FrcºµêE|vó:ʶ‡mჽ³hgçÇwÏ­oV¾wŸù)ï—ÎèiëñvŸèéÜ÷!Ï@þ¹$þþþÊ;vžžNxx8}úôÆÚúÁ £°eË^|ñÅ'Ý Aá/†……Å“nÆ#‘““óÔžKNN~~~Í:¶¬.Æ_(==gg͘žžôزS_ßrý½éߟE‹ÔV»ù'xný`†xŽâÕ®3ŸtSØ‚àyèjéñÑÀÅOº)‚ð?­°°ð¡óß¿©1™™œ8q___lll3fŒ˜V#‚ ‚ Ûí„Õ*3¹aa''?Ј•§AyU9Ý™Ðá¥'Ý”f©¨®ààµ@j”5Ü*ÎaßÕÝôu{öI7K„'DL«AA„Ǭ²°³¯¾Jea!ºVVt]»Vm…š¹–œ÷ŸYð¤›ÑlJ¥’Ï/`æö—0’3­ÛtoõÏÉ#Bó‰ÎAAá©t÷»w¦¾¾ oú@á/#×’sbvØ“n† bZ ‚ ‚ð3dHNN‘H$—þòºÛ·æÈ‘æÏ™ß²%—Áƒãc‹4%oßÎÉçžûKë„§MæÑ£ùø°Çɉ¨¥KŸts„ÇLtŽ‚ ‚ O­"$’K|ñE†Úþà`7ÂÂÚ>‘6}ø¡-zûOŸ¾‡ÇÕ¿´-GýýÉ>}šÈO?}à—»£Fqc×®Ó®ýç?\š;÷Q4±AW>ûŒ‹o¿Ý`Zæ±cìïÔ ”ÊÇVÿŸ³|9Á~~ì²µeÇŽ$oÝú¤›¤æèàÁ¤ìØ@ÖÉ“<˜ÝzzrõóÏÕ®ë­óç9е+»lm95nå9õK—efrbôhvÙÚr°GrÃêGáDEqbôhö8:r sçFÛsï½¾ºd :wf‡™™Æó×X{îVYP@€BAÉÍ¿0 °îߟ¡‘‘ÍZ©Ixú‰ÎAAᩘ££6ûöå?馨ŒoŠƒƒö“nÆS«º¬ŒÄÍ›qŸÙðŠ7±«Wã6cH$q˚DzGzmÛÆðèh:­\IøüùDG?éfNqJ £FP’–†Ûôéø‡†Ò}óf7o&ñ—_¨)/çÌ”)8OžÌðk×êèþᇪ²ÂÞ{FÄÄà4v,g§LAYU»ä±T[Å„ xÞu|Cº×F®®tX¶ Cµc›jÏÝþØ´ ë¾}Ñwt|ð‹$üÏ#‚ ‚ ÂS+(¨€… í -&;»ªYyjjà½÷R17ÀÓ3Š)S’˜6-Y•~èP!^^Q˜˜D0p`7nT¨åW(®ðý÷ÙøøDck{™^H`ùò,Š+èè„iL«qu½Ê¸q $$”£P\A¡¸ÂÑ£Eªôª*%/½”ˆ¡a8={Æ“S{.Ó¦%Ó·ïu¬­/³`A:ÞÞQ´oMQQÍC]¯»)«ª9“Ýõ÷§üÖ-â×®%ÈLJœóç ûà‚||8=q"PûräãÃõ•+IÙ±ƒ ‚||¨.)àük¯qnêTŽ ÂÁn݈üôSLJ ktKò¶m˜x{cܦFZQ|<¹. ¸Óž@OO ¯_¿çÄ”(œ™<™„ 4Ê8=q"ßz‹³¯¾ª‘»jÕ}G¬Ôi¬ÎÒôtÌ;uÂÈÍ SSôhiQßh™u®¯XA{Ýݹ4w.Õw-]œ’Ânþ™`__öº¹¿nùW¯r¨W/ö89qeñbvÙØPš–¦y~?ü€ë´iHµk;ï'â8v,zvv˜uè€íÀä^ªŠ–sîÕee¸Íœ‰Ž‰ ³g“HMEÕ¥¥¤¡‘‘Xté‚ß—_242’žÿý/PXvhd$­ß|§qãÉÐÈHdúúªró._¦×öí 8~œÌÒÕ꽘HYVÖ}Û·f n÷5·z5­&OFËÀ³È‹ˆP;¦(!-CC¬zõ"· ¸yØN^i¹˜wìxß¶5U§žP;rá7WW‚ÚµCnauß¾– ÂõU«è³gþ/RCìÊ•jÇÔ”•qëÒ%‡†2<&›þýA©äÜÔ©(&NddBJ¥R£Ã ôæMÒÄeÊ”û¶!/2oïÚsŠÇÈÕ•Ò›7¹úùç¹»SS^NÉ''£¬ªÂÐÅ…+Ÿ}FE^† Eq;§±{}¯ÆÚs·Ô½{‘[ZbÞ¥K퉄ΫV÷ãDG·f ‰¤¾ópŸ5 ç—_&-(ˆ!aa¤ïßOUQóþÆîçÚ×_“sæ ŽcЙ3ܼ::>øÀ†"nßV©ñÖ[VXZÖ.üغµfgȃj×NŒÑÓ“2dH bcëG 8;Ëqv–ci©…‰‰ …BNff%Ë–-ÃÂÂBã3iÒ$U^ÛAƒÐµ±ÁÌÏÓvíÔê4ñòº_?dzzØÐìÑ M±:-CC¤r9#G’¬–>,*ŠË–5˜7óèQ”UUØ ‘V‘ŸOÊθNŸ®ÚgÞ±#yÔTV²×Í‚èhòîtp˜ÝI8=a ë×S–‘AMe%6Ï>Kyn.ùù\ÿö[ÎM› ÊÛ˜Æê¬ÓrÜ8?N§+PL˜€¶‘Q#%ÖJß¿ÇQ£0tqAÛØ×éÓI R;FYSƒ×üùHµµ‘Èd:;SKIj*n¯½†T[›Ö÷õ¿v-Ž£G£cfÖ`zÜêÕ´zñEªŠ‹Ñ20 (>žä­[‘ÈdªýU%%H¤R”UU$oÝJqRZFFT7yžu»× i¬=÷žÇݺ66øýßÿqö•WˆY±‚Î߯6-Ë@¡À@¡ÀÐÙ™žºVV”Ý'žIs%þò žóç#·°@ÇÌ ç)ST ©”.kÖýÿǹiÓè°lr ‹ÚŒJ%Iÿý/>Ÿ~ŠÜÒ™®.N"xò_B,å+‚ ‚ <• xöYcž}Ö˜çŸO ²R‰¶vã±(23+±±©²oc£MJJí/íYYêiVVZH¥‘Q‰««\µßÙ¹þû£`n^ÿßr¹\Bii}gŒL&Q}j·k§áLŸ>çïš_GOO3lCî~I–ÊåjS8þ “ú簾”eg7;oìêÕµ# ˆ'’¸iÖýû£ïà ÚgÞ©‘‹“{ñ"º66d;FéÍ›˜w숉·7ÅÉÉTRž“CæñãèÙÛcÞ±#©³öíÉ‹ˆ ë÷ß©,,¤"?ŸòìlŒZ·n´ÕYG¦¯¾¾>ŠI“ø}Ò$Rvíjò·,;[­K×ÚZc„T.GׯFm_yv6rss$ZµÏÜÒRãúU—–òÇæÍô»gO´  âV¯¦_p0R´ ¨*.ƺo_†FFR‘Ÿ¯Ú_SY‰²¦©\®ZR»ª¨H5¢§9»× i¬=ur/]¢$5‡‘#5òÛHø`Õ³'z÷L÷‘ÈdªOÝv]ü”¦ìmÝšò;ϸÿÅ‹:;ƒRIiz:—Þ}éûRSQ¡6}HßÑó.]Ƚx›gžQí¯ÈË£º´'§ë+Šå@×®èÙÚ>UKšÿ݉ÎAAá©SPPÍï¿ßæÌ™Û|ñÅMÊË•œ8Q¤ê0ÑÑ©}骮®íP¨cm­MFF¥j;3³þ»••zZVV55µyîV×Qñ ¤RÉ#[`E©„5kÖ°dÉ´Áƒ³õ¬"‘JQÞ¯ÁRé}W‹)ÍÈPû®[÷‹xŠââȽx‘î7j¤)«ªˆ[·Žnë׫í7mßžÂk×È8v ïO>áM›¨ÈÏÇû“OjkÓ¢m[6lÀvÀrBCɽxóN0ëØ‘[çÏ`Ñ­‰›6aêë‹DÚøàúÆêlˆD*%?*ªÉÎ]KKÊ23UÛe™™µw—u÷ƒ|‡ÜÒ’ò[·PVU!ÑÒª]Áåž{“¼u+¦íÚaìá¡‘?ëäIÂçϧÏîݪiA†..ÅÇ£¬©A"•R‹T.GßÑeuum,•ØXL||¨©¬¤89#7·FϱNc÷ú~kO{cªÜíê¿ÿY‡Ü %óØ1¬ûõk¼Â»®¡TGeuuƒ‡¸7þ €D‚žÝ7oÆÄË«Á|‡“å &^^D-]Š×¿þÔv(Êôô(NIQMqº›‘»;ãrso»ðPÄ´AAá©sà@!ææ2JKý(+«ý Þ‚}ûê§Ö˰µÕæäIõØ£F™°qc™™U¤¤T°{wž*­#ËÙ·¯€ÊJ%_~™AŸ>µSwþ,[[mÒÒ*¹u«y¿H7eÆŒ\½zUã³jÕªGR¾®µõ}WYѳ¶¦ðúõ_SûâädÊ23IÙ±»ÁƒÕÒ R»dì=bW¯¦ÕË/«Å/Q•¹w/z66˜ß³,¬LO…‚»wcݯ5UU^»†iûö@호5k°î׳HüåÕóNHظËnݰéרի5¦Ô¤ìç§ VÛœ:£¿úŠÂkרÈÏ'õ·ß¸yèw:dê\þäNŽ«¶Ïnð`np;!ÊÂBâ×®Õ¸v 1vwGßÁøuëPVW«¦Ç¨(•Äýø£ÆT€Ü‹9?cÝ6l@ßÑ‘ê²2j*k;-»uC&—¿v-UEE\ÿî;ì‡Eª£ƒLO»AƒˆY±‚ª¢"â¾ÿmUÇJ¥ª,å]ßë4v¯k*+kG1)•ÔTU©¾7Ö€Ò´4n>Œs1U²ÿ”]»èøí·tZ¹’ o½EÅt0»¹q+4ô¾$ QLœÈ•Ï>£<;eM QQdŸ9@yNçÌ¡óªUt\±‚Ä_~!çܹڌ Љ‰\¸òìljÊËﻤ¶ðh‰ÎAAá©”ÏÈ‘&Üý#ÿèѦª/éûÝwŽLœø††áª˜$ãÇ›1|¸ žžW6,ž¡Cë˱°ÐbûvgæÏOÅÒò2—/—ðÓO­šÕ&OÏ(Š+DG—ñ⋉(Wøå—ú°V­txõU <=£ppˆ$$äÏ|422ÂÁÁAãcnnþ§Ê­ã>kéû÷³¯MN¯–f?|8RmmöµmK §§jµ;NÇÁîÝq1û#Ôò''׎n¸KE^);wâv'öǽbøá¾Kûšwê„‘«+Rmm,:wÆ@¡@¦««J«.-ŬClú÷§,+ 3?¿Ú´Ž)ËÌĺ_?,ºv¥²¨¨þåþŽÊÂBŠ“’4¦X4VgQ|<'Ff_ëÖD.Z„÷Â…ØÝP³,3S#¨Í³Ïâ>kÇGŽ$ØÏCÿ}zcmmÓtNAx ¶lÙ‹w‚@ ‚ ÿ|aaaX4sÈýß]NNÎS{.999øÝyalJÙ#ŠKñ ÒÓÓqvv~låÏ›—Šžž”Å‹íš>XhÔù×^ÃÄÛ›Öo½õ@ùb–/'ÿʺÞ3mjG7œ™2…¡ª¸ÂýU³Çɉ±Hµµ9ùÜsØ‚Ëßd9ØÆîõê.)!ÐÛ›~ÁÁ73Føû+,,lú û066nÖq™™œ8q___lll3fŒ9"‚ ‚ üo©¨PX@M ääT±{wž*N‰ðd蘘ÐvÞ¼Ó* é°l™èiDιs”݉õòǦMXöèT[›šòr,:wF1aÂna½ÆîõÃ*IOÇë£DLjð§ˆaAA„ÿ)J%,XÆK/%bl,ã7¬èÝÛðI7ëZCq"êX÷ïÿ×5ä)UÙ—^À¸uk:®XÔNiûþûO²i»×ËÈÕ#W×G^®ð¿EtŽ‚ ‚ ÿSär aamŸt3þ‘ºüøã“nÂÿ$ç)SK§ƒ ü/ÓjAAáÀsë³=|K³/(ÍÇl¾„ªšG³ºÎßUNq6þ?ôD±È„I›G=éæ<6ë’VÚìモðÿ¡çclÑߣοÒìÓX~|iÓ6áBÊ9ü¾£bþ ¢sDAAøÇ)ˆŠâÄèÑìqtäÀ=Ë¿Þíôĉì|À@ºÕ¥¥ìkÓ¨]Y#ãða”UUì03 dÀ²OŸ¾oƒèÁŽð_¨Þ¦”æ£Xd¼äGRžžŽ>_Žø™Dö§ËZ~|)fó% ~ÂS/>‚Ö>¼ŸC×ÑÊÜ•¤EùüòRÀmË“âeÛŽ×{Í}då9|bˆÙ| -¶`Ü®¤G<ö:›r.é4ÿ㡱ßl¾„¡kú¨¶Ï{ çò?G—0wOÃ+) OÑ9"‚ ‚ üãHµµQL˜€ç‡Þ÷˜›‡QžýÀeç_¹‚‰·7yáá˜úú>PþðÔ‹¤ä&1Êçù®»1›B×Ò×uަ-Iy:2¦w‰Dò§Ëz«÷<Ò—”’¾¤”¾®øhàgªíööAkÞüdÜ­4_šÿ—´4kÅp¯1´Ì=ÓŽðû;WèàØ…á?ö%£èæc¯óaåÜÎ"ëv&ÇãŽ`n`ù„[$< ¢sDAAxjýñÓOèÜ™=NN4ˆÒ´4ŒÜÝi9~<úöö æ«.+ãê’%'¥iiì²±áú·ßr¸woöwè@îEõ_ó""0m׎Šü|”ÕÕÈpäɧ¿aZ÷7ЖiàÿCO¶†m¦¬ªée’£3®ò^Àëüt~úùÔT³îìJfõzGµ/·ä6Ãi17Ð(ùñ/iûo{‹Lé³Â”¼$UÚÔ_'àý…SƒÓj®Þ¼L¯oÛã´À˜Å>lp ǰ5}Yzx‘j[&•¡«¥‹®–.R‰-©–j»®óå×Kyþ§!ÌûíM¼>w¤õÕ¯÷S€ûgÖ8jÎs듚Ÿ¢*Ûï+W¾ YÌÀUÝðúÜ‘ çV«ÒÊ«Êym닸,¶Àe±ãªJ Š Àg©‚á¿°üø—ø,UðÚÖUéGãÑý/‹L³n ©ù7ÔÎÑg©‚õg¿§çr<–Ø2ý¿/VŠÛgV XÕ•žË}X|à#\[ªÝ¯û]÷?ó\N SÝ“»¯{S× î<Ü>³jpŠËŽð_ðúÜ%¶,ž§1½#5?…ÎÿñàPL°F^™T†ƒ‰ó,¢µU[¶^ÚÔd5Ê> zçOÍé¶Ì“×·OaöÎiªôðÔ‹ ú¾;ŠE& \Õ˜ÌhUZâ­†¬îM«OÍh½Ä†¯B«Òü¾reÊ–q$æ&à³TÏR'ŽªÒ‡y!ðên.Ý¥­7:2&ë O½ˆÏR+O~ÍŽð_Tå–T–¨ò¦¤2èûî8.0âÍêK)¯8ñø,m‰ûgÖÌÝ3Sí¾o ÛŒ÷N´^bÃÁk×Vxv¾\ýè¡ïÆ`o‷/¿¿s…ØO2imÝ–×·O¹§¾½q–S°(ø}*ª+ؾ…yI\ýð1ßT›Æ1Ôs‘ó“í3ž9}? r~?N¨É’[r‹—Ë‚Á_ûImm½ysÇ+íݾ…€é!Äüë&ï?»€¢²BvM=D =Sj”Õl}%Í¡kUí¼ßuØç FYÃÔ_'0©ã+Ä/Ì¡¸â¶Ú=iìDÎObùÍ@º©ù)¼³{ë'mãÒûñ„ݸ qLEuñ9×)*/lànÖkgßÈôð&ëÜ}yû£÷rfîUþ;eG®ïW¥å•ä2vý ¦u{ƒ„·x¥ëL^ýõyj”5,=²ˆŽùcÁ-.΋£·Ký*GaïdzñÅ´2s!r~‘ó“ÔÒGxeßÕÝDng¤÷sͪÓס#‘ó“x³÷»Œó¤*Wÿ®¿£C1A¬ø+çß‹á`L ¡Ég‰=ȪS_³gÚ.΋%&3Š•'ÿ@rn"ïî™ÅO/îàâ¼8µ¿[áñ#‚ ‚ ÂS)ñ×_qŸ5 //$2–Ý»cвé)%·¹ñÛo¸ÏšußcÇŽÀ¢sgŠâã°8¡‘‘*ŒŒÇaĺoÜȨ¤¤Úi6 ­î,§jçï®F¹kϬdt»ñ˜é›«öô~ù¬k IDATŽ]SrôÍ Èµtñc?žÿi™E¬;»Šö_¶bgįÌî3ÈùÉükÐlíÔÊ]}z9³zÎQÛw0&=ÞFWK—¡ž£pµh­J“H$TVWr=+šÊšJ¼íÚci¨ÙQt¯ØìRóSx­Ç[hË´ï3"ê£T–^Ý`ZcLœ˜Üi*-ôL°2´`Nß°4´BKªÅ‹¦rå¦z ‹çÚ¿@kO$ ™…µS8¤)eù$äÄ¢%Õ¢ë3ÍjljøZšµbp›áèÈtx»ÏœLávÅmµã¦w ‹;S0Ü,k¯¯…%ƺÆ(Ìœq±pÇÉTAvQí”Æ®ûÃ>±Y1Ü,LcZ·7Бéðfï÷ÔÚø°×àPL]=èÒ²;:LíöºÆ1Îæ®ä.U2¶Ý„FË2”RXZÐdÁÑLêø 6F¶(Ìœá=V•€»•ã|'!“ʘØáeòKòøãV¼ê<Ó RI/LÃHnDWEóƒ½ÚÛQQ]Áþè½ôsØì:›2Òû9œLØÛÓÁ± 9±ìþQ>Ïãbᆱn ¦w‹ ¨Úx7G®ï§KËtt삑܈©ÝÞhöyŽXÊWAAx*•¤¥¡ïäôÀù">ü¯?D*—7˜.ÕÑA¦« €DK‹ê²Úá¸P]V†²ªŠ}T‘Œû¬Y´ýàƒ&ë-­,esèZg4üK°±^víp»áÁõ¬k”U–’œ›ˆR©ÄÛ¶=m¬½I5ƒ£^ºJj~ #½Ç©öÕ(k¸Uœƒ‘­ZùufÎ|3f _ù”—ÒÇÐß}+ÇmÀX·E£ç};s ´¤µ¯–†VH$î÷V…™³Æ>¥RɲcŸ³3âWŠ+Š©®©¢ªºR혻ۭ%Ó¦´ª€q¾“HÎMdÚ¯É(ºÉ«]g±`ðçM¶#çvV†õ[uç™U”¡yýˆ …¹f{¥wî‘L"C&•!“ÈTS“šsÝô9È)ÎR»'ÖF6j÷äa¯AVQ&VFõ×ÀÚH³³¯¹n—߯X¯ñgKU§{}=VF6¤æÕN¡JË¿ALf”ZPÕje59·³pµpg¡ÿR–ø˜g¾ë„\[—Eþ_2úâúLî4•˜Ì(tµtUûšª³)ww‚ʵä”VÖ>—Ù·³hgï§J³6²!«(C•fm\ÿw{oG¨ðøˆÎAAᩤooOIJJÓÞ#7<œ›‡©íÛafÆàsçÐ20¸o¾‘ „Í›‡Eçιºråßÿ¦÷ÎÍ®wkØfÚÙûáaÝVmÿµÌ(¶\XÏΈ_ioß׺¿Å!H%R>úfôx›ç×0ø‡´¶jË‹¦2Ìk´ê%î‡Óß0­[} ¨ýÝÂÀ’‚²|Õ¾üÒ<µzÇûMf¼ßdòJryaÓ6‡®ãÍÞï6z–†ÖÜ*Ρª¦ -©9ÅÙ(ïLkxêüÙµ›¿0=k#¢nF2èûî äÖ¤#Óᣋùhàbb³cü}Fxm2¬…¡•Ú´ ìÛY(•5XÞÉ¢jo3WòQ¢T}¿ßuØçÀÒК²úQ…ej÷äa¯•‘5RΪ¶³îŒ~y‘éa òÖäqVFÖªN‚{ë´7q¤§s?¶¼´§Á¼6F¶¬·€€ÈÌÞ9•QÞãT1m$©Ú}¸×¿—4ö5U'Ôþ­)•÷/·!–†VdÞuž™EªgËÊÈZm Ò½·Âã#¦Õ‚ ‚ O%Åĉ\ÿþ{ ¢¢PÖÔsîÅÉw–±U*©.+£¦²å]ßF\¿Î¸Ü\Æåæ2àÄ $R)ãrs1roú—à¼Ë—1õõ%7<³öí›ÝV¥RÉ¿¯`VÏw4ÒÆ¬€ŽL‡ÃoœcÛ+A j3 é]¿ü;˜8ò¯AKˆœŸÌË]^cãù5¬;³ ¨ øx8&X-†Iÿ¶#Øñ_¢nF•QC$%/‰“ G©¬®D[K‰D‚¡Ü¨Éóp·ôÀÁĉugWQ]SÍêÓß6xÜÕ½øüЂ&ËkŽ‚Ò|¬l±6²A©Tj£m̹¤ß¹–…R©D.“£D‰Ža“ùz»ô'97‘×öQY]É·'¾¤{«>5ã5¦±ëþ°Ï›ek, ­UAQ·…ýüH®Á@¡œK:Í¥¡”V–²±ëžšŸ‚ßW®Š ÒH«®©&5?…//$&3Љ§4Yç¶£øõÒF²ng’šŸBàÕÝw¥äBÊY‚££º¦šÂ²B¶†mVÅÙ½Wµ"޶L¹¡ÚJK6F¶Ü,H#·äV“íhnÖF¶\ÏŠ¦º¦ºÙån3‚€Èí$äÄQXVÀÚ3ß©â h=„sI§U'Ûö4»\áÏ#GAA„§’ãèÑTäærfÊÊ22hѶ-]ׯ àÚ5õ¬9°ÛÎÇÑ£UéCY]MIJ F..Ä,_ŽÝàÁÍÎ{e ϸÒH»úoJ+K(*+Äg© K޾u'–ù‘—‹%ãý&«­µY“r(*/ÒhçèuÏb¨cDç–ÝÙ÷ÚqÕ¯ÆêÓn<—Ó.Ñm™'¶Æv ôŠTZûl™é›³uJ ¾ÃëÛ§ ×’ÓÇõÆûN ,õs÷̤¤¢GÓ–¬¯ÞIÔÒ¬“:½J·ežhIµøþùMMÆ_iªN€á^cÙ¾…¶ŸÛ£%Õâ¼Xµ ¬ y¶õ`fõœÃÈûSVU†ÛÌîû>ަ-Y:â[Ƭ€‘®1m¬½-Kxt$þþþÊ;vžžNxx8}úôÆÚúáç“ ÂŸ±eË^|ñŦAþ°xÀePÿ®rrržÚsÉÉÉÁÏϯé²²¦—}ÔÒÓÓqvÖŒíð4yný`†xŽâÕ®3Y™%•%xáDðÌS´¶jóÈÊ}ÅÅ8-4&cI™Ú´áŸçPLËIðÌ“Y ‚ç¡«¥ÇG7}°ðQXØøêG166nÖq™™œ8q___lll3fŒ˜V#‚ ‚ SyU9Ý™ÐA3¦ÁŸ‘^ÊGÿå#ç’N«¦/l:ÿ#=Zõ#ÿP‡b‚(©,¡ª¦Š-6Ð×­ù£vFEu¯ª ﻺû±×)uÄ´AAAxŒäZrÞæÑÄ߸›«…{³VÌxÔb2£yéçÚ%V[[·eÅØuy„¿FàÕ=ÌØ6=-=úº à^ìý³”J%Ÿ^ÀÌí/a$7fZ·7èÞª÷c­SêˆÎAAAšmJ—טÒåµ'Ý á/°â¹u¬xî¯ëü’kÉ91;ì/«Oî&¦Õ‚ ‚ ÿ8C†Äáä‰Dré/¯»}ûhŽiþœù-[r<8î1¶HSòöíœ|´NAxÚd=J{œœˆZºôI7§IÁ~~ܺpáI7ã©%:GAA„§VHHÉ%¾ø"Cmp°aamŸH›>üÐ=ý§O߯Ããê_Ú–£þþdŸ>Mä§Ÿ>ðË݉Q£¸±kWƒi×þó.Íû(šØ +Ÿ}ÆÅ·ßn0-óØ1öwêJåc«ÿψY¾œ`??vÙÚ²¿cG’·n}ÒMRstð`Rvì ëäIŽÌn==¹úùçj×õÖùóèÚ•]¶¶œ7ŽòœUZYf&'Ff—­-{ô 7¬~ÄGAT'Ff£#:wn´=÷ÞëÆê¬»j;ÌÌÈ9w®Á2+ P((¹q£yå>¬û÷ghd$-ÇÿSå<ŒÌ£G9ûê«zzRU\üØêjì9¨.+c‡™™Ú'qófUÞšòrÂæÍã7ggv;8páÍ7ÕÊþ3ÿýÕDçˆ ‚ ‚ðÔ ÌÇÑQ›}ûòŸtSTÆ7ÅÁA(}XÕee$nÞŒû̆Wö‰]½·3@"ù‹[Ö<–=zÐkÛ6†GGÓiåJÂçϧ :úI7 €¼ðpŠSRp5 €’´4ܦOÇ?4”î›7“¸y3‰¿üÔ¾ôž™2çÉ“~íRÂ?üPUVØ{ï¡cbˆ˜œÆŽåì”)(«ªjk£˜0Ï»ŽoȽ÷º©:Ê22¸±gZ÷-÷M›°îÛ}GÇ¿Hy—/cÖ®¥ééh·hÑèùþY=uFÆÇ3&=1éé(&MRí\´ˆÜ°0ú0äÒ%/¼ðØÚù¸‰ÎAAá©TÀÂ…v„†“]Õ¬<55ðÞ{©˜›GàéÅ”)IL›–¬J?t¨/¯(LL"80Ž7*Ôò+Wøþûl||¢±µ½Ì /$°|y ÅttÂ4¦Õ¸º^eܸÊQ(® P\áèÑ"UzU•’—^JÄÐ0œž=cÈÉ©=—iÓ’éÛ÷:ÖÖ—Y° oï(Ú·¦¨¨æ¡®×Ý”UU„ΜÉnŽúûS~ëñk×äãCÎùó„}ðA>>œž8¨}¹òñáúÊ•¤ìØAA>>T—”pþµ×87u*dž á`·nD~ú©ÆãÆ5ú rò¶m˜x{cÜFsž¢øxr/\@q§=žž^¿~ω) P(83y2 6h”qzâD.¾õ–êWù»Å®Zuß+u«³4=óN0rsCÇÔ}$ZZÅÇ7Zfë+VäãÃ^ww.ÍKõ]Kg§¤°ÛÁÄŸ&Ø×—½nnį«’õ*‡zõb“W/f— ¥iišç÷øN›†T»¶óN1q"ŽcÇ¢gg‡Y‡ØHî¥Ú©h9çÎQ]V†Û̙蘘à1{6iÔTTP]ZJúÁƒ´yç´[´ õ›oRž›KNh(Fîî´?}{ûFÏ÷Þ{ÝXu">ùÏ>@"“5X¦²ºšøuëpŸ5ëÎ%'ÇŽ%fùrÕöñ#ˆýþ{ v*Ê©çŸgŸ‡1Ë—³¯MB_½Ñv×)ËÊâì”)üæâÂþH T¥]œ=›Kï¾Ë‰Ñ£ nß^cDEIj*¿OšÄo®®ìmݚؕ+ÕÒsÃÃ1mßžÜðpÌ|}Uû3æ@—.(ûú’~à€Z¾ìÓ§ öõ% U+µç?ãðaÕ3²¿CâÖ¬Q¥5öÔ‘êè ÓÕE¦««ºö5••$nÙB§ï¾ÃÄÇ]kk,»woÖµû;#‚ ‚ ÂS)6¶œÄÄržÞ OO=‚ƒ š•oÛ¶\öîÍçêUOöíseÿþú|·nU1vl_|á@VV;¼½õxå•$2¶l¹EHˆ;7o¶cáB[æÌ±")É›¶mu5Ž÷bÇ\\ä$%y“”äMÿþFªô“'o3y²9ÙÙíË¥¬][?• GC¶nu櫯28uÊ[[mNœ(Ò¨£!mæÌÁÈÕ‡áñó÷WKË>s†–ãÇ32.™ŽŽj¨¼ëôé ŒÄ¢Kü¾ü’¡‘‘ôüï0õõehd$­ß|§qãÉÐÈHdúúªró._¦×öí 8~œÌµF€Û‰‰”eeÝ·ÍqkÖàvŸQ#q«WÓjòdÕ¯èf:¡vLQBZ††XõêEnx¸FyØN^i¹˜wìxß¶5U§žP;rá7WW‚ÚµCnauß¾– ÂõU«è³gþ/R£ñÂ\SVÆ­K—Êð˜lú÷¥’sS§¢˜8‘‘ (•JµÎ„:¥7o’~ð .S¦Ü· y‘‘˜x{מS|kÎ/¿LZPCÂÂHß¿Ÿª¢¦ÿÆÎMŠÜ‚áÑÑtß¼™‹sæP’šªJÏ9s†[¶0èìY²NžäÖÎ#eu5§'LÀ¸uk†]¹Âàóç1P(¸²x1 iAAüþÒKœ›:•{öèé ÀÅwÞÁgÑ"F%%Ñ/0''µ6åœ?ÏÀS§è¶a‘ ªž¥R‰ßW_12!^Û·ýå—dŸ9ÓàyÝýÔ9Ü·/û;u"âéº}€â¤$”J%éÁÁ´jÅþHÞ¾]-_cÿý݈ÎAAá©´o_>]»bd$eÀc›×9Ï+¯X`k«³³œ±cMUi!!E´j%gøðèèHøàBBЏ}[}¤Æ[oYaiY»ðcëÖš!ª];= 0FOOÊ!-ˆ­1àì,ÇÙYŽ¥¥&&2 9™™•,[¶ Ϥ»†¼Û„® f~~˜¶k§V§‰—Öýú!ÓÓÃvÀ€fnhŠýСh"•Ëq9’´à`µôaQQtX¶¬Á¼™G¢¬ªÂ¶—ߊü|RvîÄuútÕ>óŽÉ‹ˆ ¦²’½nnDG“w§ƒÃìNÀé HX¿ž²Œ j*+±yöYÊss©ÈÏçú·ßrnÚ4UÞÆ4Vg–ãÆ1àøq:­Xb´Œ)±Vúþý8Ž…¡‹ ÚÆÆ¸NŸNZPÚ1Êš¼æÏGª­D&ÃÐÙ™ÂØXJRSq{í5¤ÚÚ´¾Ï¨‡øµkq=3³ÓãV¯ Õ‹/PU\Œ–Eññ$oݪ-PU\LUI ©eUÉ[·Rœ”„–‘ÑÅÅhè^7VgMe%áóçÓî³Ï-7nõjµŽ]üþïÿ8ûÊ+ĬXAçï¿W›–e P` P`èìŒLO]++ʈur·â¤$rΟÇçÓO‘Êå´ðôĦ2ŽQcçï–2==L¼¼(JHjŸ³Ò›7ñúè#dzz蘘`?lÞ ÐcËìüýõÇ:;3$<œaQQH¤RŠââ¨,,DÏÞžmÕc+¹¼ú*Z††X÷ëÔÆ†°8‹nÝjkcèâ‚UïÞä_¹Òàõƒúç@ª¥E×uëèµm¿ù†¬S§ˆøøc*‹Š¨)/§8%…aW¯Òá›o¸8{6·ïœ'4þoÐßèAAžJ<û¬1Ï>kÌÁƒTV6¤33³›ú˜ wÏÊRO³²ÒB*…ŒŒJµ2œå¶ùjÌ͵Tßår ¥¥õ12™Dõ©Ý®†3}út"""4>+V¬hVw¿$Kårµ)†Ž‰IýwSSʲ³›7võêÚ‘ ÄIÜ´ ëþýÑwpPí3ïԉ܈r/^Dׯ†ÌcÇT&ÞÞ''SYXHyN™Ç«F†H¤RÌÚ·'/"‚¬ß§$5•Šü|ʳ³1jݺÑ66Vg™¾>ú(&MâÖ… ¤Ü'°íÝʲ³Ñµ¶VmëZ[kŒ°‘ÊåèÚØ¨í+ÏÎFnnŽD«ö’[Zj\¿êÒRþh$ŽKZPq«WÓó×_‘êè e`@Uq1Ö}û242eMj¿–¾>Êš¤r9â¢0ïÒ…ª¢¢Š‹Ñнn¬Îø5k°èÖ cû–™{é%©©8Œ©‘f;p UÅÅXõì‰Þ=Ó}$2™êS·]?å~JîL[:Ò¯:wæ@çÎdŸ9CE~}ü#mcãú:´µ©.-UåÕ³³Sݳ:·.\ @¡à䨱d;FÀÑ8ºt!ëÄ º®_Oö™3z{s¤x6:¦õ½RUÑÑœ?žàöí òñáæ‘#(+Õÿ]kè9hiá8f †..XöìI»Å‹U£Á´ôôPVWãñöÛh`Õ»7&ÞÞ÷ ”ûw':GAA„§NAA5¿ÿ~›Ï?¿‰®n£FÅSTT£6ÝDG§ö¥«ºZ=¯µµ¶ZgGffýw++õ´¬¬*jjjóÜ­®£âAH¥’G¶ÀŠR kÖ¬ÁËËKãóÆo<’:$R)Êû5X*½ïj1¥jßu-,šU_Q\¹/¢˜0A#MYUEܺu/÷¦íÛSxíÇŽáýÉ'dŸ9S£¡cG¤ÚÚ´hÛ–„ °0€ª’r/^ļS'Ì:väÖùóXtëFâ¦M˜úú"‘6þŠÔX ‘H¥äßùÕ¿1º––ª_ù¡ö¹¥¥zY ÄÙ[ZR~ë–êe¾<'GãÞ$oÝŠi»v v,džÁ¡¡  eØÕ«xÌ™ÓdÑúöö”¦§ktÀ˜wêĨ¤$ ]\ð¿p¶ï¿×Ç3*) «>}TÇôܺ•‘ññXté•ŋ›>àÜôéØ>û,þaa ŒÄ¦_?µ¿ïû=ÿÏÞ½ÇÕ|ÿî©’nJM!—P.å2·¹å~¿Í0¶03Ìlfæ6¿ ³ac˜Ë°f&w‰¹_r+Dn1)I*ÑU¥:ßÍ—³¢¶!æý|žhìÛµÓÉßß¾}þQ¡qeñb^4Hgÿ¥Í­[1µµ¥Ü_Ž…Õ75ÅÌÙ™7bÓ¢y¹¹¤^ºDÙ:u€ü%0,Y‚M‹XÖ­Kä/¿(3<ÊÕ¯OÄÊ•”oØÛ-¸²xq%51›7èé©lV[œ>/ΞMê¥Kd''³e ·víÂêÏ€Ìg?ÿœC=zè¤Ù·kÇÍ›Iˆ '5•«K—xï £®R…R\]¶ ­F£,‹Phµüñã–šÜ=y’ÆÑpÅ J9:¢ÉÊ"ïÏÙå6Dߨ˜«K—’›–Æåï¿§B‡ùsššbß¶-áß}GnZüð†eÊ('´Z¥-í#¿?ð¸ÏúI}z/_N¯»w•Cµš¸ü¹±næÍ›ÜÚ½›J…ì©rûÈ¢7l ÞüùÔ_°€Q£È¾{·È÷Vy+WæNp°ÎýnæìŒºZ5Îù%šÌLò²³‰?p€ôÈÈ"Û+[§¦vvœûßÿÐdf’“’BìŸËÏ4ùKfìí•MYu}Ý:rÓÓ•™.ÅX²ùÇ—qwG¥§GúµkÄÿ9ž|Ü=}šÛAAd'%‘zù2ç¿üR¹/ Ḭ̀iÑ‚+‹‘—“Ãà`’ÂÂ(ß°a±Æô¢‘àˆB!„xélßžL—.exôüݺ•% @÷Hßï¿w¤_¿k˜›‡*{’ôécI§Ne¨Qã<;^¥C‡‡íXY°n]%&Lˆ¡|ù³œ=›ÁO?½V¬1Õ¨qgçs\¼˜Å€‘8;Ÿã—_>€½öšC†XQ£ÆÂØ»·x›ª>Ž……~Ê•+÷¯Ú} ÊˆÄîØÁ¶jÕ8ܧN^…NÐ34d[õêÔ¨¡œVùû,îÕ‹ß5¡sg*tî¬S÷Þõëù³‘”DôúõTþs²hÑc—„”«_ WWô ±jÐ3ggôML”oÉçÎá½|y¼»'Ortð`:œ9S`QPî½{lªX‘qqèr¨gO*´o¯Ì°(iOú¬ÿ)MFîî´ D]Äž1âÅ—ššZt¡ÇP?²ÏË“ÄÇÇqðà!<<<°µµ¥{÷î2sD!„B¼Z²³µ¤—‰‰¹lܘ¤lì*J†Q™2T?¾Ð¼œÔTê~û­Fž ñøq²þÜëåÚªU”oÜ=CCòîßǪAƒB÷q))Oú¬ÿ©ŒØXjNœ(ñ¯È¿0B!„â•¢ÕÂäÉ780µZŸ‘#­iÚÔ¼¤‡õJ+lŸˆlZ¶|~yI¥†‡stà@ÔU«RïÏ‹ôŒ©þñÇ%9´žôYÿS®®X¸º>õvÅ«E‚#B!„â•bl¬âôéê%=Œÿ$¯,é!¼’* üL‚B¼JdYB!„B!^iB!„/½”Ìdœ§–áFÒõ’ŠBˆ—G„B!ÄKoUðRš»¶Æ±¬SIE!ÄKH‚#B!„⥦ÉÓ°ìØF¼>€èã¼õsw\݃V«-áÑ !„xHpD!„B¼Ô¶žß@ys¼œPÝÖ&•š3)àC¼¾©Æ¢ y¤d&—ì …B¼Ð$8"„B!^j‹ƒæ1¢Éåµ™‘Ã@И0~賊ðø 4øÆq›F”à(…B¼È$8"„B!^Z§n“M÷^…æ»ZU¡¦]mÊ™Yžpñ9N!ÄË ¤ „B!Ä?µ(h.ï4‰¡¾¡’¦Õj9±—_BV°ï]t¬ÑÅ}~¦–½G ŽT!Ä‹L‚#B!„â¥t3%†Ýá|Ýe¡Nú‰ëGùtëhÞöÎ7Ý¡6)]B#Bñ²àˆB!„x)-;º€uúQ¶”¥Nz]ÇûðB J!ÄËH‚#B!„⥓‘“Áêe?\ ïÑ%6B!DqȆ¬B!„⥛ÃÄÖÓ©j]­¤‡"„â?@fŽ!„Bˆ—Ž«U\­ª”ô0„BüGÈÌ!„B!„B¼Ò$8"„B!„BˆWš,«B!„ÏŒF£!((ˆ„„233éÙ³'¥J•RòCCC¹~ý:©©©4mÚggç’¬BˆW–ÌB!„Ï” Mš4)4O­VãííZ­~ΣB!’™#B!„â™Ñ××ÇÍͬ¬¬Bó]\\ÐÓ“¿Ù !„(9ò_!!„B!„B¼Ò$8"„B!„BˆWšG„B!„BñJ“àˆB!„B!^iB!„Ï”F£A£Ñ Õj•ßòòòÐh4hµZåw!„ây“Ój„B!Ä3µ~ýzîß¿À† 055¥W¯^>|˜ëׯDPP]ºt¡téÒ%6^!„¯ Ž!„BˆgªOŸ>ÍkÖ¬Ùs‰BQ8YV#„B!„BˆWšG„B!„BñJ“àˆB!„B!^iB!„ÿy=—·c]¨_±Ë§d&c9AEn^î3•®í6㳨ÉsëïYèýS{Ü¿ªˆåUI???š7oþTÛLMMÅÙÙkkë§Þö͘1c˜4iRIã™Ü%ÅÕÕ•”ô0þ“$8"„B!Ä_˜•bVçïÑWé?•ö>7Çr‚ §)¥éµÂ‡s±g ”©iW›÷^ÿð©ôW\VŸŸ§“Öz¡7‹Ì×IëóSfí™Vd{ëÞäÀ§Ÿê‹Lƒ P«Õ¸¹¹±uëÖ§Ònrr2*•JùñóË®©Õj¢¢¢øá‡[700Úµkcbb‚‹‹ 7n,VŸ»wïÆÓÓªU«Æo¿ý¦“ÿù矣R©8vì˜Nz¯^½°³³ÃÌÌŒF\¬þ:vìÈÔ©SuÆmmm͹sçžXo„ tëÖM'í믿¦E‹:i={ö¤S§N:i}ûöUÞS;;;Þ{ï=²²²”üU«Váââ‚Z­¦iÓ¦\ºt©Èë8sæ *•ŠÉ“'+i­ZµbñâÅEÖ}TçÎ10xxfÉœ9sP©TâêêÊ_|¡sܸ···Î=2`À€bõsõêUz~rss™7o*•Š}ûö)å /V›éééJZÍš5•û ##SSSÚ¶m[¬q>àààP`¬{öìžÝ÷¯$HpD!„Bˆ¿0Ò7âÝFï£R=½›ÞÙѱç¨ëèE§›—vK'ßÉò5:ÕìþÔúû§ZUõápÄ~åµ&OñÈôªêS‚£z¼þýûÓ¶m[’““™;w.}úôáÎ;ÿºÝ2eÊ™™Iff&Õ«W/v½K—.Ñ»wo>úè#ð÷÷ÇÐаÈztíÚ•÷ߟ„„~ûí7ÒÒÒtÊàèèȶmÛtÒ}}}9~ü87nÜ`àÀtîÜ™¼¼¼bòÒ4h›7oÆÝÝý‰e}||8xð Nû÷ïÇÇG÷iÒ¤ ^^^êüñÇäää°ÿ~öïßϬY³¸~ý:C‡eñâÅ$''Ó¸qcX¬ñ°zõjàÅß±}ûvâãã ¤7mÚ”¤¤$~úé'Ö¬YÃĉuò7nܨÜ'?ýôÓßê3..N©›™™©fLLLX²dÉ?ºŽ'Ù½{7VVV>>èééqíÚµeqww×yèŒŽŽ¦k×®”/_[[[¾ùæ:&&&˜˜˜ü­ ÙìÙ³éÙ³'o½õjµOOOeö„ .TÊÞ¹sSSS˜3g:ubÈ!¨ÕjjÕªÅ;ï¼£”½yó&çÏŸçóÏ?' @§ÏÖ­[ãää„¥¥%NNNÄÇÇ“””@ZZ½{÷¦L™24kÖŒ6mÚ0oÞ<úaaaôèуŸþ™FéäEEEáêꪓָqc4 gÏž 77—Ç+Á‘ãÇãììŒZ­.tYJ¥ÂÀÀ777:vìHHH111˜™™ÑºukôôôèÞ½;/^Tê}z¡c577çõ×_gþüùüðÃäää(ù†††Ê}ò öæ›o²hÑ¢muîÜ™_~ùEgÌꚘ˜(éM›6åèѣܾ]ðžÔ¢E‹¨U«V±ƒôéÓvíÚ¥“7eʬ­­©\¹2C‡¥k×®:ù^§‰‰ zzzOüþiµZÚ¶m«¾´Z--[¶dîܹÅkIàˆB!„xi<ÄÐ5}ñú¦×ïFòVýü‡ÉÉÛÇSÊÈŒ+“PoG#*u"ïD0wÿ—ì~ÿ‘Sî² ç L K)ùËû¯%hLX¾´Z-C×ô¥Ÿç "¦ÜA«Õ’­¹¯S&[“ÍÕÄˤÝO}â¸kW¨KXl¨ò:lBóºÿX \Lr4c7cù›¿qê㫜¾¢“?tM_¬Ì­¹8ñ&«l`Ì_b’£ü`ä(ÎzƒðÏné,Ùù±¯a¢ÐSé³wTa¢ø°Eþ_Ä=êa g ,ý ŠØOË*mÑSå?:”1-‹ÿDMK棖“迲3÷²ï=ñz‹räÚA&nËòþk¹<)µIi¦ïü€ÁÞÃøãöeêÏ©ŠïÚ79uD©§R©=z4þþþdgg³uëV¬¬¬¨Y³¦NûIII´nÝ___† €F£¡S§NT«Vèèh.]ºD¥J•þÕuœ>}š&M ß;¦ÿþ¬]»Vy½qãFš6mеµõëAþƒmݺuéÑ£.\àúõë:ùo¿ý6eË–¥}ûöôïߟråÊ0cÆ ’’’ˆeúôéìÝ»W§Þµk×hÛ¶-S¦L¡]»vźFZµjÅþýù3ŒBBB(]º´2ãÄÛÛ›¨¨(† òÄv¢££ÙµkÕªUÀËË‹š5kÈýû÷ñ÷÷§C‡ü ÞÝ»wiÛ¶-#GŽäÎ; >œÞ½{ëÌ^ñõõåÇ ~(ì>øê«¯èÓ§666OoݺuIOOçêÕ«JÚû￳³3={öT‚rÞÞÞœ ?ѯ_?V®\ùØ2Ë—/gÁ‚ìÙ³Gù¬ŸD«ÕH«V­hÕª•Îì£;v°téRBBBØ¿¿²d¦(Oúþ©T*V®\Éwß}ǹsçøî»ïP©TŒ3¦Xm— Ž!„Bˆ—Nе4žëΤ€q4qiAØ„ë|ßs9µ+xð{xÃÆÄÀ„5ºâjUU©«R©ÈÑäp9á"9y9¸Ûס¼¹u‘}^¹NLr4¾Ga¨oXèþ •ʹrw¦–µû>±-scsR3SŠìsWøv¼œãåÔ3#3†6|OÉ‹º{QG˜Ö~6ÆÆÔ°«EË*mØsy'z*=R²’‰H¼‚žÍ\ß(²¿õZVi«,­9±ŸÖ,©Ôà]*—¯ŠžJN5»cd`LÔˆbµý8?‡,g—/îöu0Ò7bTÓñl¿°O‡úüÐ{%g?‰ÂÛ¹ ¶~Àëóë(A’.]º°víZŒyóÍ7Yºt)¦¦¦JÛ©©©´iÓ†-Z0jÔ(%ýÔ©Sܼy“/¾øSSSÊ–-[`">>KKËBóºuëFhh(11ù³üýýéׯŸRïÁCîÒ¥K±µµÅÙÙY©@ëÖ­±´´ÄÃãÀ ‰¹sçÂÔ©SéÓ§’¾mÛ6FŽI©R¥hÖ¬Y‡óŸþ{{û"g\ü•)lI͓̚5 •J…““ÞÞÞʾ' <˜^½zabbÂæÍ›™3g›7oÆÍÍ7ß|}}} DRR’N ¢]»v\ºt‰7nèóq÷ADDþþþŒ;¶Èq›››’’ÿÝ8q"Û·ogË–-¨T*:vìHnn.ÞÞÞœ:u €N:ñÃ?pëÖ-rrrpqqQÚsqqÁÊÊ +++FŒ¡Ó—¯¯/Ë–-+t¿üò cÇŽeÏž=X[ýoäíiÚ´)­[·&00P ,mÛ¶Þ½{ãä䄃ƒC¡{§ 8Pk­Zµ”ô'}ÿìììX¸p!½{÷föìÙ¬Zµê©.U|Ú$8"„B!^:·ÓˆO‹Ãݾ5íjcal¡äåió¸s/[ ;%ÍVm¯üîlY‰¹Ý—0{Ï4*MµdàÏ=”%Oî3žrfVèåï PÞÜ•êŸýïtúýtÔ¦¥‹,—µ…­òÚæ‘ßo&ç?¶ø®. æ¸Ñ`ŽG¯"939E/7éX£;ï¬éÇkÓ,™¾Sw¯„'i]Õ‡ kûÉÖd}ŒUÚ(y6Ñz¡7µf:Sk¦3É™Iääå<¡µ¢ÝL¹Áš“?)×ÑweGrór•¥=j55ìjãn_‡¸ÔXîf$’ššJûöíùì³Ï¸ÿ>;wî¤oß¾:›W†††boo϶mÛÈÈÈPÒoܸƒƒƒÎœOR܇:îÞ½[hž¹¹9;vdݺu$&&rìØ1ºwï®Ô{°æÍ7ßdýúõÄÅåo–›••ž}ûhÕª@¿üCþ)®®®|þùçøúú*3KtfDüuvÄ»ï¾ËÞ½{ eÕª‡K½zôè••žžžDFF*Æ6¢m×®‡F£Ñ°oß¾¿ùä“OÐh4lÞ¼™ýû÷+û„8p€qãÆqàÀî߿ϨQ£xã7ÈÉÉáÆ\¸p777åG£Ñ ´«§§ÇàÁƒ *<î>3f Ó§OÇØØ¸Èq?ا£téüïnçΩQ£µk×fÕªU\¹r…K—.Q§N®]»FJJ ìÙ³§ÐY#‡æÌ™3œ9s†/¿üR'ÏÕÕ• *(¨Gagg§sèéþoуô€€5jD©R¥hÞ¼9IIIÊÆ½EÝ#|{0ÖKrŠóýëСééé4oއǼ³/ Ž!„Bˆ—N·Z½ ýäµ+x2v£/ ¿­ÁÂÃß’xï6z*=¬ÌÊ“’•¬”0x ç[ìq„sŸFs;=žÕÁ…ÿ…öQåÍm¸s/QÙ‡$ñÞm´Ú¿·éåa±§q·«Sd9k 9M&!íᆑÊ8bnlÁ‰q—þ(œàÂ9?ñcšäo*;±ÍtŽ»È®‘ÇXyb gnžÒi_O¥‡m~[ViËñ¨#œˆ:BUëêX™•Ïï?=žak0«Ë÷„Mˆ"lB&j´Ú‡mêù¹>ÊØÐDg—´¬‡K*”vdtóO”ëùè2|ž€žJ„ôx¾;ø5 æ¸1~óHê:zqæ“HÚWïÂÕ«W¹wïC† ÁÈȈÆSµjU£N7nÌ–-[¨R¥ ãÇWÒ‰‰‰!7·èãšÓÓӕ⌌Œ Ýü³víÚ=¶­Kk6mÚD›6mP«ÕÔ©S‡£GPªT)¬¬¬”:{÷î%##ƒ¶mÛbbb·ß~Ë ÝTóÁ©'Ny±¶¶Ö üuÓQ{{{Ê”)Ãüùó;v¬Y²d gΜ!00Š+*Æ6Hupp bÅŠ;vŒ%pS\zzztéÒ¾ýö[ vC½zõ¨_¿>FFFøúúrõêU"""ptt¤E‹„‡‡+?qqq–" :”U«Vølw„„„ЫW/T*h4T*U¡§Ãœ>}ssó{°@þ¬===òòò044ÄÝÝŋӾ}{îݻlj' GìííqppÀÁÁ²eËhóqË„¾ÿþ{V­ZÅÇLdd$fff:×­Ñh”Ù.abb‚••¹¹¹Êl¡¢î€råÊ)cµµÍÒçû7iÒ$¼¼¼8vì»wï.Ðî‹D‚#B!„â¥dalÁï cn÷%œ½yŠ7À§zg6œù€ ·Â¸÷p‘è¤(Eì#G“ƒ¡Qþ†‹ÌøàÞ{ï= IHH`ݺuܺu‹[·n1vìX*V¬¨Ü{ÞÞÞÌŸ?Ÿ6mÚàååÅŠ+еßÈ£ºwïÎáÇ \£ÞÞÞøúú2hÐ òòò°±±¡råÊüøã¤¥¥±sçN"##©]»6qqqÊûúàš3gŽ2ó¤S§NøûûÍÍ›7Y³fM±ÆWÔ÷ïàÁƒüúë¯,]º”+V0dÈú$ Ž!„Bˆ—ž·s~ìû ËúçD¦úÌârÂ%Z~_ŸÿíúœÚöžJÙû¹÷™¶c.ÓÊá1«.VUèÿg âbÜ9jÍt¦É¼ü5õž³]©5Ó™È;¨T*–÷_‹_È \¦—C_O#}ce£RÈß5êni÷u`趬¾­É©èló= ,ûY¼”Z3³Ñ—И“ÔšéLËïëàP¦"óºÿÈ Ÿ{PïëÊx:Ö×éoEÿ߈H¼B­™NTù†ùf)y éq¼µºNSJÓqI3>i5…Êå«êŒé³63½þj|éÀœ}3tòZUõaWøvÞ¨úp“Î×ʹ0¢ÉXZ}߀îËÚp.ö J;¸ÖY]¾ç_ûáð¹9¿_Ê ôõȽì{4ïÁ—»>§Q¥¦Jù¦.-™Øf:C×ôÅq²—4#êNþæ–«ßÚÀâ>«iàÔ°@?jµš 60wî\Ôj5]»veúôé4kÖ¬@Y{{{æÍ›Ç!CHNNF__Ÿ­[·rñâE©Zµ*üñ?«dܸqX[[óöÛo3wî\ý? ?8òî»ïââ₹¹¹²E­ZµX»v-³fÍÂÊÊŠnݺ‘­Ô344¤GDGG+›>hoýúõ|ýõ×XYY1qâDåÄ“ÀÀ@ýP èØ±#±aÃ<==qvvæ÷ßgÓ¦MØÙåß_“&MB­VcggÇ´iÓž¸éê?üÀž={øí·ß”´²eËêÌ´x”Û·o/ÐæÐ¡CqvvfÅŠ,X°ggg}>U¿~}jÔ¨Á‚ hÑ¢S§N¥sçΨÕj¾ÿþ{6lØ€™™åÊ•# €9sæ`eeE•*Uøý÷ß ]î4lØ0²²² é­à}P”C‡Q¶lYL¿~ý”å/†Ù³gS¹reÜÜܸzõ*Û¶mÃÈ(æTÆ ÉÌ̤Aƒ´mÛ–øøx4hPd222¢_¿~:3³õÅ_˜˜¨Ì¼Y»v-ëׯÇÚÚš÷ߟ•+WâääD`` UªTÑ vëÖ°°0¢££ñññaèСԫWæÍ›+§+åIß¿””ÈÒ¥K)[¶,Í›7§gÏžøúúþ­÷àyRùøøhýýý‰%44”fÍšbcc[tM!ž??¿B7Bñßtúôi©ã/³ÄÄÄ—öZñôô,º <öãYŠ}*§ˆ—>w…ogÞY?TtañÂúâ‹/¸|ù2~~~Ͻï¾}ûâííýBŸ"JÖâŋٹs'›7o.‘þSSŸ|ÚדwS||ÂÃÃ[[Ûüãˆÿq¯B!„B¼bŽG—v €U'~¤ñkÍžy`dWøv2r2ÈÍËÅ/dÍ+ÿ½½Ä‹%--+V0hР’ŠâB!„¢˜Âã/Òt^ª|aÃŽK[™ßcé3ï3àü&j|é@­¯œ07¶`äëãžyŸâÙXºt)+V¤mÛ¶´nݺ¤‡#„xDñέB!„B0ØË—Á^ÏwÍüw=—ñ]Ï¢OÓ/¾wß}—wß}·DǰvíÚí_¼ø†ÎðáÃKzÏÌB!„/%WW×'RÓ>‹ãxT æ¸é¤¥ÝOÃz¢çoUÒ4yœ¦”æÈµƒOl¯eå6„Mˆ¢OÝÏd¼B Ž!„BQ, 4 oß¾Lœ8È–ìܹ“Y³Ÿ€££#Û¶m{*}^¿~¡C‡²xñb’““iܸ1>ßäÅAóÖøƒb϶)Š·sê9z=•¶¸5#“­¾û¹›q‡¾+;>—>ÿ. c ¼œÅYÁŽ IDATs8b¿’vææ)85*©a !þ$Á!„BñÒÚ¿?...XZZ²xñb%ýÎ;tìØµZMçÎ{q^^ýû÷gÀ€äåå°zõj*V¬ˆ­­-:å¿úê+¶oßNHH£GfÖ¬Y”+WNÉß¾};S¦L!88˜Û·oÚgbb"îîî,Y²DIóóóÃÍÍ KKKúôéCJJ ¿ÈÌ֭̌[£§§G÷îݹxñâ?{³C«Õ²÷Êïô_Õ…s±gtò®&^!$ú8ýêVÒÎÞ<ÍëóëºÄã~î}|×Àeº.Ó­èóS%/$ú¸²4ä¯K\ò´y|¾ý#*M+GÃokðÞºÁ|°þ2[έ§Á·Bg†SÕºKúúq.ö !ÑÇÿuŸ¡1'iûC#œ§–¡Í†„Ç?|ß#ïDÐ~qS^›fIÕ¶ÌÞ;]ÉóœíÊ`¿^DÞ ÖLgjÍtæPÄ>ZUõÑ ŽŽØG3×Vêrýn$l‰óÔ²Tÿ_>Ü4œ¬Üâí9ÛU¹f—éV\¹@Ž&‡i;&àþUEÜfØñÕî)Ê, ŸEMX{zu±ûâ¿L‚#B!„â¥uäÈΞ=Ëo¿ýÆøñãÉÎÎ`üøñ˜™™‘À!C8x°à²­VË!CÐh4¬Zµ ==="##1bþþþüñÇê•+WŽ3fбcG öš,Œ-p±ªÌ¹ØÐÕgRÆ]z,oË; G1ùo{gÈšÞäióh3÷L¥®c®M¾ÃÉñÐÔ¥¥R÷ôÇWY9ÀŸ×,]›EØ„(%¿uUŽFRÚ9±ŸVU}¸¯¹ÏÀïr~â Ž»Hxü¾;8û±×Z\ßìûG#±ÿƒSýð<»Â·³ùœ?ÛŒÁ?ôÜ¿ªÈ”À‰º{í_÷'ÄËJ‚#B!„â¥5bÄÌÍÍ•cQoݺä/o=z4&&&tíÚ•ªU«¨;lØ0.]ºÄ/¿ü‚¾¾>;vì qãÆxyyaaaÁÈ‘# ÔkÚ´) ´lÙRg©É¶mÛðööÆÂ‚֭[˜u’ššJ›6mhÑ¢£FRÒ—/_ޝ¯/uêÔÁÈȈñãdzyóf -î_÷+ÄËFŽòB!„/­G—´“™™I^^‰‰‰ØÙÙ)yöööêFGGÎ¥K—pww !!¡Èzï¿ÿ>ÇgáÂ…øúú*ehÕª­Zµ¢wïÞäää`hh@hh(;wfÛ¶m̘1ƒR¥JpãÆ öìÙÆ €ü-¹¹¹äååqèÐ!ÆÇ¨]»6K–,á7ÞàâÅ‹èéþwN===<<<8s¦àƒõƒ:áñ(onMM»ÚT*Wøi<«‚—Ò²J[ÊTTÒï%PÎÌ ½üÇ [Tª‡ãèåñ&×ïFòΚ~Ä¥Ýbˆ÷&·û²Ðö•u•‡Ak [b’¢‹¬W˜{Ùé¨MJÿ«>o&ß <þ‚Φª­†Äô\­ª0Åg&3v~Æß×ÇØÐ„©>³èV«w±Æ÷FÕvŽØOVN&Îå*Q¡´?3æ³€9u˜M÷²Ó©_±áß¹ô´Z-±©7·i„ò™ek²©f[S§œ­Úžšöµ©|ÃË —ÈÊÉüWý ñ2’àˆB!„øOÑÓÓ£|ùò$''+iIIIÊðí·ß2pà@‚ƒƒ144ÄÆÆ†ÐÐÐÇÖóóó#&&†;v`aaÁÇŒŸŸ)))9r„£G*'ÕÜ¿Ÿƒ*“ƳeË:tèÀøñãY¸p!ŽŽŽtíÚ•Ñ£GãéÓ§©W¯õë×À××—>ø€ˆˆÊ–- €F£QÊk4ÌÍÍ9}ú4íÚµ+О¹¹9111ìyœÓ1!¬8¶ˆÿíúœVU}x«þPšTjŽJ¥"7/—eG°¼ÿo:õË›ÛèÌÊHÍJAûç#}#&¶™ÎÄ6Ó¹r;œv?4¦³{êT¨[`,²¶°!á‘Ù iñO,ÿ8i÷ÓˆHüƒšvuŠ,û¤>+”q¤I¥ø ÜTh][ ;ôZÀæ0>X?”®î½”™D*•Z ?ݧUUüB–“•“©,©øß®ÏÉÖdsxÌYJ–bÉ‘ïØÿÇ.ºFúFh´š¿6‰‰¡ š?—iµZÒï§ý9öê ¬~k5íj¨w)þ~!ËYf u*ÔÅ·Ñ(Z»µGO% Ä«Gîz!„BñŸÓ¹sg~ýõW +PÆÀÀ€qãÆabb´iÓhß¾=AAAÄÅå?4?z,nrr2}ôóçÏÇØØ˜Ï>ûŒßÿ£G²sçNÊ•+Gff&YYYdeeÑ©S'Sk òÿ.¹téR~ýõWvïÞ ÀàÁƒ™?>çÎò7a}0‹ÄÃÃS§N†V«ÅÏÏsss±±±¡råÊüøã¤¥¥±sçN"##©]»6žžžœ?¾ÀOpp°2O‡ú,赂Ó_¥N…º|¸i8gcO°õÜlÕö4pÒ¹P¹|Uʛ۰+<?•ßNÿ¬“<ê—â/ Õj1Ö7F‹3#ó"?¯öÕ»²æÔJÒã‰IŽ&àüÆe6‡ùã9۵РYïçÞçrÂ%†­€»}êWôþW}¶¯Þ…èc^Ü‚&OCjV*kO¯Vö Ùqq+qiùK¸ õ 136×YbekaÇ­”›Ü͸S ßf®­‰>Îþ?vëGR²’©bíF)ÃRÜ˾ǺЂG2W.ïFðõ£hòt$•ÊU&øú1¶]ØHŽ&[ÉëWw0_ìœÈíôò´y\¸ÆÑÈCt_Ö#}#v<Îooo§mµŽ¯,™9"„B!þsf͚ŠAƒ¨_¿>öööxzzZN__Ÿ•+WR¯^=:uê„——óçϧuëÖ¨ÕjjÖ|¸ü`âĉx{{ãã“ÿ@[ºti&MšÄ¨Q£¨^½:]ºtÑYêÒ­[7f̘Áüùóuú´··gÞ¼y 2„sçÎѲeK¦OŸNß¾}‰ŽŽÆÚÚšáÇТE ¦NJçιsç...lذ3³ü R×®]ˈ#˜†úFJ~Bzïû¿MBZ¥MËðI«)Tþs“×Që‡rðê^’3§ÇºP?ÚUëÄì.ßÓ½vÎÞgøÚ·ØsyÆj¾ö:Wo_Öi··ç/nÆiJijØÕâ÷÷Ž*ußýµ?;/m¥Eå6”-õp¹ÙGoLbæî©¼± >ÉI¼fåʤ¶ÿàì„(Œùü„x•©|||´þþþÄÆÆJ³fM±±yò&GB<+~~~ 0 ¤‡!„â99}ú4VVV%=Œ§"11ñ¥½–ÄÄÄÇþêqGâ>K±±±TªTé¹÷ûª:yãƒîÉ™ ‘Ê>ÏÛäÀñ˜˜2±Íô¢ ¿Ä} ! JMMýÇuÕju±ÊÅÇÇqðà!<<<°µµ¥{÷F!„BñPjV ßv_ò\#Ùšl~¿@ž6;÷Ùv~#Í+·úÏõ)„xqɲ!„B!„¢eå6ϽO­VË—»'3|Ý@,ŒÕ¼Óp$^kúŸëSñâ’àˆB!„¢DsðƒÓÿù>…/.YV#„B!„x&z.oWè©+³ýÂf|5y†#úûFú¿ÍkÓ,q™þrî)$„(™9"„B!žFCPP dffÒ³gOJ•*À½{÷&!!­V‹½½=^^^—ð¨Eq¥d&S{–3‡GŸÅ±¬Ó¿n¯¦]mÞ{ýç0²|UgØr;==•>Žex¯ÉXÞmô~±ë_?Ɖ¨#\ž'§ºñ'Á!„BñLÙØØP¥JvïÞ­“ž‘‘µµ5uëÖEOO#GŽB“&/ÖÌñx«‚—ÒܵõS Œ@þ¸N–¯=•¶X;x;Í\ßàXÔaú­ìDu[wWjV¬º7’¯ãlYI#B¼dYB!„xfôõõqss£lÙ²òÊ—/O5P«Õ˜››S¹reK`”âI.Æç£ÍïñÓ‰%:éš< ËŽ-`Äëc•´»w軲#'«é·ª3Y¹~¾Ÿ{ßµp™n…Ët+úüÔA§½Z3©ü…u¡ËjüC¡æ—Ž¸Í°cràxÒg u©\>ÿ=kàÔˆ«‰W€üc‡=µ’iígSÞÜzÖé_äxÌŒÌÖø‚Æ„ñCŸU„Ç_ Á7nŒÛ4¢8o½¯ÙsD!„B”¨´´4<Èë¯¿Ž……EIç•wýn$Z­w»:T³©‰¾ž~2§n“M÷^JZž6;÷±µ°SÒl ¨ôòx“ëw#ygM?âÒn1Ä{“Û}YäxÒâ±¶°U^Û<ò{q¬{{M][òå®Ï9yßF£¸™rƒƒW÷°íÜ´hÉÍË%O›‡žêñC¾™|€ßÕUÒ2²ïQݶ–N9'ËJ:¯µZ-±©7·izùaÙšlªÙÖTʨMJ+¿ê’•“ @Ræ]2s2¨XÖùÇÕª 5íj}Œð„‹½>!^UB!„%æÞ½{ìÙ³‡zõêaggWtñÌ}ÑaÃfå‰%´[Ô˜ªÖÕP(kvÃÄÀ€EAsy§áH õ •zz*=¬ÌÊ“’õpÙFrf’ò»‘¾ÛLgb›é\¹N»ÓÙ½u*<|¨/Œµ… !ÑÇ”× iñûšòûþÏY.];@“JÍ©PÚ‘5º2¼ñè¿ÕV…2Ž˜bÜ%T*ÕcËý5¨¤R©°WW`õ[¨iWûoõYÖÔSÃRD'EánZ§ØãÑjµŒØË/!+Ø÷Ç.:ÖèÆâ>?SË^fh ñW²¬F!„Bi5EÙ_cUðR¾Ù÷?2s2HËJ¥ÖLg¬ÌʳoTe*2¯û ú¹Z´ôñ|«Àæ§Ùšl¢îFv?í‰×3¨Á»ÌÞ3àëÇhêÒ’‰m¦3tM_b’£±2·æm¯áÅz_VôÿO·¦ÖL'r49x8ÔãÛn‹‹¬÷Ñ“˜¹{*o,¨OrF¯Y¹2©íÿŠÕçôs˜¶cÍ¿óä^v:-«´UöXyÜxê:6àØ‡ŠÕ¾¯:•ÖßßŸØØXBCCiÖ¬)66oŸO‹ŸŸ (éa!„xNNŸ>••UIã©HLL|i¯%11OOÏ¢ ’?Ûãy‹¥R¥JEÏ\FNî_U$pøaåÔ—çmWøvæ˜Eàð¢g\!^>©©…Ÿ:UjµºXåâãã8xðØÚÚÒ½{wYV#„B!„(žØ”&¶žþÜ#»Â·“‘“An^.~!+h^¹Õsí_ñß'Ëj„B!„ÅâjUW«*Ͻ߀ó›öÛ[˜˜Ò¼rkF¾>î¹Añß&Á!„B!Ä í»žËø®ç²’†â?L–Õ!„Bñ è¹¼ëBýŠ]>%3Ë *rórŸá¨tm¿°ŸEMž[šw`&¬穵|ýž³]qžZ†ïÍyjí !ž ™9"„B!žFCPP dffÒ³gOJ•*À½{÷8|ø0ÉÉÉØÚÚâåå…©©iIù¥•’™LíYÎ}DzNÿº=S£RÌêü=ú*ý§0:H¼w›O¶Œbß»ð¨P…½Wb§¶WÊÔ´«Í{¯øTúû7îÜK¤òåuÒê:z±{äñb·1kÏTƵüŒ7ë½ý´‡'„x$8"„B!ž)ªT©ÂîÝ»uÒ ðððÀ€3gÎL³fÍJb˜/½UÁKiîÚú©FŒôx·ÑûO¥-€q›F`l`ÂÉ®°çò4™•âdùN–¯=µ>ÿ©rfVÄÎÈ$øúQú¯ìÌÕ)‰è©þÞ¤ûIשRB'ú!þ>YV#„B!ž}}}ÜÜÜ([¶l ø—éV4ü¶Qw¯éôib`‚‘¾Qß‹êsÚŽ ÔšéLäÝÞZÝZ3Yzt’ÿÝÁ¯©5Ó‰*_Øðá¦áÊç⳨ kO¯.ð9 !ž Ž!„Bˆµyóf~ýõWΟ?OÕªUKz8/œMή¥ýâ¦ôZÑŽ2¦–´«ÖI§ÌÖó(onƒ—S#%mòöñ”22ãÊäÔÂÑȃJ^äÿÙ»ï¨(®öãߥ¯¥Wº±¡Q±Ç†…(ˆ-Ñ-j^kb{ŒAc‹%ÖøFCÔˆ]@£Q¬± bÊX¨ ‚¸Àºìï~Œ®+‚‰õÍýœÃ9ËÌÜ{Ÿ™EçÙ[î$2/z{†çÚä»,ê¶ ¹aiÿO½×sxdœN,†k{Ò«ÞG$N¾ƒF£¡P] sܵ; ¤ßOÕÚæã\ŸÐ?¦p a/J•R§LÜø$æ-×Ù~3;™Q›óÓ¿qúóÎÜ8ù”öÙ=ì(áv1%ês Õ…ÌÙ÷-^;Hôg§ùsôyvÇG²õ\8᱿²;>’££/Öw+ç·èÔû,¥µ99 ”¸ñIT©T•_ún!n|’Ôgï_¿³øÐ¶|ò§ÆýE|Úýÿ|$CšŒ$<æWj}çÂä¨Ïu’5‚ ¼\"9"‚ ‚ ¼VíÚµ£]»vT¯^‡×ÎeåÑÅøÌ¬ÆÆØµ|Ö|qã¯3¡Ýt­y:–žÏÐ&#µ¶ýÁ`ÿÿ`b`BÇ]p·y”x’Éd¨Ô*.§_DU¤¢–“¶fveÆóWF<7³“ä?C}ÃRç¹ðÕMæv]ªµmI÷5Ô¯âǨ̓qŸfØ-CŸš$yÒîøHüþøUmŒ©‘)}ªsL7ŸÞT·¯L&#-'€_O­b|›©Ø˜ÚbUÁš~~ƒ‰º°µøú\Š ×»agf›µ‚ËŒ¥Ë΋ÛèR»;n6X˜Td`ãDþ<ï×êÆ¦¿³oøIŒ L\Þ’îÿí@Znjµ ‚ð"ˆäˆ ‚ ‚ðZÉår¬­­qsscß¾}¯;œ7Êõ»×Ðh4Ôrô¡º}Môõt'G=}ã7³“y¿Vˆ´­HSļLÌ¥m%TV®Ì ZƬ?¦â:ÅŠ¾¿““_ö¦ŒûiX›Ú` WT¢Ñh¸s‹1[†Ò`¶7 f{3/ú;îÞÿÿsIǾ”ëS¾óÑm³,ÅmjŸKúÉ 'j:ÕÁÃÖ›Ëé—È/GI„NLÈ*‚ ‚ ¼d2¹¹¹<|øñßT€o:Îf°ÿøùø2Ú/ñÇËî>ô@§š]110`Éáy|Òh†ú†R9=™6¦¶ÜËÏ–¶e+³´êîQ¯=êõ!ëÁ]z¯d͉• o6æ™ñØšÙs'/“‡E1Ð3 3/¦è¹ÏËÍÆƒÎ5ƒ¸”v¾ÌcíÌí9™|Tú==7­\mÈd2œ,*³¦Ï&j:Öyj½÷”]ŸY:Ǽh¶fvZ=AÒrS±5³àRÚÂNþÄÆØµøT~—AGÐÆ»ÃsO+Âß#>i‚ ‚ ÂK¥V«Q«Õ@ñœ%¯oݺErr2J¥’ÜÜ\Μ9ƒ•••HŒ<Á¹R&´›NÜøë|ä7ˆŸ/c埋¸uï&{â£èç7X§\À;lŠ]À…”8.¤>šC$9+‰ƒ‰ûP©U!“É036/3O[oœ+¹°òèbÔEj–þá©ÇuXÚ”»'im Ý3…37O¢T)ù+#žmç©íT¯Ì6ÛzwäXÒaNß8R¥äç'&£}–^ïöã›]_‘q?"MRâøóÚAª²ýüF=$· —¨‹Û´Êæ?Ì—æyüõ?Ѿz [ã6˜y…œü{¬øs¡4LÐÊ6é±gØ1~û8’vÕ;‰Äˆ ¼Bâ_AAá¥Ú¸q#Å“vnÚ´ ¹\NHH¤Xb IDAT2™Œ¸¸8rrrÐÓÓÃÖÖ–fÍš½æhß\†ú†t­Ý®µ»Kê+ÿ\D°O/,+Xé?%`&C7|D«…¾8X8Qç±DDÁæîÏ•ôxŒ Mh_½3½ë÷àbê9zþÜYêR²2̶{©fíÆO½×3ä·¾„î™Ì`ÿÿ`¤o¬óýî5¼íkhmÓ×Ó瓵½¸}ï&V¬éR»;ƒý?Š—!ž³ï[”ªäæçP;T©-ûFœÄ¹’ óƒ–óÑ/ÁhÐУ^nf'—ëš}o¡{¦ðÞ"_²dQÍÆ í¾ اgoŸ¡Å‚w©(¯„Oåw¥rwò2ñøÆVúÝi‚œw«ø±gرrµ[šÖ^íÚd$ï/oEþÃ|Þ ä³Ÿpv|’ÖŠ8‚ ¼Z²€€Mxx8·oß&&&†æÍ›ao/&Â^°°0>üðÃ׆ ‚ðŠœ9s›×Æ ‘™™ùÖžKff&õê•ý >@~þ«_fôöíÛ¸ºº¾òvßtT¨õ QCáeWýµÄW˜‡Ëd R§çk ëy™vÇG2ÿL¢†|%í ‚ðjåääüí²å:.--•R·n] ÃjAAámtûÞM¾j3í•'FŽ%&5·xe–ÕÇ—ã_­ùKOŒìŽäê‹vr-í"})^öÖËþ¯|émFœßÂàßú 7Ó£ Ú>{âXA„ç%’#‚ ‚ ‚ ”[?¿AôóôJÛ\Ðm% º½ü$Œ ÿ^bX ‚ ‚ ‚ ÿj¢çˆ ‚ ‚ðÒ¨Õj>Lzz:J¥’nݺQ¡Bã.^¼È©S§hß¾=vvv¯!RAáßLôAA^*{{{š4iRêþ””„øÞNAx=DrDAAxiôõõñööÆÒÒ²ÔcN:E:uÐÓÿ5A^ñ/ ‚ ‚ðÚ¤¦¦RXXHåÊ•_w(‚ ¿˜HŽ‚ ‚ ¯EQQ'Ož¤~ýú¯;Aá_N$GAA„×">>{{{*UªôºCAþåĬW‚ ‚ Âk‘™™IRRñññÒ¶]»váç燗—×kŒLAø·ÉAAá¥R«Õ¨Õj4 jµ}}}š5kF³fͤãÖ¯_O«V­ÄR¾‚ Â+'’#‚ ‚ ÂKµqãF Ø´ir¹œ×• ‚ <"’#‚ ‚ ÂKÕ£Gr׳gÏ—‰ ‚ <˜UAAA„5‘AAAá_M$GAAAAøWÉAAá^·ŸÚ³!&¬ÜÇßSfc5^ÆÃ¢‡/1*m‘¶°¤É+kïm±ïÊnj‡*p™dAèž)¯´íììld2¾ºûàUúùçŸéÔ©Óÿ|›¯ÓÈ‘#™0aÂK©ûرc¸»»¿”º_„:àââ‚L&{Ý¡”‹HŽ‚ ‚ ÂäF˜¸}™þ ©/3/ƒk{RmªÕ¦Z´²-)9·µŽ©éX‡O›Ž~!í•—Í—¤å¦¾ôvþ¸¼ שÖiФmgnžÄiBòæ?³l+¶ÄO¢Ç»}_v˜:*T¨ÀÂ… Ñ×tœ:uJëa¯  €V­ZñÉ'Ÿ”YßÎ;ñññÁÔÔGGGÆ'-s-“ÉhÞ¼¹tì¸qãÉdœ:u €Õ«Wãææ†……Íš5ãÒ¥K/ê4Ÿj×®]Èd2d2r¹???Ž;V®²ùùùôíÛSSS˜?~¹Ê•v}âââ055Õ9þÈ‘#T®\ù™uæææâääÄÖ­[¥m«V­¢zõê’ŸŸO·nÝpttD&“‘šú|Ÿ‡ï¿ÿžªU«"—Ë©_¿>ñññZû»uëFçΟ«Î¿ãÉû I“&,Z´xö{RXXÈðáñ³³ÃÊÊŠQ£FQTT¤UWëÖ­Y¿~ýsÅÅ™3gþæ½z"9"‚ ‚ O0Ò7b`ãá/ìÏ1[†¢¯gÀ©±qjì_ô¨×õ½RªZU£sÍ ÒÞ›¦‰[ òUJ.¤ÄIÛ%FÓÄ­&&¯1²g322bøðÒïµZM¯^½°²²bÙ²eeÖçææÆªU«¸~ý:`ß¾}ZåÒÓÓIKKà?þÀÖÖ€ëׯ3`À–.]Jvv6þþþôíûò“Eööö¨T*233 &88¸\å¦M›Æ… ¸rå ›7ofâĉ¸CÏŸ;á2É‚^«uz'Ìß?“w¾­ŒbŠ%ÍÔ#9+IÚ7`mOj}çòÔa5çSÎÒô\&Y0m×—8|m­{7¥ý7³“i0Û›ÝñQZåN%㣱6µÁÚÔ†õúà\ÉEÚ_;TÇ7vOVó+5gTÁ{º#“¢ÆQoÖ£nôõf¹3kï4Ú.nDÍUXul©´oÏåR¬ï~ïÁ²# ¤}ƒÖHíPE5ï-ô¥v¨‚¹Ñ3¸uï&_?JX\L=×t­˜j‡*øéè4™_ïéŽ \וZÅÔã©õ ÞÓùnÏd4 &&4qkÁ¡Äh©ŽC ûhíÀõ»×\Þ ÅKÞù¶2£· )³GÉã×àdò£ nÓlø+#þ™ñ,iÂú3kJm§gÏžÒ€Ò†Õ 4ˆÜÜ\Ö®]+õ.ÉËË£ÿþØÚÚ¢P(X¸p¡t¼§§'õêÕÃÆÆ†Ê•+S±bE­ AAAlÞ¼™'NP«V-éáýæÍ›˜ššÒ¦Môôô ââÅ‹R9www¦M›F£F¨R¥ K—ßJ¥sss’““¥c—/_Nûöí¥ß•J%:u‚Î;s÷î]­s400ÀÔÔ”nß¾-í?{ö,>>>XXXðå—_bbbÂ͛ş… 60fÌœœœhܸ1]ºtÑêuPZ›¥]¹\NåÊ•INNæ—_~A&“‘••¥•yV<½{÷ÆÉɉE‹1}útÚ¶mK‹-011aذaÔ©SGçý½yó&&&> çÏŸÇÁáÑgá»ï¾cÚ´i4nÜsss:vìH­Zµ€â!/ … a5­Zµâ—_~‘~¿rå +V¤  €C‡áââBVVÑÑÑ( îÝ»Àš5kpqqÁÁÁˆˆ˜Kó¬÷äÀ|ðÁØÛÛS¹re ÀæÍ›X´h …‚#GŽ0bÄ R½ÎÎÎ|úé§4jÔˆš5kòÛo¿•;¦7HŽ‚ ‚ o­?¯dÀÚžøÍ©Îõ»×èã[<´aRä8*™òפt>¬ßŸ?¯Ê\»“ȼèì~œk“ﲨÛ*䆤ý?õ^Ïá‘q:mi4¬íI¯z‘8ù†BuÖ1…êB2/“[£µÝǹ>¡Lá@Â^”*¥NÝq㓘´\gûÍìdFmÌOüÆéÏ8sã¤Î1×î$²{ØQÂìbJÔçª ÿ?Þ"f.$qò6|ÅÌ?¦òçµâoŠ—÷ #n|z2}öŽ8IÜø$F·üªÔëü4bÂØ:p/ñRø¼õdæìû–?¯$ú³Óü9ú<»ã#Ùz.€Ö^ºZœQ©UK:,%G Ôôm0ó_ÝàØ˜‹Ä§]`ÁYÏÏÓ<+ž!MFó+µ¾sarÔç$ݽªUvýúõÄÅéÞ%>ÿüs¢££Ù²e‹”Ę>}:×®]#11‘ßÿo¾ù†Ã‡Kû/\¸€ƒƒ–––œ9sF«Hpp0›7ofÆ tëÖMÚîççGÍš5‰ŠŠ¢  €ððp:vì¨Obb"Ge×®]|þùç"—Ë騱£ô  °iÓ&BBB¤ß÷íÛÇðáÃÉÈÈÀÄÄ„/¿üRç\ X·nXZZ¢ÑhèÙ³'}ôwî Š? >äêÕ«Zsa¸»»sùòårµYÚõñðð 99™ØØXœ‰‰‰‘’#ÏŠ§ÄâÅ‹™1ckÖ¬aöìÙ¥½­å–••ŵk×hÒäéó5lؤ¤$ú÷ﯳ¯{÷îlÚ´Iú}ãÆbllLÓ¦MéÓ§Æ #''‡ðóÏ?S±bE®]»ÆÐ¡C çÊ•+8p@§î§)Ï{ò8FCBBÇ')) .\HRRÛ·o×:>;;›£GÉàÁƒIII)W\o‘AAÞ:‡¯îÇ^-&DŒ¡‰[KâÆ_ga·Ÿ¨S¹¿ÇG0Øÿ?˜˜Ð±FÜm¼¤²2™ •ZÅåô‹¨ŠTÔròÁÖÌ®Ì6ÿʈçfv2ƒüG`¨oøÔùA\­Ý¹ª!¸NO­íKº¯¡~?FmŒû4ÆlúÔ$É“vÇGâ§ðǯjcLLÐèScºù÷Ú¨n_™LFZNñƒI[ïŽ4ªÖC}CÜl044D©,>:pöìYRSSÙ¾};M›6ÅÚÚZ:ÖÞÞ^ëõãózØÛÛK½0Ö¯_OÏž=¹~ý:iiiØØØ``PüY°³³CO¯ø³ —Ë‘ÉdRûPüpnffV®6K»>%=G222èÒ¥‹VräYñ” ÅËË‹ÌÌLžGÉ9<9 ©<ìììhРQQQ\¿~+W®Ð®];i¿¡¡!ýû÷'&&†AƒIÛÓÓÓqt|ôwÍÉɉ'ÅÆÆJ?uëÖJO,,,2dMš4ÁÇLJfÍšICžÊËÒÒRzmee%%ŽÞ6"9"‚ ‚ ¼uºÖîNÌW©S¹£6¢ÑÜ,>4—̼ ôdzؘÚr/?[:>[™¥U¾G½>ìz„s_&“q?5'V–Ù¦­™=wò2¥yH2ó2ÐhŠÊ(¥ËÍÆƒÎ5ƒ¸”v¾ÌcíÌíI¬'Cznù:®ëMk¯Î|ž@Üø$Zz¶EƒFë=™žÎ6Ôµ47Ç“C„J<¹’L&ÃÉ¢2kúlâÄØxNŒ'ö‹«¬ûèуhk¯%Fs(1Z+9òí 94ò,qã“øü½IðD\FúF¨5j8L M¤Ém5 ÷ rËÏ¥´ |1ššßUáד«Ôx1Ÿ'RÕªZ©×ôqNNNŒ;###¦L™"m·³³ÓJ†¤¦¦j%§§§§3t§o߾̜9SkÛ™3g¨_¿>¾¾¾1hÐ HLL,3N¹\N@@[¶lÑé¡P_‰´´4ììt{Q@•*U8tèööödffJs±dddH«›P­Z5­ØµzÈ”§Mо>:t777jÔ¨ÁåË—¹yó&nnnÏŒ !!¹sç²hÑ"fÍšÅèÑ£u†Ý<‰‰ jõ£ÏBNΣς¥¥%UªTÑ2õ‘ÎÅÞÞžììG×Jæ%yœ³³³ôS2ÜËÀÀWWW÷ÄÓÓ(~/^LZZ×®]ÃÔÔ­zõôô¤ëð¤Ç‡Ñ¤¤¤h½Ÿ%1”¬Êô&ÉAAá­dnlNÿ†C9<2ŽyAË8{ë4£7 à@6Å®àBJR=€&g%q0q*µ C#d2fõ<)§­7Ε\Xyt1ê"5Kÿ sÌÍìdêÍrgw|¤ÖöÐ=S8só$J•’¿2âÙv.œÚNõÊl³­wGŽ%æô(UJ~>^öŠ(%î)³©å䃞L«w8påc,µV°ª`¹±1·Š—Ý|¶üËwöz·ßìúŠŒûéiЏ'ÍsÅÉ‘½í"îv M\[<Š5?O;o*V ¯0 1º+‡xØzsâúŸ¨‹´²\­=8qý(;.lFõÿs®”OÐÊ6é±gØ1~û8’vÕ;¡WΞ@% X±bsçÎåôéÓ²páBrss¹|ù27n”–r]¶l‡æîÝ»œ8q‚~øF•ÙNݺu9}ú4qqqh4ÂÂÂ033£J•*åŠ3$$„Õ«WM×®]µöÅÅűk×. X°`ï¿ÿ¾Öþ‡¢T*‰ŽŽ&!!¼½½qqqañâŨÕj~øAû³Âܹs¹}û6ÇŽcëÖ­tïÞ½Ì6Ÿu}<==9uê¾¾¾èëëcjjŠ©©)r¹¼Ìx†ÎСCñòò¢C‡( æÌ™#í/((’%¿¶¶¶ÆÂÂBZNùÉ¥l¿øâ &MšÄÑ£G¹ÿ>»víÒfUš   ~ÿýwÖ®]«•°Òh4ôë×Q£F±`Á ˜5«xþ:pøða)¹ô<+ì<ùžlÛ¶ž=‹‡ÿåååqòäI²²²Ø±cóçÏgÈ!ZåKƒgñâÅÜ¿Ÿ£Grúôi­^08::–kµ¢×M$GAA„·^CE–÷ü••½‹"Sfr9ý­úòíî‰Ôy,Q𰀩;Çã6Õšº3]q³ñ¤wý~@ñP”Ú¡ šÌ¯ ¯„R;TÁµ;‰Èd2~꽞°“«p›f¾ž>FúÆZÔ…êB’î&’ûÿ½JèëéóÉÚ^T›bI—åïѽ ƒý?`õ‰ÔU0ró bnž¢v¨‚V }p®äÂü å|ôK0õ¿÷ ^ßr?Àße1£6¦óò–„î™L·–:Ç|Ýv:ÿÙø 5f83{ßt ¸ÇÅôNsèBוm°3{z¯‡§ûÞj8Öá½E¾(&WâÓð~äæIû›¸¶àäõ£¼ëܹá£ùƽ7‘]wÐ~‰?ýºѨZSº»×ûccªN®H»k• ;ù–6årÚE,+<2ò¬xÎŽObr@(.– ¶Î;‡B¡ víâûÀÝÝ…BñÔž 4`È!ôïß•JÅ„ ¨Zµ*®®®´iÓ†/¾øBš3"77—~ýúáääD`` ­[·fúôée^×–-[2eʱ°°`áÂ…lÚ´ SSÓ2ËBñCõùóçiÔ¨‘Ö(^9eÑ¢EØÚÚRPPÀ·ß~+íKKKÃÐÐJ•*1xð`¾ÿþ{üüüÉd¬_¿žU«Vamm¾¾>ÆÆÆÒP–É“'ãé鉇‡ï¿ÿ>“'O–®Á³Ú|ÖõquuEOO_ßâÏÆ»ï¾+M0ú¬x6lØÀùóç™8q¢Ôþ÷ßOhh¨´šMåÊ•qvv@¡PPµjU©Þ9sæB›6mtz}úé§ 2„îÝ»cccÃ×_¡¡!P<‚ Âÿ¾3gÎ`ccóºÃx!233ßÚsÉÌ̤^½²{1äç—oyÕéöíÛ¸ºº–}à+–W˜‡Ëd R§çc¨oøJÚÜÉüý3‰òæ +ü{äååaaaA~~¾”ñ¼¹æÎË?þÈ‘#GJòU^ÎÎÎlݺ•úõë¿ è´‡/=¯òΕ’––Ê©[·.‰ž#‚ ‚ ‚P^Ç’“š[<¾~õñåøWkþÒ#»ã#y zÀ⇄\E Ö/µ=A(ÇKsM,_¾œæÍ›¿ÖDěϛlôèѬX±¢Ô¹^þ­ ^w‚ ‚ ‚ð¶ˆO»Hß_‚ð²‡ÁeOäúOEœßÂàßú 7Ó£ ÚŽyém BY.^¼HppñgáwÞaåÊ—ÿYx›âyÓµl©;ÌîßN$GAA¡œúù ¢Ÿß ²|t[É‚nâAOx³ 4Hk™Ù×íM‹çߢdÞ–ÿbX ‚ ‚ð¯2mÚ4 FFFìß¿ÿu‡#ü011ùŸzH|ÑÚ·oÿ\+«ü/ Óš„Vxó‰äˆ ‚ ‚ð¯2iÒ$’’’ðôô|î²ëׯ§Aƒê,uù,ùùùôíÛSSS˜?¾´¯¨¨ˆÑ£Gcooµµ5|𹹹Ϩ­Xûöí‘Éd:?fffe–ݸq£N¹ñãÇ—û|ÊËßߟ_ýUúýÁƒÈår­Õ9Ê’™™©kÆ v fÏžýÔ²ÇgöìÙZ¶>D&“qþüùrÅ|øða¼½½Ë}Žåa`` -)[^AAAÒ2°kÓ¦ .ÄÈȈ³gÏJÛÕj5+VäÀϬwÊ”)Èd2¢££ÈÎÎÆÈȈN:=W|Ï«´kpêÔ)d2™ô{AA­Zµâ“O> S§N:ï³\.שçIùùù:å®Ó°aC­}ga‰ÀÀ@ tt´nÝZgé`€ÔÔTÚ´iƒ\.§V­ZœÙÌA$GAA¡œ¬­­™0a={ö|®rÓ¦MãÂ… \¹r…Í›73qâD,^qfíÚµ¬[·ŽS§N‘œœLJJ Ó¦M+³Î;v T*Q*•sàÀ”J%wîÜ)³lPPJ¥’É“'€R©,×’²ÏãÔ©S$%%ѽ{wiÛž={°±±áÀÜ¿¿\õØØØ T*Ù»w/¦¦¦(•JéÚý“k0jÔ(©¬R©dæÌ™Ô¨Qƒºuëþ½~yzzrýúuíÉÉÉÔªU )Ápúôi7n¬Sæiuoܸ€-[¶P­Zµõ?£V«éÕ«VVV,[¶ (Žïñ÷ºcÇŽôéÓ§ÜufffJe?þøc­}›7o–öý÷¿ÿ}®X###IKK{®2Ÿ~ú)–––¤¤¤Ð«W/ºuëÆÃ‡Ÿ«ŽM©TÍ;w¤™¹¹9³gÏfܸq’››Ë×_Í’%K022z­ñ–F$GAA„·šB¡(uHC¿~ýž»;RRfffüôÓO¸¹¹aggÇâÅ‹âoÜ©X±¢N¹ÄÄDš5k†••Z Ž 60fÌœœœhܸ1]ºt‘¾¾~ý: 4 J•*˜ššÀÅ‹¥²§N¢qãÆTªT‰FIû 111ÁÄÄ###LLL066F©TbnnNrr²TÏòåËiß¾=zzz˜˜˜`ÎM| IDAT`` õ !!SSS>ûì34h@ƒ tzL( ~üñGj×®££#½{÷Ö¹óæÍcذaZ+†DDDУGÜÜÜØ½{·´=,,LŠ `Ñ¢EZ (éêñ×Ϻ*•ŠñãÇãââ‚££#“'OF£Ñ ¯¯/•ÍÈÈ`ÆŒüôÓOåzp;sæ >>>XXX0eÊ­}îîî„„„˜˜ˆB¡@¡P°oß>V®\I£F ÄÇLJ.]º-•9s&•+WÆÒÒ’zõê‘””Tf,gÏž•âùòË/¥a>Ò=àææÆÈ‘#¸qãh%GöíÛGëÖ­¥÷l÷îÝÔ¬Y“J•*Ѷm[nܸ!Û¬Y3>LQQáááÒd¨€ôlaaA`` ´øŠ+èÑ£‡NüsçÎeàÀôìÙSêEÕ¾}{­{¸< Dnn.k×®E__оO¢¢¢ˆ‰‰áûï¿ @ú|—Ä.—ËIOO—¶KåKê,ñxÝ¥­Ž“™™I­Zµ¤d ÷J™0a‚N"tÑ¢E( Ž9ˆ#P(ÅIˆˆˆ¾üòK*UªÄرc¹sçþù'—/_ÆÏÏsss† ‚ƒƒ±±±e^3www:t耣£#¡¡¡899ѯ_¿2Ë=~}ªW¯NXX±±±;v €Þ½{ãääÄ¢E‹˜>}:mÛ¶}£‡‰äˆ ‚ ‚ cÉ’%R‚³wïÞZÃW6oÞL³fÍ´–¹­W¯^^^Œ9R§ÇÓðáÃQ(tëÖ«W¯ê´™••E›6m4hÖçá»ï¾£GØÛÛëÔ—””„¿¿? .$))‰íÛ·píÚ5T*žžž|ýõ×ܽ{777éoIß¾}i×®wïÞÅÅÅå¹z¥Œ5ŠAƒ±uëVÙ¾}{¹†÷=ÎÜÜbbb¤m‹/fÆŒ¬Y³†Ù³g?W}¯šHŽ‚ ‚ ÂÔj5S§NÅÐÐ}}}­ÄFiôôô¸yó&·nÝÂÜÜœ&MšÅßöj4­9är¹ôåììLÓ¦MqwwÇÒÒ333é[ô­[·âííÍ|€¾¾>}ôYYY$$$”OHH›6mŠÐ:D—.]Ê} J"ÁÁÁ\¾|™k×®ií1b¶¶¶xyyií[´h=zôÀÚÚZÚvæÌ233iÖ¬mÚ´!**ªÌ$Ï?µjÕ*¦NŠ­­-ÖÖÖ >ž[·n1lØ0ŒŒŒ;vìsÅT¯^=Þyç øoÛ¶ ™L†J¥ââÅ‹¨T*|||´ÐKOrr2#FŒÀÐÐÑ£GKû<==INN&66–öíÛ“‘‘Áõë×qwwG&“Q«V-ÌÌ̈‰‰A¥RqäÈ©ÏÞ½{©V­;wÆÈȈ/¾ø‚½{÷j%ºuëÆ°aäž %"""øÏþƒ‰‰ ]ºt‘î5j™™IVV³fÍ¢W¯^@ñpž’äÈ_| 0 \=JDFF’žžÎ¹s瞺ÿË/¿¤I“&ZñvíÚ•˜˜©÷Yxx¸—ëׯ'22’eË–±oß>F%•ýꫯˆŒŒdÛ¶mÈd2:uê¤5Ä%''‡¶mÛÒ²eKFŒ!mOLL$<<\«®òÈËËC__•JÅêÕ«¹zõ*æææÜ¿ŸôôtNœ8ÁèÑ£¥ûàÉ^.ï¿ÿ¾tOÏ;WkŸ««+®®®xxx —˱··—zÏ”öYx333îÝ»'ýîááA… ðòò’þ^¼©DrDAAxëÌ;WúÏú7¨]»6666|ðÁ?~\Ú·~ýz†Š …¢Üõ›˜˜àèèø\1…††R¡B|}}©V­6lŠ!2™ ¥R)«T*¥ICg̘ÁÑ£G¹yó&999XXXHIÞ¸qƒ .àíí-ý¨Õj­.ÿ¥éСgÏž%55•íÛ·Ó´iS­dųajj ÷¨X±¢Î·Ð÷xœR©dÅŠÒŽ4nܘ *ТE ²²²8qâD¹âù;4 ·nÝbèСҵûî»ï´îïÝ»ÇÈ‘#Y±b…N/úõëKll¬4@zz:666Ò0$ôôÊÿXeii)½¶²²’®«««+Ë–-cêÔ©XYY¬õYš´´4­xììì¤xP«Õ:t___¼½½Ù»w/RùöíÛÍñãÇquuÅÙÙY:ÏÇ„KêMMM•¶µlÙ­IEEEdffj}~œœœ€âbýúõ9}ú4û÷ï'99™¬¬,ÒÓÓ©^½:†o¿ý–5j P(h×®*•ªÜ×vÏž=L˜0þýûkõæ8vìááá,\¸Pk»™™:ubÆ dffrôèQ‚‚‚€âäH=ððð E‹Ìž=›-[¶He©Q£uêÔaõêÕüõ×_\ºtIÚƒ““;vìàÁƒÒö‘#G2mÚ4iøWy™šš¢V«¥aS7&77333ÒÓÓ122¢R¥J@ñß0 ­ò+W®”îé’l }}}é§ä÷’DOiŸ…§¹ÿ¾Ö°ÃÐÐP¼¼¼ÈÌÌ”zÀ¼©DrDAAxë 8PúϺ““»wï&66– P·n]i_`` 3fÌ 66V—_ÂÈȵZýÔúŸüƵ<Yµj)))Ìš5‹O>ùFƒÕªU#11Q:611Qú6ýÌ™3R¹reÌÍÍéÛ·/¿ÿþ;UªT¡eË–ÄÇÇK?©©©R¯”g‘Ëå°eË–çRPXXÈÝ»wâ•?²²²tz1”vÖ¬Y£Õ;¢DDD‡ÆÄÄ>|HDDPü ÷ä7îÿ”L&£råÊlÚ´IºvW¯^Õz@ûâ‹/èСƒÖ°—ÆÆÆ8;;ãììLåÊ•¥íöööZI‹{÷îéô€ÑÓÓ“æ6yRJJŠÖëǯkŸ>}8räÉÉɤ¥¥i­ŒR{{{233¥ë—‘‘¡»»;Û¶mÃ××___¶nݪ•)™wäñ!5Pœ y<’žžNQQ‘Ö0öíÛ§UŸžž¶¶¶Zs©deeI¯6lÈ‘#GhÚ´)+V¬ ~ýúèéé±yófÖ®]ËÞ½{IJJ"**ªÔëø4NNNŒ;###­¹`T*ƒ bÞ¼yOí½P2´fË–-´mÛV'©PâY3JæïyüÚûûû³mÛ6<==7nœ´ýäÉ“„„„ “ɨ[·.jµ™LF||¼tÌÓî¡jÕªahh(%`T*W¯^ÅËË ;;; ¥{3??_çsdkk+ÝÓæææO=Ç•´_ÚgáI¹¹¹\¹r xˆÙܹsY´h³fÍbôèÑ:I«7‰HŽ‚ ‚ osssé?ëúúú888àì쌵µ5FFFÒ¾ *`ii‰³³³ôíu oooiÕ“òR«Õäçç£V«µ^lß¾]zð544ÄÌÌLZ^4$$„¹sçrûömŽ;ÆÖ­[¥U\êÖ­ËŽ;ÈÈÈ   € 6P½zu ¸üÑ£GÙ¶mjµšœœÖ¬YSîá(!!!¬^½šèèhºví*m/**"??Ÿ‡j½~Üœ9sP©T,Z´//¯r­F¢ÑhX°`ÎpÔÔTNŸ>M\\ùùùäçç3{öliÞÎ;G^^ZßÎCñƒ^ÉòŸ¿.K¿~ýøê«¯¤û¸¸8é=ÿóÏ?Ù¶mÓ§O—bÊÏÏ/óÚzyyaooOTT¿üò‹Î1ŽŽŽÜºuë©«æœ;wŽ;w’ŸŸÏâÅ‹éܹ3P<ð¾}ûP©T!“Ét` µbU«Õx{{ãââÂâÅ‹Q«ÕüðÃZe<==¹pá5kÖÄ××—ãÇk%3Z·nͱcÇØ³gVr¤U«V\»v;v R©˜9s&Í›7/×Cu`` ëÖ­ ..ޏ¸8i_Æ Y¶lM›6¥mÛ¶ÌŸ?_R“££#h4­ LŸu g``ÀŠ+˜;w®ÔËaöìÙ888¬U¶D@@W¯^eþüùÒ(Nbìß¿Ÿ»wïréÒ%&Nœ(½_ééélذ””RRR5j...Òg·$(žˆvݺuìÙ³(þ|8C‡ÅËË‹: P(˜3gÎ+‰éïÉAAá­öÅ_ýû÷×êQó,#GŽdûöí899iÅ´xñb222°µµeÍš5lܸQZ%gÍš5ìܹ+++222t&{}Yär9-Z´ÀÊÊŠˆˆd26làüùóLœ8Q:îûï¿'44´ÔÕÅ^7Y@@€&<<œÛ·oCóæÍ°·ö$+‚𲄅…ñᇾî0A„WäÌ™3ØØØ¼î0^ˆÌÌÌ·ö\233µ^žåño[_•Û·o—:¿…ðâ%$$àã㣳*Gy´oßž.]º0dÈ—ÙÛoåÊ•lݺUNô2äååaaaA~~~©KË º¾ùæ._¾üÜK¿Éصk—4ÌåmñO†Õ•6$êIii©8pºuëâàà@PPè9"‚ ‚ Â?WPP@ãÆéÛ·ïëå_çðáÃÒ®åË—Ó¼ys‘y¹¹¹¬ZµŠ>úèu‡"¼F"9"‚ ‚ Â?fll̤I“tV}^¾‹/âãヽ½=Û·o—†z e[±b...´k׎6mÚ¼îp„×Èàu ‚ ‚ ›ÃÝÝýo ©ÊöÉ'ŸHË4¿Hƒ bРA/¼Þƒê,kû¿âñÕ†„²‰ž#‚ ‚ Â[ÉÝÝcÇŽ=w¹°°0Ú·oÿ"z9ÜÝÝÙ¿ÿëãëׯ³gÏ~ê¾·í=yÄ5x»äää P(°³³“&Wñ¼ÝDrDAAøŸ3}út:uêôëÙ³g^^^h4¦L™‚L&#::(^ÉÂÈÈHjçÁƒ :,--éÞ½»´2…ƒƒ2™ ÜÜÜX´h‘ÔÆÆ‘ÉdZ?ãÇÿDZÿ]-Z´ B… Òª.‘‘‘Èd2)±‚££#¦¦¦4nܘ'NĬY³têkÓ¦Í?æqãÆ :uêD… °µµÕZâÕÛÛ™L†žžNNN|öÙg¨T*öïßL&còäÉÒ±¾¾¾˜™™ÅKÂ>;;;¬¬¬5j”Ö2¾«W¯ÆÍÍ š5kÆ¥K—ÊŒ³¬6Ÿôõ×_—»×Âýû÷8p VVVXXXСCå—Ÿ¦dIéŠ+@ll¬´¯¨¨ˆÑ£Gcooµµ5|ð¹¹¹@ñª:S¦LѪ«aÆ|ûí·e¶Ù°aCé^vttdÔ¨QÿxYÙÔÔT©NCCC<<<´îƒ’ó|üçñ¥‰Ëb`` ÓÓbþüù:upÿþ},,,HJJâÇ|j}ÎÎÎZï>¼Ü½†žu¿—¦¬xòóóéÛ·/¦¦¦8880þürÅ2{öl­dËÇ‘Édœ?¾\åß&"9"‚ ‚ ¥˜?>Ÿ}ö™´Œ­§§'7n`Ë–-T«VM:vĈœ?ž£G’@»ví´–íŒŒŒ$//eË–1nÜ88'”J%“'O& ¥R)-ïúºT©R…¨¨(6lØ€§§§´oРA;vŒ7nзo_)**ÂÓÓ“ëׯëÔ•œœŒ‡‡ÇߎE£ÑðþûïcooÏ… 8qâ¾¾¾ZÇüúë¯É–-[øá‡€âÔ’ó¸víšÖ*K–,áСCœ;wŽsçαsçNV­ZÀõë×0`K—.%;;ÿrO4û¬6—ŸŸÏŠ+9rd¹ê}úü£¶FŒ!Õ©T*ùþûïiÛ¶m©É®¡<÷ûß1mÚ4.\¸À•+Wؼy3'NäàÁƒ/ âÿ"9"‚ ‚ ¼µ¢££qssÃÊÊŠ¥K—–»ÜÇéÛ·/fff4iÒ„ÌÌLcþúë/Ž;F¿~ý¤mÍš5ãðáÃNpp0·nÝbÍš5,_¾¬­­0` …B*«§§‡±±1­[·¦fÍšœ>>Ô¨Q£ÌkpíÚ56lØÀŠ+pssÃÚÚšO?ýCCCÂÂÂx÷Ýw©R¥ ½{÷&$$Μ9#Õ«¯¯‹‹ S¦LáwÞaõêÕ@q¨AƒT©RSSS¤ó¬W¯]»veúôé¨Õj¾þúkBCC‘ËåÌš5 WWW,,,¨W¯žÎÐ/=== pww§qãÆœ;w(^θQ£FâããC—.]´’ˆIII˜™™ñÓO?áææ†‹/Ö:¹\Npp0îîîÒ{mllŒ‰‰ &&&L›6ºuëÒ»wonÞ¼‰ 6¤víÚ|õÕWZ½1>üðC jµ___ 3fÌÚ*©ÓÄÄ„_ýõ…®hóóÏ?ëô&)ë~OLL¤Y³fXYYáàà %³Ê²aÃÆŒƒ““7¦K—.¬_¿€Ë—/ãìì,ý˜™™•k9îƒâî­wïÞ„††••Eçα°° €æÍ›¿ÑK%‹äˆ ‚ ‚ðÖ:rägÏžå·ß~cܸq–«ÜÁƒéÓ§?uØÇüùó0`¦¦¦Ò6}}}6lHDDwïÞÅÕÕ€¸¸8*V¬HõêÕŸÙnQQÇŽãâÅ‹eû¤Ÿ~ú‰E‹ñÇ`mm-mÏÎÎæèÑ£DFF2xð`RRR066&((Hzøâ‡£^½zÅÉ“rãÆ .^¼È… ´†ÄÔªU‹„„¶mÛÆ{ï½§ËÇŒ¥¥%:t wïÞX[[KÉ‘œœT*111ddd “Éprr`ûöíDFFrþüy¶oß.%s ô÷äìÙ³Ô¬Y“Þ½{S±bEüüü8}úôS¯QZZûöíÓêéR’èÙ²e ï¿ÿ~©×W£Ñ€ŸŸ5kÖ$**Š‚‚ÂÃÃ騱céoÎÊÓæ?ü Ók¤´k‹½½½t¿=ÉÄÄ„K—.±mÛ6ú÷ïϨQ£X»víS}÷Ýw‰‰‰ W¯^\¿~ÄÄD²³³‰ŠŠÒ:Ï3fƤI“¨T©={ö”öYZZ²sçN²³³™0aÒP¬Ç]½z•ƒâçç'm;yò$¡¡¡ÄÆÆâôìwTTÇ÷À?KÝDAT±DÅ`Cì±W¬[DŒ½$1±¿¶xc/ñkÁDłЍ‰]"v,” "M± JùýÁÙ÷ã…jL¢æ;Ÿsö¸Ì¼™¹sß¼=Î}wî­R…™3gÊÚdffrñâE"""xüø1íÛ·/Ôï… ˆŠŠ’ÝkmßÛ¶m“K_¼xÁÏ?ÿŒ™™999Kº  &&]]]._¾LLL ß|óM¡ñÂÂÂˆŽŽ.q ý”¶ÞgÏžM£Fxòä ÷ïß§U«V¥ö™MTT”ÌáääÄÝ»w¨Q£qqqÄÅÅJ… ¤ßŠ’hÖ¬’Jë½Õ»wo¦NŠR©$))‰Ñ£G¿÷ž*Â8"@ ø`5j&&&R NíqÒ¨W¯mÛ¶E¥RѱcGîÝ»'«OMMåÇd̘1…ÚöîݛѣGÓµkW©,11Qf°¨Q£ÖÖÖ²Ø":t@WW—Ö­[³páÂ7Úloß¾‰'rìØ1,--euŸ~ú)vvv¸»»säÈ(ó$ –6·...ôïßÊ—/O¿~ýd±(>ùä&Mš$mt ²lÙ2._¾ÌìÙ³éÛ·/ä(66–ððp>ùä’““ùý÷ßqrr’Ž%õéÓ‡êÕ«cmm”ú,îž¼xñ‚Ó§OóÉ'Ÿ@·nÝèÓ§,†…ŸŸVVVØÚÚR§N&Ož,ÕuéÒ…­[·bjj*»G^^^lß¾ÇóðáC¶nÝJFFbÈ!x{{£T* *6˜lQ7¦–Ÿþ™ìììB11ŠÓAbb"æææÅŽçàà€‰‰ –––8;;cooObbb‘ך˜˜ðüùs ßó¨yóæ899aff†‰‰‰,J•*U?~<óçÏgÙ²eÒ}„ü,/5jÔ¨yò IDAT+RSS¥¿Ïž=‹‡‡‡,%îáÇÉÊÊb̘1œ={¶Lrj9wî•+W–´˜™™IßÍÍÍ¥MŸ——ééé„……qøðaj×®-ÅHyöìÆ C­VcooϬY³xýúµ¬ßþýûS³fM7n\hÌ *àääÄŒ3ðõõå÷ßÇÚÚšœœΞ=‹»»;...?~\oÄÊÊJö]ÏŠ¿'FFFXZZ2|øpT*“&M"**J:°hÑ"nܸAFF{öì‘yû˜˜˜Ð©S'† &›ƒŸŸÍš5£~ýúxzzÒ¹sgLMMüÀª“'OæÔ©Sdee1vìXZ·n]HGÅQܘZüýý?~¼ÌØP’¬¬¬xúôi±ãéêêJÿj?ÅkMKK£|ùò@¾gÈùóç¥8¦¦¦…Žy´nÝccc<<B¾HQÞ8ÑÑÑ,^¼˜ 6”¸fµüQVbu¶oß>ÌÍÍiÚ´i¡:ƒ"3ñËÊsrr¤@®W®\!<<œ¹sçÒ³gOÉðP«V­R×{åʕٴi?fñâÅ|öÙg2¹‹’GOO™gOdd¤ìHÒ™3gX»v-Û·o/dØ144”â‘T­ZUV׬Y3ž={Æõë×9pà€dÑÑÑ¡R¥J…žù÷a@ ü+ÉÍÍ%33Súdee•©Ýž={¨R¥J‘ÅaccCÿþýñóó#::šgÏž_䵌7N ¤¨•3;;[ö½ zzzxxxàëëËàÁƒeLW­ZEZZçÏŸçÊ•+²ø dÇŽ„„„ЧO©üÙ³g¸¸¸H){Ë$122’U«VCRRÓ§O'##ƒzõêùGknݺE:upwwçâÅ‹2ãH`` ÑÑÑ$&&²uëÖ2Åoðôô$33“;v““êU«°··ÇÎήL2Gzz:—/_&55•ƒâïï/¡tuuåÊ•+\¿~¼¼<011¡Zµj¨ÕjΟ?ägq)k¬È7šŒ1##£2·qtt¤GŒ1‚¨¨(RSSY·n]™=YrrrˆeÖ¬YܺuK 2ìêêÊÁƒINN&++‹]»v•éèCZZúúúÒµ»ví’yNAþºÎÎÎ&11‘Ý»wËÖ6;Pff&«V­¢K—.eÔDñŒ5Š &P­Z5陓ûR¹re®_¿^dݦM›Š5fÕ¨QƒÛ·oKA’µ4kÖŒmÛ¶ñäÉ"""8zô¨XµjÕªØØØ`ff†‘‘‘dxÐ××/u½8p@2.éëëK)ŒK“ÇÛÛ›¥K—Ï… Ø¿¿tÌ.%%…O?ý”-[¶”É`Yí±ªqãÆakk+;Õ½{w–-[Æ«W¯8zôh™Òa¿K„qD @ð¯äðáèT*éóÇ7žÅ±lÙ²2§W-ÈêÕ«qppÀÍÍ GGG¬­­‹M%:bÄ.]ºÄùóçÙ»w/*•Š9sæH2OŸ>½Èvß~û-))),]ºT*óððÀÁÁ¾}û²qãFÙ<4h€‘‘ 4mzf̘ÁÁƒiÚ´)½{÷¦yóæeš£{öìÁÍÍ {{{Ž=ʾ}û¤#jµšzõê¡§§GÆ d›¥®]»Ò©S'j×®M÷îÝ‹ŒgòGŒŒŒØ½{7sçÎ¥|ùòìܹ“ÀÀÀ½ ÊBVVÆ ÃÚÚš &àïï/ůhÙ²%³gϦk×®˜šš²bÅ Ùq3fðÃ?мysnß¾]¤'CQ<}ú´ØX6¥±qãFìììhР¶¶¶•ÉC2$]¼x‘S§NI÷kêÔ©RÆkkkâââØ¸qc©ý9::2qâD5jD»ví— GZF¾¾>µjÕ"--5kÖHu-[¶äûï¿ÇÒҒׯ_¿uêê'OžpôèQf̘!{æµÙ™Ê¼yóøì³Ï°±±‘ÉóðáCNœ8Qì³\£F FŒ£££,žË‚ ÈÍÍ¥zõê´hÑ‚~ýú1`ÀYÛ ÊfTÚz¿|ù2 4 |ùòÌœ9“mÛ¶•Iž™3gR£F œœœhܸ1cÇŽ•╜;wŽGѯ_?éˆÏ”)Sʬ;oooNŸ>-;jùG€222¨T©+W®ÄÓÓ³ÈãFï Š:äÏÕ«WiÑÂ+«÷û,àßK@@€TL ÿ~°°°x×bü%¤¤¤|°sIII)”µ8 ¦{ý§ˆ/6KÇ_ÍÅ‹éÝ»7ÑÑÑŦÓ}Ÿ°±±!((H2DEëÖ­éß¿¡X‚m†–‚Y„þר¸q#AAA¿kQþgYºt)«W¯æ×_}cO‘·ÁÝÝE‹•šaçÅ‹zŒ?ÆQ)ŽÄÄNŸ>ƒ««+ÖÖÖôìÙ“÷ÿ_ @ ø‡xþü9ëÖ­û #eáÚµk\¾|™½{÷¾kQäýcÚZàŸfÒ¤I¸ººŠõòWsÿþ}ÒÓÓ©_¿>—/_&::š ü­c¾ ÿŽ_}@ à/ ]»vïZ„¿ŒÁƒsôèQ–-[&e'¼[|}}ßµ´éïæùóçôíÛ—çÏŸceeÅ?þø^ÿ ãˆ@ @ðWlÝ–-[þAI‚²ñÙgŸ‰#^ÿ#4lØP–!ç}çý†"@ $Ú´ió·ÅªX¸pá{µaý«å9þ  „‡‡£P(dq<Ú´iÃÚµke}Ìž=›iÓ¦ñìÙ³7ÊÂñgxôè:uÂÔÔ[[[™B¡ E‹Òß_|ñ …‚ÐÐÐ×%Ào¿ýF­ZµP©TtèÐäädÙ¸õë×—Ú•Õ°ôêÕ+ÆŒƒ¥¥%æææLœ8Q–&ZOOOvµÌž=…BÁÉ“'üÔÐtîܹÔ1Ï;‡‰‰I±õiiiŒ1sssLMM騱c¡ÔÖoЇ‡‡¤›Ê•+3qâDrrrÊÔö§Ÿ~¢fÍš¨T*œùþûï¥:ooo*W®Œ±±1Mš4áÒ¥K²¶!!!Ô«W¥R‰£££,öÏ/¿ü‚››J¥’š5k²sçN–,YBƒ ¤ûðêÕ+Ôj5¥êÝÅÅ…BŽŽUªTaܸqeN÷\GÅÙÙ¥R‰——±±±RÝ•+WhÞ¼9ÆÆÆT­Z•¡C‡¾õxï a@ |T¬X‘éÓ§Ó¯_¿Buøøø0wîÜBu={öD£Ñ0kÖ,:tè€F£‘RwZXX Ñh8~ü8ÆÆÆh4Μ9#µ C£Ñ ««Ë¯¿þŠF£A©TJõÁÁÁT«Vƒ+÷Ò¥KÙ»w/GŽ¡nݺüþûï…®‰E­VËʺuëV¨ìïà÷ßgøðá¬]»–gϞѴiS|||€üô·ÞÞÞ|þùç<þœÁƒÓ«W/ÙF³nݺh4ésâÄ æÎË­[·¸ÿ>{÷îeÆŒ2Ý–FLLŒ¬_këü ›zzzlݺµÄÍîï¿ÿNÍš5ÿŒ:Þ˜1cÆP©R%’’’رc£FâîÝ»R}RR‰‰‰;vŒJ•*%¯Ë¬¬,z÷îÍgŸ}F||<†††Œ?^6î¥K—Ðh4… …%±fÍΞ=Ë7¸qã‡fÓ¦MejëììÌîݻطoÕ«W/ó¸%1räH¢¢¢¤ž;w&//ï­û]µj¯_¿æìÙ³—iž×¯_gÔ¨Q¬^½š§OŸˆ¹¹¹TïëëË… xøð!>>>tíÚU2jܹs‡>}ú0eÊ’’’ D__€ÈÈHºwïΘ1cHJJbçμ|ù€ñãÇ“™™)Õ–/_NµjÕ¤Œž¥é}ûöídeeqèÐ!öíÛÇòåËßJoOŸ>¥wïÞL:•øøxìíí2dÙÙÙtîܙ޽{“œœÌo¿ýF½zõÞj¼w‰0Ž@ >HÚ¶mK×®]‹ ðçââ Aƒ¨V­Z¡:”J%zzz²ïZ”J%…¾C¾ÑEk )ø]Ë¡C‡˜5k—.]*ôfò=2üýýùå—_°´´D­VóðáC†Nýúõ¹qdÚ´iØÛÛ£R©dÇjΜ9ƒ“““¬ÿ°páBYÙ!C•åää0iÒ$,,,¨]»6QQQR]\\ÆÆÆ´mÛzöìÉíÛ·¸{÷./^¼`Ĉèëë3~üxžúè#Ö­[Wìý×ãˆ@ @ðpïÞ=¢££éÓ§µk×&$$DVàÀ|}} ÁÖÖ{{{233IMMåÚµk<þœ¬¬,™qä?ÿù1114mÚTÖ_³fÍÈÈÈ6?ڷŽ{÷.UÖíÛ·sèÐ!nݺEPPûöí“ê>þøcêÔ©CHHYYYÒ©S§û{ðàA‰õÙÙÙDEEÉŒ9NNN’GEIò”___Ö¯__¨|áÂ…ÄÄÄ`ggǾ}ûˆ‰‰a̘1Rýĉñõõ%((ˆÈÈH8ÀË—/yúô)íÛ·gôèÑ}JHHˆÌð[ܳp÷î]Ùs[½zuttt¸wï666TªT‰Ñ£G&;’UÔÔTÚ¶m‹¯¯/#GŽ,vÞïa@ þ<ˆ‡‡åÊ•£mÛ¶…<~øáììì8tèT¦««‹ƒƒQQQdeeѬY3nܸÁÇK=B£££C¯^½Ø³gÃÀÑѱ7IQ3xð`¬¬¬P«Õ²·Ïzzz 2ooo”J%AAARÓ5j`jjʺuëÈÌÌÄßߟ¬¬,222¤ö7oÞÄÂÂBú ÑhÈËËC¥RIשT*ÒÒÒJ•GKíÚµ¥>kÔ¨!«ûä“O¸sçN¡Ísi888ààà€Z­F¥RaeeERRAAA¸¸¸0pà@tuuÞ„qD @ ø ¦M›6@~PУGÊ‚!®Y³†-[¶0wî\Y µZ-Åqwwçøñã`ffVê˜ÞÞÞ’qdÏž=x{{pñâEÉ {aaa!mÀ’’’¨\¹²ÔO•*U¤ï§NbòäÉœ:uЬ¬,ÆŽKëÖ­yýú5†††²nÝ:,-- ÅÕÕSSS©}5—>={öD¥R¡P(Ðh4ÒuF ÌY’|È­[·pqq‘>999$%%ImGŽÉÕ«W6l˜ì(ËŠ+HNN¦J•*L™2…ž={ÊôùG¨zôèQèXVq“––F›6mˆ‰‰‘ÞÐØT¡PÈþÎÍÍåÓO?Å××W–=ÄÏÏfÍšQ¿~}<==éܹs!YK¢eË–¨ÕjiÝiIJJ’bÂX[[“PjVVV<}ú´È:BCC9vìæææ\½zõ|XÈSâž###Ù|sssÉÊÊ’ÖžZ­fëÖ­$%%±}ûv¦M›Æùóç¥ë¯^½J•*U8xðàÞÞÂ8"@ ¼%ÏŸ?ç×_eþüù(•JºwïÎË—/9}ú´tM•*Uhܸ1ƒfذaÒ†G­V³ÿ~ÜÝÝqww'((¨ÌW›5kƳgϸ~ý:6K®®®’!¡k׮̟?Ÿððp)N†••Ïž=“úÑnÔ ßå¿aƸ»»c``€¯¯/< 22R3,,Œ/^°~ýzîÞ½+ÅJü˜ c\¡§§GõêÕ¥> ?(¥Ö¤$y´T®\Yê³ !EËðáÃÙ²eK™³G^^ÕªU£eË–DDDHŸ„„é(DNN#FŒ`ܸqÌš5K¶±­Zµ*‡âÙ³g\¸pGÉôàããâE‹Ê,“³³3÷îÝ“ÖLDDJ¥;;»Bצ¥¥ŠÃ3þ|rrr˜>}º¬ÜÀÀ€U«V‘˜˜Htt4ÆÆÆ…d- ===Nœ8Qh½ZZZÊt’PdÌ?R·n]Š<¢5Žœ?žÙ³gBdd¤T©TJYmòòò¤§¤råÊøøøpôèQ í”U,÷2vìX^¾|I\\\¡z…BAvv¶”Ù©^½zÅ%ª_¿¾ô,aaa!«ÿꫯèСþþþèêê [œÞÊ—/¥¥e¡Ø3ÕªU#&&¦HyŠ#//gggÙs«5ÎiëhQ(´jÕŠ† róæM©¼iÓ¦ìß¿ggg¾øâ‹7ÿŸFG@ $999dff’““#ûùÿ©ÏÌÌäõëײïÿæ333“ììlÙw-™™™R Ë‚ß!?­¦6uoÁïGŽ¡bÅŠh4233ÉÌ̤K—.Ef­ÑÆÁЩtvv&44www\]]eA&KC0uܸqØÚÚJGj dÆ 333lll$Œ®]»²{÷n²³³yùò¥,`¨««+W®\áúõëäå倉‰‰Ü6,,L Æ8bÄ6lXh£TÞÞÞ,]º”øøx.\¸@PP}úô)U-YYY’n333 e0©Zµ*uëÖ•åü³tëÖóçϳÿ~rrrxñâ[·n•ŒóçϧB… ,_¾???†*ÉEdd$III,Y²„ëׯù†ÿ”´.›7oŽ¡¡!+W®äåË—|÷ÝwtïÞ‚ƒƒY·nÉÉÉDGGsüøqYƳgϲfͶoßŽŽŽ|û—žžÎåË—IMMåàÁƒøûûÊv£]çÚOYŒO]»veÅŠRÌŠÝ»wKÁHµìS{?éÑ£#FŒ **ŠÔÔTÖ­[Çëׯùøã¹rå úúú´oßžM›6ñÑGIAkÕjµä±°wï^Ùs«Õovv6‰‰‰ìÞ½[zƺwïÎæÍ›ILL$66VöìÙ³¬[·ŽÇ“žžÎ²eË(_¾rrrˆŽŽæìÙ³¥¶ëС=bóæÍ¤¦¦2oÞ|•J%½QOIIA¥RѺukÒÓÓQ©T²sùnnn¨T*rrrhÚ´)*•ŠÌÌL:D·nÝd›Ï=z™AÅÔÔ”•+WòÍ7ß…Z­FGGWWW”J%µjÕ*SÜ-ÞÞÞœ>}ºØ ¸···l³п<<|¸LG¤JZ—†††ìÞ½›5kÖP©R%222¤ØÕªU# ÜÝÝùä“Odé­÷ìÙÓ'O¨S§ŽtÔJð6++‹aÆammÍ„ ð÷÷—­w@ZçÚÏš5kJËôéÓ±³³ÃÁÁ¶mÛòÕW_ÉŽóhŸ­‚Ÿ[·n°qãFìììhР¶¶¶¡P(055ÅÊÊ OOOT*ŽŽŽ²#53fÌà‡~ yóæÜ¾}[Šé¡eôèÑèëëHʳZ IDATS«V-ÒÒÒ¤yôíÛ—.]ºP»vm:wîL§N¤ç¸bÅŠìÞ½›ZµjaeeÅîݻٿ?°gÏÜÜܰ··çèÑ£ìÛ·Oòjª[·.;vì`Ñ¢EXXXУGÉ`S£F vïÞÍwß}‡……ß|ó kÖ¬!;;›Q£F1kÖ,ÉÓ¦Y³f4iÒ„Y³f•ª÷’°³³ãàÁƒ,X°333Ú·oOVVV©í,,,صkóæÍÃÚÚšÈÈH6oÞ äacbbhÞ¼9*T`ôèÑ,[¶Œ† ê§J•*øûû3lØ0™—Øû„¢C‡yÄÇÇsõêUZ´ðÄÊʺô–Áß@@@€,=–@ þÝ„……r'þPIIIù`ç’’’‚››[™®ý+6žoJ|||±þ—ðòòbܸqôìÙó]‹"ükùâ‹/P©TÌ;÷]‹ò?Ë‹/þtÛ²ÆÌILLàôé3¸ººbmmMÏž=…çˆ@ @ð¾óôéS)@¨@ øëxõêÁÁÁäææ’’’ÂÞ½{¥ÀÊ‚ÿ-„qD @ xO¹wï&&&T¯^!C†P«V­w-’@ð¯"//™3gbaaAÆ ñóó+t´Hð¿Þ»@ @ ³³3iiiïZ à_‹¡¡!aaaïZ Á{€ð@ ¥RYdjS@ð÷±víZ,--©P¡Â_’¡I Œ#@ ‚ÐÐP …ôwVV­Zµâ³Ï>`êÔ©( écccS¦~¤6úúú¨ÕjÖ­['Õgdd0jÔ(,,,033£OŸ>o•yáÂ… 29 ~–,YRbÛœœüüü¨V­&&&´hÑ‚k×®Iõ .ÄÉÉ •J…³³3[·n•êvïÞ]h¼©S§JõëÖ­ÃÞÞccc:wîLJJJ™ç”‘‘J¥¢}ûöo ‰âY²d 4Òè¾zõ µZM@@<@¡PȼkêÔ©C@@ÉÉÉôë×sssÌÍÍi×®ñññ¥Ž‹§§'fff˜™™Ñ«W/¸t馦¦ääÂ8"@ ø ÉÉÉ¡ÿþ˜››Ë &L@£Ñ ÑhˆŒŒ|£>ãââxñâ .d̘1„††0vìXnÞ¼ÉùóçyðàíÛ·+ãÈÇ,Éøí·ßÒ¶m[éï‰'–Ø6''}}}8@dd$nnntíÚ•¼¼< ?»Í¡C‡xôè›6mbܸqܼy€ž={¢Ñh˜5k:t@£Ñ0oÞ<ÂÃÙ66µZýVkïï$99™œœªW¯.+0`€ô›´lÙ2úöíË“'O˜;w.·nÝâþýûìÝ»—3fÈt[Ü:xúô)½{÷fêÔ©ÄÇÇcooÏ!CdãîÝ»WúÝÒž>D„qD @ðAãëëËË—/ùñÇÑÕÕ•ÊuuuQ*•(•J ¥òÜÜ\¦L™BÅŠ©]»6C† )ô&]WW•JE¯^½prr"<<œG±uëVÖ¯_Z­¦bÅŠ >{{{Fމ 666T©R…BABBŸ}ö .”úïÝ»7k×®E¡PH2êéé¡££#ý­‹ÖPbkk‹™™}ûöÀÀÀ€+Vàêꊕ•“'O&66VÓÃÃ5j`nnŽ­­-úúúÒ&];NÁ1õôòÞ9sOOOÜÜÜ022â‹/¾ ((Hz{Fýúõ155eöìÙ…îGpp0}ûöÅÑÑ‘Ÿþ€²fÍšB×víÚ•íÛ·0kÖ,,--Q«Õ >œîÝ» ¯¯ÏŠ+˜6m‘‘‘,^¼˜Õ«W—º. ß3gĈXXX`aaÁ Aƒ°µµ`þüù2¯Ù³gÓ­[7ÌÌÌðôô¤råÊXYYaeeÅ;w¤k,XÀ¡C‡¸|ù2ãÇgÑ¢ET¬X€Ó§Oóù矣T*iÒ¤ -[¶dÿþýRÛÇS·n]ºuë&óè(Iž¸¸8”J%‹/ÆÕÕµZÍÅ‹¥kïÞ½ËÇL¹råðóóÃÚÚšððð2é¨8yJÓä?'&&&|þùç¼xñ‚{÷îÉê‡ R¤—E@@...˜››Ó·o_ž?.Õ)•J }_´hÞÞÞ²~\]]Ù»w/×®]“Öå×_-;îȨQ£¨Zµ*­[·¦yóæ¢V«yøð!ǧ~ýúÀÿGJZ{4hЀjÕª1`À¼½½±¶¶.sü’âtPÚ<½¼¼pww'++ {{{ìíí %//‡槤ÕÑ¡C‡èèèÀ®]»˜}š‰'²cÇ(_¾<_ýu©íú÷ïOHHˆäôàÁ¢¢¢èر#yyyôë×ÁƒóäÉòòòdëòîݻԬY“uëÖqþüyjÖ¬ÉÝ»w±··'33“ÔÔT®]»ÆóçÏÉÊÊ’Œ#PòÚS*•ܹs‡ýû÷3lØ0&NœÈ?þøV:(iž§NâüùóCLL 6D¡P0~üxyõêÀ‚:uêMTTNNN’ NNN2¦âÖÁÝ»weíªW¯ŽŽŽŽÌ6fÌìííéÝ»·dŒùÆ@ ÁË¡C‡HJJâÆ²ò.]ºpàÀŽ?N»víèÔ©“t´&((ˆ¡C‡R¹reèÕ«W¡~ëÖ­KÅŠi×®S§NÅÓÓ“ÄÄDÉ;ò7ÖÖÖ¬\¹RÖöäÉ“lܸ‘mÛ¶Éâ¢üòòòؼy3‹/ÆÒÒ¥RÉ€ ]÷ôéS&NœÈòåËeÞ3”6Ú>>>˜šš–:¦§§'gΜ!44”´´4–.]ŠŽŽDDDðèÑ#FS¦L‘µ #%%OOOÚ¶mKHH¹¹¹xxxpåÊ ÿÞ¬^½šÇóúõk9xð }úôÁÎΛ"-xzz’””Dë֭ˬ¿­[·òñÇ3räH,,,5j”d$ÑÑÑaÛ¶mÌ;—þýû³víZ*Uª$köìYÎ;ǘ1chÑ¢E‘ò´jÕJvŸ½¼¼X½z5œ;wŽ3gΑ‘ä…:zô(mÛ¶¥U«V<|ø[·n•YžþýûФIisš””Ä¥K—˜4iúúúLš4I¶J¢$yÊ¢•J…R©ä»ï¾“6ã¥ñÃ?àëëKýúõ100<“JÃÖÖ– H^8»ví¢G(•J"""ˆeìØ±’ ’žžŽ‰‰ üöÛo”+WŽ´´4tuuqpp **Ь¬,š5kÆ7xøð¡dâמƒƒ&&&XZZâì쌽½½Ì0Õ­[7ÉkiéÒ¥eÒAIó,nݺ±cÇ 8p 6l@¥R¡ÑhÈËËC¥RIתT*ÉSÒ:ÐÆÒ¢££ƒ¡¡¡Ôö›o¾áСCìß¿…BAçÎËttí}DG@ ,¿üò Ó§Ogذa²7ÅM›6¥Q£FÔ¨QƒiÓ¦áêêJHH‰‰‰X[[K×ü®åøñãÜ»w/^0gά¬¬HMM•®9{ö,²¸IIIøøø°mÛ6™ÇŸåéÓ§ddd”øF^£ÑЭ[7†Z(࣑‘¶¶¶ :” .ðÓO?•:fýúõY²d ÞÞÞØÙÙQ¯^=òòò055%)) é޵µ5::ÿ¿¥¦I“&áååEjj*—.]¢~ýúDEEñüùs’’’8vì˜ä5ùz+èeóG›'Ož0{ölÆÏ_|!ñ)8vA´å¦¦¦,\¸Îo¿ý& DiggGÓ¦MyõêŸ|òI¡~,--quueèС… 7n$<<œððpFŒ!•—¦ƒâæY/^¼ cÇŽL›6¬¬,Ž9B¿~ýˆˆˆ@¥R¡P(dÞSFÒAIëÀÈÈHÖ.77—¬¬,©m×®]©]»6õêÕcË–-Ü»w;wÞG„qD @ðÁ¢Ý€ÿB‹´ñ°²²’ârEA¨T©’ÌKà£>"55•û÷ïHžZòòò4hǧeË–²¶J¥R¶i*kàJsssŒŒŒˆ‰‰)²þõë×ôîÝ777¦OŸ^b_:::\¿~½LãúùùÍ“'OhÖ¬ÖÖÖXZZbee%‹ñüùsÙf188˜sçΡT*±°° ;;›àà`ôõõùè£X»v-;v$==‹/JÆKKK’’’¤~þxO¾úê+:t耿¿?ºººlÚ´ ÈßôBþ›o-999ÒÆ­ jµšž={JAi!?ÎBxx8õêÕ+qýèèèpÿþ}i“@\\þþþ :”/¿üRº¶jÕª:tˆgÏžqáÂ=z$ŲæÕ«WT¨P¥RIhh¨,¾CYå)ˆ¥¥%¯^½’îKff¦l}«ŸÒä)IZÌÍÍùþûï™1cÏž=ÃÕÕU2tíÚ•ùóçK†)€jÕªñÕW_ADDwïÞ%))©XCWA¼½½9qâ—/_æñãÇ’'‡••)))Ò3–œœ,[—ÎÎβx)Ô¨QÈ_û÷ïÇÝÝwww‚‚‚d^#Å­½âÐD†üßm¢råÊIå¥é ¸y–ăHOOgذaдiSjÔ¨Á©S§ÐÓÓ£zõê²ÀÔ‘‘‘’JZÎÎβv111äææâìì\Hm £ÜÜÜR×Áûˆ0Ž@ >hôôôذaK—.•ŽnlÞ¼™¨¨(ž>}ʦM›¸xñ"íÚµ {÷îlÞ¼™ÄÄDbcc¥@‡¥accCÿþý%ÃÁ³gÏd .äõëײXZÔj5.\òß—uƒ P(}ú°nÝ:8}ú4gΜ‘³3¡¡¡¸»»ãêêJXX˜d)ií½ ¥é ¸y–„6m÷¶mÛÈËË#<<œ›7oJ1’¼½½Yºt)ñññ\¸p   úô锼:tèÀ£Gؼy3©©©Ì›7-Z`eeERR»víâñãÇ<~üX ]³fÍR×Áûˆ0Ž@ >x5j„ŸŸÆ ãõë×:tˆ† RµjUV¬XA`` ´IèÛ·/]ºt¡víÚtîÜ™N:•é­5ÀêÕ«qppÀÍÍ GGG¬­­4hèõòåËXZZJîäZoÒÓÓquueÆŒxzz–ynK–,ÁÅÅ777¬¬¬Ø·oñññìܹ“Ÿ~ú ###T**•J2ݽ{—6mÚ`mmÍW_}ÅÂ… ¥c{÷îE¥R1gÎ>ŒJ¥’yžøûûSµjUÚµkGûöí¥´µ:::ìܹ“Ù³gÓ¸qcîÝ»' ÁÙÙY°¶G\¿~ØØX7nŒF£¡Q£F´oßžÄÄD5j@‡>|8 6ÄËË‹.]ºÍ¨Q£˜5k–tÔ¦Y³f4iÒ„Y³f°cÇvïÞ¥¥%cÆŒaóæÍØÙÙùÇú÷ï™™­[·¦mÛ¶Œ7ÈÏ 4hÐ 7nL¥J•X³f ƒ âÅ‹èêê2wî\hР …‚]»vù1<<<èСåË—gúôéŒ;–ÜÜ\bccñòò¢ZµjrøðaÌÌÌˆŠŠâîÝ»²£)nnnT«Vå)­[·røðaÌÍÍINN–KZ°`¹¹¹T¯^-ZЯ_? Pª<%é (´1o 3óööÆÕÕUv]«V­˜;w.ýúõ£\¹r´hÑB â™’’‚J¥¢uëÖ¤§§£R© =+ àöíÛôë×O*S(ìØ±ƒM›6Q±bEtuu144”žëÑ£Gãåå…Z­¦ÿþ¬ZµJZ§jµ\]]Q*•ÔªU ''§2­½?KI:(iž%ajjÊž={X¶l¦¦¦tïÞ¹sçJqbfÍš…³³3jµšnݺ1kÖ,¼¼¼J]ìÚµ‹yóæammMdd$›7oò½/^ŒZ­ÆÅÅ…pðàÁBÁ±‹Zï#Š:äÏÕ«WiÑÂ+«Âç.‚‚€€€wž7\ ÿaaae à÷!’’òÁÎ%%%Ez“\™™™³4…‰ÇÁÁáoëÿ‹/¾@¥R1wîÜ¿m Á›±víZŽ9R¦@‚ÂX[[säÈé8Ï¿‰³gÏ2`Àbcc‹ xœžžŽ©©)™™™E¦–ý(Ë<ÿ­”õØaQ”%è4@bb§OŸÁÕÕkkëüÈzT@ àäÕ«W“››KJJ {÷îýS)nÁ?K^^ßÿ=>>>… çÎãñãǬ_¿ž-Z|°†‘’æ)øûÆ@ ÁÿyyyÌœ9 6lˆŸŸßsÿôÞµ@ Á?‰¡¡!aaaïZ A øùùáçç÷®Åø`)˜éßBÍš5K³yóf:wîüV}¬]»KKK*T¨ edú³üüóÏØÛÛcjjú^é÷Ù³g( YF§¿‹˜˜˜"SQ¿ïÔ¯_ŸcÇŽ½k1Þ a@ |Ð\¹r]]]æÍ›À©S§P(²Œîîî² GPP …BúÄÅÅ•:NÅŠ¹té’¬,//CCCîß¿_bÛ *ÈÆS(L:õM¦YˆÌÌL©/===Ôjµ”EB;fÁãBóæÍ“2_Ìœ9“˜˜œßhÌO>ù¤Ð< E™7sçÎÃÅÅ¥Pynn.ß|ó U«VÅØØwwwNœ8!ÕOŸ>)+IY3fL!Y9"Õ/^¼KKKLLL6l˜,m,@›6mÞh<€Î;³`úÛ3gÎàææ†R©ÄÁÁ¡Ðš*Š%K– P(Ð××ÇÉɉo¿ýV–®·8N:…¹¹9©©©RÙáÇ©T©R©éh$ùõõõQ«Õ¬[·NvMNN“'OæâÅ‹<{ö 2=ÅÑ®];bbbðññ)õÚ¿Š.]º0aÂéï»wïbjjʃ¤2###V¬XQdJÝï¾û;;;T* 6$""Bª+n½ÿÌž=…BÁÉ“'|ƒŽÌøõgå±±±)´¦ÿhùúë¯ÿ±¹þ]ãˆ@ àƒ%77—‰'R§NY¹ !!!DGGrSïÚµ+¦LF-jµšßÿ]V– ¥I-‰ÄÄD4 †—/_baaAÏž=Ë}FÓ'OÞªßÕ«WsèÐ!NžX6nÜHãÆ mŒ±µµåÎ;Ò«W/Y½ŽŽJ¥CCCYùÙ³g±µµ•Þ´Ÿrþüy^zé%lmmiذ!»wïV¶cnnŽ‘‘‘ÎßPÒÙëÛ·/-Z´ÀÍÍ·ÞzK%2oÞ<´Z-?ÿü3£FB«ÕÒ³gÏrÛêÀÔ®]›ƒêµQbb"‡æ³Ï>`ÅŠ2jÔ(\\\hÔ¨uëÖUÊúàƒpttÄÛø2 IDATÛ›Aƒ¬l+¡Ó¦M¾üòKæÏŸOAA1114k֌ڵkóÊ+¯†³³³’Wç³Ï>#::šŒŒ ¾ûî; trsäææÒ£Glll âÚµk:×W£F Ôj5½{÷ÆËË‹#GŽ%òóó•ï³4pRÙïïôéÓ¸¹¹)/++«‡Ê/“ššª¸(•’’BÛ¶m±··ÇÙÙYgiîÛ·oÓ·o_lmmi×®:uböìÙxxx0fÌÞ}÷]6oÞ̹sçtFuõë×:uê”;­æ¿ÿý/Ó¦MãÅ_ÄÚÚšîÝ»óÜsÏTz¿Ò“'OÆÆÆ†Æ+m%Ö°°04 ÞÞÞÄÇÇ+ÛÚ¶mËÞ½{)**Òk÷ÊêSTTĻヒƒƒ~~~DFF2xð`ú”½¯ËþŽfÏž­LÑ{p4I~~>h44 Ý»w/÷;ý·àˆB!„x,ýñÇÌž=›I“&•»½OŸ>¬Y³†øøxzõêeP™mÚ´áµ×^cĈܺu‹Aƒ±téRjÖ¬©GŽ9‚››ÉÉɤ¥¥áíí €›6mâÆ¼ÿþûôìÙ“»wïêcñâÅôïß_yŸ’’ÂôéÓÙ¿?×®]cñâÅXXX(Û‡Ê'Ÿ|Â7ؽ{w¹Oƒ Ù¹s'W¯^¥I“&Ê篿þ:qqqäççtýsÎÊ,]º”3fžžÎÖ­[Q©Tœ;wޏ¸8<==IMM%55Uyúܼys/^ÌêÕ«ÉÉÉy¨sEDDàïïÏÕ«W™:u*¡¡¡T8}ú´N'ÛËË‹3gÎP\\ÌÈ‘#IMM¥U«VÌ;—ÔÔT6lØ WFrr2}úôaõêÕøûûël»uë£GfÁ‚Ê(£GâèèHóæÍ±··'$$„«W¯%©E‹qðàA~üñÇJó74kÖŒ;wî(S?ÌÍÍ9yò$ßÿ=d̘1|÷Ýw@Ɉ§ðŸÿü‡É“'3sæLŒÿÿº;wîdäÈ‘\½zsss&NœXî9÷íÛÇùóç•Ñ;»ví")) 333åûlÞ¼¹²E¿?___ÒÓÓIOOçСCØÚÚ^áµjÊ”)´hÑ‚?þøƒ³gÏêŒløè£¸~ý:™™™L›6;vè;aÂ~ûí7ú÷ïÏW_}¥ì[µjÇŽÓ;ßõë×¹pá­[·.·>•ÝïUµÁÝ»w)..æ?þ ÿþ„‡‡S\\ ”kÉÈÈ`íÚµ :”´´4 $Õ²eK¸víÕgõêÕlذßÿ~øM›6Üîo¿ý6©©©<ûì³zÛbbbHMMåÒ¥K\¾|™±cÇ\nuàˆB!„x,M˜0·ß~›r·±|ùrlllppp0¸Ü)S¦pöìYèÝ»7@Éõ´´4>Ì!Cô‚#C† Á××###BCC133#%%E§ì+W®°eË"""”ÏT*œ8q‚‚‚7nŒ£££²ÝÈȈӧOsóæMÜÜÜô¦Õ¯_ccc:vìȤI“tööö(Cí UÕ9«LÓ¦M’ÎùƒÓ4zôh&L˜ÀÌ™3quu¥sçÎzS˜Ê“MRRãÇÇÄÄ„œœœØ³gO•ÇÞ»wO'ˆZ­¦°°Ðà@Òï¿ÿN§N˜7o/¾ø¢Þö‰'Òºuk'·nÝ"66–èèh.^¼Haa!ãÆJ¦,õíÛ—ºuëâææ¦s<¨4GiÞ¬¬¬pttÄÇÇ­VKVV–²TT øøøÐ¥K²5jD—.]033côèÑz#“6lˆƒƒ:ub„ /{]Õﯨ¨ˆˆˆÞxã ÚµkgP™•122"==ŒŒ ¬­­u‚?üð#FŒÀ‚víÚѲeKcÕj5þþþäååéŒèªLiûÚÛÛÿå:WÖï¼ó&&&J îìÙ³œ?žŸþ™O?ý3336lH§NtòèôéÓ‡#FT8Ò©<ëׯgÀ€¸¸¸àáá¡7ÒJ­¥#@6lhP¹FFFܸqƒ3gÎ`llL‡ ®SuàˆB!„xì:tˆƒ2hР ÷±²²¢{÷î 8°ÊòÊvÞMLL8p ‡Ö™~PväH¿~ý¸xñ¢Np$>>ž–-[*CÖ¯_¿®—`ùòå´mÛVgn¾‡‡ .dêÔ©ØÛÛÓ»wod™«W¯fÏž=Ô©S~ÿýw2Ož\I ™­“¤²\$wîÜPF¤”& ­Q£†ò*;ÄÞÞž&Mš¨WÖƒç,ÉRjÇŽœ9s†[·n1uêÔ ëô ª~ü1@…#¿Ô»wo4 M›6åÂ… JG}þüù̘1 üýýqww'66V9¶ª¶Ý½{7»wï¦G¯’SZƃÓFEm`jjŠPòï‘ÙÙÙ\ºt (9T¯^=êÕ«Çž={tî¶oßooo ®GVVÎÎÎÊû²—š5kGŽáÈ‘#lݺՠr_}õUBCC ÇÞÞÞ <9ÕI‚#B!„â±sèÐ!Ž=б±1*•Šï¿ÿžÉ“'+«±”š={6/¿ür…å<ØÉ„’¤™~ø!#GŽdðàÁ%Á‘K—.‘ššŠ··7®®®8pooo²²²ˆˆˆP¦`¤¦¦bcc£ …/µdÉ)5¥^{í5~þùgÒÒÒÈÊÊâ›o¾Q¶µlÙ’~øœœZµjUî´###üüüèÚµ+[¶lÑÙÖ¶m[®^½ÊéÓ§õŽ355-wÕCÎY™òVõ(­çƒmò {{{† bP@ÆÑÑ‘‚‚ê•+W Jrêëë«3²'%%E/ákeõ}ë­·X»v-»wïÖ H•æô˜5kÏ<óŒÎ1žžž:¸²å;::’­l+;òãAÉÉÉXYY•›{£TUí\ªl )++KgÔÀ3Ï<óP#¯Êªè÷·gÏ¢££Y¹r¥’»¢¬òîË… räÈ©S§ŽÒQ/]ÙÆÅÅ…Å‹sùòe>ýôSlPÛðæ›o2cÆ ¾øâ –-[fPc;;;j×®ÍÞ½{+ܧ²û§²6¸ÿ¾ð(((àúõë899Q»vm¬­­9yò$§NâÔ©S\ºt‰ñãÇ+dzsçN%hkH}œœœôîƒ988(9RÊ ž”ÇÔÔ”iÓ¦qâÄ ’’’X¸p!¿þú«AÇV Ž!„BˆÇΰaÃ(..V^½zõâÃ?4hÙÕQ£FqøðaîÞ½ËòåË•) PÒ¡ŒŒŒd̘1Ì™3GI´ `ccC5Ðh4¨T*üýýÙ¿?ÞÞÞܹsêׯ”$û,û4à—_~!==]o•šÔÔTvîÜIAA¦¦¦¨T*¬­­•í111ܹsGPv”ä¹ÿ>gΜ©°S4xðàr§Ö”>y~PUçü«\\\ÈÈÈÐ[ÝfñâÅlܸ‘›7o’Mtt´2-J:ˆyyyëüíèèÈóÏ?Ï'Ÿ|BAAñññdffÒ¦MþüóOòòò(**Òù ,,ŒØØXsæÌ18?Ï_•““CDDË–-«0ˆU¯^=~ùå‰F£Q:æ5jÔÐIf °aÃ._¾ ”Œ¶°²²RQAAA,X°€{÷îñÓO?±ÿ~¥ÜÏ?ÿœš5k‰““cƌ᭷Þ2èZÆOTTIIIܹs‡Í›7óÛo¿)Û+ºß iƒ™3gRPPÀ¼yóðððÀËË 4h@TT¹¹¹Ü¿ŸíÛ·ëMß«HEõ féÒ¥dee‘––ƺuë *¯*?ÿü3ÇW–}t’­BÉŠ&LÀÁÁ|||ˆŒŒT¶ÇÄÄP§N4 Gå¿ÿý¯Îñ 4@­V@»víxûí·õ®¹ÿþå>1ŽŠŠbíÚµXZZê<}®êœ•»»;ÄÏÏ777%1¦QQQ¸ººR¯^=òòòX°`r\xx8jµš'Nðú믣V«•‘0+V¬àÀh4¢¢¢X³f ¶¶¶@I²HµZÍ?þÈĉQ«ÕÊ”€Î;óÞ{ïÑ­[7ÜÜÜpppPò”zûí·Ù°a®®®®´Ñ­[7‚‚‚2dß}÷Û¶mC­Vë¼®\¹ÂóÏ?ÏÔ©S ÄÅÅ…ââb>ÿüsºvíÊ AƒhÞ¼9éœgÏž=ØÙÙIxx8Ó§Oÿ_¿^zé%æÍ›Ç3ϯ¾úJ ¤Œ;–'N°~ýz~ûí7´Z­’cÃËË ­V«#†ΰaÃèÛ·/†I“&é¬*ºß«jKKK d´ÆòåËùî»ï”ú­^½š3gÎP·n]œœœøä“O nûŠêóòË/„ŸŸ=zô {÷îåŽè)ŸŸZ­–'NV«eåÊ•@ɨ¤jÖ¬I»víøàƒðõõ5¸¾ÿ4U×®]‹ãââÈÌÌäðáôk×''ÆÉñ¨ÅÄÄTš|J!Ä“%99FSÝÕx$rrrÛkÉÉÉÑyJ_™Ò'àÿ¤ÌÌ̇Îa!ÄÃØ»w/ÁÁÁÄÇÇ+£Nª[tt4›7ofýúõÕ]•'N¿~ýhÙ²e¹DãÆC­Vë,‡üO2dµ©ŠT” ûAYYWؽ{Mš4ÁÙÙ™ÐÐP9"„B!„xºµnÝšÄÄD7n\ÝUâwÿþ}(**"''‡uëÖ•›¼÷I'Á!„B!ÄS¯E‹,·Š“ââb¢¢¢Ðh44oÞœaƼdó“ĸº+ „B!„B×°aÃ6lXuWã‰dHâæ§‰™™ÉÉÉÕ]j'#G„B!„(ÃËË‹]»vUw5ªMzz:æææÕ]ÀÀÀ‡êLwëÖ:uêè,Ùûo””„——¶¶¶JØ¿êÖ­[hµZ x4ÿj7nÜ@¥RñçŸVwUþ6B!„O¼V­Z±råJæÌ™C·nÝô¶üñǼúê«•–qîÜ9T*wîÜQ>kР111ÕaïÞ½Ô«WOïóàà`T*•ΫeË–Êö-[¶àãヹ¹9¤¥¥pèÐ!½xëÖ­™7ožAõù+¦L™‚J¥âÇJ:L¦¦¦ôèÑ€=zè]‹Z­6¨ì={öдiSÌÍÍñððàÀ@ÉŠ–©R©”¥sW­ZE‹-011)w¤ÅüùóÑjµ˜››Ó´iSîÝ»”¬ˆRv…5kÖàåå¥wü¨Q£hÖ¬™Ám”˜˜XéSøO?ýGGG¬¬¬8p ÷ïß@¥RÑ®];e¿qãÆ¡R©8tèP•mpïÞ=Þ|óM4 vvvôíÛ—7nèœwÊ”)Lš4‰7nè\wEõ™={6*•Š;w*ûsêÔ)lllHMMeþüùå^c^^¯¿þ:–––8;;3{ölƒÛoÙ²exzzbccCÛ¶m9yò$ß|ó²R”$’V©T¤§§WYfaa!o¾ù&ÖÖÖÔªU‹%K–(Û6mÚDãÆ±´´ÄÅÅ…qãÆ)K`aaÁÝ»wظq#*•J'¸Ô¸qcåû}: œ:uŠC‡1cÆ e{vv6YYYlß¾gžyFçøŠÚ`Ô¨Qüþûï$%%qîÜ9:wxñ"õëרú˜››³pᇾÎiÓ¦qüøqΞ=˺uë˜S¶GFF2~üx:uê„››îîî±bÅ 5j...4jÔˆºuëêÔÇÌÌL)»ì´ŸŽ;Ò³gOjÖ¬©×ÑÑÑLž<™nݺáèèHûöí133«´íJMš4 ­V‹Z­Ö›Vãååž}û”÷†S§NUYfll,}ûö¥E‹¸¹¹ñÖ[oé”ʺuë8pàÏ=÷¦¦¦U¶AFFË—/ç믿ÆÛÛ „V«`„ hµZRRR A«Õ*£‹ªªOÛ¶mùå—_¸zõªAmVö:ßyç\]]yñÅ VÊ=}ú4nnnÊËÊÊJ ,¤§§ciiIÇŽ122"44”'NtN///¦M›Æ /¼@íÚµ‰ŽŽV¶ÅÅÅñæ›oR«V-:tè@›6mˆ‹‹J‚}M›6E£ÑP«V-jÖ¬©NúôéÚ5k¸ÿ>GŽÁßß_缦¦¦˜››ëÝ.^¼H×®]±°° nݺøûû+×Ò¿úõëG­ZµhѢݻwW~cM›6%$$„>úˆÂÂB&MšÄŒ3”XÝï{öìÑùôÊ+¯(Á.KKKêÔ©ÃÉ“'õ~·¥–.]ª÷ï”,}\:Uì¯L«‰ŒŒ4x„]u’àˆB!„xbeff’Ào¼”Ròòò¸~ý:GåæÍ›äçç—ùöÛo™7oÛ·oÇÁÁ¡ÊsEDDàïïÏÕ«W™:u*¡¡¡ÊÓÙsç·§§'©©©¤¦¦òÒK/镱bÅ Úµk‡««+PÒ‘,ÛáqwwÇÈȈ3gÎTZ333BCCu:º±±±„‡‡°zõj6lØÀï¿ÿÎ?üÀ¦M›tŽ¿ÿ>·oßæòåËDDD¥l«Q£-[¶$!!k×®ááá¡wþ[·n1zôh,X ×‰[ºt)3fÌ ==­[·¢R©8zô(ŽŽŽ4oÞ{{{BBBºC^ž£GòÇP·n]eÊDQQ‘AÇ~üñǤ¦¦ÒªU«ÿ¹¥ü>½¼¼8sæŒ2¥¢wïÞ¬[·ŽØØXúôécP™ÇŽ£fÍšz£BJ͘1ƒÔÔTêÖ­K||<©©©Œ9Ò ú˜˜˜ÎÒ¥K ¾Æ?ÿü“óçÏë•{úôi|}}IOO'==C‡akk«Ü—Ï?ÿ< 4 11‘üü|âââèÞ½»ÁçNII!))‰Í›7óÞ{ï)S„NŸ>MýúõY¸p!IIIÔ¯__©ÀñãÇqvvÆÎÎŽäädÑ*Ï=÷çÎãûï¿§C‡×eĈlݺ•›7oröìYŽ=JÇŽËÝ÷ðáÃ4iÒDy?}útbbbˆŠŠÂÖÖÖ Q"­[·æÞ½{9r( nܸQç>* ôÄÇÇÓ«W/ƒ¯eÕªU;vÌàýWB!„O¬yóæñòË/+Á5jàááÁùóçÉÏϧuëÖüöÛo\ºtI'8²råJÆŒÃöíÛqtt¬ò<ÙÙÙ$%%1~üxLLL ÁÉÉÉ ©e-Y²D§cvïÞ=œFFF˜™™éä=Ñh4Ê«ìW_}•Õ«W%¥„„¥“µ~ýz €‹‹ zO‘‹‹‹3f ¦¦¦ëcúôéÈ#èÙ³g¹×1qâDZ·n]îöàà`š6m €··7*•Š[·nKtt4/^¤°°qãÆéççç§\§¯¯oÅXÆ­[·Ø´iûöí#))‰µkײ|ùrƒŽý;<ø}ªÕj ÉÏÏÀÕÕ•û÷ï³aÃ:uê¤w|ym••¥¼óõõÅÙÙÙ Ü3Õ'//OùlèС|óÍ7_cnn.ÅÅÅzå–½g¡dôRDDo¼ñ†’kÅØØ˜ÈÈHÂÂÂ077gýúõ•Ïâ•W^JÚI¥RqùòeîÞ½‹••qqqüòË/X[[ëÔÇ×חÇ“À€ôî¯.]º0vìXƒVP’Ó#??[[[|}}>|¸2R­¬/¿ü€*Ÿ¹ººòÖ[o1}útfÍšeÐT###z÷îÍÚµkضmžžž:Aª   –/_ŽAß‹þýûWøz”$8"„B!;_|ñ…ÒI¼té 6D£Ñè$UÍÍÍeÑ¢E¼ýöÛ:Çz{{³yóf6lˆ¿¿?;vìÀÔÔ;;;eŸ½{÷âââÂ?ü |fdTþÿ:‘‰‰ öööÊçÎÎÎJÂLC8p€´´4BBB”Ï,,,ÈÍÍUÞ‘ŸŸ¯“HñÈ‘#Ê«ìÓ瀀îÞ½Krr2›6mÂÏÏOÉ’••¥ä«(­kYfffÊ9ÌÌÌtê%I#½½½ Ó»Ž}ûöÇܹs˽ÎòFšXXXÐ¥Kš7o޵µ5ÇW’¾–Ú¾}»r†®&daaÁàÁƒqqqA«ÕÒ·o_¥Üò¾ÏоãGåÁï377W™ŠTjРA„„„”»bPymàääÄõëו}~úé'Z¶l©Œ0´>5jÔÐ9·——µjÕÒû>*¢V«Q©Tzå>˜ü³4Áè¤I“”ÏvíÚÅ;ï¼Ã®]»ÈÏÏgÔ¨QtèЂ‚‚J¥ÊŽR211Qê`iiÉ;wؾ};ï¼ó·oßÖ©±±1...têÔ‰ºuëê$¬§~ýú¼ð •^{Ù Æ«¯¾Š‡‡wîÜáÂ… üßÿýŸN"X( R~ùå—lذAo U‡°´´ÔIÎ\•°°0%8²víZ½ß§••Ý»w× Ä@Éï_£Ñ0jÔ(bbb”[=jð¹´ÿ~¥œU«V) ƒK§{=ŒeË–=Ôç•ñ#-M!„BˆÀ!CèÛ·//¼ðñññ8;;ë<­^¾|9M›6åÙgŸÕ9ÖÛÛ›ï¿ÿžW^yÆŽ«7¥fîܹXYYÑ¥K:v숻»;–––ÊJ¥[YYáèèHAA×®]S$W®\ÑI*jdd¤LW(ÏâÅ‹ Ó¹bcc•÷©©©áãã£äNqssS¶—í`ñòË/³jÕ*222”© PÒ¡.¸©*‡ÉƒŒ•œ)es«0tèPfÍš¥—L´Ty Q===uòÆ”×V...zAœªxzzêtXË–kiiYîwisss%÷Bqq1·oßÖÙ^ú=ê\¯¯¯/)))Êû””½Ÿ•% -¯ ž{î9®_¿ÎÙ³gñööÆÑÑÑक़}||ʭσ#†Ê×_­w¼©©©NBɽáîîNJJ -Z´PÊ-;cÏž=DGG“œœ¬ÜHNN¦yóæJ^¡C‡2zôhRRRÊý¾åwYÕužûõ×_)..&..Žƒòé§Ÿè%E~Mš4Q¦øŒ;–6mÚò—Ë–-Ó)ò¨# #G„B!ÄcÈÚÚZIèX£F œqssS†Š3gÎÆŒ£w¬‡Âßߟ&Mšœœ¬166¦eË– :”þýûSTT„““ÞÞÞ|ýõ×ܾ}›Í›7sáÂ5j„££#Ï?ÿ<Ÿ|ò ÄÇÇ“™™I›6m”2]\\ÈÈÈ(7¹knn.«V­Ò&ÞµkW222Xºt)ׯ_ç£>¢]»vwZ^}õUV­ZEbb¢L‚’©-K—.%++‹´´4Ö­[gPyUùüóÏqvv¦wïÞäåå)¯ª„„„˜˜È‰'ÈËËcáÂ…´oß^gŸüü|2Kƒ¥Ó@ uþ.-÷Ûo¿åúõë\¾|™ØØX¥ÜÖ­[“˜˜È¹sçÈÉÉaåÊ•ÊtŸªx{{“”””¬&RšÛ¢” ...zӪˆåàÁƒddd0gΜ‡Zu¤¼6pss#<<œaÆqáÂnܸAff¦Aå•WŸ²÷I©ÐÐP~úé'½|-¾¾¾œ8qBïž ã‹/¾ 33“}ûö±~ýz¥Üœœ"""X¶l™Þ}ܤI~ýõWŽ;Fqq1111XYYQ»vmZ´hAJJ ;vìàöíÛ,Z´OOÏrñ>¨oß¾,\¸+W®°{÷nöìÙ£ŒªX¸p!{÷îåÚµk8p€/¿ü²Ê" ,\¸«W¯ráÂvìØA£Ft®eåʕܿŸ«W¯²yóf%/Ìþýû‰ˆˆ 66–ºuë’——GAAA•ç„Êï÷Ò$¶£G¦N:å.M]žZµjáææ†Ê¿­&&&ÿçŸêÜ—˜šš*åXXX`gg‡›››’Oé¯( ˆüàˆB!„xÌ?^¯ƒ´eËŠŠŠèܹ³ÞþÞÞÞѤIÌÍÍyöÙg+ìD|øá‡äääðÅ_%‰ ׬Yƒ££##GŽdéҥʪ*+V¬àÀh4¢¢¢X³f ¶¶¶JYîîî 8???ÜÜÜØ±c‡²mݺuØÛÛÓºukók4bccùè£pvv&%%å¡’c6kÖ š5k¦Ó}ùå— ÂÏÏ=zн{÷G2¥ä»ï¾cÛ¶m¨ÕjWUÓ‹žþy¦NJ`` ...ëåš(]9¦ôõ믿%+ ©Õj¢££ùæ›oP«Õ,Z´(ybÝ AÜÝÝiÒ¤ !!!Jê•W^¡W¯^¼øâ‹xxx`ff¦³Œme&OžÌ·ß~K›6m8qâD¹ùæÎKxx8VVV$$$йsgÞ{ï=ºuë¦óÌ­R™ŠÚ`þüùxxxдiS<==qvv6h å.]º(õñõõ¥qãÆLœ8Qo?SSSÂÃÃõFóøúú2dÈ<==±²²âæÍ›|ðÁøøøàííM¯^½øàƒJ¦¬eddЯ_?eÚEé4–öíÛ3eÊzöì‰ sçÎeíÚµXZZâîîNtt4C‡ÅÉɉ7*+ÎTeĈàííMxx8_}õ•¨¸}û6‘‘‘¸ººÒ³gOù裪,³víÚÄÄÄàáá¿¿?]ºtÑ t•.ìä䄟ŸÍš5ã½÷Þ`Ñ¢E\¾|™^xAù.ËN§«Le÷;”¦vïÞ]î”·ª4kÖL/ÿÐo¿ý†V«UVºòòòRV?*5qâDû²ì(µÒ:•ò÷¿ø»#ª®]»ÇÅÅ‘™™ÉáÇi×®-NN7\MˆG%&&FYºN!Ä“/99Ygòã,''ç±½–œœƒŸ˜2 àQËÌÌ,7OEeºtéBpp°²DèÓ¬C‡„‡‡—»Dg©qãÆ¡V«™6mÚ?X³ÇC@@£G&44ô‘–»wï^‚ƒƒ‰×a$ÄÓ®t•¯¿ÂÆÆÆ ý²²®°{÷š4i‚³³3¡¡¡2rD!„Bj2rD!„B!„O5 Ž!„B!„â©&Á!„B!„B<Õ$8"„B!„Bˆ§šG„B!„BñT“àˆB!„B!žjB!„B!ÄSM‚#B!„B!„xªTw„B!„B!ª1@qq1fff8;;•u¥Z+%ž^;Êý'„O‘Zµ\«» Ìã|-µj¹Ê…B image/svg+xml These SAML tabsmust be active This window showsSAML Message This window showsweb page Selected HTTP mod_auth_mellon-0.16.0/doc/user_guide/images/saml-tracer.png0000664002412700241270000041131113367654776024004 0ustar jhrozekjhrozek‰PNG  IHDRNmP• pHYsÃÃÇo¨dtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwxÕÞÀñïÖôÞ„¡÷.Mª((v vl×î}­×.Š,XPP@D*Ò{—’ÐÒ{Ýúþ1ÙM6Ùd³! ø}žgŸdÏÎΜ9söÌœ9eT(T@lûöí‡/X°à…¸¸¸æ:NƒB!„â¢b4Íñññ§'Mšôò¡C‡þNVJ¥¡Ó’%KÞ3fÌX³ÙŒÅbÁb±4j„…B!„ O­V£V«Ñh4üðÃKo¿ýö'ƒ Õ’%K>=zôX£ÑˆÅbÁjµ6v|…B!„ÀjµÚºwïÞ¶M›6-.\¸IÕ¾}û©{öìùÒd2©;’B!„Bˆ¦E§ÓY»wï>Eµk×®øvíÚµ‘V!„B!DE*•Š£GÆ«²³³K´Z­¾±#$„B!„hšL&S‰ª°°Ðb±X¤›’B!„Â)µZmUK¥A!„BQ‹Å¢R7v$„B!„MŸT„B!„.IÅA!„Bá’T„B!„.IÅA!Áò+¸ûþ}åÕŒ{97Ý>…w?øƒÁà°Ü¦-[8lWM¼‹ÅRi=ƒGŒbà°¼ø¯Túlëö 6‚ÃF°aÓ&ûò7ß1µÆñLIMåÐáú¹‡B!.4RqBˆöË‚_yùÕ×9rôÍ›5£k×. þÙ°Nç°ìŠU«ÈÈÈ`×î=U®sÕšµœMNv›ûÃçןüÊÔ{îãà¡Ïy]B!ÎoÚÆŽ€B\l-ùFÃß|CttV«•´ôtTª²²KJJX·~;uâÀÁƒ¬XµšÞ½zVZŸŸŸùùùüôË|}ð9Âö; $;;»avL!.r‡¨öókW7X<ºuíÂÿ÷_ÈÉÍå™^d×î=µŽ‡T„¢Y,ÔjÇGè¨T*ÂÃÂÂ6mÙBaa!“®™€ÅbfͺuÌxäat:Ç¢;4$„^=º³ä¥Üyûíøûû1÷‡ñõñᲑ#øeÁ¯µŠç£O<Å–mÛxxÆ ¼ä^ûïK =–iwNe÷Þ½ì?pÁðüžæÉgžåБ#äççÆ„«®âæ&£R©0 |÷ìZ³–Ĥ$üýý7f4÷O»›ü‚>úäS6nÞŒÉd¦oï^<òÀb0œnï¥çž­Õ~ !Ä…®Ul,{÷íçÁÇf0óÝwP©T<4ã ŽÆÇÓ2&¦Öë•®JBÑÀ®¼|F£‰[§ÞÉ›ï¼Ë¿‡;]î¯U«Ñh4 p Ã/½”¼¼<û…|E“'M¢¨¸˜_-âLb"k×­cüUWâãí]ëx^;a¼½…câ5xìᇸfüUöÏ?ÿj6j•šݺáëë‹F£Áßߟ W]Åãûœõ•ñ/¿ú:_}ý þþL½ý6Æ^6ŠÀ€¬V+Oüç–ýµ‚q£G3bØPV­YË+o¼áŸŠÛBˆ¦jãÚÕ¯†öÞ[oÒ¼Y4 ÇŽóàc3xð±'*2’wÿ÷†ëTAZ„¢Ýpýu„óé_²è÷%,ú} C dú½÷Ó¢…lÜ´™Þ½zâëë˰¡—2ëó/X±jƒ¨´ÎîݺҾ][æÿúg“®½†E¿/©u<‡ Èî½{Ù±s—DßÞ½ì¸{õìa?Y­V^|öû÷›5‹æGcÏÞ½DF„³æï¿éѽ½ÿêr]²vïÙËž½û˜tÍî›v7'NždÓ–­ääæâåéYåö„BTÆGï¿Ç€á—^êPi8vâó[Èüß:|v69™Ö±±UnO!„s:­²÷:zj¾ášT„¢‘xxx0nÌhÆŽ¾Œ>žÅÏó°iËV®¼|+W+MÛÇŽçØñãöï&6lÜȨ•à>œ?ýŒŒÌLn¸îºÛ›M›·ðÒ+¯Ò¹S'~p:……E¼÷áL¬(-ÎZ üý”nG“®™À°¡—:|Ö¢Yóz޵B\x223yðÑœ8yÒ>¦áä©S<øØ >zÿ]B‚ƒkµ^©8!DÛ³w]:wB£Ñ`òóóe†¤ìœ¶nßÁ€þýxçͲ¾¨YYÙ\5q"+V­vZqÐé´|ùÉ, ‹ŠhÛ²NâªQ+Cá \.»mÇîž:…~}zs4>ÞþYû¶mXµv-“®½µºlˆ]«Ò…´ôtzõèQi½Ÿm!„ >²ÚÏ7¬YÕ@1Q¸š]©¾Íxê?öJÃGï¿ À<ÆÉS§xìɧùæËÏkµ^©8!DJNIᾇ&0 €Ö­Z¡Õi9OVV61-šÓ»WOV¬ZÙlfè¥C¾HnÝÙ´e+yyyøùùUZD„ëîI©i©¼ðßÿsëÖµ+“®™PiÙè(eºØO¿øŠø„cX­Vn¿åf§ë à‹Ùs8rä6o¶צ c.Åò+¹ÿ¡G}ºìÚL8¥R©((( >>žŽ;âëë뺂VúÿÅÀý¦• U¹L«9þµ±eN«ÕªÔt­V(½ÐºØX,’’’¹ì²1ètZ~ûm!©©©´ì׌Y'_m1±ZRè‘@at& ëð°ø“±YE/KO4 …–\Ì*~ªàÆÞ ^`` iiiÌš5‹M›6‘íPØVÌó%%%lÞ¼™#Gްwï>n¼ñ""Â%îB!.L¶JC¥Ê¥ç£*.j“’’HJJ"..ŽßÿAƒ6ÞN4q…ìÛ»—°wï^ºv튧——ó Zù¿6*\À•3·+*Ê2.2«ísPšÃTå*¶÷V«•úâ“­ÑhèÞ½+K–üÉUW]ÎåW^ÁœSr̼Ö^1h4*Ò­IäRÑk´„ú CKÚeGؘó]ü/auÉ"Õmä5©±w炈V«å¹çžÃh4••…F£Åh4’››Kqq1f³‹ÅŒ——AAÁáïïGaaz½__ŸÆÞ !„j[*V Àþ÷ðáÃŒ3­V˨Q£Ø¶mQQQ¨/Âë1W<È´iÓ £oß¾ìÙ³‡Î]ºà¡×—Ý4/ŸÖT¾q~!·>¸]qðñõ-kq rµQC%•ÕŠµ´v±´8X,vîÜ…Ñh cÇŽ;vŒ¡CGb0šxs÷ódúEï¡áD~zµF‹^­E¯Ñâ¡Ö¡×h ò!Ãã‹ wÐ)²=iÉi½[½^N§Åd2£Õj ¡eË–„‡‡cµZ)..¦  €ôôtŠŠ KFk0MX,<==Ðh4½B!. å+ ê**/jQ©hÓ¦ û÷ï§GtìØ‘’’6lØÈàÁƒ.šk²šêСÛ¶mc̘1øùùÑ¥KŽ=J§ŽÑêtίqRy¸@¹]q0 Uö§+_y°Z­U6׸J\«ÕÊò•+Y½vGÆãááAëV±Ü8ù:ºvîÌÕ¯ç¥çŸ¡WîF¿A¥¦¦B—.]ؾ}+ƒÃÇLJö|Í>ó^ÔF5:³ZljÂDN$’o.$PëM{¿zu"@ï‹N­ÁËÛŸ3ùi˜ó?çÛ›õÙ,ü} o¼òr“OwU<æ ‘üýýIMMC­Ö`µZÉÌÌ$""‹Å(}M•îKJ~6›Íäç瀇‡Gµë~îåW8|äh¥ðÞ{›°Ðÿñ'C $$8ˆ[§Nã‰Ç¢[—.u¾B!Εg­å–±-ЦM¶nÝʉ'ˆ¥{÷î³qãF Ti;>ù4þ[)|æ»ïо]\½îcc $,,ŒíÛ·Ó§o_ˆ‹‹#1)‰ØØX{…­RÍ…GŸ|šýûsÝÄkêwê™Û£ÑèX³…Jïíl•‡ ªKd«ÕÊ[ï½Ïž}û™rë-Ó·¾®QöîÝGNN$'ŸÅ××/ooÎ&%JË–- ÀC¯WzÍh4•*hç«Ú¤k­æU­²µ¡ô3ÛL3öqþ¯.¡×ü½Ž»öðéÌ÷ ´‡·kÛ¶6QmT;vd×®ÝüôÓ<&MšÀÒ‹IË¢Ä`B¯Ö‘lL¡yA0Ï´{‘ácF³ió&>ùäKŒÉhÆeóoÖ>:tC«Ñ¡QkhÙÌé¶âÈÊÎáî©w0ï—¾Ál¶`µZ),,D«ÕâïïWã‚ÄC¯Ç߯ró‘·—ŸÍ|¯Êï™M&9VBqª²«’Zm¿˜Õh4”Þà²:¼¢££® FŒÁ¢E‹Ø²e+ýû÷sØ–§‡ ¼‡ÎÕ×5ÊîÝ»¹æ¥@£ÑеkW{×ã6­[£Ñhìçt///û÷ÊŸçÏçÊCmÒÕíŠCù.¶N»%•Ÿ}¦|G¥šŒ1ŸûÓûò+ÖmØHvva¡¡ÜqëÍ »t×\#ÿ}á9>ûj6'Ožâçï¿uÈçÂÓÓ“®]»—· £Q90O®#³Y.>V4V=Û3-êN†GŒ`À%0øäócѨ<<Ò„Pd,Âb*¦ÈRLaN·µqóztëÂà˜õÙ‰§C»vdfeqíwðíWŸÀ_+WñÓüÌþtϾô2Û¶ï`ìÕ×лW^}éEJJJøfî÷¬ß¸™üü|Ú·kÇý÷ÜE‹æÍXµf-?Ìû™ä”T‚ƒ¹êò˹~Òµ—”0çÛïØ¸i EÅE´oÇCÓï'"<Üiš«Õêj·Õ˜ôz=¡¡¡¤¦¦’‘‘ŽŸŸ‹£±ìnŽÑh$22¢žq¢¸uê4žÿÏ“´këØ$üêÿÞa÷ž½L¾u ݺváÙ'gŸŸÏìo¿gÏÞ}h4† À ×M´ÿn¿ë^ž~üQ¾žû#‰‰‰|1ëÃJùÜÙ2f³¹Úõ®[¿‘ “š–FP`cFdüUWJ·ÆŸ~YÀ–m;((( ®Mk¦Üv Í¢£ÈÊÎfÚô‡ùè½·‰Wòôš¿ÿá·ß—ðáÛoVµFÃó~aÛö׺5Ó°ÐP—iP]\…â|ଋ’ºÂL—§]–lÊ_§3†÷Þ{¯RÅ¡*Ÿ5›­Ûw0ëƒ÷Ðëõääær×½÷s×”;sÙ(®ž4™«.˶;INI¥}Û¶<úÐt¢£”î®®-*^#tî܉»vŽ×(uÁËË‹6mÚTù¹­òUiæ$*§©»ˆ«'Mfò¤‰¬Z³†ŒÌ,ztëÆ”Ûná—_U®ÉT*¥Õçò±öå›Bºº}•c; ƒóDr¨4Tß`k¨Jqq1gÎ$Ò£[w·âôú[ïàããß|DqQ1Ïÿ÷ÿð÷÷gré]þApÍø«  bÙŠ•¼ýþ‡ôëÓooo F#3?ù”é÷ÞCófÑuViÈÎÎæôéÓ¤§§Ó“ÉLQQ!¹¦OXX(û$4$Àiš¿óÁÌ*·UWÇä\xyyLzz:¹¹¹hµZL&e tQQ1¡¡!.Ç5Td0)((°¿W«Õ.÷õÙ'g0÷Çy$&%óÔŒ‡íá|ü)ÞÞ^¼÷ÖëóÆ;ïâççÇÛE¼ÑÈ—_ÃÔÛo#*2Òévœ-óê›oW¹Þ¬ìlf~ò÷Þ=•Kúö!¿ Ü¼\ûú¾üú;Žç©À¼_~å…ÿ¾ÊGï½Uãô©Ÿ>ùœ3II<ýø£„††pèða‚KbT]¸Š«Bœ*µ68™ßh4Vêªd±XìïÃÃÃiÞ¼9V«£ÑÈ×_ͨQ#+m«Ä` ??ßþ^­VãííÍm7ßÄ?62oþ¯ÜzÓ |þÕZµjÅhÛ:¬Vrróxhú}„†„0盹<ûâùêÓQ«ÕÕ^[xyyUºF vzRrrrX¾|9 ÜìU«Õèt:ôz=Z­___bccí]ïm=jÊòÿWü¼:f³™Ô´4ž{úI´Z-O>ó<3ž~†é÷ÜÍ]S¦°fíZf}ö9ƒ\BPP`“IW·çá²]è”oúªø¾|¸í³ò«’œ¢ÌRãø$'§°}ç.ºÿ^ˆŒŒ`ìèÑlݶݾL—N C«Õ2rØP É))öÏï½ûNºwíBHpÝ=aÓ¦-·¤[·DEEa4š0Mhƒh¦"Æ#†Íiæ^£wøî5>1êì4:lÏõÅOsŸ÷Û<:›ÿ5›Ï„¶÷³iã‡å“ΞåÄÉ“ôí­Œ£è×§76o>§øgeçð×ÊU<<ý>b[ÆÀý÷ÜV«eíºÈËS “n]ºàëëKdD„½¢’ÍŠÕkxôÁˆmƒ·7ýûöq˜e¨|š»ÚVSáççKPP©©©PXX@QQ!AAøÔ¢ßç¼ù¿rÇ´ûí¯÷?ú¤VñJMMc÷Þ}Ü=åvüýücİaìܵÇa¹Ûo¹‰Î;Tuk^ùe\­×VtîØ"ÂÃh[zç&'7—µëþaÚÔ;ˆiÑ??¦Üv3†›·ÖxßÊÇ'''‡¿×oàÞ»¦Ó¢9Þ^^ôêÑFsNqBˆóF…Ðj'­ ¶‡ÀÀ@‚ƒƒ !44???Ž?Nhh(V«‹Å¼yóèÖ­+-[¶¬´©o¿ÿko¸Ùþzâ™çåûûîåÇŸaÉŸËX÷Ïz}pºÃóˆa—Ò¥S'"#"xà¾i$§¤°ïÀÁŸïëãºÌ™ Æ@`` >>Þdff‘‘A`` ÇOœ  ´«–J¥";;Û>9Š-Ìö·ÊiY]1ìRZ·jEL‹ôêÙî];3bØP‚=j$f³™Óg–oìtu¿_EéÅ¿Š²g6TœbÕÞ…©ô½½;S¹ï;c«0ddfâíí]£èœMNà†Û¦ØÃÌf3ÑÑe#ˆþ{ˆ_~ý•ÃGâ)(,°wêeÚLƒÁÈ¿ÿž@§Ó Óé ô¢µO…êZè[à£ñ&LL¶1oMÙ…§N«'&8–P¯–øú’›QL¦1£1Ǿîò6lÚBddþ~~äççÓ¹SG~úe>'O¦eL‹ZÅ?1)ZMûvecK´Z-;´ãLb"c.Å%ýûñÀ#3;z׎¿šÐÐPÎ$%¡Õhh[¹ ²)Ÿæ®¶Õ”””LppIIIäçЬYt­Öu듹úÊËÏ9N)©©Ü=½¬Âb±TzZí:Ÿ—_ÆÕz££¢èÝ«'O?÷#†]Ê•ãÆ\Z%MF£Vצ•ý»Z­–vmÛtö,½zÖ¬U±||’’SÐj4Ä´¨Ü}í\â*„ç‹JS¯– ·ýµq(n6›Ù·oÇÇÓÓ‹ÅÂâÅ‹iÕ*–¸8ç³$Ý=åŽ*gêß·/éχ´;§Yeœýüüˆ')) F]£ó}CMgj¿v±Å%+;‡¤³géÜ©“ý>z¼¼ôxxè)Ê33 v­ÕmÈ*Î"Pˆ¯ÊJ 5鹩XÌbbbÈÉ.ÂËÃ//=ÞÞz +§Ûæ­[ ¦C{ÇЃ äÈÑxÒÒÒñóó%4$„5kÿ&+;‡»v±~“㈠  ŽŸ8Innž2ëLP #‡åÃY³8“˜D~A_ÌžƒZ¥âÒÁ1™Lü³a#ùùùdçä˜tejÑà`† ÀÇŸ~Azz:ÅÅÅlß¹“¬ìœqµ-p<æÎÞ7$³Ùö#SU×ó®ÎxxxpüÄIû¾ròÔòòòIÏÈÀÏÏ—¡CóÅì¯9}&£ÑH|Â1ü{蜶ëj½&“‰Í[·QPP@n^g“Sðõõ-cC ä‹9_“t6™‚ÂB¾ûá'Ô*ú÷Å××‡à  ÖoØDNn.{öígËöÕÆ'8(Kúõaö7ß‘™™Iqq1»÷î#'7÷œâ*„çjZÊ)ëžT\\LNN-Z´`ùòå¬_¿ž5k×Ò¹s'—›² Ž.ÿ2•Î"øÁG³¸ñúë¸åÆÉtèО™³>u¸™·cç.eÒ”ìlf~ò)íÚ¶¥}Û¶5:ß;Sñ¥>õîÝ›ýû÷³oß>Ž?N·nÝì3WUd`^PP@FFIIIœ:}šcÇ×ËÄ.M!]Ýîªdk-¨4r¼ü‡r­ å[ŒF#EEElݺ•Áƒ+?©”.Ï=ý$,[ÎïKÿཙÇÑët´kMÇÒÚñí·ÜÄW_KNNÓîœÊô{§ñÕœoyö¥—ÈÍÉ%,,”k'Œ§{×.ôëÓ›«®¸œ§ž{_oÆŽMXX¨Óm×µ#GŽœœOLL'ôz-:–¼¼b‚#è՛܂ÎdǤ.ÂKíMˆ%ŠPÿhüCüÉË+ /¯ooŒF118zô,ùùyÍe6maðÀ•ºÌôìÞ ___6nÙÌø+¯dÆ#òÁÇŸð×ÊÕ´nÝŠ¾½{;´ ПßÿXÊmwM£kçNüß‹ÏóÐôûùrÎ7<ùìó %têØ‘·ßx ÒÓÓY¼d)|<‹’­Z¶äñG²¯ï±‡âÓ/¾bú£30”hѼ9N¿— @çsBW·-¨|Ì+¾oHŽ••ú¯9Lžt-ßÿô3¹¹¹ÜvóôíÝ‹å+V1ýÑÇéÔ¡O?þwÞq+ßÿø3¯þïmòóò âŠqcéܱƒë T£ºõæææ²ì¯U|öÕ #-[´`ú=wÚ¿;íÎ;˜ûã<^~íM †Ú·kÇËÏ?ƒ^¯Lpÿ=wñùì¯ùûŸõ´lÙ‚žÝ»“tölµñ¹oÚ]|ýÝ<õü‹JŒ4‹Žæ®©·àïNqBˆó³';¤k±X(..&33“3gÉÍÍ%<<‚]»v0pÀ%öÖ‡ê|ûý|ûýa3ß}‡»waµZ¹îÚ ¨T*ºÿ>îyà!þþg½}ªûĤdîyà! éÞ­/?ÿ¬=n®Î÷Î8»F©/Z­–þ—\©“'éÓÇqb—Š6£ÑH~~>IgÏ’’’‚J¥"Àߟ5kV3qâÄ:[SHWUròÙ_ùF>b>>èt‚ƒýñòÓ+ݸLVÔ* :­ZENN9f Ø·o/mÚtâôéãtï[íœÃ¹;·Ó¦ó`îŠ?Æ€HJJâøñã´kW7ëMSBB<½zõiìh!.r üÊ”©SQ—{vTžÕÇjµb0ÈÌÌ$>>¨è("#ÂY·nyùy :¬^ZvõÄëyéùgèÕ£G½m£¾$%%‘““Ë%—\âð| gim6+×h‰II¤¦¦Ò½[7JJŠ™3g6ãÇO¨QåÌM%]ÝîªäééY6–¡üÔ«åÇ8غ'•6“y{{ãééÉþ½{éÔ¹ÓEQihÞ¼9Zm‡m¦}ûìܹµÚ‚——£ÑHan1˜¬xêuèujŒ†²²²1 xyy V[صë:uŠáðá-ètÅ4oD»Ø™Íectkœ…Bˆ‹OùËýªÎ?e‡,223‰gãÆ$''3dðóúIÇ ¦id«8¤¦¦áëëKII1³gÏfìØqu^ihJÜŸU©b•ŸmµUÌfLF#ÅEEl\¿žÖqm.ª)=<<;v4V«NGpp'NÄÓ·o?ôz-jµr×ÀZ:ØV£QáååÅbÅ`0rà@C‡¥yóftèЕJåð>Ñ8Ê?ž½¡k !„Pýø(ŸŸOPP ÿü³ŽäädÆŒƒVëþ,ü#g)«ªÐ%ßl6STTDA~>‘aÌ™3›1cÆ]»)ÚÏn·8ÙBS®µÁ>0Ú6ƒR¹§®[·î¢«4ØØž@pôèQzöì…Z­âÏ?ÿ`ÍšÕ¤¦&£×ëÐëu¤¦¦°ví*–-[‚Z­¢{÷žÄÇ@¯×K¥¡‰(—§l ´BÑ4X,ʵةS'ÉËËcìØ±r QǬ¥×º^^^,\¸+¯¼ê¢èRëÀÙ§b­ôqYkƒÁ``ÇŽíôìÕ뢟ÁDéºeâØ±x¼½½ˆˆ#::šÕ«Wqë­·°jÕ † ¢ŒÊ?u‚ÂÂ"L&‹¥Á†"\s|ºtUBQÿt:›7ovù8³ÙLaQ1 ñ´iÝŠ=z4h÷¤Å ~n°mÕ5NDZcǪ}>†Á`$;'‡'Ž3qâDüýýë5nM%]ݪ8¨ÕjÒÒÓøý÷ÅJ@ÅŒXnjV‹Å‚Z­á’Kú×Ë”TçFÃ!C8rä©©géÝ»7z½žÈÈHû`¦ÈÈ(¢¢¢ aÇŽøúú2dÈ©44"séÔsÊàþrrrزe3G¥  Ÿââbt:xzzàááN§“c&„¢N5’ŒŒLÌfe–JÛùÉd2•Î\iÂbQf²´X,DGEáííCBBZ­F™ÝQ«Õ¡Óiíç*峋÷œe›ùÓl6£ÓéhÓ¦5EE…NÓ”kaFƒZ­A¯Ó2dð #999EšºUqÐh4 >¼¾ârÁóôô¤[·nSÙ¶jÕŠ¹sçЯ__T* 0@05eO;·âá¡çÒK/¥¤¤O<==€r!„uÌÛÛ½^_®¢ T ƒý½m |Û´áÊdʬҡB£ÑØŸ±¥V«/úó–ýaÅ V«•jÓT£ÑØ_¶Š‚Õj¹hÒTFÉ4‚ò‚-ZØŸPXÕ2¢ñh4¼¼¼¤ÕL!D£Ójµ2À¹ŽIšºÇíÁÑB!„Bˆ‹TÄEA§ÓÛŸz.DM™L&ôz™‰D!„©8ˆ‹DppiiiRy5f2™HKK%88´±£"„B4 Ò©K\¢££IJR'o4;:â< ×ë%*êÂ~˜BQSªää³îÐo!„B!D®JB!„B—¤â „B!„pI*B!„B—* ŽÞ¹s{cÄC!„BÑ„ôêÕÇá½ÓY•Æ»²A"#„B!„¨_þ¹?ÿ ·¾“—›U)Lº* !„B!\’ŠƒB!„Â%©8!„B!\’ŠƒB!„Â%©8!„B!\’ŠƒB!„Â%©8!„B!\ª]ÅÁjê‘0?„Ã_ƒ þ³²eþ ?¨*¿2·Ãâ8çŸÙ^¹‡êh÷Ü4Ï’W6ζ]9½~k¦¤}uau¥bZ4å´iHçc:\ù¤ü>Ôå6¬8ø?(Jr½lq lž‹bá'Oø½=$Ìv\¦b¹· BùŽ¡ò\ت!ËàÆþÍœPŽËÏþ°ñV°””}–µK9‡ÙŽÎX?~TÎk›îp~ìv<¬¤SÅýrg[™;aÝ5ð[ ø%þ¾ò•-_Õ9´àDÃm§>ȹŹó%šrÞs§<¯KM9MêûK ¬ ñŸCÜ4½.ûÚÞ £ã²]_‚‰iޝÀî0vkÙû.ÏAè@ÇeüâêfïêÚžçàŸë\‡Õ‡Só!f¨ÔÕ‡ QÑ…OêkLùpx&äüëzY?h¼ ßg0zÄÝ[ï´ ŽËÙʽk“aØeÝ;«yœ¢LiJep}—«»ÿ`ÜÈO€ãß)áV3l¹º¿^ÑJXÒ2ãvÁ{¡à8ìx¤Â ­pz!x„Ãéßj¿­ÄÅÐì · ®Ž0X{•ãyÔÙ9Ô»EÃmGˆŠšrÞs§<¯Ž»åOSN“zàôÉÑÕ:ô”¤+'wY¸G'k÷ÐÊáúà²ÿ5> Ö9_®©±šÀjtV×ÌŸ†-­>LˆŠ.„|RŸû ó‡kN×lYôUö>¸·r¡™ºÂ•…—/÷<# ó`Ë]5SC”)M© ®ïr5{?ô~üÚBÔXÈÚ«„ž j=ÄÝS¶lÇŽßmÿl{Ð1,s—ÒúÔó-8øô™YV¡ug[]_r\oŸág?¥Õ#¨‡VÕ9´¡¶#DEM9ï¹SžWÇÝò§)§I=póöþ}º>ïXi¸$̆?ºÁTùžZ¯Ü¹¬ØMàÔ|h1QÉ\U…YŒJFò‹Ò”¾V\ Q£áÊýÐéiØþ r§ `ÃÊw®<—­‡¤?áÐ{5O§mÓ!y û®> ¾­aŲÌo1Ba¢Ò­cü eŸl?Xgª‹O·—Á\ÞTÞïz\éòÖúå}‹k•®#×çA§Ç•ÂΖ×ÜM—ꎻñvfë½J×–aK”<ÖþAðnVó4ug_àÂÈ'Îò¿;ÛØx›Ry³.™ !`)v¾®ç[+l½<Ã!ò2ç벚•±^‡ÖSÊ«KÓªÊWÌŰí~èõ¶Òü­õuÿ7X‘«t=ò Ìrþ:ò‘㺳\íñªÒw|aŒÒjÔf*lŸí¦C@çûn1(­\ß‚¯;~vz¡ÒÕÀ+Z)‡ÊwWªÍ¶lήPnÈu/ «tÍoØíÀùYf”'ç–º=·4dÞs•fÎÊ=›˜ë”‹y«¹tK i©Ò嵺c[UùSÝñhÈ4iÜ«8§)õeapØîâì}Áñd²rxD¹žµœ ¡” Úr²ÒU±7)•ŸXèû1䇴ÊÚE UÒÑ'Bú)a* ø¢Ç•­ÇR¢d~[f¯*̶ÍÀnàߢFAÄ0%Ì#L)­få.Cþqå.n¿O”æ0ßVÐæNHü£fûV’¦Üñì÷)tVÖÑçC¥»ÃÉŸÊ–kq­²ožá{#äT‘–®â£õQºˆxU€òg¥o»‹6Héÿ§ÖAìÍÊEaþq÷Ó¥üòÎŽ»ñ®¨8UéûØÿ %Ýtþ}¹rÜkš¦îìË…OªÚ‡šn£è,$ý½?Pö7¨'´žZ}¼k’o÷¾XÚ}j‰’?ËÛõ„Rþ¨…?{CÄåÄ瞦Õéýžr±ìåÞ¾8S“c×á¸ÉêüÕñqÇõ5f¹êß®ün(†áË”‹‡œCÐùÙÒ•Z·QpBi‘úû*ðŠpìÖ•Ÿ9ûÊÊèf—;VÜÝ–Å ô“î8´~eáÏ¡ëohØíØœOeFyrn©ûsKcä½êÒÌY¹Ðìj¥\ÈØ¢¼O^¥\Ô‡QÞ»:¶î†N“FæÞßXåoáe (ƒ6@¾¼¿ãò=ߪ|9ŸxG»×ÄZ}’éóŽBØ%CÿÙGÉx¯fU÷ìJÐèË2{UayE;ÎŒ¢TjµCÙþ_ËýЬ&ð­á€ÈÜ#Jd;1ƒò㠽ı¬ŸªÒ²&ñ‰¾šOPîªôzG¹cb“¾I¹3˜±Œ¹JXUÛª.]œ)ìlw0݉wyyGA¥…À®•?«mšV·/B>qµ®¶á×P;Oóšp–osÃþWaÄ_JÞ¨¨ëKʦSóaïóÊÝ+UiQ{®iZ•‹â¼ºß`E59v–09ÿ¾ÆSy9ÓXåªZ¯Ü½Ûþ0 š«Tüv<¢ÜÝëú’r‡”uÞhTÖ{ôSX9T¹(ðm­´6ø¶}ˆ²®ÐAJ‹Rãüjº-›*ûÓåÇpWçІÚNyM½Ì(OÎ-õsnÆÉ{ÎÒ¬ªrOQ—ÁéEÊä§*NÛòîÛšÆH“Fà^ÅAã Þ1JBÛ*¶†ì:ŽÚÆjTJ_·¡ áÌïÊ”¶‹Û+µÿ6UÜ==ß1³Wæ} òÝÉUwÿ¨–­ö¬ª®¢V3üÖ4>º€ÒÊm#ÿ¸2-pï÷¡çÊ]‡ŸýÝCulÇ®"wÓÑj•JyUþ°ôo¥)\ùÄå>ÔdVÀRómºrö/¥käHçŸÛ¶ÅÝ­\xîEiJ‡:HÓ†Rƒt=òIY—£ŠêÄçn¹ºûiˆ !ý•~Ûƒ~Pî:þ5š_ >-•åTjðo¯ x<ó›r·±í}Êø†ücÊ]ÀòN/„Îk·­c_é_àòݵû­6Ôvj¢)”µ‰œ[j§1ò^UiæLÌ$¥Z×”ÙŒ~¯„»{lÝ9Mé÷XOÜÏ5…o(5¨‹‘³þˆ®æÇ/N…ÂSå 4r%ô|SiªrÆbTè8tSræ.ßV€2w¸X°bóZé{¿¶Jm;kg¹LJ…2°sýÄ'mœ˜ ]_V¦J+8©„§®UšVã¦_;Çæ¾ºPéØ=jœŽ¥üâ”;¹Gœ|VÇiz!ä“j÷¡†Ûðo¨ k·ëíÕTø¥Ðw¦ëåTåæá•;dPó4-_¦¨õÊßÂÄZE·VjrìÜéªäJC”«i”nE=ßRZÍ­hv¥2“‰o\w_U öTŽAqвŽÑ÷µÍ]•§e­é¶2·+ýÏýP6]£;j;5ÕØeFmâ#ç–ÚiŒ¼ç4ͪÑ|¼Ò½ðØ7`1)]¡fǶ|ùSÓãÑÔ~õÄýŠC»û•Y&–Pša N)3‹Ä¡|^~ôxņl%Þ¯<#”}5dBáéªÃ@¹CeÌQ2ú¶û•å!}•‹¡Ó ”´(IW.(ZÝ®4ÍæTºdlƒÔ¿Ë–Ñú(*± ,*ÿÞ3Bé¸õ^%þÆØù„rìc®w½ýŠûé*>ƒ2Nçg•½B/QÒ«2Ð2{¿òC,NQfJ8WU»Šéà*Þ÷Ó+Jä»ýA(JSòYIÚ¹§iEB>©jÜÙ†g¸ÒGvûCJßus!œ]Vû4qWäHe€á®'”÷5IÓŠeŠ>Hévsâ{%¯$¯¨|¡Z×ê:?ºÚV}–« ”![¦)ý¡=BÀ§…²/§~ôÊòþ”±+©ÿ(ùĘ ûÿO™9)r$þ®ü†C*tÉ™™Û” w¶UœªÌöÕéIeýîj¨í¸£±Ë 9·Ôÿ¹6ïU—f®èƒ•2x÷[®]ÛJep òuC¤IÅã¾ãa¥K,(Ýb¾ro}µä~ÅA­‡Qk å Jü•©»ò”;1å’Uà1?¨nïü5´×(oa«ÒÂ¥Š0€¼xe ±ßÛ)Á°Å€ JRáÈÇÊ´« c”iÔNýf)Qj¶¾…é[•þºj]Ùz…ÕFß l0¬ ¿+3’”ïÓØýÿ”–¥]O9ßÿ3¥]9µQšïG­«º_sy÷ÓU|þ}GùÁtzP)”’W)Ùš]®TfWŽ„eý”&Ås}PJUÇÎY:TogûyÉl%~öQ¦tÛûBÙǹ¤iEB>©jÜÝFÿÏ”þçËú*Ÿ§:ŒèŽ3¿)Ýtjª×[ÊM–äUÊ{WiZ©LQ)eÄÙå°0V¹¸ ésnûPu™«Sßå*(O“õn® ¤¥Ûí%s`Ç ø{¼rŒ¼›+ã/v<¿F+ëI]#–+Ý N/,Ý«Âi3b„R¹;³°æÛò‰Q*%…§áÀkÊ”¶—í&œ+ µwɹ¥j¹6ïU—f5sR>”o¹vul•?®òuC¤Iùãnµ@ÊZ¥‚º^©x6UròY‡6Ã;·3nÜ• ²ñ Ö<_¥¿m}ßÝuOŽÝ…/ïˆ2_ùÄtǻҢi“ߦ8ŸIþuŸ¤YúóÏ%øù¹^°œ¼Ü,zõr¼QUG£…âdä·# ù_áo…3hö >Ûñ™}™Á³£zYUéµ=i;qÆ9ýÌö:”~¨NvÎ]¾¯ù²òØÊFÙ¶+ þ]@³w›a)—i…•×”÷§):ŸÓËh1òØòLj|;ÿ×ýÿÓxó.ûÖÆ·P½¬bÒÏ“jüù´Lž?™È·# +œ;ÞAVqV¥ïºÊ“碦ǧ)Äá\Ôgü›âvëKUǪ1ö³|\R R˜²h ±ïÇâùŠ'í?jÏì]³–Ï7äóØòLjy/W ,¬åàšÅý¹ÝâPb.áŠï¯ 9?™ç‡>OÈX¬¶'m'§8ÇaÙ—†½Äô¾ÓÂ<Øz÷V{¡ýÁ–X}|5‹nXd_&Ð3°6n=q† IDATûRïž[ý‡3óËu¿TVæœÏ¤N“P—kÞr&.No®“yæñã¤iÔ†ûþ¸{~¿‡%7-qX.)/‰WÖ½ÂЖC®§ªÏ—Å/cHÌÞû>*TLž?™G–=Â7¾qX®®òä¹ü®Î÷ßEùø×uùRÝúê;ݪ¬t¥±ó‡¿‡?^Z/>»ê3B½CY}|5÷,¹‡ö¡íÔb÷.¹—=){˜3aíBÚq*çóöÏÃCëá°.+VZH¸O8¿úQ­GUÚž§Ö“׾Ȃë8„/=º”}nv_°ÓÍV3§rNñàÒylùcÌ?ÇÍThxõ•ÿ&ušÄØ8Ç;¹™E™ôÿ²?3̨ÓmÙÔ¤¼¯Ëã^QU×v¹ºÖ{`ét ëÄœ s¸ý·ÛùnÏwÜÕë.ÌV3w/¾›WF¼B´_4‹/抶WðÉŸ UkyjåS\õãUì¾w7:µ®ÊxyÓ¾:È9Yö‹Ðs*ø„Cöq¥›Pt Äc$n³¡lùf}¡Íè²÷>°þM0;®3¼\e3õüû«Ò…©Ýåe-  ´l„´…6—)ÿ§ÿ[}Ü›»¿Ëθ]qø`󤦳õî­xë¼íáC;VZÖGçC¨wh¥ð`¯`‡etjÓ嚓ńÑltV׊MÅ,9²„¥7/­6L\¼V[ɽ}îexìp¦÷ÎM *ßÑxrÅ“\ßùz<œÞ‰©êóŠ'À‡ú?ăKt«Ë¼˜ßnø‘­Fп…½RQÞ®³»HÉOá­ÑoñÆú7˜9nf¥ ѽîäóŸ³ãìzGõ” ÇKk_bz¿énÝñ.Nð‰à?CþÃ]‹ïr; C}å?O­'žZO‡°'WŒcæÖ™qZž°Ë5[U |#NýCß/úâóšù†üj÷§ºx¸ú¬ØTÌã=NëZôfcæŽápÆá*Ó4«8‹[»•ð·Âiön3žXñ†Òšøã=N§;Ù›(3Š2{+Œ9»•;X3þšAÌ{1x¾âIÛ™m™w`ž}½~¯ûñʺWhÿQ{ü_÷gÂO8v€;ÞAÄÛD¾ɧÛ?uX¾ºãâN¼)2ñèòGiõA+BÿʸïÇq*çTÒÌ}q–O:‡w&!³l_2‹2iÔÚ!~ëO­ç£ðÚÈלÆßÕçåÊ9E˜O˜CXÅOÊÚ”eeϕĊǣ¬cöŒ üpƧwPoò&çþN¸Þ0ÓÔŒTVôXÁ¹ÏáåàEÈúTjwŸÜeÀ¯×rwCïrhà!í´K‚}ƒ (Ô³Üra ¯Õ| Ë+æ ‡eåe1l×0æv™Ë•O®à`ã [99äↇg_ü>vößIÜÈ8ü]ýi½¦µA~{ËÛä*s¹0ü‘ƒ"Ù·›,`jû©däf0;Rj¤B#B©çYõзF_¢G‘öy¡-C¸m ÖŽr•¹$ÕÆç©ò>‚©í¦ân¯ûÀoJ¼†ea—Ø5‡¯:~¥WØ&åt'‡!¿ÃölH}~aL¯æÈmŽÞŒùUaù ÕƒœüryÈÕkáz3æ+úêUÎÆ åmÌ/JâSú(XNcú(ͶÇjÔ|¼ëc<ÊzÐùéC„SÚNaôžÑŒ¡3»0Û.n£{`w*8V žg=¶^ܪ÷ºÏ[ξ«û8–xLûÖ9´e(Neœô^o ¥ZÉÅ”‹|ù•Î[uc¾õþ¶÷¹œz™ï`ó›)o_žŒÜ “ó-mÛ_~|9®³]õ~–D/ÑÉÛXÛ‘›Á‡;?djû©E~ÇÍEÎþŒµ÷LÑ{Ïj= Ëo ß8L¶2›½WöjÃv_ÙM¯ø>]œ[ZÏv3:Î`ü~|øbgeÇ ƒ>œáM‡SÛ£¶ì½û®îÃÞÚžz¶-,×3íTd?š¯°„ê½¡f_ÈIƒGáìùñ =ø;xIÿÇí–¶JEa¸ãðø&䦃]9p®^õ¥EÕF^Œ åkÌW Ö«¹íoKJçäÊi¬®J»íÑǔߦv9Œ°þa”µ.« Úx(‘ƒ"9}÷4‹ùpç‡E:!W\å\Ò9B¥v [`7¶Æêï8Tv®Ìû ÞgÒÁIl¿¸ë¯3²™ù£ ãöC1UÕÿYÑhU#:TéÀŒ3ã¶y/ý¿ÆþÊòË©çYåk0¤¡éÓœž…ín>õµÞOhËPkMy6øòЗ¸Øº0¦yÑN…¹ÈÙŸ±ö^ƒ)zïÔ“ÓwOk!‡Ç…3¤á¢nF‘ž›® ëÔK{Oi=ÛÕ(_ƒØá±dMÌbÏ€=„Ç…s1å"_´þ:ÖúÈQæðßÿel‹±8Ú8”ë­-oK.ã¨áêÈz$mÁZw´š ­O°²ƒŒ)Nƒ'i‚Z±[ááÓµ›A=kzÓ“¤™,¬À£6x7Âïœ~Få1³Ö8ø¹øø8‘šî5H'u&âÄÓì»f:×Ïé<§ˆóý¨àXAvjŠ©¸ÚºâçâG\j­}[Ó+¨W5fpƒÁŒi1Gƒ÷îߥ ­ ¬Ò×ÒÐËädP¹¸Ë©—±TXê4ÈÖÖ4¯Ø\ï4ÍÛ2ï¹ù™LÉWŸ¯¬×âØ¸±{JÛ§ —ÓX]=˶àRê%fAÄ"´¿­iæÓŒÈA‘ˆ?ÀŒ£3¨¶¸Qƒ¢´ob·]܆¿«?nvn<ÌzH°o0_E~ElJ¬Þ5‡Ÿ·þœÀEÄ$ÅÚ2GGšyò­fñé– [˜ôÛ$Æ×Ú1ÛŒKC¡Ph磛˳°ýle6™¹™zãô­]РïÙàÔS,úsdž3ú[l rö'×ÞE2¦÷JN•¨çY=Wö0¨Á ÂãÂÙôú&¢¢8týü;q þSÚNÑÞSÚÏv6–6<ÌzȨݣXßw=a—ýg4™y™|ÙîË"SêÆìƒ«­+Ÿ·þ\'ü¹>sFΚù;Ù:K뺂o0l~óéÈøt}ƒ2nEK §=jƒ7xÕ…GЦ÷,” ”Ö9h:wÏ<ûr™€YÖmom¯³/Ñ·¢µÍBs J•…B…‚momcç¥,úsA‹ƒX²ˆA é½oË…-ô­ÑW§!Òf.rrÈÅ©ÕÒ€Âs7(ôÎGt±uÁÊŠô/Òu¦ZƹŒ³V. ×^£ã÷YØu!³:ÍÂÛѧ¯Š7´n^Š+·•Z…B¡Ð›–¹uf.é¹éüû+'?<‰µ…5 ½rð½ƒÔXZƒµgÖ’§ÊãAÖBÖçPäªrQ«Õ8ÏrfrÛɲñ&ä¿9±PXäÄüWæ³5v+{¯îåãÆ±É硻˜âÆôú,å6æû¥!¿¹í‹©ù‚a_âÙ¸±{JÛ§ —S®®ž‡ýF\ °\ v´>(èä߉U:Ðkc/fEÎb}ßõ€ÔqˆëlÝ]c¶]ÜFVE;•+3ºùhöÇïgDÓÅ’Y³øôƒF°âÄ ¦™ÎÜ.Ò"[c¶©FZ-}Š3õÿYØþòã˳Wÿè€9¡yª<†ìÂèæ£iàÕÀàu*µÊ¤6_ÎþŒµ÷£šÒI˽÷ êÉî+»iS¹ ¹Ô÷ªO·ÀnD\ Œe\l]hàm¸\¥Á„ýèЕf›á5׋_û‘JN•h¹º%½‚zQÙ¹2 ­{Ù|~3g†ž)•Z±IO‚ý_@ŦàÓ*·–vXij‘'¸¸ }¤@jœ´Ð¹Àúª´×ßq¸5ûIÇŠ€ZÚeé%Àì§–1ÍÇ0+r™yú{éÿtô͇7¶xRz ´oÎ(èÔ‹ýïîgvçÙü÷ÀõÞ—«ÊeûÅí:Sô…99 ź’§ÊãÔSÚkóTyDߊ¦–{­"yTq©‚Z­æ¤ÌÂQ7£Xÿ×z¦¶›Êă¹ñt˳C×Q½|u>lô!Õܪé I–…õùC£¦È]€rä(s¸œz¹Hœ¹uf. ÔjµÎ~êöÖöx;xc¡°`l‹±¨§¨Éš˜¥ýŒi>†¾5úòhÂ#£ñúP ÀÖÊK½6)§;KƒçLh0gŸ}Sý˜^M±9CÃç¦`ȯää/X¦Ø¥±ö¥`z¦Ö›>_)ˆ©6^0oc÷”†Oite¨œ†êêY´=…í¦Må6,î¶ØÀÕºX(,*¤K~/ýQ Qü>øwé5C18] àëÎ_sê£S%.¥Â’y¯ÌcÑŸ‹ˆ{zˆ•1Û r B¡PpÆÀ[ScíBiÛ>˜7UIŽyÇæ‘–“Æ”vSd¯K|œ¨]£#‡œýkïõaLï=«õ$âjÛ/m§[`7( aÚiJ%YèmŒ¨›Ql½¸•9]æø8•ZEj=¨ïUŸ€rÚ)'nŸ`xøp~|íGí6­/„²žP¹´áÑM8ó=D/ËŸJäà šµÀ›‚fKXk{hüì\Vé MI*ŒÆO<ê@GHOG‰Ï®Lf`vÇaX“a¸—u§Åw-»F£Î%ãÛSßJ 0Ü Uf=$O•WzÒ?g<<9—tŽû™÷¹ùø¦Á0vïx”ýˆ¤ô$†íFŸ&4ñiB®*—_báaÖCR2RˆKÃÍÎ µÁ;q5€ñP(´ók§MW_Xq“C.γ¬'ïÔ}‡¡aC‰»Ç£ìGŒÛ7 …oÔz£HÊÙ•ã½úï14l(’/­Ìæøíã¾q†^?Úù_´ù‚Im'Ѽbs†í†5e=ˆIŠáÄíÜK¿Ç؈’ï‰mH/ -~;uçJµÒ¨Ü…ËéíàÍk5^cdøHn¥Ý"=7ˆ«$g$­3s)œ·•¯Öx•1{Æw?Žle6ËŽ/ã|òyºv/qL94…£ GÉÈÍàqöc¦™Æí´ÛÚaìÂ6)§;W;W|}Øð×’3’Ù¿¯È¢NC~eSý˜^Ù\A19¿2$áz0&¿\úÒ“«79_)Œ)6^8oc÷”Ô§ êJ_9åꪴÛsìæqöcºüÐ… ç6›Ëõ‡×Y½„¥ÑKµ›Fì¼´oGošùèNîW³Ço'ñqñ0ôýnëë,w¬Ò‘.U»0.b`šo½Uû->Ùý ×^'#7ƒ°ËùçkJÛöK‹+÷¯ðå¡/ùºó×dçéîÒô(û[.láXâ1ÂãÂÙpnC‘³6ôÕ·{Ywƒö÷,ÚûÆcomÏÜßçjÓ®Ìí´ÛlŒÙ¨³¾ÁÌÅ}¶ËQæðáÎYðÊÜìܨäT …›Ïoæ÷›¿w?Žêå«“”žDߟûòYðgzÏ+‘£ðoæ¨=£Øra “~›ÄêÓ«ÍÚÉš„–cÁ¯­4ý(j.$Ë¿Æîé¹Ïn(WÚN„Z^`x7{¢y$Å€27#õ²´`ZŽ2ÎR?eŠž¯QRÌî8ØXÚðÛ{¿ñVí·˜~d:µ–Õ¢ÏO}¸zÿ*¿þ]gÁdá…*®³] ¾mø;ðjõW±²°¢Ê7U¶k˜Á0“:ËêPmq5”j%;ÞÞIéI,^JÀ¢|øòGâ¬í#m=š­ÌæÐõCÚ¹²Ñ·¢Ü`°öpCaÅAN¹8€•=VÒ¼bs:|ߪßT%þAjôêй__}¿RõYû+íöÞBaAj=HÍH¥£¿4uÎÆÒ†ÎU;“–VäÅBi>Û}õ5*òv·iôdm﵌KïŸz3§ó|}™vd7ßdæÑ™ØN·Õ~4/­å(ø›©R«8týç’¤Cð""uF.M"û‰4Åȧ‰Ô ¨ÞGÚýH³sÀÝs€…á“4XÙI‡ÆæQd¦HÏ„úõk]è~üÚ˜T$sPܽ{GçU©S' éQêý›p˜éÀ¶·¶™Ý+<[„^Ó¾"x–¨‘θþð:U¾©Â±wð*øÐ%¼ŒÌñ’vL². oý þA•É =Y:wÁÑR.æ~`e ïk;éü†ÃÓóÓòk õß•þ?û?Ø6>>+-šŽ ‡{Á€ÝùíCàüføüéôáߦÀ‘Ðq´zúB"7³è¨ÄÁÉprØ—gwíïptÊ_µkÏ^ôѽë+ÚÿÓ? aCÝü \Y"à߯³œ//<3œ*I‡ÜtˆùIê¸úƒ‹Ÿô7ë¡42pq;<¼A½¤ó²Á­ãÒbi 9O àé4&g_é ·Œé´ȩ́ëWnŸÏ§ëÊ’ÎKÛ¹jN£ÖìØ”¦{Bua4ëzJ8[£ ¢ã @ ÈáUî>]}zd?–s¬ çrîýÇW€*GÚAI³cÒå0ÝCánþž§VIÛÇ!ýÜ“¦Qq㨴NA­†”‹Òuš{4gA<¸¦W& šõNÊ+)¢ã @ ÈQç-iZ‘Z¨áʸ –ÒÉÎÊ\ÈË–: J\Ú.ýŸSh QzhNW# DÎK+) €„H¸w6?òïÉ~ú=nÜ8dXæì4PXH²_Ê*YùŸ":Ï€'Ÿ?ÃcÎÅFèE 0 á+‚矋ê)ÅßÞX x®xÕ‡Zo@ÌÏ€Zê î$û±ôчZ OtOƒ×Y ›!} Røžœ'ù ½( ö›’ì—þ¹ÎtDÇA @ þ¼´ 9ç $“D~°Y°¶‡²îÒ9õß•d/%DÇA @ 0FÕ.`ë Qs¤ó^ÖŽƒ]9ðnÁã¤mcKÑq@ LÁ§ ¼±éEKñÂ0û8@ @ðïCt@ QDÇA @ Et@ QÌê8¬;³ÅT…öSfzê.¯Ë¶‚Çh*µŠyÇæÑjM+œ¾r¢ÑªFLØ?̼Lëžä<áÓ½Ÿâ»À—2ÓËPyaeÞþåm®Ü¿@À¢ü .¦\,añuq˜éÀþøý¥šfiñKì/øÌ÷A¥9>Ü@XA^æò¼Œüë+W•˧{?Åk®N_9Ñû§ÞÜJ»¥÷Ú9¿ÏA1UA¿MýLŽ?Ÿ|ž7·¼‰×\/<æx0pÛ@d=(r¯1›, ¦êçe¡$³W'lÊ¡)T^X™äŒdm˜©ÏªjÔ¬9½†Nÿë„Ë,*Ì«@×õ]‰Lˆ4*ÏÅ”‹ØN·åÀµ:ágïÅfš ÇÒïøú¿ÖSgyS/íóˆÙ#U]«’<.™¤qI\q‰þuúóæ–7‰@ž*^{±îÌ:Æ´éN1­ý4NÞ9IËÕ-I-pÀÅа¡ì‹ßÇÚ>k¹òÉ~|íGÜíÝ)cU€è¢I—Lò¸d&¶™HËJ-µß“Ç%P. ”ªÁ4&œÈë›_7ö,Øra ýjöÃBa!&øw2;r6?Ÿÿ™ý63,†"êfT‰äø»Ò½ZwöÇïGMþzJµ’ƒ×’£Ìáäí“:×¾~˜V¾­p°qàIÎÞ«÷y“ó´Ÿ£o<ï"ðe»/I—ÌÝл„õ#69–O÷~Zêùüû+sŸË¶·¶ánï˜÷¬ªFÍ{[ßãËC_2°þ@ŽxœýwÒºrkrU¹Fó¯^¾:_´ù‚Ệ“­”N…V©U|ö14ú€[p'íkN¯aJÛ)ØYÙ•z=”foÇjeaEù§I¸Û»3¡ÕVž\IÔÍ(ü]ýYsz —R/qâÃ8—q  \«vxEÝ– IDAT¦û†îL;<…]¢R«Øqi[ßÚJÇ*¨äT‰àJÁÚ¼ÊÙ•Óþ_Öº,ÖÖÚ¼_yªMµ×¾q˜õRçÏ©ŒÓ‹[‡²ÖeµÏtže=ùoëÿ2dÇRÍã\Ò9ÞÛößõúŽ^ ´á¦>«l<·‘ýñû93ô e=´i4ònd²ãƒÇósÌÏÌû}Ÿ·þœµgÖrãÑ vØ­½Æ×Ù—ƒï`à¶%)ö3¥T^-Y(,ÈQæ0+rZMÐ*Bƒµ…5_¶û’eÇ—ñ8û1 …‚²6eK}ºQIYsz u—×Å~†= V6àì½³tÛÐÙQ³Ù~i;VÿgE· Ýô†8~åȸ}㨳¼Ž_9Òáû\}pU›Ç†s¨±´¶Ómñ[èÇœßç™—IÀ¢G/Ö‘iïÕ½”µ.KËJ- †¹ÌráhÂQš|Û„²3Ëòäéä†Ê#'‡±¸¬¼,B#BñÿÆ×Ù®¼²þ.¥^2X§²ðŸ­ÿÁcŽ>ó}·oœÖ^B#B©¹´¦¶žš™ŠûwÖžY À؈±ø.ðÅvº-‹ùùüÏÚt¿rdú‘é- Âé+'úüÔ‡óÉç¸m žs=ñšëÅŠ+t®—Ó‹9rë#3/“1{ÇPå›*”ÿº¿0¦W9›+Œ¹6(çW…å7TÆä7”‡\½êkc bÌ~ïgÞ§ïÏ}q™åBƒ• ø(ì#ìfØ‘’‘HÓiþHüC[g³£fÓ`eìgØSgyŽß>n°ŽMÑ‹!ß3Tæ‚ò˜â›ròš£S¹r<+¿0f/rå3¥þäÚ=Ce-®m$G™Ã¹{ç¨Q¾†Q;0EÏi9i øun_»QyaeþïðÿéägL¶le6³êýdåe²!„‰'jïÉÊËÂ~†=³£fkÃ% ˜ª áQ‚ìo–¿«?AnA:ÓYöÇï§“'ºt%<.¿£z/ýS.Ò¥j@ê88Ûê>£¤¸>Zž{òÁÎtÂÞÜò&ÃǼG­V£@¡ý^Üg ©™©ôÞØ›ÌÛµßÖ‰3õYàÿÿÿmý_NCaŒÙ‡¥ ßöú–™Ggrâö ÆïÏ’nKŠäÿw Ø5jÒsÓYsz ×\£q…Ƥå¤qíá5Zû¶Ö{O³ŠÍi¾—SÚNaôžÑŒ¡êô¢)kS–•=W?*²ŒÙ3€ðwÂ<žÞA½É›œGø;ázÃ@2þÔŒTVôXÁ¹ÏáåàEÈúTjwŸÜeÀ¯×rwCïrhà!í´K‚}ƒ‹LÁÚra ¯Õ| K…¥Á°¬¼,†íÆÜ.s¹òÉldË#'‡\Àððáì‹ßÇÎþ;‰‡¿«?­×´ÖvV óö–·ÉUæraø"E²;n7 þXÀÔöSÉÈÍ`v¤Ôp†F„RϳžöÍHß}‰EÚçi„¶ eඤ夫Ì%áQ›_ßLôÑDߊ¦ÍÚ6t©Ú…˜a1Lh5‘»Gr/ýžQ½˜+·>†† %*!аþaÄŠgdÓ‘ø8ù˜Tgæ”EŸ„¶ åÈ#ôÿ¥?Ç1íÈ4>m‘?ä›§ÊcDø¦¶›ªª-ˆ±x 9ÊÂ.‡1'j_uüJ'®°MÊéNC~†íÙ úü˜^͑۽ó«Âòª9ùåò«W}mLAŒÙï»[ßå~æ}þòkz¯ÁÍÎMû°¤¯În=¾ÅÊ+¹>ú:å¾ËðÄ)z1ä{re6µlròš«Sc壸~aÌÞåÊgJýɵ{†ÊZÛ.ˆ5ïú²t®ÚÙ$YŒéyðöÁ\N½ÌÁ÷²gÀ*8VÐÉÓ˜lË/Çu¶«ÞÏ’è%ô¬ÖSçþðÃd+³Ù{e¯6l÷•Ý4ðj€¯³¯Ñß,Ít% šŽC'ÿNœ½w–¤ô$)Ÿë‡q·w§žg=@ê8üví7ÚßçYδ[×NgêYq}´ ¯×|—vh×Zd+³ §_Í¢kë”j%S.òUäW¼ßà} dÏ( ý¦½¹ùM®?¼ÎÈfº£ãæ<«¦ç¦s)õ’vfŒ!L±Ý[ð^ý÷h½¶5­|[ñjõWeÓ|Y1»ãp)õŠ© ,¦Zà0ÓЈPæ½2zžõ´=û§ÁRa‰·£·öÊÐÆC‰Éé»§ \ȇ;?Ô>ä½(Þ¬õ&-*¶ÀËÁ‹7k½ILRL±Òé_§?Á•‚ñsñci÷¥\{x£ Gµ ÚúµÅÅÖ??íp¢¥Â’ïû|OH@ˆ6le6;.íÐq6}a ^Y@ÛÊmñvð6Z99äâ’3’Ywf+z¬ –{-ÊÛ—gQÈ"¬-­ù)æ§"õpíá5ö^ÝËòË)o_ž*.UÜp0».嵐ÊeÝ—1ãè Vž\ɦó›XÕs•ö­Cp¥`*9UÂÚšwê¼CV^×\ө纞u©^¾:ü;ÑίýëôÇÝÞõ¢T)¹”rIçz}z1WîÂ$¥'ñÃÙø¶×·Ôr¯…S'ºvÃRair™Z}vâfçFÏ:Dߊ&xµT¾7j½¡_yr%Jµ’aM†é•ßXnß8í"íF«Ñ¡Jft˜ÿEÃÂ?ÿ ž&>M˜üÛd8sžUo<”Ö~Tr®d°¬æØn3Ÿfdåei;'GÌ^ãPÕµ* ‘†(­-­q*ã¤}Èóq”zö÷ÒïißxD¥VqïÉ=4óiFä HÄ`ÆÑT[\¨AQÔö¨]¬•&+ÈNM1W[Wü\üˆK£µokzõ¢ñªÆ n0˜1-ÆhëMûã÷cci£ãlúÂ@Z"GÁò¹”C.îrêe,–:NjmaMóŠÍõNWÒŒ$yÏÍïÌä©òtÞ–w ìFŸê}6”y]æéL±9–xŒ9Qsˆ¾­64¤“ Žt¦¾¹Øº`omoðú‚z)ü¶Â¹ w?+ +í\Ó‚˜[gÅ)Ë;?ÀÖʖرœ»wŽa»†Ñì»f(ý¨L:8‰_ÞüE¯¤d¤ÈÆkðsñ#wR.q÷ãXqbm×µ%vx,þ®þzmÒ݇ÂþiŠ_˜¢×’È-§79¿’“¿ Æä7–‡>ŒåkÌ~ƒîa¡°Ðkû¦PPgɷMt⌀‹­‹A½Èùž1Šë›¦´£ ¿nŸ§_˜Û޾߯êÞœ²š*ë¥ÔKÌ8:ƒˆÿDàçâg’,Æôw?€ú^õ‹-[¶2›ÌÜ¢»ñØZÙbkeK%§JÔó¬Çž+{Ô`áqálz}Q Qº~ˆNþ8€)m§èM§ðoVkßÖ( ¢oE“•—E5·jÚé4šéJÿ©ûß8Ì„V´éŒh:B'ݹ]æ²îÌ:""éäß©H¾…mÂBa¡³([ƒ5–R§ÑÅÖ…ÎU;³ýÒvZVjɶ‹Ûè[£¯N{üe»/Þd8[.laÒo“<^_Üg ³³÷?{ÉQæÐü»æŒl:’†Þ óžU5Ï«·Ónä¤W/¦Únjf*¡¡|ü3ŽÌà­ÚoQÅ¥ŠÞ4_fJ´8º0.¶.TtªÈ‰PÕµj‘ø3wϫʥzùê:á tòïD‡*èµ±³"g±¾ïzsE{©Qª”( ,l{k;/ídÑŸ‹ZÄ¢EßÐm¹°¥ˆ³é 399äâÔj©±(8Qó]ߎ!.¶.XYX‘þEºì@Í<¿‚i\{xŽßwda×…Ìê4 oGoœ¾*Ý]½Wn *µ …B¡7-sëÌ\ÒsÓù5öWN~xk kz7äà{©±´kϬ%O•ǃ¬„¬Ï¡ÈUå¢V«qžåÌä¶“eãMx¤ ·PXäÄüWæ³5v+{¯îåãÆ±É硻˜âÆôú,å6æû¥!¿¹í‹)ùšb¿jµ•ZUb{n\¡1ê)EHäô"ç{Æ(©oš«Óçíæ¶cæ"W÷æ–ÕTY#®FX.°ÈÔ‘’´Áyª<Ôjµt32E¶åÇ—3fï½qs:ÏÑuØ}e7m*·!#7ƒú^õé؈«”±,ƒ‹­ ¼èMt³l,mèìß™ýñûÉÊËÒyè á먯¹óä±É±z;¬-¬qµuå~æ}ƒ×ÄËÁK»~© ÉéÉ:oñûÕìÇìÈÙÌì8“—v°áµ :×kGÐèVœXÁô#Ó™ÛEÚFµ¸Ï(B[†j§f½SçÆìá‡P 0ëYÕÑÆ‘*.U8zã¨ÁŽƒ©¶J³ŠÍ˜Õi)) Û5Œðw‹ØåËN©ïWÚ2”¯Ž~EFn†N¸R­äËC_òAÃpµuÕ/ŒÂ‚ òA&Í÷|Qè›oloî¤ô$%hß„(PÐ+¨ûßÝÏìγùïÿê½/W•Ëö‹Ûu†¹õ…99 ź’§ÊãÔSÚkóTyDߊ¦–{­"yTq©‚Z­.²5\A¢nF±þ¯õLm7•‰'rã‘44xèú!ª—¯Î‡>¤š[5mK\æ‚Ö  }‹bŠÜ (@Ž2‡Ë©—‹Ä™[gæb¡°@­Vëìmom·ƒ7 ƶ‹zŠš¬‰YÚϘæcè[£/&<2¯ l­l±±´Ñk“rº³±´0x΄sö¼7Õ/ŒéÕ›Ó÷¦ÍT ù•œüëÁ»4Ö¾LO._M9Ùo[ …‚3wϘX æ#§9ßÓ`È–JÃ7ÍÑéóö sÛ1só”«{S|É\ÛhS¹ ‹»-.^’6¸š[5Ô¨ Ú°)²™2U ¤uW#Ø~i;Ý»¡@AH`{¯ìÕNS2ô ©ï7K³Îaü~:úçw¦êzÖÅÖÊ–¯£¾¦®g])Ì…¹—~›oRËÃ4›oèÝ—v ß~i»ö­>@ï Þ\}p•ïÏ|Ož*Ïà6à– Kæ½2E.ÒŽþ@ñžQ4Ü5jfGiAòÖØ­Ú0sžU¿hóÓŽL3xÆ„)ö±?~?›ÎobqÈb(˜Ýi6Ñ·¢Ùxn£Á{^VJ½ãðqãñrð¢ÍÚ6ìŠÛE£^;HŸŸúpõÁU¦¶Ÿ HÃH]~è†sˆM‰åúÃë,‰^ÂÒè¥Ú…±/žžœK:ÇýÌûÜ||Ó`H;i<Ê~DRzÃv £‰Ošø4!W•Ë/±¿ð0ë!))Ä¥ÆáfçHèàƒ‰¸Hs íüÚiÓÕVää‹ó,ëÉ;ußahØPâîÇñ(ûãöÃBaÁµÞ(R†rvåx¯þ{ Ê…ä d+³9~û8‡o¤aëv~Äm¾`RÛI4¯Øœa»†¡FGYb’b8qû÷Òï16b¬þ˜!½€´ÀðÔS(ÕJ£r.§·ƒ7¯Õx‘á#¹•v‹ôÜt"®Fœ‘l´ÎÌ¥pÞvVv¼ZãUÆìCÜý8²•Ù,;¾ŒóÉçéؽÄuÒá9GŽ’‘›ÁãìÇL;2Ûi·µCë…mRNw®v®ø8ú°á¯ $g$³/~[/nÕÉÏ_ÂT¿0¦Wc6WÐFÌEί É_¸ŒÉ/—‡¾ô å[°œÆì×£¬ýëôç“ÝŸpýáu2r3ØseÙõ#‡œ^ä|O_™uê·„¾i®NŸ·_³SËS®îù’¹¶mŒ’´ÁÞÞô ê¥cÚöµ´êQCã ±·¶gîïsµísp¥`n§ÝfcÌFõ ÿ›Bô­hb’bt¦Ä)PÐ5 +ËŽ/ÓaxøpÂ.‡ñ$ç 7ßä½­ïÑ£Zjº×4© Ÿ¶ø”ð¸pB#B¹|˜¤F„à裌nž¾O9»rt¬Ò‘ÿø¯ÑÑÔŽU:Ò¥jÆEŒŠÿŒ¢ŠNù,ø3Æí§ÝÁÑÔgU€÷ë¿OSŸ¦4YÕ„Í6sýáub’bø:êkß8lÔ>2r3ø(ì#¾l÷¥vz]yûòÌî4›Ñ{FëœaˆÂ¿û£öŒbË…-Lúm«O¯6šFiQêKöþg/ýjöcú‘éÔZV‹O÷~J5·jDgYOíµmýÚ²ð…¯¦ÞŠzüó›^ßôÒ®4µú«XYXQå›* Û5Ì`À•ûW¨³¬ÕWC©V²ãí(P”žÄÒè¥, Àw/$þÁÚ>ÒÖ£ÙÊl]?¤#}+šÁ cma­MW_Xq“C.`e•4¯Øœßw ê7U‰Ï‘÷`ke[¤ Kº-¡•o+º®ïJ¹Ùåðëmü¼cóP©UŒk9 –÷XÎøl:¿‰nÝÖdÿב¦ß6ÅÅÖ…JN†(™‚!½Lk?Y‘³¿o¼Q¹õ•sMï5Tr®DãUñ™çÃäß&“ø8Ñh™‹¾¼¿ëõ­|[Ñ~]{Ê]ž ç6þNx‘-Y‹ƒJ­¢‚cFïM…yð]àË‘GØûŸ½Tv®¬×&åt§@ÁÚ>kÙ{u/~ ý˜òÛWh¬“§!¿2„9~!§Wc6WØFÌAί ɯ¯ää7æ»…Ó3”oár³ß•=VRýM¾mBÕEUõn8PŒéEÎ÷ŒÙRI|Ó\>o¿y{1cyª{c:3×¶5lÝÊòãËõÊZ’6xu¯ÕØXÚPå›*T]T•³÷Îj^—F=j°PXУZR3Rµ#6–6t®Ú™´ì´"x¹ß,ÖÔö¨MSŸ¦Eæë‡†£Ìá•€WtÂy7bòo“©0¯-W·$¨|ëû®7yÊLM÷šü>øwþº÷íÖµ£ãÿ:rõÁUþòåu®}½Öë$¥'™4KbN—9„]ãÀµ%zFÑGhËP²ó²Yü§4Zeγª…‚M¯ob\ð8–F/¥ÞŠztü_G~»þ›vJ£œ}L94{k{NÀ ƒ(@h„4"õö/oc;ÝÛ鶤禲!Ûé¶ôÜØSçw_¥Vqèú!Î%¤­Ô ަ=kwïÞÑw?uê!!=ž›ÿDf:°í­m²s Ï¡àùp9õ2AK‚Hù,EöM @𲑖“VêÓb‹‹øÍ”&»w‡á蔿T`מ½z¯ëÞ5¿£™öø ê¾À(õ@ ü»IÎHÆÆÒÆàz6àeåeé4/+¢ã ‚“Ã÷g¿'53•´œ4þ±ÎþKe×0@ ¼<ˆV] %B©VòsÌÏÔ]^ß¾dçe³¢ÇŠ-–@ J™â 0ȓϟ¼hzzž õ<ëþNø‹C øG!~³/#bÄA @ Et@ QôNUzð@ÿéx@ Á¿Ë»§°;µË»'±xrûE‹£•C”^Èl8¥WCã7˜q@ Œ`•…íÙUX¦œÇ"çñ‹Ç 9±L9íÙUX%F•jÚbq´@ @ ƒ"û6±›°¹¼Ôª-Ž<9O°Èy‚Í£ë¨-Ê t¯]jI‹@ @Ë” ”¹ø“n§Á ,Ë€•-XÚ€ÜÙ5Ö`ùô£ï: «üxKk@‘§°ÔÓÜ_0\aY4MµŠ2Â2åB±Ê¬1â @ ÈPd¤ÁÚÌqøñÇ)W®œöãååE«V­Øµk—Îu*•Š¥K—BåÊ•iß¾=S§N%33Sçº'OžðÅ_P§N¼¼¼¨[·.C† !>>€FéäWøsùòå_—J•*qèСRM³´Ø±cµjÕB¥RɆ•…ëâe®›çÉß±þ vò,Ë`Œv'((ˆ#FððáÃç.‹@ ^ VIgó¿8V€Öã¡e(4 u@£¡Ý—àÛºèÍå«AÓaÐäé§nÿ¢×Ôy+?¾É0¨Ô2?®îtã*4–Â+¶È«ÜÊ4ÙKˆÙ#UªT!""µZMFF¿þú+ƒæ?þÀÏϼ¼< ÀÍ›7?~<µk׿êÕ«,_¾œ®]»²uëVÊ•+Àرc‰‰‰aÉ’%˜˜ÈÖ­[)S¦ û÷ï×>(¬\¹’#Gްaí,ÎÎÎ¥Q&3cÆ ®\¹ÂÚµkeÞ;vì W¯^XXXȆ …ù'ØÉó*ƒ!?~“:M?†È¯ò§tÕMÌ«Ž4jñ Þp†Þõ¥¿eœ \@éÉ^BJåõ……99RÏjáÂ…Œ5JÛiÐ`mmÍøñãY½z5?F¡P`ooO\\œ¾$_6l U«VøøøÐ¶m[bbbxã7øæ›oÇÝÝ7ÞxCo€¯¯/“'O&88___z÷î͵k×´ylÞ¼™æÍ›ãííM½zõX¼x1™™™4jÔˆU«VéÈtðàAìííiÚ´©Á0___æÎKÓ¦M©\¹2 àâÅ‹ >œ   ªW¯®óõáÇ :”jÕªQ«V-&Ož¬Õ¡)dee1iÒ$4h@•*UèׯŸŽ.}}}ùæ›ohÛ¶->>>sêÔ)ƒéÉÉ3iÒ$Z´hAvv6÷ïß'00P;ú4qâDêÔ©ƒ··77fëÖ­:r˜S/ÆtgŽÜúÈÌÌä‹/¾ ~ýúðú믓˜˜hršSøg؉¾2ÈéÈXž†üÏ?ëC­V£Pä/\3VO†ò,ò«C¹¼Kª_@ ø÷¡Èû¯Ì†ÔËpzœ\ —vÀ¹`e—¹uYð}:èAç ÍHAažÜ“þº×”T{Õ“òÔ„¿`ŠÝqP«Õ¤§§³~ýznܸAƒ HKKãÆ´hÑBï=5 ..…BÁgŸ}ÆçŸÎgŸ}Æõë׋+J©booÏ‚ 8uêîîî|ñÅlÚ´‰Q£FÑ­[7’““Ù´i“Þ0êæÁƒÌŸ?ŸÈÈH<<úˆ#FpñâEvîÜIË–Ò<6š5k†¿¿¿ŽL;vì gÏžXZZ ËÍÍ%11‘µkײoß>N:E÷îÝiß¾=QQQŒ5ŠñãÇ“””À!CÈÍÍ娱c„‡‡³ÿ~–/_nr=}öÙg:tˆ7râÄ *W®L÷îÝyòä‰Vž;wî0þ|Μ9ƒ¿¿?Ÿ}ö™Áôää™0a|óÍ7Lž<™ÚµkÓ¿¿4G°gÏžìÙ³‡„„FŒÁðáÃIKK+V½ÈéÎ\¹õ1vìXþüóO~úé'N:Ň~ˆ···ÉujNYàŸa'…Ë`LGryÊùŸ!.ˆR©äòåË,\¸PkÆêI.Ï’–ÇXË»¤ú‚%Ù¤¿ K¨Þjö…œ4¸qÎþÒƒ¿ƒ—ôÜnÈy( wß„Üt°+ΕÀ«¾´¨:õÒ3-’©˜Ýqˆ‹ QX¥ IDAT‹£\¹r¸¹¹Q©R%&OžÌ´iÓ¨]»6·nÝÀËËKï½–––xzzró¦4×êý÷ß'<<œ¿þú‹Æ3zôh‡žÁ«¯¾J“&Mðôô¤OŸ>ÄÆž3&Çk¯½F³fÍðõõeΜ9ܸqƒcÇŽiTãì쌯¯¯¶CeiiɲeËèÔ©“6ììlvïÞM¯^½dÃúõëG­Zµ¨V­mÛ¶¥U«Vôë×òåËÓ¿”J%W®\áÆÕ«WÇÍÍY³famm­ó¶¿G4jÔwww^{í5ƒuiLž²eË2wî\æÏŸÏºuëØ¶m ,оñmÖ¬>>>X[[Ó¯_?²²²HHH0»^ŒéÎ\¹ “œœÌÏ?ÿÌÂ… ©^½:NNNtîÜKKK“ëÔœ²üìÄP éÈXžrþ'ÇäÉ“)W®îîîtèÐÖ­[3qâDÀ¸˜’gqËc¬åò.©~à߉®€¬GÒ¬u@« Òú+;ÈH‘â4øw’Ö)¨U»ÞÂ+6ƒ2.E“OO’vf²°ÚàÝ@ ¿súÙÍн8¤éGŽŽŽÚ8Í›ÓäädŠÜ«R©HNNÆÇÇGÖ¸qcvïÞÍáÇ™?>Mš4aÏž=Ô¨Q£X*M¼½½KeØÞÅÅ___âããiÑ¢!!!tèÐðñÇS¡Bƒ÷>|Q}a…ñòòÒ™Îàì쌽½=999ÚÑ‚uœ——Wd¤ÃW¯^ÅÒÒ’† ó1·¶¶¦qãÆ§žyyy¬KSäéܹ3ݺuãÓO?eúôéøùùiãŽ?ÎâÅ‹9uê”v¤ÁP^rõ¢‚º 6[î‚ÄÇÇceeEÍš5‹Ä·NåÊòO°SÊPPGVVV²y¾óÎ;fùŸÍâè;v0sæLF…••Ô|«§ÀÀ@³ò4§<ú(X‡ry—T¿@ð¯%rxÔÌßùÈÖYZÇÐ|ƒaó›OG€À§ë”9p+ZZ8íQ ¼Á«.Ü8R4ý»g¡\ ´ÎAÓq¸{æÙ—ËÌqÐ,ŽvssÃÉÉIgž¯³³3*Tàĉzï=wî¹¹¹ê„+ ÚµkǶmÛhÑ¢ .4W¬—¥R HëAÖ¯_ϲeˈ‰‰¡Y³f¬_¿Þà};vì GÚ‡Caæàì쌕•‰‰‰Ü¹s‡;w̟þiÒýjµ@G÷šïÅÙõÆTy4ëf æ{ãÆ úôéCÇŽÙ¶m›vMJi¢Ñ]qåÖ R©P(Eê J¿NáŸa'¦–A£#cyšë4‹£ß}÷]¼½½™;w®6ÎX='OSËc ¹¼Kª_@ ø×’žû¿€}ãáÂ/Òw ^õ¥QG©'-t¾5ÿÚ*íõ§ï鳌o08VÔÒ.K/¥¾·áˆ#X°`:áJ¥’Ù³góî»ïââ¢ghéG. @;w÷eDß\wc{Ë'''“˜˜¨}Ó¬P( aëÖ­L™2…iÓ¦é½/77—ððpiúÂÌ¥råʨÕjΜ‘ï½jZ ÷÷÷'//³gó÷ÎËËãäÉ“T¯^ý™Èó矲yóf&L˜ÀÌ™3µÓÝ"## ä½÷Þ# GGG³ó—£°î ¿L­G UªT!''GgZ”†Ò®Ó‚˜Z†‚:2%Ocþ'çÏ–––L›6U«VqõªÔø›RO¦ú|qÊc Cy›k¿@ @ÚJµri £›pæ{ˆ^–?•ÈÁ*4‘þ¯Øìžîjm?¯ùiUé MI*ŒftÁ£”q„ôx”øìÊd¥Þq4hžžžôèуˆˆ9rä àÚµkL˜0€ÇóÚk¯±yóf.]ºDBBß~û-«W¯æí·ß.m±Jwww.\¸Àƒ´ë9ô…´ÌãÇINNfܸq4hЀ† ’››ËŽ;xôè©©©ÄÇÇkϵP©T|òÉ'M¤N@õ>ÒîG6º{°0¼s’+;éиÂÂoŸÏ:ÒÿIç¥í\5§QkvlÊNÓ=¡º0j•VöÒBt@ ”îµ±LþKúrzd?–s¬ çrîýÇW€*GÚAI³Õêå0ÝCánþž§VIÛÇ!ýÜ;'…ß8*­SP«!å¢tæÍY®éßÎUÃãD­ì¥…è8@ 2dW{›ØŸž¾ÅWÕ=p#,,¥“•¹—-u@•¸´]ú?§Ð¢ô$Ø3Fú_4‚9,­¤4"áÞÙüxÈ¿'ûé÷¸]pãŒÐi ° »Ú«p»då× :ÏÍV¡‚¿Bw/?ÿ4ýÓÊ#ÿD”îuÈ ìƒÍå­€Zê î$û±ôчZ Oîé†\ ›!} Røžœ'ù ½(È©ö*J÷:pû¢Ìu¦#:@  j;7²k¼‰"7ËÔ Xd¦}°Y°¶Geç†Ò­&Ù5ÞDmçVjI‹Žƒ@ @`„¼ÊÈ,ã‚Ý©%p÷$/iÇAUÆ¥G}2Ž@éÕ°TÓÖÛqpuuÕ,@ ü{qí5:¾h)d±xú)ÙIN†Ó@ YDÇA @ Et@ QDÇA @ ¥X•ZÅ¢?ÑñqûÚ 9¯ fåÉ•ÚkZ­i…bª¢ÈçÄí, Чù\L)½fÍÅa¦ûã÷¿¼ñKì/øÌ÷AõôøpCa¥Eáºx™ëæyòw¬‡’؉J­â먯¹VJ'Çüyª<³dIÍL¥ëú®8Ìt îòºdc÷‹ç¡ï¿£M à߉ÙÛ±f+³é¾áÿÙ»ï°(Ž>€ã_šRDŠÒD,   `ÃޢƂ ‚½Ä޽—h¢F£FMb‹QcרcÇèk7*¢J ½ ǽOîŽ" ê|ž‡ç-³¿™Ù]nngf;•Åœs¨e^‹,i7"oŸ/·íÜ–sí:Zn™¶~ÃüdbV\[ÁÙгîuX¶¡¶aaòòÞÍ>;›Àìë¹Oå²÷aÀ~<n~}ˆë_A(^—/_fРA<þœ>}úw8Ū@_AJ‘²ìÊ2æ´˜ó^¾½+ 6ý½ çµÎè.Ô¥öºÚÜŽ¾ €ÛN7–\^ÂáÀÃhÎ×Äm§›Âeú‹ô™ú¿©8­uB‘>­¶¶"86XvŒwvRcM ´hSåç*,½²€ÔÌTª­¬Æ*¿Ur1 >‰ž–­+]¦¿HŸ`¿Úž²‹ÊÒuwWî=»Ç Cƒ0[f†ù2s~½ñ«lÿØ´XúìéRS,´dêÿ¦òJò*ßå”–™Æ”SS°^aÑ#ÚíhGà‹@ÙzýEú,¹¼„Úëj£»P§µN\¼®4=UñL95‡5¤KÒì.(&KMØ|k3“OM¦ÒO•Ð^ í*[öÜÛ#GAÊ%¯º+HÜŠ¤f¦2ñäDª®¨JùÊÓagÂâÃò]¦É (>OTåÏp±!—Â.áºÁ½ïõHz•D™ïËàîKç]vt˜\ú^û½í3Ze=(»NòSv9ãËë¼(;džζÛÛØ~{;†‹ i¹¥e¾ÊX‘‚Ü [O‚ BÉE=èÔ©>¤I“&hj~ÞïN.PÃáyÊsž¥<õ‚k¾¶OÍL%.-Nö“ø*±PA~Hz¥ôX×y!ãC0Õ3eâŸðéëÃô&Óq·w'ó›L|úú(\ •Jy‘ò‚_;ýÊQw0/cN‡È’f•E¿?ú1µñT¢¦Dq~ÐyZTn@)R4©Ô„jÆÕäbÚ°Ÿ=ÐPÓPº,C’AX|ûzîÃo˜~~4ßÜœ¶6m¹ë}—Mg0öÄX¢“£轿7’ Fð×WqâÁ ~òý)ßå4Úg4ÿ ùGûåÁØXYÓlS3’^%Éâ‰Hˆ`]§u<šðˆjÆÕ}|´ÒôTÅ3ï‹y¤d¤°ä¯%@vCÂÅÌ…AµнFw.¹Lâ¬D¦4žÂ CƒdçZAËEUÝ4nEFÉå°ËësŒñ!Œ­?˲–ù.Ó‚ärŸ'yå/-3 ïãÞ,k»Œ‡ãR¦TYZ=zr$ð©Èî¶èóÀ•õ ì:ÉOÙ):÷_SuŽ­ï¼ž.à2€¸qœt>_e¬HAî …­'A¡äÈÈÈà»ï¾ÃÕÕ•ƒG™2e˜9sfq‡Vì Ôpx–ü ƒs`óó”ç²ußœû£%F²Ÿ/¶|QDa¿?^Ž^4ªØó2æx9zq7æn¡ÒéãÔ‡&VM¨bX…5×Ê¥°KĦÅТJ µ ©bX…ú–õÐPÓ`k×­t¨ÖA–Nº$#GdΔ-{}Lg3gª—¯Në6´¬Ò’>N}0Ñ5aP­AH²$>$4.”“Á'YÛi-åuËSÕ°*Cê áxÐñ|åíYÊ3¶ÜÚ¯~ÅÑÄ‘òºåYÙa%ZZì¾»[¶]÷Ý©oYS=Sz×ì­´,óŠGOK_:þÂÂK Yws{ïíe}çõ¨¡@«&X•µBK]‹¾N}IËL#46´Àå’WÝ4îœb’cØ~{;ºlÀÑÄ‘²¥Ëâf놆šF¾Ë´ yQuž¨ÊßOí~¢EåX”±Û¯‹}bSc¹~ €3!gÐÑÔ¡Y¥fùª‡‚–²øß–ßs òÞæT˜{BAÏ9A¡d8uêNNN,Z´ˆððp¤R)fff4nÜ8½?}zÞRŰ ÝwØÁÄ€gS³!±!4ø­ÜöK¿\Ê”ÆSŠ ÌâQA¿Bºï(c¤mDÃ*T•e~âq³u£kõ®Œ<6’åm—cmd-[w5ü*K//Å/„ô¥ÇRU.м]w¯Ÿ $î·=xùMuMœLr­+l™ªÊK~ÎEùÓTW|[0Ô6äK›/9x˜ÆV9tÿÝkt—m_zÈOÙå'þ·åu½¦Œ z eûäœA>¼'Ož0bÄ®]»ÆË—/s­oÑ¢jjjÅYÉR †ƒ®–.• *áá'k8¼Ô—WôÑ}B$YÔÔÔPWSçP¯C <ÊÊk+±_eÏÊ+ùªöW ÷Û°_îÙ²ea¨mˆ¦º&É_'+ì’—×­ï×ßø¿¦†Z¡fîÉo<¥ äŽJë­­ù¹ýÏ,n³ } Ê.*[àTy]w…ûµ,ijjj Ó*ê2…üŸ'Êò§ˆ‡ƒKþZÂ÷­¿çHàvöØ ¼òSvïzžçô>ÊXAø¸¥¤¤0sæLöíÛÇÓ§Oncbb¸qã>pd%Sÿ[Nl8‘Å-&53õ}ÄSâ)êëž×üø1É1„ŇɾiVC.ö]8=à4K¾\ÂÌ3ŠûÌedepøþa¹®Š–TUêH¥RnFÞT¹©Â¿mËÙ’™•‰ÿSٺ̬Lü"üp4q|/ñ\~r™ÿì`^ËyÌ>;›Çñ8ÿè<ÕËWgxÝáØ•³C¿”~¯Jκƒ7åßr|­šq5^I^ô"(׺¢.Óüž'Šò§Š»½;Á±Ál½µ•̬LÙSŠüÔÃÛ×I^eWçyNE]ƯÅ{Tr^k‚ ‡qøðaNœ8¦¦&¥K—V¸¡¡!...8²’©À oWoLôLhô[#Ž#,>Œ;1wØà¿!;Á·¾¹Ë98:.-ŽÌ¬Ì¢‹þ3+cƘ;¼L}É“„'J—Aöl0ñéñÄ$Çà}ÜWKW\-]ÉÈÊàÀ¿ˆK‹ãyÊs¼x@9r@ö!G†p*ø݇\MM–UZÊÒU´¬ ŒuŒXk #$àYé’t®G^çÂã ²môJéáÿÔ_6öí¿ÍôÌèëÜ—‘ÇFòàåâÓã™ú¿©¨«©ãéè™çñsæ3¯x^I^1âè¾nþ5sZÌ¡aņx÷FŠS=SîÆÜåFä ¢“£™|jr¡Ëå5eu—³òŠ;g>-ÊXУFÆúŒ%"1‚äŒdNŸâYʳw.ÓœT'ªò—ccZWmÍÌ33åžäU9¯“¼Ê®(Îóœò*ãœõ•¯4•\ÿ‘óZA>œÞ½{D@@sçÎÅÜÜ\n½ššnnnÅ]ÉSà†C)RœxŽ^5{±àâq¤ë æÊ+rïhÈ98Úh‰·¢ni>¤nÕ»¡©®IÕUñ>î­tÀ×qúÅ »UvH¤Žô>‚jÄ$ǰÆo ÕVV£ÒO•ð ÷es×ìiEÓ%éœt^ÖÚ/Â!µ‡ ¥®%KWѲÂXí¶š¦•šÒ~G{Œ—Óï~rý°¿ûâ;ÿµ˜éÿ›®ðïuÖѰbCZmm…Í BbC¸8ø"ÚšÚy;g>óŠgùÕådI³˜Úx*j¨±¶ÓZ΄œaï½½¸ÙºáíêMëm­©¿¡>†Ú†X•µz§²QVwŠÊAUÜŠò¹É}VVÔ[_Ëå–|sîÂÂß¹LsRuž¨Ê_~ôtìILrŒÜÓ€¼êAÑu¢ªìŠê<ÏIU+ª¯¼(»þ "ç9%‚ |xeÊ”aøðáhkkS¡BY^SSS¼½ wÿ©EE=•{Fîïƒ:W<Ÿ„2ß—áP¯C´±nSÜ¡ô©×ݧž?AA(ŒÌÌLÚ·oO½zõ˜4i®®®„……Q£F Š;¼wvâÄ1ôËÉþ>þçI…ÛulßNö{bB,uêÔ“[/F ‚ ‚ Ÿµ‰'¢©©ÉÂ… 155eÞ¼y”.]¢kW’h+Ï¡­wÑpAA>[[¶láôéÓìÞ½ ì™þú÷ïžžÆ +æèÞŠ–r/«{YNŸ÷{³AA„ÏÖ•+W˜9s&/^ÄÐðÍ Ž544¸qãVVï6n²¤²­–ýÞ¤ðˆHPS£b… ØV³És?Ñpx’f%wB!}êu÷©çOAò+,, 6mÚ„­­m®õU«V-†¨> uuuìíì°·³+Ø~ï)AAA(‘RSSéÑ£Ó¦M£C‡ÅÎGC4AA„φT*eðàÁ8880a„â磢p:VAAAøíܹ /þ-­¢}_Ч&çt¬¹Æ8äÜ@AA>'NüÉÙ³g9qâD®·D yƒ£AA„O^PPÓ¦Mcûöm¢ÑPHš¢k’ ‚ ‚ð)KHHdâÄÉ <ˆ¬¬LÑ5¿rqAA„OEFF½zõ¦~}W¦OŸ^Üá|ÔĬJ‚ ‚ Â'köìÙèëë3uêÔâå£'Æ8‚ ‚ Ÿ¤-[¶påÊUŽ?†ººø¾ü]‰†ƒ ‚ ‚ðɹvÍü‰Ã‡Q¶lÙâç“ ©qP˜êWA„áÉ“'Œ1‚_~YCÕªU‹;œO†h8|Ä>¦âcCGA„Orr2 düøq4mÚ´¸Ãù¤ˆéX?bþþ7°±±E[[›ÀHIIÅÙ©&“’šŠSMGBBBINIÉþ=ôÉÉÉ89:ò(Œää$j:8úøÍïÂÂHJJ±F ž9j>>G¥ÅùÍõë†ËÇôíyI kð©ibog‹žž^ñ¤@rJ ¥´´ÐÒÒÂßÿ†¨cAAÞ«%K–pùòe8€––Vq‡óÉÃË?råË—/‘=]]nþ}«¸ÃAá3pü¸û÷`Ó¦M¢Ñðžˆ1Â{¥¡!N1AAÞ¯»wï1uêTvïÞEùòå‹;œO–xâð‘‹|ú´¸CPɵníâAA„OسgÏ8p ‹/ÂÙÙ¹¸Ãù¤‰†ÃG®’UÅâA%_¿ëÅ‚ð‰IMMåŸþ!==½¸CyoùçŸÈÌÌ,îP ,..Žþù§¸Ãá3‘‘‘Áðá#èÝ»7]ºt)îp>y¢áð‘ËÌ”È~OMMå×ß6Òwðܺö ßà!|ÿÃ2""#sí÷˺ ´íäŽÿ­Üc&N›AÛNî>!×úM›6áééÅ·ß~+[vóæM<=½x¬à|Qæùóç=z”´´´|ïóÚÇiÛ¶aaaÞ·¸:uŠàà<·»yÓŸ¶mÛÿ¢*œ×u˜³wîÜ9Ú¶mWLQ ‚ð¹™9s&FF†Lž<©¸Cù,(l8téá©ðå‡pêôÚvr—ý¸uíÁðÑã¸|Õ·Xâ)éÞbÍZüÿ¾Å”ñãØ²áWfLŒAYƒ\„¤R)W|}140à¯+ŠËµtéÒlÛù{®å~7nòèqþ?°5¬ïšïm•IIIaçÎŒ7þÓúÔøú^£Kw’’=Ú›iÓ¦Ò²eKΞ=«ðjHHÛ·ïàêÕ«ìÛ·?×úøøx.^¼ÈÕ«W9tèÜ:‰DªU«ñõõåöí7ß(¿xñ‚‹/’œœœï¸6l8qqqÈíÇoÊ”©œ:uª¸Ã(÷ïßWX‡vvvŒ3¦˜¢ás²aÃþþû«W¯FMM­¸Ãù,ê‰Ãæí;ønÑ’<—–…¹9{wncÏŽmlZ÷ ­Z6gá’¥<Š*’ôß§¢,‡üxÝU)++‹«×ü5l(µk¹`jbBMF†©‰‰Ü>ƒƒ‰‹§O/O®ú^#+++Wºí¾lƒ¯ß ‚<-“J¥lÿ7‰% IDAT}]:vÈw|…íª”••ÅÅ‹éÕ«7Íš5cÖ¬¯Å›Ø´iUªTaçÎxyyÑ­[7&NœÀÙ³gpuÍ=ýíîÝ{°²²¢ÿþìÞ½[iºmÛ¶eÏž=rË.^¼DRR7*ò|䔘˜È£Gò|"!‘Hxüø1/_¾,ð1¢££‰Ìñ4.))‰ððp…×D~eddNxx8¯^½*t:o§ªòéCZZ¡¡¡¤¤¤ä;]©TJtt4*ó›••Å“'Oˆˆˆ ###Ït™=ûë|Ç‘SDDQÁ½^„âuñâEV®\ŦMÑï‹ú` ÕpdJäºÈ([VXš`dh€¹™½zzPÎØ˜»ÿIúïSQ–C~¼>–ššÚÚ¥yžç>W|¯Q˹&M7âÅË—=|˜k3Ú¶iÅÖ»dË®ù]'òi]:u,º äðàÁ¦NJÆ:tçÏŸ'""¼½G½·ã~¬^¼x¹Öå\&‘HØ·o/=èÕË‹   üýý¦Û³§·nÝâÁ[ Ç={öÐ¥KtttÞ)f___zôð V­Ú˜›[àêZ€   Zµj­­ 6¢jUk¼¼zñüùó\é\»æG½zõhذ5k:1qâ¤}š«W¯âáÑ—ZruxðàAÌÍ-€ì=k×þš+íλ0rä›kúÂ… 4jÔ˜ºuëQ«VmjÕªÏ •ùá󯨱ãX·îW*W®\Üá|V”6Nž:ÃðÑãèÜÓQã& À×sç±÷À\½vö]ºñõÜy —tñðbÃ¦Í =–.^L9»Ð³©©«É ®û¯Çn 6’ó/ɶëæÙ›;wï1fâd:wïIjjªÊí»xx±s÷^¾1 ÷ž½øö»ïyô8Œ¥?ýŒgßxöÈQŸ?eÛ'&&²dùôìÓŸÞ³aÓfÙ‡eå je1Ÿ9wž!#½éØÍƒþ_ eïþ?–ËëòTSS£_ïެݰ‘Ukש|:sùê5ê»Ö£|¹rXW­Êe%Ý•zõìÉß·nðïýÿž6ì¦g7÷½ :?]•^¾|ɪU«iÞ¼ݺugûö„……‘𦽥¥%¥K—Î÷q?Í›7ãÆLœ8‰³gÏ)—ðÚùóç‰ŠŠ¦gÏž8;;coo¯ô©ƒ¥eEš6mÊž={ì.L'NœÀËËëc®W¯Û¶màÒ¥‹ÞçÌ™Ó@vcgܸ±Ü»w—ÈÈþ÷¿ÿÉ×_ÏΕÎòåËX»v-aaÙ¼y‡â·ß6æyü 6йsg<âСC\»vž== çúu?þþûoLLL˜3ç›å+::šéÓ§3sæ Âßðøñ#þüóŽ@öãòåË3}útïxŸZµj©LóÇdç΄…=fÅŠlÚ´™}ûöÉÖ/\¸Ý»w³iÓFž< 㯿²Ÿ 1ReºffælÙ²™GByüø‹/âçŸæÏ?ßÜëöï?À´iÓÚÚÚ¹ÒöòòbÁ‚ï˜9sÂÒÒW×z¬][èpÐÔÔ”½´P__Ù:llld׬éÈøñã™=;wÃaذá4lØ€víÚÑ¿?Ö¯_ǨQª?4·nÝš~ýúаa¾ø¢%W®\aÿþ}²§)ƒ dÚ´édeeåë^Ù ‰D‚««+hhhP£F Ùú²eË¢®®Ž¶¶¶\žU™0a¼¬qÑ£GwÎ;˺uëñòò"--71þ|Zµj€µµ5߿ܧbEų®¹¹Éw7ôôôäðá#\¼x‰öíÛ°råJÚ·o/7àðuyJë0'OOO¶oßÁ;wprr`ß¾½˜››Ñ¼y3~ûm#Õ«Wç›o¾‘õSž2e2>>>III8:Ô <<‚ÇaO¦ß«gOî޽NJ5kñèæ^¤ýSRRxõ*{F©Tªr[©4ë“üy—¾ôZZZÌš5“»wïrøða¾þzñññ 2„½{ß|3ýòåKNž[ùz­o9£rE2Ÿ¸¾¾>f¦¦DFFââTSévææ¬XþCv€ššèéêÊ}` ø÷>ûþøƒÀ ‡$ÿ70#ãÍèœ}»óÚþmÆFÆ< ý]¦L´K—&S’!ëþÓkÀ`Ùz‰DB… Jó’ß}ÞŽÙª¢% ÔḡɴoÛ†îî]”¾1òéÓ\ïrPSS£N­ZÔrvæ›ù Ù½ï3¦dkxÕ÷QQÑtïÕWnŸ+¾¾T®d•+}s3SººwáÖíÛ¸blƒª®J:u¤S§Ž<}ú”Í›7sìØ1âââtÕÕÕcêÔ©>þçBKK‹ êÓ A}† F‹-Ùºu+žžÙýÐ8@FF6l`ûöí²ý444صk=ztÏ•¦ŽŽ]ºtfÉ’%ܽ{—{ïùX¹rëÖ­ã»ï¾ÃÅřҥKsñâ%¦NšëTºt©gweKKKC___é1J•’ŸeL]]=WZ}¿É»Aûf½––ÇŽåÈ‘£œ={–¥K—2{öl–-[&«‡‚ÊÙ=O[»4™™™H$Ùubee…©©©Üv³gMÅŠ¹¯gÈþàÞ»wÊ—/ÏÂ… °²²B]]ٳ癙݅R]]ý¿r)šî^^ž¬[·žo¿ý___"""ðòzÓˆ}ùò%¦¦¦¹ú*П*UªI ‚ |ÜŽ=Ê‘#G8qÂ'×l‘‡“¯†CQÊÊÊ‚<¾µ|=8Z‘¨¨h¦Ïþ†‘C¿bÈÀÑÕ³·Ò´ º½*zzºhhhpdÿ…ƒQ‹juuuæÍž…¯Ÿã«ÞxFû/¿Ìµ­ªÀ©««cU±O³/±±qÜ àç¥Kp¨Q]¶ÝO+Wsùª/½•|¸þÕ |Å­ˆª®J¯YXX0kÖ,fÍšÅíÛ·ù嗵ܼy“ØØXÙŸ‘‘¤§§‹qùPªT)lllä6ïÞ½‡ àáá!·mPP7näÉ“'XYåþ éååÅŽ;iÑ¢*T(²54²o=9?˜Ÿ:uŠ^½¼èÙóMœqqG¦ñàÁC\\\d¡§§§´‘ý. Ê’‘‘AJJŠì©[FFF®Y™J•*…‡G<}:Ó§Ï`ÕªÕXYYEHHkÖ¬ÆÆÆZyå‚ðÉJOOgðà¯0 ?íÚ‰7Ò—j>>G¥uêÈÀëÒÓ¹sfQç¿Ù<®ßðçû–rpoöœþQÑÑ̘ý-qññ89:ðÝ·s.ëÒÓ:µkó ø!)É)¸8;3~Œ7F†oº!ùûg~éÓgؽÿ›~ýEaÀR©”_7lääé3”ÑÓ¥}Û¶œ8uŠofÎÄÞ®»yðýüoqùoöŽ‚n¯hV¤·Ë#-=›·qåš/ ñ ˜˜”§{Ww:uh¯´lòÚ'g ÏŸ?gÉòŸ }üˆôôWT­\ïC©noŸ«ÜÌ-,©`¡|ŒEq{ÝUÉßÿ9Ï3¡h<}ú”Û·oƒ¾~lmme³×@vwuu ¥¾ž?Ndd$5jÔ ==lmm•¾¯áÑ£GH$ÙìG }ûøwïÞåöí055¥M›Ö ßóæMþEKK+++êׯO©R9ÇmÈóõõ%(è&&&´jõ111ddd`m-žÜ¹s‡[·n£¥¥‰µk×–{ª“³ãââ ÃÙÙY.W¯^qÿþ}ttt°UòåÑÓ§Q\¹r…ÄÄDLLL¨[·ŽÊóG„OÛ¤I“IHˆgÆ âÍÐ%„†CQÉÙQ$gÃAÈÑpAáSõË/kùã=zô_<*BqJ†°'y¿)º8¶«’ ‚ Ÿ§óç/°aÃz¶mÛ& %Œh8|äJòÓÈ~â ‚ ‚ÁÁÁŒ3†µk×él~BÑ ‡˜T*Í÷¯‚ ‚ %Ybb"ƒÅŒ3äÞT/”ïõ=Gì}ŸÉöÔÔÔˆŽŽÆÐ ,zzzÅN.É))Ô¯W·¸ÃA¡„ËÊÊbÔ(o¾ø¢%ýúõÍ{¡X|ðÀ EËÆÚMMMîܽGrJ N5 }DRr2NŽ„>žöv<Ž&..žêvvDÅdÿnokK̳g$''cggK™بA¡ä˜?>Ì™3§¸CT ‡œAYœj:Ê–Õttýî$÷û›§Ôtxó»ã[/Tq|kùÛo—6([Vñïo½áÛð­ivŒ AA>Wûöíçĉ?9qÂGn l¡äµ#‚ ‚ ‹7n0wî\þøãÆÆÆÅŽ5Ÿ£ÒâBAAø¼¼|ù’‰'3z´7õÅôíM/_AA>œôôtºvíÆ¨Q£9rTq‡#䃿ÿ 1« ‚ ‚ðáH¥R&Nœ„5ÞÞÞÅŽPbŒƒ ‚ ‚ðÁ¬ZµŠpäÈáâE( ÑpAA>ˆ³gϱyóf|||ÐÑÑ)îp„ AAá½{øð!ãÇgëÖ-XXXw8B!ˆ1‚ ‚ Â{Gÿþ˜5k&uêÔ)îp„B AAὑH$x{ÆÍÍÞ½{w8Â;]•AA„÷æ›o¾E*ÍbÖ¬™ÅÊgÅßÿúe ´m5•ëEÃAAAx/öìÙÃùóçñññACC£¸ÃÞ‘h8‚ ‚ Eîúõë,\ø=‡ÄÀ lq‡#ÑpAAŠTDDC‡ã§Ÿ~ÄÚÚº¸Ã€¬¬,>$""5À²Bìl«¡®žÿ!Ï¢á ‚ ‚ ™´´4¾új£G¦uëÖŎ🠠•ýŠšØÛÙå; 1«’ ‚ ‚P$¤R)&LÄÞÞŽáÇw8Â[Â##s-{‘{™*≃ ‚ ‚P$~üñ'ÂÃÃùãÅŠCzzz¾–©"‚ ‚ Â;;qâOvìØŽÏ J•*UÜáïh8‚ ‚ ï$((ˆiÓ¦±}û6,,Ì‹;á=cAA„B‹eàÀAÌŸ?Zµjw8Â{$‚ ‚ B¡¥¥¥1bĺuëVÜ¡ï™h8‚ ‚ …faaÁ A‹; á AAAòôÑ ŽNNIaä˜q¤¥¥S¹²Ë}_Ü!}–>…zøòPœFŽÀð!ƒ¨#ú³ Ÿ)qásóÑ=qÐÓÕeû¦ßë=²ÈÒ {ò„¶ÜiÛÉÎ=<3q2·nÿSdéŠÞG=|hï+™ m;¹0nÒTnß¹S¤é—½zöÀʲbq‡ñÁíܽ—k~)²ô‚<`Ò´™tîÞ“Þ³ô§rëÿ¾u›¶ÜÙµwŸlÙößwѶ“;·þɾO%%%áæÞÙs¿`Ú×sØwà`âH$¬Xó } ¡KO&ϘIpHhÞ;/cc™>û:uïÉðÑc¹ø @Ç.jE]GÊäuù\î‚ |>>º†ÃûtdÿöîØJó&M˜»pÉ))Å’ ”X-›7ÃĤ|q‡ñQË”H˜3Íš4bïïÛùiélªV‘ÛÆ÷úuLLÊãëw]nyEË \üë —¯úbnnöN±dee¡¡¡É¼9³ØúÛzªÙØðíw J¥yî»ê—µ”Ñ+Ãîm›ù¢y3,^L¦DòNñ‚ %O‘wUŠÃçä)^¾|ÁXïQD>}ÊòŸWú8 MMM:»u Ÿ^ì=ðÇ|þ$>!Ë Œú.NNœ>{Žƒ‡“ƒ™’LîÜ `áÜ9ØV«¦òø}~E£®<%55…¾½¼hÙ¼™l}tL 3¿™Ëˆ!_ÑÀµžÜ¾jjjhkkÓ¥“¿mÙJxx$övÕˆcõ¯ë¸}çeôÊ0tð@š6n 2ªbyÅ+Vó08FŽ‹SMA›V_pý¦?Ï_¼ ·gO:»µÏóxªâT&>!Qã&òõ´)8:Ô 6.ïqøvöLÂÃ# ]…̓ª:*içRa :‚S&Q£º=}úñã’ET²² ÿWCéÙ½;ÇNüI|B<µœœ™9m²ÊòTUfªÎ3ee–WYÿqè0åÅ‹—,˜÷\W¥ÂÖ­ªü¶n {Ý*‹30è! /&99…L‰„7ýXÿËjt´µ8vâOþ8|„/^R¥r%fOŸ&k\)J3::šøøÜ;wB]]mmºwu—;gü®ß¤_ï^¬Xý ±qñàäèȽ€ÉÊÊââåË4mܘÐGù:Å¢¥¥Å˜‘ÃeÛxtíÊÁÃGyK9cc¥i¥¥§sÍï+~\J™2eðèÞ]{÷pg'G•õž×5¦ìzPv^çUGª®Uõ—×~‚ Ÿ‹"k8üsçG}|¸ù÷-š4jH—Ž€ìGêöv¶,[ü=©©©‡¾yô]FO…ó¾Á²B._½Æ7ó²gû´Ji±qíj<û`άØÙVãÜ…‹y~ØHJJaåòˆŠŽaÔØñ8Õt”ýãËÌÌ$<<‚”TÅO^½zÅÿΜ£T©R˜™™°ð‡¥T²²â÷-›ˆ`ú×s°­fƒ™©©Êü©Šeñ²©éèÈ¢ïæâëwùß/fÛÆõèéêÅÊå?ðèqã§L£ý—­ÑÒÒRy”©«©óìù ž?®®.NŽŽ²}ÝڷêbEÔÕÕiÖ¤¥´4‰|ú s3ttt044¤¢eÌLM‰‹ËWL­¾h€¹™)vv¶²o,+TàÔ±Ã|Ѽy®ý:÷ð¤S÷žìûã óæÌÂÐÀ€§QQüËð¯QªT)¬«V¥Ní:²4UåOY,qqqü{?¯ÝÑÔÔ¤iãFpçî=Ù~-[dÇW¥r%ÔÔÔxñ2VåñòŠS•zuêàZ·“¦ÏäÅË8úxõ”­{—z(h^SVG%å\Úð0}úåúY´ôG ûÉU‡vmhÔ Až œºvî(ûVÙªâ›1ÊÊSY™åuž©*³¼êH™ÂÖ­ªü¦nßõºU§2§Îœ¦»{l¬«¢¡¡³“£\÷!Ei–.]šå‹‘–žÆ¬oæÒ{à`9*[Õï:5ìíÑÕÕ¥nm®]—ï®Ô¬IcV­]O£®ùŽ3?ùKHHd톌> •i¥¥¥¡®®Nff&ÿ;sާO£ÐÕÕ!5-=_ÇSu¿†Ü×C~䵟²ú+ìñàÝï‚ %Í;?qˆ‹‹'66–Æ bSµ*º9¾ùjÐ6oÛÁ˜I“)¥UŠ¡ƒÒ¢YSþºr•½ûÿ 6.ûBbR2™™™²oÝÔÕÕe?IV¾bÒ/SFî÷ü~H(¼K=4y))çR—Žn´hÚ$×¶¥J—ÊWšy±07W¸\Yy*“×y¦ªÌÞGåEYþ S·ïzÝƳgÏ13+ø8ƒŠ–˜6i"R©”ÛÿÜá›ïbok‹Cê\»~Úµ\¨íR‹K~åÀÅÙK š5mÊ»wß)þ×ÒÒÓ™»à{ÚµiCsçyNÚÚÚdee¡¥¥Åï[7’’ŠŽvé|/¯ûuÎë!?÷OEòÚOYýöxBÉ—€––::: ×;vŒ„„úôéó#{#88˜³gÏC×®]©Y3÷—8ÉÉÉH¥Rʼu-½–˜˜ˆ††ºººÄÇ'yYÊ‹žžR©””·ÆêëëË=->]ïÜphѬ)®uëpæÜ~Zµ†¬¬,Ú·ý’Ö_´ÄÈЀrÆÆL™0È~”ÿãÊU4oÚ„¸¸x–,û‘¥‹¾§º½-Ý{õE*USz¬·éiii’%Q|¼ýMlll,††ªµ¿­lY}¼G cÒô™´jÙS“òèèê²qíÔÔrǦ,¯·U‹¡™ ‰²F±q±æŸ²ãå§*‰„ŸW¯¦kçNlÛ¹‹† êcld¤tûüÖCAóWÜ%å\:~â$¿ïÙ›kÛzuëðõô©*óPª”™’LY ))©¹¶QSSý o~©:ÏbcãT–YQ×QAÏÉ×òŠS©TúÎ×­*êj(0lbRžèèèüf-555j¹8cg[GS©’ÿðï}vïÛ@FFÿ¼Õ@ÐÔÐ`é¢EÒpÈÌÌä»ï—PÍÆš¾½<óµ¹¹)š„…‡ckcCff&O£¢¨˜Ï·òº_ç¼òsÿTTGyí§¬þòs¼ÂÜ…â×´i3:wîÌÂ… ®?sæ,‘‘‘ùn8¬_¿W¯Ò3fL‘Ä@»víiÚ´ +V$#ã•ÂíFCtt4'NøäZ×±c'œœj²fÍ4h@\>¿HUåèÑ#<}ú”áÃGÈ–iiiammÍ„ ãÅÛ£?qEÒ<ÔÕÕ¥sǬ_³’ñcFñàa0«~Éž ïêµk¼xùÈþQG[555RÓRÑÔÔ¤r¥ì..ýERRR¾YÑÒ’ÇOžŸkÝÑã>¤¦¦ðï}‚<¤^íÚ²uÑ11 :‚k×o(M»JåJÔ°·çà‘cX˜›S¥²[wì$-=ŒŒ üoÝ’u•P–?U±RÝÎŽ=þ 33“¿®\åÅ‹—ÔttÈ3ßÊŽ—WœªìÚ»=]=¼G £c‡ö,ÿyE¾fRÕõPÐ<¼¦¬ŽJʹԱC;Ö¯Y•ëg̨ R—gYÁ’ÿ ²¿IûÛãw¡¨ÌTgy•Y^u¤LaëV™ÂÖí»^·ªâ466&,,\nü@ÛÖ­9pø!¡¡deeq7 €¨¨7D¥™”œÌ¦­Û‰ˆŒ$==ÿ[·zðknÜôG¿¬>ÇþØÇñƒû9~p? ë»â{M¾»’2’, éé鲟·Ï5E±dee±dùO”ÑÓã«Adû½}/P´ŸvéÒ4¨_}’’’‡ ¯_‡öù*OU÷kEòsÿTTGyí§¬þòs¼ÂÜ…’oòäI,*À»9üýýñóËßõ™>>'¨\¹2»víbéÒ¥ÔÎãÚÈËû9uê$§NäСCŒ9B¶,çÏæÍ›˜9s†Ür‡7çþæÍ›9uê$ûöíÃÆÆšQ£¼9yòä;Å)”lE>«’“£#NŽŽdddô€«×’–žŽ©‰ Ó'O ‚…Ý»º3vÒÊ—+mµMíhU±"Ú~É ¡#H$ü¾m³¬«Cêv 2œR¥K1iüÊ—“îëoÃT èÑ­ —,ã›;_OŸÆ/ë6ÐðP$ ¶¶Õ?z”Êü½¦,–i“'òóê5xôé©IyæÌš®ð1cNªŽ§*NeîqäØqÖ®ú€Þ^=™0e‡—ë:¢Œªz(L@y•¤s)g—¼üêÛ«'‹–ýˆ¯ßuêÔ®%×ýá](+3eçY™2eT–™ª::j ééi¼xñ’ÅË~¢t)-õïGë/Zºn•y—º}—ëVUœM›4æÌ¹ ôøšl\÷ :ÚÚ´hÖ”øø,þç/^RµJe¹'PŠÒÔÔÔ$::†IÓg’””Œ™© £† ÁÞ®‡¥qÃrÿ›4jÈÎ={ù²ÕyæÓÖílÚº]öw³&˜3s†ÒX^¼xÉ…KpîâEÙòU?.ÇÞ®šÊr3j$K–ÿDϾ¨`aΜ™ÓÑÔ|óïEUyªº_+“×ýSY©ÚOUýåu¼ÂÜ…’ïÅ‹¤¦¦bmm-[vûömüüüHLLÂÌÌ”&MšP¥Jüýý‰ˆˆ ==£G³Ç)U¯^[[[¥ésæÌY’’’pttàË/¿”]ïgΜÁßÿ&R©T–^çÎß)?ŽoåJNNÀ¢ÎÎÎ ·××ÏþÿT©R%¥ÛØÛÛÉʧvíZ8:ÖäôéÓ´k×Náö·oß&%%gggŽ9Btt 6 aÆH¥R.\¸È­[·¨R¥2;wÎ5ÆêÙ³güùçI^¼xµuUZµj%w-feeqúôi³¿ «\¹2Íš5Ãè¿^iiiœ8q‚ÐÐGèèè`ccM³fÍdÝÕbbb¸té/Âß```H:µæýîÝ{œ={†Ò¥µqsë@ZZaaa´nÝZn»7npíÚ545µ¨S§6®®òãÑ^ççÙ³gèë—ÁÉÉ™úõ]Kt·/5Ÿ£Ò:uêå½åG¢ÏÀ¯øöëY²t"¡°Dý Âû%®1¡89;»¨ìª4qâ$"##Ù³g7+V¬`Ù²å4oÞ##CÂÂÂPSSãðáì\¹’ ~C"‘P³föT½½{÷RÚmgûö̘1GGGLMM¹zõ*NNNìÚõ;:::Œ;–Ë—¯””D­ZµPSS“őӠAƒ•vUjÞ¼…¬«ÒÛ’““±±©Æ¼yó1bx®ýBCCiÔ¨1k×þ’+GŽaøð\¹rY®aU»vêիdž ë•–i@@’‘‘Áõë×ùù矸yó&×®ùaeeÅåË—i×®ëÖý*Û÷ܹó 6 KKKlm«qûö?H¥R:HÅŠ‘H$ôéÓ—;wîдiö˜µû÷ïÓ©SG¦M›F||<:¸‘™™‰«k=RRRøçŸ;Ìû­¬QV©RejÖ¬‰¥¥%111øùù1uêT&MzóåÒîÝ»™4i2µjÕœ›7ýqvvæþýûøù]²0&LäàÁƒ4mÚuu .]ºD¯^^üðÃܹswwwlmm±µÍžBüúõë\¸p Åc Êßÿúeßt=?þ§â§AÛ¿ièÙV³Q™^‘?qAAøÔlØðóçÏcðàÁ²e‰‰‰Œ7Ž€€IIIaÛ¶­*Ó‰ˆˆ`Ö¬Y òóçÏàÞ½{tèàÆš5k˜2e «V­bÖ¬¯¹sç{÷îÉ3¶ÈÈH,X˜kùóçÏ ’Åwröì9¢¢¢äžl(rûöm6mÚ„›[ö´ýãÆgæÌYtíÚ• Σ¦¦†Ï † ÂôéÓ°¶¶&>>‘#GзoæÎ‹ššiiixxôäÛo¿eãÆráÂ.]º(÷¤çu>}†çÏŸsûö-Ù†¬¬,RSߌ5ôõ½J… dïÞ½›©S§1hÐ@ŒIHH`îÜy 6”yóæB›6m011‘í·}ûŽ9‚Nÿ½óåúõëtíÚ 7·Ž´lÙ‚;wàââÂÁƒÈöKKKËs&»âVrŸ…‚ ‚ ”¥J•âêU_ž|ÀÅ‹HLLbòäɲm´µµ0 ?—/_²§·8yòä3He{]G¥K—"55•S§N‘žž=kŸºº:zou+¬P¡ÏŸ?ÇÏï:/^ÄØØ˜ŒŒ ‚‚‚¸zõ*qqqràml¬åòÙ3ruìè&k4¸ººR»vm®^½*‹7<<_ßk²±dÚÚÚ²÷3•TŸÜ‡×Ó–%)¡àDý Âû%®1ácòÃK˜8qõë7ÀÁÁ6mÚ0bÄpŒ ø~ް°0Ê—/AY¹åÕªUãüùó…Š­jÕª »5oÞ¢Péå—«k=ŒÑÑÑaΜÙ4mÚ4ω4,,,äþÖÓÓ͵üõ‡ùÄÄìÉ0BBBJ¥¸ºÖ—Û733“ääd±±±aêÔ©,]ºŒ~XŠ««+;w¢oß¾hiiѾ}{ÜÝ»àí=š4iBÏžtêÔ @Ö˜Û¿?•+WÆØØmmm {ìd?-ÒÑÑ‘{ºÙc)®¿5ùCpp0~~~¹‚)))XYY0jÔ(þþûoºv튙™-Z´`РԩSGeù·O®á |É))Œ3Ž´´t*W¶bYfž()>…<¼o#ÇN`øAÔ©U«¸C„Bœó‚2mÚ´áÖ­¿¹qã&—.]dçÎß9òöÎ;,Š£ à¿£w° DšŠ ŠX°`ï½w4bï½ÔØ5&*F% –Dco¨±Ä¨XPQ¨t¤ ß'''WÌcö÷<<‰;»3o™ÝÛÙyç#G8wî,ZZEßÇÇÀÀ@f„|ÒÒÒŠ5ƒñOÒ«W/™5EAÑÀBÙ€#/Oò…þĉãrç4&Lðgذ¡\ºt‰Ó§Ï0{önß¾ÃêÕ«ÐÐÐ 009sæpîÜ9Nž<Å!C™5kC‡áÀ„„„pæÌii¨SRR+V’Θ˜˜™™É»wïd|^p†C"o:t`üøq…dÍO®bnnΑ#GxöìçÏ_ 88_ß¶>ü3µk¹k…P%b¡¯§Çîí[=bØ?-J±)‰9b1ÞmÚ0Æÿ[îܽû¹Åû"èÖ¹#6EÌÇÿ5ñã¾ý¬Y¿á³Õ÷èñcü'MÁׯ3ÝûôgÙª52å·nßÁ»M;öîÿIzl÷ž½x·iÇí?þàíÛ·´jçÇôÙó˜4m? þ$9Äb1kÖo G¿´íØ… “§ñôY‘®MHL$`úLÚøufÈÈÑ„?|üIm)¨òíçìóÿ•çÄ uuuÜÝë2qâD6lXϳgÏxþü9 3Qµ1'€³³3oß¾åÎ;2Ç/^¼ˆ³³óß!ö¿žÚµk‘™™Itt4vvv…þ &ôõõñööfñâEŒ1œ3gÎÈÔU¦LüüüظqÞÞÞœ9s€‡ao_^f}ÄÅ‹e®­V­"‘Hf&A,súôéä­ÍÍ›7±²²*$ëdzöööôíÛ‡C‡R¦LÎ;Ï—Œ0pPHãFžŸ”ÚV 09b13æÎdza}öïÙͪeKp°//sÎÕë×15-ËÕrÀ[[Yrþ¢$~÷Ò•«˜›úÎÔÉÍÍE]]ƒ93¦²këf˜5o~‘ömY·a#úìû~My2ñbr>ÚËâk@èóÿ-ž={JHHˆÌßÉ“' —žžÎäÉS¸ví:qqqÄÄÄŒ±±¶¶¶¸¸¸pëÖ-BBB8þ¼ÌZˆ‚xyyáääÄØ±ã¸uëÑÑÑÌž=›»wï2xð ¿Uß+ 6ÄÓÓ“Ñ£Çz”ׯ_ÉÑ£ÇX±b%aa×X³f < 99™Gqöì9i–«àà`víúžˆˆ§$'§pãÆ îܹM•*UIúÜðð‡„„„ÀéÓ§Y´h±ŒŽŽŽtèÐvìØÁñã'èÓ§/ïÞÉnÎ7~ü8^¼xÁ°aø{÷O¸wïëÖ­ãÒ¥K€$KWhèQ¢¢¢HJJ"44”„„™uöö˜…¨¨h‚‡˜œLÕÊ•Éçp÷Þ}Ìž“£òô‰} ¥Ù7M¸þûMââãéÞ¥3¾­|ø+&†«×ò,òø¶jIïݤ׾zýš)3g3tàÜë|˜®SvÝþƒ‡=v‚䔬,-:hÕ«VåÔé3ÅÖ¡¸ô4”Éý©TQ²V§½X¹d¶ïã{Dg??BŸ 9%™U«1eÒ¥6Sf—˜ØXV® äID¦¦¦Œ6„êï<Šì¢Êž‡~>Lð‘âã˜?g¦LØFqý§L¿âú/11‰Àï6qçî] ô Ô¿/ ê[·ž0ñbÒÒÒÉ‹¹ñûM6oD÷}¬kèñ:|„øøÊÛÙ2=`’ôES^¯^½"99…v¾mPSSCWG¿öídú̵ë¿Ó«{7Ön 1)™RïwO®êêʽûÈÍÍåü¥Kx4hÀÓgÏ‹ÔåÉ¢©©É¨aR/vjßžàÃ!$$&RFIœvfVa×n°få2 èä×½ûpÿ~8Õªº*õ{qû ²2e~ù÷˜_ûvJ}«¬Ï+»ÇTÝ·_.ÖÖÖDD<-”¨lÙ²4oÞœ2eÊ K6NTWWçùógtëÖôôtÔÔÔ¨V­»wÿ ƒïÓ§7‘‘‘¬\¹Šôôt†N¿~} µ«©©É¾}{ùöÛI´iã‹X,ÆÚÚš ÖãåõaMBéÒ¥03Sý±ÀÔ´,jjòÃ|,--ä¦†Ò½„455±³³“Y<œ¾¾>vvvŸ¼·L™2…vÀÖÓÓ+4k¼o[Ö#‰Øµk' ,dÂihƒCúô‘ØXWW‡ãÇO°té2Äb1šššx{7gáBI²––6Û¶­& @ªC§Nùö[É"õN:rãÆuFŒIvv6ööö,_¾ ÿ 26X¾|9Ë–-cÇŽhiiÒ±c'*V¬ÈåË—¥çT®\™C‡2wî<|||‹ÅhkkS·n]é^bq.3fL'&&333¦OŸŽ··7dggK¦_ ÂÀá?ޱ‘þcF²|õZ6®]ÅÚõëiåÓ‚ŠÎÎDEE£©¥É¶téÙ‡S'ãìäÈ™sç‹ôÒËÚKyù‚±'áÓ¼)šššìÞ³g'–/^HFFÏdC%rrrˆŠŠ.´Y”²ë ôõY0g&V––\ºÆÌ¹ Ú½ D:(B]Mñ£Gнk'¬­¬>éúßΞeÉ‚y”21æeT”ô¸"›b»,^¾’*®®,š7›«×®3wáb¾ß¶}==…vÑÑÑQjO¿öíðkߎa£ Çg×Êô+®ÿ,]†­ {vn'*:š€i3prtÀ¬\¹bÉéâìÈîí[ùqß~ââã;r„Ì5ç.\bßþŸ˜3c:åíl¹w?\fwbyu–-Scc#7n¢e‹æ8T¨ óMì«Wxy4äç#¡\¿ñ;Þ;@M]J »~Ô”·X˜›yà Ìù>¦§§Ç‚ó™?)))èééÉø Zµjœ8qœÜÜ\RSS122’Y7Ѻu+Z·nEvv6…l®¦¦ÆÒ¥KY¸PòÌÉ_oò±ºººÌœ9“™3gJµk×{{{™óÜÜÜøùç`²²²ÈÌÌ*´Þß<þþãÉÌÌD, ]»v cccúöíSÈ6ÿ$B¨’µÝܨSË ÿ€)Ä'$Ñ£kgi™…¹ººº˜˜˜`me‰Y¹r$&%©ÞÆ^(og‹H$">!5‘oâ≋‹GOOªå|¶²´ä×ÐÃ4iÔH渲ëZù´ÀÆÚ555<ÖGKSƒ¿bbŠ­ÃàÃtêÑ«Ðߢe’)Q‘HDË’¯õÝÝ•~­•G{ßÖÒ¯Ê6Öâ©ÙL‘]’’’xþ®ýÐÐÐÀ£A}J™s÷Ï{*í¢ÊŠ(®ÿ”éWÿÅÄÆrÿþ† 臖–ìíq«é&ý’\9ñëo§ðkׇ ö¨««S­ª«Lø¼:µµµY±x™Y™L9›î}û|$DZ~åÚu*¹¸ §§G­šÕ ».®äÙ°ë6n¦¾»ìŽ£ªP¥_JJ*·lcøA*ó†gff¢¦¦FNN';CLL,zzºdd~ˆçVÔ^qû ¢2U~ÏGÑ=ö©¨ºÇ@q¿.ésBàËÄÐÐð³¥ÌÔÖÖ–;hPŒH$ÂØØX¡ÔÔÔ066V¸ØZSSS©Í544”.Rÿõ×_9xð?æÎ;Lž<™k×®Ó³g¹çkkk4DGGGX,fñâErËþI„ZùøðsH(ãFy‰Èÿ2ª¦¦&ý‹s‹T§Aή¡¡.]46 _v|ÿ£ü' ¥©Å þ}ñòôPYŸ²ë.^¾Âþ‡HL’ü`§¾M#''§Ø:´jÑ/†…Žki=s†2,Ìåï ©ÈfŠHJNFC]]fº¹”I)%vPf—¿ÃªP¤_qü÷údc£ãü¥ufffQÁÞ®Är*âÍ›¸"…|Œµ•%“üÇ“——Ç?î2sÞ\œœ¨\©"aׯS³FujV¯Áü%K¥ºT¯V + <=<¸ûçŸ%’?ŸÌ¬,fÏ_H‹fÍh$§ŸŒŽŽ¹¹¹hjjJS¨¦§g «£­òÚâöAEeªüž¢{ìSQuÁ§ß·ÿnÖ­[Ç‹’0Jggg¶nÝJƪŸ¥ŸÂ7ß|óYëû\Äb1«iïÛ†ïÜK=÷º”.UJáùRjjj+V½°² eJ—fâ¸1€$ôcåÚu4òh¨2÷³¢ë’’’Y²|%Ë-¤¢‹$‚_·žäå)Kí¦\‡£ÇaOÐþB×ծ寴€o•Ê©¥¥IÎû¸Ø¼¼<ÒÓ3 #}ž!MŒÉ‹III•¾Ø$&%RÊÄ„ÄÄ$¥vùÜ~Pu"TÉ)¼¼<Ê™–EWOm×Ëm»$rª‰»`ØÔ´,¯^½*ªj…‰DÔ¨^ g'GžGFbkkÃýû¸ÿ œ}? ;;›? 4ÔÕY¶h>Àg8äää0oá*г[—"]cn^ uu^DEáäà@NN1±±X«È>T’>¨¨L•ßóQt)ò­"”Ýcÿ=¼½½¥ëþ‹¡JìÝ}=}F Lë–>¬X½¦È?¬ÖVVD¾|IrJŠê“ßs%,Œø„@òuNWGGæàÕë×ô4”°›©(».#3 ìl%/1ç.\äíÛ·E–Gž­[¶`óúu…þF ª²>+K+>*®•UÚE•Q\ÿ)¢¸þ³07§¼ »~ø‘̬,²³³¹yû¶4 ¦$r–.]š/¢dÖ/x7mÊÁÃGxúì¹¹¹üyÿ>±±òê|›–Æö]»‰þë/²²²¸yû6?Á¡‚7~¿‰¡‘!¡‡~âhðŽ ^Ý:\ “ WR„8W,‘ÎÊÊ’ékòdÉÍC»(® IDATÍeÉŠUèë3 _éuïwy×éhkã^·6? &==C‡`hh@åJ.J¯+ITT¦ÊïªPä[E(»Çþk3ÿqÂ=âHèQ6®[ @÷®7q‡CÊL¿+ÂÆÚš–ÞÍé7h(b±˜=ßïPyÝÃGY¸‘̬,Ê™š0a¼Lyþ×ÌY*ºÎÒ¿öíí?‘²eÊâìäøIéé¿I˧ҳ[g-_ÉÕk×q«YCiÖŠOA‘]&MÏêÀõtêÑ›r¦e™15 ”ÚE™ EVV&ññ ,^¾ m-MúõîEÓ&‹í?E”ÄÓ&±aÓz÷„X,ÆÉÉ‘±#‡«ÔO•œ ðÛ™stï; uu¶mÚ€®Ž^ž$'§0ñRââ°/o'3%¯N ^½zÀÞ¾Mìœ)ÃÄÅّá!4¨ç.³Xºaýzü´Ÿæß4Q©ÿö]»Ù¾k·ôßž ë3cÊd…²ÄÇ'pî‚$/ù™ór…¯[¹gG¥v5|KV¬¢sÏ>XZ˜3cJ~Bä]§Ê·Ê|¤¬L™ßU¡È·Êú¼¢{L@@@à¿†èØ±<7·/w‡:OãæÍ}=?zâ¹çµöi!ý'G¥õ 3*ÈÉÉáìÙ³<}úŒ¼¼<ÌÍÍpwwÇ\ÁBü3gÎ’™™IË–…÷õˆŽŽææÍ›hkkË—ÿã?ˆŒŒÄÊÊ 777ž?ÎÝ»wñññùlY>aà  „˜˜X:wîÌË—/©^½"‘ÄÅÅqýú5lÞoðYðü^½z!‹9yòWª¾ßô0Ÿ«WÃ9R²ÇÇ¥Kqpøð•7//aÆóôéS:tèÀÆ8wî<“};vaÔø ܾóÇß*Çÿ›’<#ÿ),--¹té?VynXØ5ž>}JçÎéØÑÔÔTNœ8!÷ÜÖ­[‘ššÊ…÷ÙÎ222 ¡k×®ŸEî{÷nݺ“—yò믿ү_¦NʹsçñôôäÍ›7tèà'³OÍ™3gññiÉ›7oðñiA©R&Œ1‚ 6JÏ=z4?üð# 4 uëÖ˜™™súôo€dMˆŸ_G®\¹B³fMñööF[[‡Ë—/K¯ÿî»deeѸ±îîîœ8q‚6m|eÒ69r„~ýúchhHóæÍ¹qã:$00´´4@2X5j43gÎÄÑщ¦M¿áÎ?ðñiÉË—/É_ß¶$'§Ðºu5’è®Ð~=â§Ÿ~¢|y{Z·n¾¾cÇŽcݺuÒs’’’ dÀ€\¼xºuëbffNvv6ݺugݺ@ªW¯Ž‡‡§NýFëÖmHþLïYŸŠ“£#ŽìÑÑÖFGGÇ ”.†–‡0ã @衟ˆ‰}Ŷ]ß3sÎ|Ö®\ö··Ù¸Qá/5_S{_ ÇNüŠ[͘›•û¤2ÿ&_CŸøt8r ˆ¼¼Lð‘âã˜?g¦LèPßACiöM®ÿ~“¸øxºwéŒo+É”çþƒ‡=v‚䔬,-:hÕ«V-Q{1±±¬\È“ˆLMM9lÕ«VQ)KI¸ýÇ,Z¾‚·oÓèß§7¾­|”êðâE”Rýzô@}÷:<ŽxFFF:=»u•’ù(ŸW¯_3eæl†€{Âi™Åb1GŽeÊ·U–:}†àÃ!$&'SµrerÄ9ܽwŸ³gàä訴/—¿ÃŠäLNIaø˜ñL›4×Ê•HLJfĘqÌš>…ŠÎÎ û§*»(ó_qä, ¡ÇOpèðâã(ogËô€I˜š–UhOU:äó5ô y}^™”µWœgd>"‘Ú¶iÅÖ»ˆŠú gå6Sv¿We6SÖÞßáÛ777._¾DhèQ.\¸Àùóç ¢R¥J8ðeÊ”$³U¡¡¡|ûí‡ý]ºtéÌÈ‘£ˆ‰‰Á¢PÝ]»v¥[·n¤¤¤D§N¥aKŸ‘HDëÖ­¤ÿ®T©€Ì1Ê—·'** €—/_MõêÕùã3^––¤§§AÕªU±³³cÍšÕdffÒ¼y3Œ?¬÷(WÎ }}}f̘Aÿþýñðð@WW·l/^äîÝ?‰—Êñ„:½xñ‚öíÛÉ\סC~ûí7鿯\¹"}9/(¯««+ü!™Ñ²··'!!€€:uêDÍš5eö¢Qd»äädŽ;Ftt4™™YDGGóúõkRSS14ü°OSÇŽ~2×^¾|…J•*‘@ÂûÍ,*W®ÌÝÁ,›"„ƒ€ zzzXYZð$â)•*º°`é2lmlسs;QÑÑL›“£fåʱ{Ï^\œX¾x!D<“Ä͹8;²{ûV~Ü·Ÿ¸ø8ÆŽ!Ó†_ûvøµoǰÑãäÊËÚKyù‚±'áÓ¼)šššèë³`ÎL¬,-¹t%Œ™s´{g‰Ú[¼|%U\]Y4o6W¯]gîÂÅ|¿m³ôËž"Y”¡®¦ÆøÑ’lÝ»vÂÚÊJ¦üÞýp6­[Ãýðpæ,XŒOó¦JuP¥ÀÛ·é¬]±”ØW¯>z,U«¸R¦ti…>Ê'''‡¨¨h…›µ]¼|\+W*R™¦–&Û6Ò¥gfLŒ³“#gÎÇÉÑQi_*.‡ÿ”Éé?f$ËW¯eãÚU¬]¿žV>-¨øþË¢þ©Ê. ØÅ•Sç.\bßþŸ˜3c:åíl¹w?\º‹²"{Eø:ú„¢>¯ÈGÊÚ+î32ŸwïÞqò·3hiiaff (÷»ªû½8:(³™²ö”É©êù¥bbbB¯^=éÕ«'‡føð¬^½†yóæBZZéééìÞý™™äææ²ÿ~¹_™ÝÝëbaaA`àz._¾Ì’%‹?«ÜÚÚÚ2/ìÚÚZïõ)õÑyZde½ 22€•+Wz¹¶³³#%%€;w0oÞ|&NœHvv6µk×fæÌÔªU cc#~üñ–,YJ¿~ýÑÐРI“&Ì™3;;;rss4h—/KB™ÌÌ̉D¨©©‘’’ À›7o(]ºŒŒ ùµ|"##yóæ ƒ)¤¿Ùû™Ãzõê±rå ¶lÙÊ®]ßcbbB‡˜6mªÂ ïÞ½K‡~899áææ†®®.jj’’””™ÃÇë2"##yúô©\™-–ÿ7 ¬q(„ŽŽ.iéiÄÄÆrÿþ† 臖–ìíq«é&ý:¦&RãM\IøÙLjD"ºtéB`` nnn ÃzþŸä¿oذ°°«…þ6l€ƒƒ;wîàÑ£‡íC]]=z’‘‘H^Öƒƒþ€7òäɆ•¬OºuëÇŽ'8ø̘1éÓ§É Tò÷Èxõ*VF¾˜˜¿dþm``ˆ\YùjÔ£GΜ9Í;·™6mdÉ’¥(bÍš5Ô©S›ãDZ`Á|¦OŸFË–-åž[0ä*߆uëÖ•+ÓÖ­[ÿ G˜q(Dffúzú¼~Àˆqþʲ¨`oÀ€~}ØñýŒòŸ€–¦ƒú÷ÅËÓ£ÄíèëKÿ_CCwï$©Ã.^¾Âþ‡HL’üh¥¾M#''§Øí$%'£¡®Ž‘ч/¥LJ‘˜øáEBž,­Z4ÇË£a¡ú´´ ġQ/ZZšRýJ‚a¯%†Òº’ø(üÑ#Þ¼‰£‘]•©©©Iÿ›ÿ'çªìKÅ¡¸þSFQälåãÃÏ!¡Œ5B&œ@YÿTd—|ù¯$r*âÍ›¸B_Æ@¹=555Uêð5ô e}^žTµW’û/äà~²²Þ1ñR¢£ÿ¢VÍš%~&Ge6SÔÞßáÛš'OžàX`v $_œ_¾Œ’fVŠˆˆàúõ [¹z5ŒöíÛvzõÜ Õß­[7ÒÒÒhÜØëïSâ¨R¥ eË–åÇÄݽ®LY^^"‘Hú_„;{xx——GçÎ]ˆÇÊÊJZnhhH«V-‰ŠŠbÙ2Éʸ8I?±,Õ'?m>¦¦¦¸ºº²{÷|óÍ7ˆD"²³³Ù³g¯ŒLMš4æ§Ÿ~âúõëÔ©SG¥¼fffôîÝ‹sçÎIâäñæM\¡Ü¡C‡Tøæ›&,]ºŒgÏžJ­[P–ÂÀA@†ôôt¢ÿŠÁ¡‚=¥J™ «§Ç¶ëåvð2¥K3qÜ@þ°rí:y4”ž«&Bîו☘Ēå+Y¶h!]œðëÖ“¼¼r}j{&ÆÆäˆÅ¤¤¤J4“)eb¢ôº£ÇaOÐþBÇk×rcZÀ·r®(:ÊtPVVð«ibb"&&’8SU>RÆ¡ŸàÛº¥ÜPee“——G9Ó²Jû€¦¦¹bùúÉ++®ÿ”¡JN±XÌêÀ@Úû¶áû÷RϽ.¥K•*Rÿü˜‚¾Tä?¯{Qì©SÓ²2™SòQfÏ·ï3—(ÓákèÊtç#Uí•ôiddȈ¡ƒñ˜Â7½TÚLU{ÅÑAŠÚ+IÿüRéÕ«7¥K—ÆÓÓ;;[ ÚOFFÆ `ß¾ tuuåîíî^kkköîÝ+wà`aaÎôéÓŠ$ˬY³ÐÖÖ–9Ö±c'¹õ---/^̰aÃHMM‘îVýäÉBBB¹pá>>ØÚÚðæM›6m’~±wssÃÈȈaÆѳgO¢¢¢Ù²e ææ>lˆD"æÌ™M÷î=hß¾nn5¹té²tF$ÿį¯/¤gÏ^ <’’’¹qã:¦¦å˜1c:üùç=<<<05-ËãÇ9uêóæÍSh‡&Mš°jÕ*°¶¶æèÑc<|ø°H6ìß¿?GŽ„Ð®]{„½½=oÞ¼áâÅ‹¸»×cÈÁÅuÏ?Šª$Hbi#_¼dñŠU8ØÛS©¢ ææ”·³a×?’™•Evv67oß–†_\ #þý‚ ututd~$J—.Í‹QÒøé’‘™††v¶’ÅOç.\”I×VœöLLL¨èìLÐÁCäääpñòâã¨âZYéu­[¶`óúu…þF Z<动ƒ²²£ÇÈÈÈàþƒp=~Bí÷aPª|ôêõkúJØõ2õ½yǵ¿ÓÚ§ð ²2E¨êKÖVVD¾|IòûØÙ‚È++®ÿò‘§»*9÷î?€¾ž>#†¦uKV¬^C^^^‘ú§2ùO‘îE±§"ßz7mÊÁÃGxúì¹¹¹üyÿ>±±¯Jdϯ¡O¨ÒAžTµ÷9ž‘åíl©äâBð‘Ð?“‹£ƒ2µWßþÛX½z5õê¹sûöm¶mÛΙ3gñòòâìÙ3ÔªU €ÌÌLƇžœìW"‘ˆñãÇI¿|[[[ãëë+wo‡|<==qsûð,(_Þ___222IJJ–ùS4›jgg'³$ƒ___Ê•3•9Þ°aCª¾_ЦMkBCC044dóæ-lÙ²•'O"¤Ù„ŒŒŒðõõ%,ì+V¬à‡vãååEPÐ>D"...ÔªU‹“'O²té2>ÌÀY¶LdjjJPÐ>´´´Yµj5×®…±}û6ºvíJ•*Âì<<<8~ü85kÖ ##ƒ1cÆÐ·o455¥kÔÕÕÙ¹sS§Náúõ¬^½†àà`J—.-Í&Õ¸qcJ•2áÀŸXºt×®]gÍšÕôîÝK¡FŽ¿ÿxBC²qãw8::°yóf|}}¥ëFôõõñõõ•Y ««Kpð!†ÂéÓgX¹rÇŽÇÑщïCõþ3´ñ댉±1uj×ÂÌ(éÍ´€IlØ´…Þý!‹qrrdìÈá<|ô˜5ÉÌÊ¢œ©)ÆËÔéѰ¿9G÷¾ÐPWgÛ¦ èêè0hø(²²2‰O`ñòUhkiÒ¯w/š6i¬P>K üÚ·c´ÿDÊ–)‹³“#¦¦²¨§½IƳ:p=zô¦œiYfL P¸H*===¹? ŸE:¨*«TÑ™>‡ ¥­…ÿØQÒÍeTù(''‡˜ØØB‹£‡¥‰—§LøBQÊ”¡¬/ØX[ÓÒ»9ý E,³çûÒ EeÅñŸ*ÝÉþèGB²qÝjºwí̸‰“8z”ö¾mTöOe(òŸ2ÝUÙS‘~^ž$'§0ñRââ°/o')+®=¿†>¡JE>RÖ^qž‘òèØ¡- –,§S‡v%z&Ge(kO•oÿmÔ«ç®ò‹~þiEôìÙ“ž=% «ÝÝë ú˜iÓ¦ÊüÛËË /¯O eòððÀÃC6<ÎÀÀ€-[6:7  Ð±5j°víZ¹ukkk3~¼ü$' úX‡©Y³¦4%l>Õ«W/t^•*®2ƒ‰¡C‡áââ,qÐÐР_¿~ôë×On[U«VeñâO[x®©©Éرc -j/h?sss¹öÉàaäÈ‘Œ9ò“Úý’;’çæV8 £€€À¿ƒ}0kÚT\œUŸ\223é=`+/ÂÎÖ¦ÈeÅãsûïÿÍ×Ð'Téðo÷|:ü7¹ví:7n AƒhhhpúôNŸ>Í–-[hÕJþBe 7oÞÀШ”ê  lC¸›7o3²ÄÇÅÓ§G¹/PÊÊþ›| }âkÐA@àk¥B{\\\¸té2ÙÙÙ”/_žŸ.´Zàÿƒ0pÁÚÚ kkùyÕ•• ü7ùúÄ× ƒ€À×JÙ²e™ñ1ªü>oÑþ¼Ÿô´t*T°gøATtvßGÊä,I?S&‹²ç‹€€€€À߇0ã À†5«9¸555V-]LÈÁý2›Ë„ú‰e ç“’šÊÌ9ó¿Èö¶íüž‡±xþvnÝ„wÓ¦€ä%~þâ¥ø¶jIpКӄy‹!‹ÉÍÍE]]ƒ93¦²këf˜5o¾tC¦EKWPÛ­&ÁA{6h –,—ÉŸ››Ë¦­Û)ogW$YTadhHÏn]hÜèÓwß–'‹2ý*Ørp¿ôoíŠåP½š$‡÷º 1Ð7`ß÷;hÒÈ“ù‹“SÄý1ÙE•=Q\»(jO•ßÿ ’SRÈÍË“4@Éú„¥K—æèÑ£¬^½Fî5ëY¿~½ÂÝž÷íÛdž X²di¡²àà`6lØ@`` qqqÒã»v}Ï™3§?IöFQ¹rÑ6ýÚB•ŠŒžžV–<‰xJ¥Š.JÏUWScühɆ'Ý»vÂÚêÓ3–µ½˜ØXÈËãòÕkL˜< #CCzuïJ³o9úë¯iØC>OžFP¦tiLŒ‰D´oëË…‹—)okË›7166¢|y[@ò…8øHkV,céŠÕE–åÜ…KìÛÿsfL§¼-÷xgmE²|LAý ’Íé³g™;s±±¯É‹±±²bÇ÷»içÛ ¢¢£ ‚Š*‹*{èë†ŒÊ IDAT³`ÎL¬,-¹t%Œ™s´{':ï7¸ûTTµ§Ì.Ê|T\9•Õ¹|ÑBÞ¼‰£ßaìÞ¾µÈ:–TÎýžÏ²Uk¸r5Œ·ii4ñòÂØÈˆøøx¹2È»Qœ~&Oø<Ïÿ6=ÂÉÉIºë3€±±É?(Ñ—GÕªUnlö9¸sç¡¡¡lܸ:È”|±Ï'))‰'N0xð 6oÞÂ;wän×°aC.]ºDDDúîÛ„‡‡çÏŸ/±ì3fL/qÿV„OBGG—´ô4¦S^…þ-[ H^ÜZ¾ßR½¾»;eJ—.Q{ÊHKË ;'‡W¯^³gç6ÆÁªuëßÇb[¡§¯GèñdeeqèpÙ99deeÉÔ‘’’ÊÆ-Û>dêêê4¨W—³ç/кC'-[ɸÑ#Ñy?;²}ç÷thë‹þG;H+“à×ßNá×®-ìQWW§ZU×Bá)ŸŠ"YTé—Ï•°kJg™™™¨©©‘““ÃÉß΋žž.™Yòª.²,ÊìÙʧ6ÖÖ¨©©áÙ°>Zšüó)fø¤öò‘ge>*®œ‡ßK*çÇ~Ïgøà¬[µœÞ=ºÓ¸QC€"ßGÊ(N?“' |¾ç‹À“>}úràÀA>|ˆ·w ||äo"ÅàÁC¨Q£&ÖÖ6Ô©S—)S¦’‘‘!sÞ­[·hÑÂKK+ÜÜjñý÷» `ÕªOÄb1óæÍ§bÅJØØØÒ½{w~ûí7¼½[+=/11ÿ TªT[[;¼½[*Ó^rr2C† ÅήLœ8‘-|HKûð;jÔ(6oÞ@XØ5¼½[žžÎ_ýÅÀƒ9rÞÞ-hÓÆW¦ÇÓ®];©­öîÝ«T¦¨¨(¹_îów5/Hpp0šššLž<öîÝ'·^4h@PÐ~é±ððpþüóO:uê¤T¦¢2räH¶lÙRH¾fÍšammƒƒƒ#~~yöì™´üôéÓ4oîµµ NNÎŒ=š„„iù­[·ðönÁï¿ÿŽŸ_Glll©U«6;wîú,2.„O"33}=}š6ñÂË£a¡r-m­¿¥=eèèh#‹éÒÉjT¯†ƒ½=÷îßǺy3¦OžÄÆÍ[Ù²}'õêÖÅ¡‚=z^l3³²˜=!-š5£Ñ{ÒÒÓ™>{C  Y“Æ<|ü˜™s°zÙb22²xøø cFÿ4Y¬,yó&3³â½0véÕ—¤¤$vlÞˆ•¥%=Q(‹2ý òË©S4ûæ›:è››‹¦¦&{vm ==]/ÝŸ*‹2{ÚÚØpñòö8Db’$\(õm999Ŷ‹ªö”ÙE™TÉ)OUu—’È …ýžôìÖ…n}úc_Þs³r*ï#U§Ÿ)’E@ $L:…+VÁòåËž—””Œ³³¤L™ÒÜ»w™3g‘——ËâÅ‹HNN¡GžÔ¬Y“cÇŽ’““üyóˆ¯oi]7ndûöíÌ;w÷º\ºt‰ñãýyýúµtÍÀ»wïèÒ¥ yyy¬_ˆ……'NüÂÐ¡ÃØ·o/žžžŒ7ŽÛ·o³eËf¬¬¬Ø¶m;[¶lÁÐÐP¡.ŽŽŽ„‡‡séÒ%Ú¶mË»wï8xð¹¹¹\½z•¦M›’ššÊÏ?¦M‰Ü©©)üñLjÅbÊ–-Ë·ß~Ë Aƒ;v,ÎÎNˆD"™6üý'0jÔ(æÎËÁƒ™0a"5jÔ R¥JreruuECCƒiÓ¦3qâDjÕr“†EÉcÏž½´nÝ===ºtéÌÚµë˜3gv¡pg€®]»²páB&¡®®NPP^^°°0WXÿ§ðèÑcÌÌ>Ôµk×÷L™2…¡C‡°bÅ ._¾"d†…]£wï>tìØ‘•+WÔ)SéÑ£'G†¢®®Njj*üñãÆgÔ¨‘Ìž=‹Ã‡0eÊjÖ¬!wvåŸ@8™ôôt¢ÿŠÁ¡‚=Gÿž£ù|j×rcZÀ·Ÿ½=e˜›•CMM‚Ï0‘šˆ<$‹]«ºº²aÍ*@ò•³[Ÿþ8ØKêÌÉÉaÞÂ%8:Tfÿøë¯¿ÈÌÌħy3D"U*WÆÚÊŠ;wïðôÙ3Z¶ó“ž%,Œç‘‘L;Z©,¦¦eee} û(üÕáÑ“' e™ð­Býò‰‹çÖ­;Œñáeßܼê꼈ŠÂÉÁœœbbc±¶².¶,]:vPhOCC–,_ɲE ¥uüºõ$/OT¨¢ÚE™ÿlml”ÚE‘“TÊ)Oeu–„’È)Ïï#‰ÈÍóòeæfå”ÞGª(n?S$‹€@I¨X±"eË–%66–jÕª)<¯JWªTùžéääÄÛ·i,^¼X:pøñÇ‹ÅlÚôôÅ}Ó¦MÔ©SWz]nn.ë:t½{÷ÀÙÙ™çÏŸ³iÓ‡P PÂÃríÚ5é n¥J•¸ÿ>Û¶mÇÓÓ“‡râÄ/ìÚµooɬۊ˹qãééé uÑÕÕ¥víÚœ?ž¶mÛrãÆ ttthÖ¬)çÏŸ§iÓ¦\½z•¼¼<ê×oPèz---*¾Ÿ-tvv’k· üiÛ¶í{ÛU!8øgΜ9£pà`ooÏÊ•+™1c:íÛ·GKK‹:uêжm[zõê)3;~ïÞ=îÞ½ËìÙ³èØ±# .âøñã´oß¾PÝ­[·bÊ”)\¸p†8pyóæ*´OIÈËËcéÒ¥téÒ…Y³fI|Ñ߸q#NNN¬Y³‘HDÕªUÑÓÓ£S§Î\¼x///é¹£G¦K—΀$\,88˜ß~;- ¾²³³¥ÙJrr²ÉÊÊ’Á¿{÷Ž˜ØWlÛõ=ööTªèBy;[7ò,TWQfŠÓž2ttt¨U³:‡‡0|ð@=~BÄÓgT©,yà?~ò„²eÊ’õ.‹»~ÀÙÑkk+rssY²búú è×Gv¡¥¥…¥¥%ZZZœ:}–fß4&âé3žGFbkcMõªUðmå#mÖ¼…8;9J_@•ÉâÝ´)›·ï Fõj”·³ã~x8eK—ÁÜÜ ±XLNNy¹¹äŠsÉÊÊBCC£PhQA|[ù(”E™~ù_ŠNžú*®®˜•ûð2¦£­{ÝÚüt0˜q£Fzü††T®¤ÜÊdIKOWhÏŒÌ 444°³•¼0ž»p‘·ÁÇ.Êü§Ê.Š|”›—«TNe(ó»2”é^9åùý¯˜nü~“ºuj££­M𑲲ÞQ¡By@ñ}T}j?S%‹€Àÿ‹Ó§Ï|ˆ/^’‘‘ÁÛ·o‰‹‹#-- }}}ÂÃéZµªÌ×~sss¥ÿŽ%))‰ d_È6l(3p¸{÷Œ9xð€Ìyiioyþ<@šm¨à‹¦H$ÂËË‹ãÇ+ÕÅÓÓ“}û$á=.\ aÆ4jäÅwß}'=V­Z5ŒŠlŸ‚¸H¯­®®Ž 11±J®€.]:Ó®][._¾Â×9yò\¸p­[?„íÛ„µµ5õë×$6nÔÈ“½{÷É8èééáëëKPPYY™¼{—EË–-¹víZ±tSFll,ñññ´iÓZá9>¤eË–2³4 6DGG‡ÂeüY¯Þ;ŠD"lmm‰-YèîçD80bìxé¾ ã'I¶u9øa6¡_gLŒ©S»þcF!‰ÐÓÓû¤0…’¶§Š±£F²|ÕZ:téN©Ò¥?z$ÖV’0‘ÛüÉÞý?‘ýîuj»1eÒDâã8wá" ÉCŸÏº•+pqvdæÔ¶íüžu¿ÃØÈ¾½zR½j•ÉâåéArr ó/%.>ûòvÒšÐãÇYÿ݇åñ_O2zÄp™—ñOA•~¿þv†îï¿ldÔða,Y±ŠÎ=û`iaÎŒ)hhÿq¡¯§§Ôž~íÛ1Ú"eË”ÅÙÉSÓñ­Å±‹²öÞ¼‰SjE>275S*§2”ù]Êt/‰œòü®¡¡Á…KWØùò³³±µ±fæÔÉÒõŠî#Ur*j÷3U²ü?Ø»w/“&0bÄ||ZbhhÀ­[·X´h1YYYèëë“––†®®n¡k þ>æÏ||ÞÇ¿¡±±¯PSS#22R渕•Îï“$''£¥¥U(<ÇÈHq˜R>žž,Y²„/^páÂEºt邇‡cÇŽ%..Ž .Ò¼ys•õ(âcýÔÕÕ‹”BY[[›&MÓ¤Ic¾ýö[fÎœÉæÍ[§bÅŠdggsðàAôôô:t˜ôºØØW<|øèèh¬ä$HèÚµ+ݺu#..ŽöíÛË iúÄÇKÖ)+Ùs*11##Ù™H$ÂÈȈ¤¤D™ãÛQCCã‹JE- ز!PîqgG~ =ü¯h¯œ©)KÊOÏÖÙ¯=ý ‘05-«´½ÚnnÔvsSÙöœS‹, @Û6­hÛ¦U¡ãíÚ´¡]›6r®(:eQ¥HâðåQ¦ti–.(YŽêí¢Ìž½{t£wnÒèÛ[úÿŵ‹¢öŠbE>R&§*Õ™/ÓÑà…Ž«Ò½¸rÊó{9SS–-R¼oŠ¢û¨(r~j?S%‹€Àÿƒ}û‚èÙ³S¦L–‹xŸ‚8[[[Nœø…¼¼<éG®ììlž>}J¥J°~Ÿ¨àÉ“'Ô©óa¯…ÇËÔeiiÀÒ¥K~0³²²"++‹ØØXÌÍ?ÄØ<ØG5044䨱ãܾ}›uëÖbaaŽƒƒÁÁÁ„‡‡3wî…×çËôwïyÓ´iS6oÞBTT+Vä—_~!!!ooo45?¼¶Ö®]‹¨¨(‚‚öãï_x£Iw÷ºXXXpáÂ~nÊ—·C$!ãß‚ØØØòQjj*qqqØÚÎö÷%#dUøˆ¼¼\’’’¥ÿNMMeûö2çtìØ‰çÏŸ³{÷Òcë×o ±À¾0:::´iÓ† 6JÓŒÆÆÆÊ„)´k׎7oÞ:ž——GÌû¬hîî˜°eˇÔÍÑÑÑ;¦@óæÍ°³+Ï©S§èÒ¥³LÌüÇùóÏ{˜››Q¯^=4hHß¾}1b„ôœÌÌL.^¼Hjj*îîî„……1fÌX"#Ÿ£¦ö!äÉ“';vŒØØW”.]ŠÊ•]iÒ¤±LìûÕ«aüòË 45µ¤™Œ=zˆŸß‡¬vòˆåäÉSÔ©S›Š%¡Toß¾%8øg*UªHíÚÞ£££9}ú ݺu•¦IÍOßENN=zô{À‰'(]º uë*žÅxöì.\äåË—deebccC³fͤ³!OŸ>åҥ˴nÝŠÒrÖ8‰ÅböîÝG•*®Ô¨QƒÐÐPÊ”)#]Dý111±œ:uŠ6mZSªT)@²ÿÂÛ·…÷‹ÒÖÖ¢K—ÂÙŽ=†……¹ÌLÆÛ·o9t(˜G¡¯¯OÕªUñön.feeqèÐ!îÝ»¡¡Í›7—¹>_¶Î;É àNž<‰¡¡!õêÕShGEܼyC£RŸt“£ƒÂ²›7oâ’””„‰É‡]§9Â!C9|ø0îî’´¬¨««K_"322èÒ¥+jjj>üó?"·À×Ïß1pB•!÷+Í@ðâåK H2"ØÙÚ0¨__jTWœûßÄÔ™³¹qóV¡ã:ÚÚ9XxÿŠ¢’#Óª¿†fŒÿ· ì߇êU«ª¼.!1‘%+Vqïþ,-Ìñ3Fš£_YY>?f´ÿ·ôéÙCš6öÝ»wlÚ¶ƒ³ç/ýî<=˜8nŒÊ²y‹–ðçýû¤§¥S¡‚=Ç ¢¢³3=a´ÿLLL¨[»ƒú÷Ť@¦‰[·ï0}&ýûô’ɲþè7ñ2*š2eJ3¸_êHí§HE²deeáÛQrŽššæffôèÚïfM‹­Ÿ*[+«`ÿC9z”¤äìlm˜0[¥×9žƒÁ‡‰xú ïfŒùáë¥2›-^¾’ÓgÏÉØ¯{×ÎôïÝKiÊäT¦»²:UÙåS}«Ê† **Z¦þ)“&`in©´*Ó¡¸÷­Àƒ¡C‡ñöí[Ê—·#:ú/ÂÂÂø{÷óýpüu—Kd‘!‘eTUP{µ¶FHÍ!R{Ô¨¥öµK ÔV»Õªj•*U£"býª!C"‘ääîòû#rDòýÞ¹P´ŸçãqqŸû~>ïÏøÞÝç¾ãÒK?i¸té={S»vm,,,8}újõC¶lÙò#„g'&Ë-D§ÓйógÍÀ˳¢Ñ·-Û³=÷¢¤½û2iúL6®[ƒ‰·i}•L™ø Z­È]X[YqôØ/Lœ2Íë_0¸oç6ý:'Ocñ‚Ç+³þzꎎ¥ùõ·Sù&3ç̧Y“F,ž?—Ógþ`ÊŒYl\·»G·¯“ªƒ¡XV-[‚‹³QÑÑŒÿt •|}(çînRý”J¥l[Ëåyà›oÙ½?ã>G…ò\¸¥_rÛ•(^œݺðÓ±_ Œ CmÖþ½v|Ð'Dÿz³G·Ó•ËS.N¹ºËå)W?SúÖP?|4òC7x|¾³¹¹9W®^¤Ç§\Aμys9qâWâ©[·“'O*°XWµjÕX¹òs¢££ÉÎÎ& €F™¼f‚ ¼,bâ ä;'Q¥2Ï7i ëOówšqê÷3$%'Ó½Kç|÷ÏW(XZZÒþ½¶¬^÷%±±·ñõñ"%%•¥Ÿ¯äÜ… ØÚØÖ'„†õsÏ9¼ÇüÏs㯛¨T*üÛ¶Ñß:2($”zujsåÚ 233èÑ­«~¡¹¸øx,ZÊÕk×pttdð€~úuäâ”+O*μ{É?n#U¾v‘‹åyËR«9ùÛi-˜‹­­-;òÕÖíDEEããã%™öfµÜEç~{ˆJ•*åûV£ÑpðÐw,š7 pxt¾§\@Í5ô—-ãHjj*÷ŸX`Ì‚òåÜùxÔ:÷èÅ¥èËúEü~;õ;=»wcÑÒ夤ޣ¤½999ÜIJ¢Aýz(•JÞ®U— ÿ\Xäb±|ÔWJ¥ ÞzóMJÚÛñ×ÍX\œMª_±bÅ$ÛºJe_Ù<7oÛAH ªVÉ]AµNíZÏÔÖ§ÏœE§ÓêŸ7¦ÍÌ”ÊBÊS.N¹1øfµª’yª<{ßÊõCÞ˜W©Ì$üŸrí"rÜÝÝqww—}¹¹95¢Q£‚ § ÂëDÜŽU0(>>žÅóç0cò§¬^»Žììì|é>ä»ÃG°°° lYG¦Ï™‹›ÖE0q\8‹–.#!1ȽC’¯7;¾ÚÀºU+x«zþCþéé,ž?‡©ŸNdÑÒå$ßÍ]\eÖ¼øøx³mS$½ztgÊŒYž¨¨KLûtTôðÀ¯†§?C»6­Q*”ÜIJ&))GÇÒT«Z5_yï4kò(Ï2øøxsú÷3Ô©]‹KÑ—™:q*•Іõë±.rþ¼HÝGwl(,N§²e$Ë3§”ÔÔTÉXboÝfó¶m¶©YñD¡PЦUK€çðKÉÊÊB©T¢ÑhøîðÞ®U kk+2³Ô²iëÖÓ±½ÓÇ<È$[£!!!‘MëÖ}9†ñ“¦PÉ×W6-oìô%¨kg~øñ'¼<=$c·´´âAFîÝ*NüvŠÊ¾¾X[[S³FuNž:EËæïP¿îÛÌ™ÿ›·mÇÒÒ’‰ãÂõG ¤êG.–¼ëp !={àäT–èËWLªŸ\[˵YÞ¯×Åm ®ìjL[K‘k3€ýß|ËáÔÿé‚ù89••Ì/%5U2NCãLŠ¡ú™Ò·ÆÄòÙ’å,ûüñ½ðÌžYhþOŽO9¦ì·‚ ÿFbâ dkc£ÿ[¥2ãáÃÇÐ{wlE­~È´Ys¸uë65kÔ ñNî7ƒ>©]V–šŠ¹ç0‡öîÅÚõ2ræ„õ ¡I£†ú×·µÍ÷wJj*©÷îåžK]âñ—š’ö%ó-²#§Ty†â”"KÛV-hÒ°àb.Å, º+LáqJ×]Ž\ý<È0©o‰¥oH/ê=qajI‡’\»Vð÷OŽOAÁ01qЬD‰â êÿ#ÃÇòNÓ&”q,•µ5kV,+ôbâRú»ªýù,^Bㆠô¯}òˆFJJ öövØÛÙ¡ÑjIK{ü¥&%5EÿeGŽTy†â”"Ëþƒß²iKÁ».ÕªéÇøðŒ.ãINNeP™™q36oOO4 qññ¸¹ºÉ¦Å\½Êõ7hðø¾Þ'NžäýŨáCQ*•Þ‡ò×Í¿™5!žT®äËéßÏP¼DqöíÜÆþ]ÛÙ¿k;u߮ͯ'Oáââ‚……ßÿð#999\½vÿýõåÜÝdë`L,:]ÙÙÙÄÆÞâì¹ó¸º:›\?Ù~É ëû|¹qQ—¢ÉÌÌäÔé3Üøßÿ n§ÕjQ«Õäètè´:Ôj5Z­V¶Í ‘ÊS6N™ºËå)W?SûÖP,†®Á¢ IDATMn{:0>| ËW~ApŸ0´Z-ÞÞ^ tJÂå˜+,Zº‚,µš2ŽŽ„‘/ŸÊ•|èÕ·Å,9|¥Kçþr8fÔ>[ºŒNAÁ”q,Í„qáØ>qZ“¹òäâ”#‹õ ¸í˜=!{ôÂÅÙ‰ cÃõw}’K“3|È`æ-\LÇ.Ý)éP’CëO‘JK¼s‡Ÿ9Áº ›ÈÎΦœ»Ç}L)’’r'gïvÆÞÎŽÚµj2rØ ¿>Mýºuò­ŽÚ ^]6nÙÊ þ0q\8kÖ­gɊϱ+Qœž= Þ¥J¥RIÆ’7ùí7x( …‚’öö4¨W‡ŽíýM®Ÿ¡¶–kOÿvmyðàÓfÏåÞ½{T(WŽðÑ# n·ïàA–}þ…¾Î}ÇÐAñoÛÚ¤63”§\œru—ËS®~¦ô­¡Xæ.øŒ¹ >Óÿÿƒ>½yóѺ …OCuAr‰•£…WJPH(ŸŽ‡¯×ËEAáµõ"Vާ* ‚ ‚ ‚`8UI^‘›¾’LkÝ¢Å3]ä*‚ ‚ð"ˆ‰ƒðJÉ»½âMpP÷—‚ ‚ ‚,qª’ ‚ ‚ ‰# ¢¿«RSîªô*DzqóV’’“>xP‘ò)jž­–¶Ú·›a#?¢oŸ^T¯V ­VËÒÏWròÔï¤ß¿··'ƒúõÓßcþnJ ³ç/äbÔ%\œ9l•|½8òÓOìØµ›k×oкes}éSj×ò“S®¼‡²rÍZ~üég²>¤q£†úu2¤ªŸTÆMœÄé3ÈϲX1öìØ*¹@tL KW¬äïØ[”*åÀ}BŒZõwØÈˆŽ‰Ñÿÿ¦MøxôHƒí"Gn;Sã¼pñ"ËW~Aì­Û¸¹ºðá!ú› ȥ剹r…¡#?¢W ztË]¾ýû]Èzêv̵kÖ`úäI’cWAøïG–/ZÈÞ[Q*•,œ3ë¥M^µXþI:33“'ŒãËÕ«ðòôäÓ©ÓÈÉÉ]lÉòØÚزyýZš5nÄ´Y³Ð<ºÇ|‰âÅéÑ­ M7Ì—§gEöîØª,ž?[[[ª¿iø–rå­Y·žË11Ìš6™u«WÒòÝw‹\?©:L™ø‰>~sssæÍœÁÞ[ÙþÕÙífΙO-¿ìÚ²‰a}™>{÷ÒÒ Æ 0qÜÇúrŸœɵ‹¹íL‰óáÃÜÕÚýÛ¶a×–M´x§SgÎD«ÕʦåÑét¬\A…òùWIßþÕ}½·n\O)ÞmǪ̈6AþýÄÄAÀÜÜ\ÿå\¥2Ï÷Eýv\£ÂÇØ­]z†¹i³Qi!aý¹}YÿÿNA=¹ùwî‘„””T¦ÎœM§ žôþ`ÇŽŸ0*–°þDnṴ́QcêÝ—½¾1Ë嘫‡†±c××>r”àÐ0‚CÃÈÌÊ`ëŽôêÛ€ÎÝ4|ç.\0Xž¡¶¶¶t ìHZÚ}¢¢¢¨Y£õêÔÁÚÚ&_ž …‚bÅŠéGŽ¥i£†XXXÈÆ"WžF£áà¡ï5|(Þžž8”,ɛժ©~ruP©TúøsóÉý^¤¶ËÉÉáNR ê×C©Tòv­š(€¸¸ýk 0ˆ“§Nˆ÷ÉróÖ0ÔRyÊmgLœ…‰½u‹Œ´mÝ •JEÇ€ö¤¥Ýçʵk²iy~{ˆJ•*áT¶l¾|-,,ôõÞøÕV<+Vä¦MdcAþ;ÄÄA¹é+|}¼ÙñÕÖ­ZÁ[Õ«•&gúœ¹ØÙÙ±i]Ç…³hé2Ú6>>žÅóç0cò§¬^»ŽììlÙX|}¼ˆŒXÍû;ðn³&DF¬&2b5V––ØÚØ0}òDvmÙD÷.]˜8e:YOL +ÏPžrÌ”JF  @÷®psu-ôuW¯_£”ƒövvÄÇ'¢ÑjqwueíúHî§§ãììLì­X£Ú rWÿáÇiñî;_+W^\|<ääpü×ßìÖƒÞ àûŽGaõ{ Úûóó±ãdggsâäIììJP¡B9ýk4 ±±·ÈÈÌ(°ý²ÏWÆÔ™³rëŒ|»Èå)·1q>‹Û·¥W=ÏK»—–Æ®={ êÚYòµÑ—¯pøÈ‘|‹!;vA„/qƒ K©Pr')™¤¤dKS­jU£Ò¤ÄÅÇu‰iŸNÀ‚ŠøÕðãôïghׯð ­M›4 Bùr( ’ï¦àT¶ŒI±´mÝJÿw£õX¼l·ãâ¨èá![ž”í»v³yÛ¶Ï׬áÇØF¢P(hÓª%€äyìii÷YñÅö ÃÌÌŒ¬¬,”J%†ïáíZµ°¶¶"3K]èö…9qò7Š/NåJ¾_+WÞƒ™dk4$$$²iÝ¢/Ç0~Ò*ùú\ Xª~/Jýºo3gþglÞ¶KKK&Ž Çò‰#X®..Ú·»ÀvÝ»vÂÙÉ­V˦-[™0y*Ÿ/]lT?–§¡í ÅY7WW¬m¬ÙwðZ¼ÓŒ}¿%[£A­V˦D¬[OÇöþØH¬p®Ñjùlé2ú„ôÊw`cÆ® ‚ðï&&‚¬ÐÞ½X»~CFŽÂÂÜ‚°>!4iÔÐ`š”Ä;I úp¤þ¹¬,5=ÊKm’­ÍãSRT*3>T› À±ã'غ}')©¹§ÌÜO€F£1Xž”¶­ZФaƒÏ[“?=(O–Zͤi3hÕ¼9åcii‰N§ÃÜÜ\»ÚŒŒL¬,¿öãÛï¿§ù;6téBjj*kW­ÀÕÅE¶ùx +V­æ‹ˆuÔ}ûm<+z`mm-›v9æ*—¯\eØGž¶óëÝØÚZó^›V’¯Aþ›ÄÄAUÊÁAqèÑŸaÁâ%4nØ…B!›faaŽF›û<''‡ŒŒLÊ8–ÆÊÚš5+–¡P(þ‘8” ôâæIIIeö¼Ì9C‡›Àn=ÈÉ1.®ÂòÜð[6mÙZ൵jú1>ü#Ùü4 SgÌÆË³¢þ.7NNeP™™q36oOO4 qññ¸¹ºgRr2üqŽaƒ ~YܺáËÏÉ•çT¶ J¥’'»N¡TCŽlžrõ{nß¾MVV­[4G¡PðF•*¸¹ºrîÂEƒ‡'©ÌÌP*èt9¸ºšÖríY”8«U­ÊòE Ü£ÝzõÁóÑ‘2©´?Î_àú´ ÔçsâäIþ÷×_Œÿˆøø¶îØÉgsç<×ýSAøw×8dggëOcÐhÿ ¹_*’ïÞrq·²´Ô¡Ksuqåҥ܋£?¡ÿßÙɉ åÝùrÃF²Ôj²³³9sö,·ãâ Æ"G.nÞŒÍwg™Ì¬LT*åËå~ñ;úó1ÒÓÓn·Âòlצ«–-)ð2°¿l^:ŽÙóbkcChï^¨ÕjÔj5999X+F·k±mÇ.222ع{Å‹ÛR¥rîiGZ­6÷µ::­µZ/¦ï¾?ÌU«R¶Œô)VO’+ÏÒÒ’š5ª³s÷^4 Q—¢¹výoT‘?5L®~ÆÔAŠÔv...XXXðý?’““ÃÕk×ùß_QÎýñ—ü„ÄDBÂúç»955•£?#ùî]’ïÞeÅkptt¤œ»›Á~ÊSn;Sã¸rõ*))©Ä'$°pñ2|¼¼pss•MóoÛšCûvëõêÔ!¤gý¤vñòtlßÇÒ¥ô}”w‘ ‚ ˆ#ƒ†Ð¯0bÌÇÀãµ.Ç\aÑÒd©Õ”qt$|ÔãuäÒztëÌÌy øõ·SøÕx‹%ŠëÓÆ‡aùÊ/î†V«ÅÛÛK¦\,räbhØ >‡¥{H(*33Ö¬\Ž‹³3:r4¥K•ÆÇÛ+ß9݆–§µµ5ÖçŽËIN¾ËÑŸ¹käY²`>¾>^ 8€ÙóÒ¹G/\œ˜06\·Ÿ}²ìó/ôÛ<ôC Ä¿mî5#‡¡{é a #WÞð!ƒ™·p1»t§¤CIF lðúCõ3T)rÛMΚuëY²âsìJ'¤gªW{|+Ú¼_þŸ¼Y«Ó±uÇNæ¶¥™•}}™2ñÌÍÍ ¶‹TžrÛ©T*“â8{þO¾Úºì‡©]˱cF•&å^Z§ÏüÁé3ðå†úçÝÜ\‰ø|¹ÁíA„?Å{süüj½ì8AAAxNΜ9Mñ%Ÿio/OÙüÄ©J‚ ‚ ‚ $&‚ ‚ ‚ $&‚ ‚ ‚ $&ÂK3`臜9{öe‡!‚ ‚ ALþãV¯ý’IÓfæ{nëöŒ;þ…—Ý­óû¸¹AQh´ZZ¾À°‘qî ÷Vž‹–-'¨w_Ú¿ß…QåÚõFåy7%…ðO&ò^`gú Jôå+ËAAx‰‰Ã\íZ59ÿçŸèt:ýsçþ¼ÀÛµj¾ð²›6nôL·?}Þt:ff*&OÇ—«WáåéɧS§XÔ­0K–¯ÀÖÆ–Íë×Ò¬q#¦Íš…ÆˆuAA^WbâðWµJet:×näþÒ®Ñj¹ðgµkú¹«+O9›NA=éýÁŽ?‘oûàÐ0öì;@¿ÁÃèÂÌ9ó¸Ǩð±vëA—ž!DnÚ¬ßfç×»  £m@`S•ââãùhì'tìÒ~ƒ‡qîŸú´°þDnṴ́QcêÝ—½¾)RÝÍÍÍ2 Þžž”,iO§H¼“ÄÝ”Ùí²ÔjNþvšn]ÞÇÖÖ–NIK»OTTt‘âAAx•‰‰ÃœÊÌ ¿·ÞäÜùÜ/è11W°µ±Á£B¦Ï™‹›ÖE0q\8‹–.#!11_‡ü‘ÙÓ§²%òKzu rÓWøúx³ã« ¬[µ‚·ªWÓ¿>°C‘«)W®\xfÍ[€7Û6EÒ«Gw¦Ì˜ÅƒŒÇ _ÅÇdzxþfLþ”Õk×µª­™RɈ¡ƒèÞµn®®…¾îêõk”rpÀÞÎN6¿øøD4Z-î®®¬]Éýôtœ‰½ûLå ‚ ‚ ¼NÄÄA vÍšœ;€sçÏë6ÄÅÇu‰~¡½±°° ¢‡~5ü8ýû™|ÛwðoGIûÜ/Ûîn¹×,(Jî$%“””Œµµ5ÕªV5Gjj*—¢/Óõý@T* ë×£¤½þ¼¨MÓ&¨P¾ …‚ä»)lßµ›NA= ßöÎNNò íÝ‹µë70dä(,Ì-ëB“F eãH½w•™%J×?WÒ¾$)Oœ:dkc£ÿ[¥2ãáC5m[µ IÃò³(f![ž¾Nj5“¦Í Uóæ4.$Ÿ§YZZ¢Óé077gÓ—dddbeY̨òAA^Gbâ àèXGGG¢¢£‰¹r¿Õ(ãX+kkÖ¬X†B¡Ü^¡(ø })F8 €£?ÿ‚ÅKhܰl>övvh´ZÒÒîë')©)”´·—ÿÁoÙ´ekçkÕôc|øG²Ûj4¦Î˜—gEztë"ûÚ>lÙ±“>Á=øõ·S$'ßåªUd·kצM7*ð¼¡#:ŽÙóbkcChï^¨Õ¹§YXXècMHLd̸ êÿuj×À²X1ê¼]‹m;vñáAì;ø Å‹ÛR¥²¯ly‚ ‚ ¯31qx»–Ûvì$4$8ßóãÃǰ|å÷ C«ÕâííÅðÁ æw9æ ‹–® K­¦Œ£#á£FèÓÂA­Î"9ù.³æ-¤˜…9½ƒ{òn³¦Œ5‚Ï–.£SP0eK3a\8¶¶¶²eY[[cýh²ó,’“ïrôçcùé'ýóKÌÇ×Ç @4!#3#ß¶C`öü…tîÑ g'&Œ G¥»“ ‚ ÿ^ŠöæøùÕzÙq‚ ‚ ‚ðœœ9sšâ%J>Ó6Þ^ž²ù‰»* ‚ ‚ ‚`˜8‚ ‚ ‚`˜8‚ ‚ ‚`˜8‚ ‚ ‚`˜8‚ ‚ ‚`˜8‚ ‚ ‚`¸ñüÜ×{÷{ë6þmÛP¾œûËGAAxE‰#ÿqõëÖÁÊÒ’ðñ=v«„š˜8`cmMdÄj†ðÌÛ~0h-ß È÷P«Õ/ ÊWKQÚìUQ”:h´ZZ¾À°‘qîÂ…çÞ+¡[ç÷qwu{Ùaüã6nÞÊ¢eËŸ[~1W®0rÌXü;Ó½Wæ.\”/ý³çhù^_mݦ.rÓW´|/€³çÏžžNÛ€@>™4€1ã'°mÇ®gŠC«Õ²hÙr‚z÷¥ýû]õñX®]¿aÔ¶wSRÿd"ïv¦ßà¡D_¾òLe¿* õíóó/ê}B®Ï{ì+==Ž]ºŸPpaQ¹4Á°Ãç-@tL CFŒ" s7B âÄÉ“^så ­ü;°qóV£Ò¦ÎœM×àü;3|ô¢cb^hþ+ÄÄáZ¾h!{wlE©T²pÎ,öîØJ±bÅ^vX‚ð\4mÜGG±P`Qh´Z&L™F£õغ)’…sgãéQ!ßk~=u GÇÒüúÛ©|Ï»¹ºðÓ±ãürâWœœÊ)N‡™™ŠÉÆñåêUxyzòéÔiäääÜvÉòØÚزyýZš5nÄ´Y³ÐhµEŠçU$Ƽi|s¿oáT¶Ì3¥ ÿ3ç̧–_ vmÙÄ€°¾LŸ={iiútNÇÊÕT(_¾À¶Rim[·dѼ¹l\AówÞaâ”éètº^—»×~‡””T|{ˆ»w“:h ·ãâ˜ÿÙbnüu•J…Û6u`ëŽì;ð ÷ÒÒpuq¦X(Õ«Uãûްk÷^RîÝ£Z•*h´.\Œbú¤ x{yŒ!!1‘±'Ñ¿o(uj×Ò?ÿä­NU*ó|“¹8åÒBÂúóñè‘T®ä @§ ž,˜=“rî¤²ô󕜻p[[Âú„а~=}™Á¡at dßÁo¸—v·ª½ÉØ1£$ëu/-ÃF0~ÌhªV©LJê= ûO?Klì-“Û,$¬?ÍßiÆ©ßÏ”œL÷.ñoÛÚ`ÝåÚúeô»)äú¤ûH®ÍäÚ%.>ž‹–rõÚ5< Õ«½!Û.†Úsç×»Ùµg/ÉÉw™6y"~o½eT?ÈÅ)W?SûOn0%ÎË1W™6kd Ñj9ýûV-_Š•¥%û~ÃÎÝ{HN¾K…òåø$|Œþ‹fay&$$pï^þï¡T*±²´$°C@¾1óÛ©ßéÙ½‹–.'%õ%íí¨Vµ*£.¡Óéøé—_hX¿>×oüϨqXX,æææ ÐOÿšN:°k÷^PÊÁA2¯,µš“¿fÑ‚¹ØÚÚÒ)°#_mÝNTT4oV«*Û年A¹4SÞ;Èö­Ü˜—ÛÇ í·…ùzï>boÝÆ¿mÊ—s/ô5gÏŸgæ¼ù¤§? O¯`üÛ¶–Ÿ7oÆÊÖ/($”zujsåÚ 233èÑ­+M72ØÖr}›G«Õ²gÿ~Æ~4Úè4¹ÏªÂên(N¹4¹þ“#5Îä>7+ùø˜ü~&×G¦Äi ©÷3©63æ3µ°ñ’““ä$Ô¯‡R©äíZ5Qqq Ø•(ÀÁoQ©R%bcoˆS*­fú¿Ë–q$55•ûééú<Ó¼¶‡ó.²÷À~ÿã, êÕ¥}»6@î!|_oæÍšAff&×n<>ÔnkcÃôÉquqá—'™8e:["×`naΚKéÒ£Æ}Œ·GŽþdÔHFClì-223ŒŽ_.N¹49ÓçÌ¥œ»;›ÖE{ëáã'àíåIÙ2É9üãÌž>•’övü+›Ÿ]‰Œ6˜yŸ-fÅâ…,^¶Œ¶­[QÉÇ‡ØØ[Ej³øøxÏŸÃÿþºÉðÑchÝâ]ÌÍÍ Ö]ª­_F¿K1S*1t0Ý»vÂÍÕõ™¶—ê#©6év™5ooT­ÊÌ©“øõ·SL™1‹õkVacm-Ù.–––²íØ!€À úaØMí?¹ú™Úrûƒ)qúúx±š›·’”œÄðÁƒòmsôç_ؼu“'|B…òå¸ö‰_Ý Ë³t©RØÙ•`銕´iÕÏŠQ*޽E|BM6àë=û8uúwZ6¥™’Ê•|8yê÷ÓÒqv*kôÄÁ˜÷¬«×¯QÊÁ{;;Ù¼âãÑhµ¸»º²v}$þïáììLì­XýÄAª~nn®ø·mKƒúuQ™™é˼ÍÊ%‹ˆŠŽfòôY´nñ®ìø44vÒÓ3X<ñ ‰ :œjoT¥”ƒC‘öi€cÇO`ooOÕ*•Ÿ)Mê}°°ºúìK3ÔR䯙Ôç&íóHªLSŽÜû™T›S‡ÂÆ‹B¡ C{~>vœ åÊqúÌììJP¡B9 ÷GÌ]{ö²hþ\æÌÿ,_œris.âį'Ið€fMšˆIÃsðÚªtîÂú ʪˆª¿Y _0jøPý T*”ÜIJ&))kkkªU­ªß¶mëV¸»¹¡T*iÔ æ*nÇÅàìT+++ìííqsu¡l™2¤¤¦“«‹ ‡öí¦YãÆF×C.N¹4)qññDE]¢_ho,,,¨èá_ ?ý/Ky:ø·Óÿbéîfø\ÝZ~~Ô®éÇÈð±$ßM%¨kg}ZQÚ¬i“ܶªP¾ …‚ä»)FÕ]ª­ÿÉ~ß¾k7‚zxÌœ»È}lÓª%õêÔ1øÆþ4©>’j3©vIMMåRôeº¾ˆJ¥¢aýz”´·ãŸ ¶‹)cИíäö©ú™Ò†ö‡¢Ä)åÐáï hgEÌÌÌx³ZÕ|§–g±bŘ?k&Yê,ÆMœD÷>ìÚ³WŸ~â·STöõÅÚÚšš5ªsòTþÓ•5¨Ï’«¨W§¶ÑqS¿´´û¬øb û…aöÄÖÂdee¡T*Ñh4|wøqqñX[[‘™õøz.©òLƒRi/ê}PŠ¡} ¤ÇµÔûDGGúöîņukèØÞŸïÿ@HX?|ó­>Oÿvm°²²Òÿ¢úä{©ÞiÖ§²eðññ~nûÊÎÝ{ hÿÌiR}$UwSÆ‹1ýWCãLîs³(ŸGR}djœr¤ÞÏ µ™¡:H—úußæÇŸ~¦]ÇNÌœ»€‡ÆòÑYëÖÓ±½¡“9¹4€ôeÉÂyu§iãë-öÚqHM½GJJ õëÖÅÓÃë§Khï^¬]¿!#GaanAXŸš4jäþº±uûNRRsßhî§?@£ÑèåS*•ú‡VûâÎ…“‹S.MJâ$}8Rÿ\V–šŠùÏùsvrzæXÛ¶nÍ×{÷ñáAù¾D¥Ílmlô«Tf<|˜û%ÔºÚîy÷{ÛV-hÒ°àE1 #jn˜TIµ™”Ô{÷P™™Q¢Dqýs%íK’’’Ûríò"úÁ©ú™Ò†ö‡¢Ä)åÎ$Ê–}öë Ü\]3r999œ;‰S§ãëíM•Ê•8yê5Þª@êo1mö}ݪ¿ù&®ÎÎ4jØ þY¤øód©ÕLš6ƒVÍ=îQ­ IDAT›Ó¸qþ4KKKt:ææælú2€ŒŒL¬, _Ïeê”J{‘1´Á³ï·úך™áQ¡<žž\½v„ÄÇ—(þ¸< s£ó”SÜÖ6ßßy_öв¯DÇÄpçNR¡ãH. ¤ûHªî¦Œcú¯0ÆŒ3©ÏÍ¢|IõQQâ”"õ~&×fæææ&}¦>ÈÈà“ISéJófM¹|å §Lç³¹³ÈÌTsùÊU† X`»Ë1W%ÓòØÚÚbkkKn]èÖ«<Äõ4EôÚMš4jHíš~>r”…K–¡Óéhݲï6kJI{;J980úÃa@î¡¶‹—иaRSï1{ÞæÎœA%_o»õ 'G!YÖ“š›«Ði _$h,©8 …lš……9­F_FF&eKcemÍšËP(¤ë¤PÈÿzø4­VËgK—ÒÁÿ=ÖoüŠºuÞÆ¡dIÉ×µÍäênÊv/¢ß÷ü–M[ ÞÕ¡VM?Ƈ$§Tÿ=éYûHн­–´´ûú7ù”ÔJÚÛ“’’*Û.Ï» m'ÅPœ…ÉÉÉ1¸?%N¥‚B/vt,MBB‚±U+@¡PðVõ7ññöâýE¹rîDE]"êR4›·m ;;›óOLTffÌ9 à¹L4 SgÌÆË³"=ºu1j'§2¨Ì̸‹·§'†¸øxÜ Ü}¨(cP*­¨ïƒR}+En3U–Zͱ_ŽóÍwßsûvmZµ`éÂy”.mÜEÙruK{ò¨EJJ ö~í/ʾ²óë=ø·kƒJUð«†\<ûû )ãÅÔþ34Τ>7M}?Ë#ÕGPøg•±ûCa¤ÞÏäÚ,ýÁƒu(ÌíÛ·ÉÊÊ¢u‹æ( Þ¨R7WWÎ]È=ŠqýÆ Úê_âäIþ÷×_¼Y­šdÚÓŸÅ …NËßÇŠ‰C½v§*X[[ãß® «–-fø\¹z%Ëso1wâäI’ïÞrݱ²´D¡P™•‰J¥¢|¹Ü³£?#==Ýè2Ý\]ùëï¿ó]åŸ'!1‘°þœ>lÙ±Fñã'HN¾ËU«lCý ÅÔþ“bjÿÚŠ§ƒƒ7oÆæ»~ å»ï²c÷®ß¸N§ãϨ(âãð–gúƒD|É­Û·Q«Õœ9{–˜+Wñ¬èÉéßÏP¼DqöíÜÆþ]ÛÙ¿k;u߮ͯ'óŸ®$E«Ó¢V«õ'ÇZa±èt:fÏ_ˆ­ ¡½{é·{r/l;ËbŨóv-¶íØEFF;wï¡xq[ªTö•Ý®(cP*­¨ïƒR}+En3Õ®¯wsèð¼×Žõ_ÔÝèIÈ×A.mïþdffu)š˜+W©õèT S÷•;w’øíôï´k]ðbp¹4S™2^Œé¿ÂêghœI}nõóHª ðÏ*cö©þ“z?+ê˜/¬<,,,øþ‡ÉÉÉáêµë¹?ž¸»áß¶5‡öíÖ?êÕ©CHÏŒÿH6ív\{öí'>!ÔÔTÖEn@­~HÅŠŒno¡p¯Ý‡§U«Z•jU«’ Àå˜+,Zº‚,µš2ŽŽ„€‹³3:r4¥K•ÆÇÛë™n«çîæF›–-èÖ­V˦õkõ‡ ó~a{úâ°AÃGð×Í¿1æcý-Y¥â”«@n™9o¿þv ¿oå;\8>| ËW~ApŸ0´Z-ÞÞ^ —9„gHtL {öígÅ’Ü Žºwí̇£Ç°{ßþ|‡ß¥Èµ™¹ºƒt[ÿÓýþô)rÆ’ë¿¢j—1£FðÙÒet ¦Œci&Œ ׺•k¹~8µ:‹ää»Ìš·bæôîɻ͚šÜRŠÒrûCQâlØ >‡¥{H(*33Ö¬\Ž•¥%M5äÞ½4¦ÍšCRò]<*”Ï÷«WayªT*>–ôô”-ãÈÀúâëãÅî}{©_·N¾‹¥Ô«ËÆ-[iñN3ƒõø2’ˆ/#õÿoÔ Æ~,Krò]Žþ| €#?ý¤~É‚ùøúxIn0dàfÏ_Hç½pqvbÂØð|¿$¶¡¾5õ=²(ïƒR}+7æ¥ö1Su ìH÷®Æíy–:J«\ɇ^}ûaQÌ‚‘Çè'+¦î+»÷í§Y“F…¾ÇÉ¥™ÊÔñb¨ÿ¤ê'5Îä>7;ø¿W¤Ï#©>éÏ*CûƒTýäÞÏŠ2æ +ÏÆÚš‰ãÂY³n=KV|Ž]‰â„ôìaÔÝ­¤¨T*~þåë6l";;›rînL÷ñ3_s(¤8p`oŽŸ_Á[¨ ‚ ‚ðïʧãÇé'ˆE•™•EphógÍ,pKY¹4AÚóî#á¿áÌ™Ó/!}zya¼½=’Ê•r êÔ“³g’™©fÚ¬Yg.åÜÝÙ´.‚Ø[·?o/OÊ–)cRÌ-ÌY³b)]zôb¸ññöâÈÑŸðöò"rÓWøúx3oÖ 233¹vã†Áö4Ô.ru*Ϙ¶>á"{à÷?ÎÒ ^]Ú·k#' [?©ö4Ôf†h4bco½¸˜¡8é£ÂÈõCž§Ç`±bÅØ»ÿ áã'àææŠÛ¶4¨_•™™ÑuAAx‘žë5;wï!0 }¾çÚ¶n…»›J¥’F êaa®Ê·Ü¹©üÛµÁÊÊŠš–\O¾›b0–<üÛQÒÞÈ]m1.>ž¨¨Kô í……=<ð«á§ÿõÝÔ:8;•ÅÊÊ {{{Ü\]([¦ )©©(Jî$%“””Œµµ5ÕªV-°½\žf¨Æ”÷´s.ÐoðPVEDPýÍjlˆø‚QÇø_Xœråɵ§\›âêâ¡}»iÖ¸±Q¯7§)mf¨ò<=Ë8:Ò·w/6¬[CÇöþ|wøBÂúqà›o®‹ ‚ ‹ôÜŽ8DÇÄpçN6È÷ü±ã'غ}')©¹_ìï§?@£Ñ¹¼Å/UoaaÎÇ/v•Š%³“S¾ÿ'ÞI`Ї#õÏee©©èQ¾HuP*•úóZ­€ÐÞ½X»~CFŽÂÂÜ‚°>!4iÔÐè:<ÍP •W˜ÔÔ{¤¤¤P¿n]<=<°¶¶.ð©8åÊ“kO¹6{äâ4¥Í õCž§Ç`•™ÊãééÁÕk×HH|1› ‚ ‚ <«ç6qØùõüÛµA¥zœeJJ*³ç-`îÌTòõ °[rr@î~6÷ cNN™ùòT*rŸ±d W®^cÉòåã”*Ϙö”#×Ö ‰‰„„õçä©ÓùžÏÎÎÖß~U£ÉÎw+V¹v‘K“»†úAÊ®¯wsèð¼×Žõ_Ô]LAAx¥<—#»÷í§Y“F”(Q<ßó.ÎÎv`èÈÑ”.Uo/êÑ­33ç-à×ßNáWã­Û7lPŸÃGŽÒ=$•™kV./p§cc1d|ø–¯ü‚à>ahµZ¼½½>x Á:@î9êmZ¶ wX´Z-›Ö¯5XÞå˜+,Zº‚,µš2ŽŽ„at¤ÚEª†Ê“ËóIÕªV¥ZÕªdggŒSªaÊ>¥VÓ-¸73æÌã·Ó¿EÛ§A^1qxJÓÆžéö R4Z-¦L£QƒzlÝɹ³ñô¨ðÂÊ^=ÿõ¾=ðÍ!üj¼…SÙ2Ï”ö²µmÝ’Eóæ²q]Íßy‡‰S¦£Ó½¸ÕËõd›ét:ÌÌTLž0Ž/W¯ÂËÓ“O§NÓ/–¸dù lmlÙ¼~-Í7bÚ¬Yh­ÑbjšT,rÛ=|øi³æàß¶ »¶l¢Å;͘:sf¡kó¼Êc")ûôõë7ðôô æÊU¼½¼^Dh‚ ÿˆ×~â’’ÊÆÍ[Y²|W®^eÀÐ èÜÈM_å{íí¸8F…%°[ºô !rÓf}ÚίwFÛ€À‡¢¯]¿¡Ïsͺõo'$&:`P¾…Ǹw/ÿ÷°²´Ä©l;¹¼°þDnṴ́QcêÝ—½¾Ño·uÇNzõíG@çn >Bÿ Ö嘫‡†±c××>r”àÐ0‚CÃÈÌÊÒçùä©.‚zróï¿õÿ cϾô<Œ®Á!Ìœ3_ßöSgΦSPOz0€cÇOì¯{iiõîËŨK¹y¤Þ£{¯>DÇÄðýG<|$A½û2sÎ|¦ÎœM—ž!\¹Zø/”O’k¹~‡Âû^ÎX2ÕÙóçéÖÀn=ôu—ëwCc"($”%ËW0lÔ>4„úÙ¨6Ë£ÕjÙ³?í ¦…„ê×ÙÈ“““CÇ.ÝéÜÛ¤´¤$é6•+/))‰š5jàT¶ %J§lGRSS¹oÄ"…qññ|4ö:véN¿ÁÃ8wáÏ|éRû‘1câé6377gÈ€~x{zR²¤=:t ñNwSRÈR«9ùÛiºuy[[[:v$-í>QQÑ&§ÉÅ"·]ì­[d<È mëV¨T*:´'-í>W®]“ÍóE½O¼Œ}:æêU¼==¹ÿ>:Ž’öv²1~½wK?_U`Œ ‚ ¼ ^Û‰Ãù ™>{.}&>!Ö-[ Óé˜1g>­š¿ÃöM‘dffê+ˆÜô¾>ÞìøjëV­à­êÕôiˆŒXM¹råò•“““ÃŒ9siñî;ìøj999ùò„Ü…Çbcoå[x¬t©RØÙ•`銕\¹zµÀ/–E)/>>žÅóç0cò§¬^»NŸnkcÃôÉÙµeÝ»taâ”édeeáëãEdÄjÞïØw›5!2b5‘« .¦÷¤Ã?þÈìéSÙù%=ƒº0}Î\ìììØ´.‚‰ãÂY´t ‰ò§Ø•(ÁÈaƒ™÷Ùb²²²X¼lm[·¢’ææ¬Y±”ã¿þJ«Íy¿C{Žýɨ¥ÚE®ß¡ðþ{YcIŠ™RɈ¡ƒèÞµn®ùïhu1*š•K1>|4_D¬%;;[¶ßéé,ž?‡©ŸNdÑÒåúU´¥ÚìIÇŽŸÀÞÞžªU*L«äëMÌS_únݺ•µ5U*ûš”&·ê¶\yyÛÍ]¸ˆÀ®A|2i Íš4Á®D ÉüòÌš·o¶mФWîL™1‹ùÛçéýÈØ1!מW¯_£”ƒövvÄÇ'¢ÑjqwueíúHî§§ãììLì­X“Óäb1v»'ݾ%õ§ó|ïÿô>}òÔi:véΪÕìüz=z÷%))‰Ž]ºsõÚuÉ}º~Ý:XYZ>~£ÇŽçèÏ¿zÔGáexí&ç.\ ßà¡¬Šˆ ú›ÕØñ£†ÅÛË‹›ß")) ÿ÷ÚannN§ÀŽù¶U*”ÜIJ&))kkkªU­j°¼›Ç’˜x‡ÿv¨T*:u (ðWíÛM³ÆõÏ+VŒù³f’¥ÎbÜÄItéî={ŸKyM›ä–S¡|9 ÉwShÛºînn(•J5¨‡…¹ŠÛqq¶7Eÿvú_ÊÜÝ܈‹'*êýB{caaAEüjøqú÷3óªåçGíš~Œ KòÝT‚ºvÖ§9;•ÅÊÊ {{{Ü\]([¦ )©©FÅ(Õ.†ú½°þû§ÇÒö]»éÔ³ÀcæÜ( Ú´j @½:u(åà/oÿvm°²²¢fúºÅ;ÍšàT¶ >>Þùú¶°6{ÒÎÝ{ =ÚPXZ%_®^½†F£¡sP07þ÷?®\»Je_“Óä³ÝÀú²dá<‚ƒºÓ´qƒm•ššÊ¥èËt}?•JEÃúõ(ioÇ…?/æ{ÝÓû‘1û»¡öLK»ÏŠ/Ö0°_fffdee¡T*Ñh4|wøqqñX[[‘™¥69M.¹íÜ\]±¶±fßÁoP«Õìܽ—lµZ>Oxþïÿô>]§v-"×®ÆÉÙ‰m›"iÔ°>ŸŒî­_áåYQrŸ.ãèHß޽ذn ÛûóÝá ëÇo¾5 ‹Väuþi©©÷HII¡~ݺxzx`mm­O»—–Š] Tff8”,‰B¡Ð§‡öîÅÚõ2ræ„õ ¡I£†òåÝËŸ§½½}¾<帹º0färrr8wþ§NÇ×Û›*•+©<[ýß*•æ~;~‚­Ûw’’šû¥ñ~ú4Q±âìä”ïÿ‰Åúp¤þ¹¬,5=Ê•_ÛÖ­ùzï>>2³GuP*•úóZ­qç—Kµ‹)ýþO¥¶­ZФaÁ/¨Å,Œ¨9”(^üñ6æúºEq[Û|;‹Ž‰áÎ$RŸÂÒ*Wò%b}$—¢/SªT)~ÿãÉÉITòõ¥’·IiräÊËckk‹­­-=ºu¡[¯>xTð=÷>õÞ=Tff”(ñ¸JÚ—$%%ÿîéýȘý]®=³Ôj&M›A«æÍõé–––èt:ÌÍÍÙôe™XY39M.¹íþÏÞy‡5u½üÈRp€(ˆ"S­[ëžu+¢Ô‰â¤Z÷ÖªÕÚº·(j]8pÕ­¸jë×Ú:jµþl­ p£ Á2IÂï4InBpP{?Ï“GÌ›ó¾ïyÏ{ϽçÞsϱ´´äë¯&²zízÖ…m¢î§ŸâQ^»ßªßÛì'Þ÷1Ý%°7 ¹•ZM ~ddfrîü:Ý#(°‡ ^©¹9îåÊâááNôí۟抈ˆˆ¼þu‡&R»f Nž:ÍÒ+Q«Õ´iÕ’Ïš5ÅÁÞôôWSÒÒÓ5/ /VŒñ£Gpú·³,Y¾‚Æ ìHIIE©R!57G–œ¢¥Ó$ ÕªVÁÛË“{÷ï Lµ'“%3Ñ΃¯=z‘ýªnftê²´´@©Ê`dgg“‘‘©£æZÿwr,µ V¯4z õ•JŲÐP:ùu`˶Ô­ó)ÅŠêßýuŸ-,¤¨Uy‹¿©íþ>séȱÙþî\ejÕ¬ÁÔIòTß7Ñ×î†d¯?µÉd8˜›ý’}á×¾-RiîîE—ÌËË“{÷îsùÊú÷éÍÑã'HKFÿ>õðôð0I&„½7‘H$¨Õ*>Œ88ØÛ£T©HM}¦<È’eupxCŸùå ïúâ©T*™9g>žåéÕ£›æ{gg'¤ææ<ˆ‰ÁËÃ¥RIl\®.®&Ë„|1T®r¥J¬ Y ä<èѧ?îîë÷¶û‰÷}LïÙ¾•«×PÑ×WW6oÝÊœïfèÕõ¹BÁ™³ç8þÓÏ<~KÛÖ- ]ºHpúˆˆˆÈûâ_7U ÀÆÆ¿ömY»r9£†!*ú6+V­¢Œ« E‹:h^Ø$%5ը߃áv×Õ~ï;—Ú·mÍÚ•+r}†lt=õ¡¯Ý É"Ž%33“ë7nM­Ó @wÌžB\|<ÉÉÉl ߊBñœòåË êtppÀ×Û›öîC©TræÜy’’žòI¥Š-cøx×3µZÍüÅK±³µe@¿>(  ÙÙÙX*DOk±{ï~222Øwð… ÛQ±‚É2Cí'T.*:™,™¸øx–._‰·§§f×y¡|yÛýć8?DE߯ÇˋȨ(¼¼Œ[Miÿƒœ8ù?ü;´gKØ:‚{Šƒ‘ÿî‰Ã›T®T‰Ê•*‘••…™™S&N`YèJ¶íÜ…——Ö]¬[‘Q„„®F®PàäèȤqc4²à!ÃQ(ä$%=eÞ¢¥²´ _Po>kÖ”)'°`É2¶lÛAg?,,,˜½:¡¼¼Ãöú‹¢R©”øøÆNšLZZ:%òÅ@|¼=ómO¥K•" “?#ÆŽ§Dñx{yæZ6°aƒúœæ¶uuuÑÌ]ϋ쿎¾œ(H1{¾¤úå‡ù˜yqª’ˆˆˆˆˆˆˆˆˆÈG†8Ué_@ûÎ]xòbcørÄh._¹ò=ú°ö^’׸qô8]ƒèÜ­'7nÞz+:EDDDDDDDD„§*}`ztýœ2¯­“ž"£¢ø~]QÑÑØÙÙQ£z5&ŒõÎì™ÊC‡sÿÁC­ï"öîÒZ G*•еë7°nU(ÎÎ%ß•‹"""""""""o >0M7z+z”*Ó¾›E.Ÿ3û»oHIIåÜùóïÌ^~X²µZ×,ž7OòF RRSQgg‹ƒ‘÷Œ8pÈ#2Y2G<ÁÓ§IŒ:„¨èh‡„G—ÎþZ¿}Ëâe˹{ÿR©¿vm ìÀ¾Ù(‚¤¤§Ìúv:5ªUÓ”»}ç. —†GÇöíØwð›Ö~¯Ù—!>!ÉÓg0xàêÔÎy?%>>ž””Tüý:`ff†µ•^ùcª½¾ÁƒiѼÿ¼LbR=»uů]M,B¿_Ã_W¯bgkGpÿ¾4¬Ÿ³û®P\,,,4K¥Zƒ!{ã'Oáql$â;Fò÷ÕkÌž¿C†O›V-Q«ÕÌY°˜Ö-š³g{8™™™deeiÊ„oß·{wleÓÚÕT«ZY# èäOxØzÜÜÜ´ìdgg3gÁBZ~Öœ½;¶’­¥r6 Š‰y¤µU‰âű·/Bèê5DEG£V«µÊäÇ^\\Ë/`η߰~ã&|ö‚…ØÛÛ³}SÓ§L"$t%ñ ãb}öÍCÈÂùXXX¶žð°õš}æ-Z‚··»·‡Ó§WO¾›3ôŒ ê×­ƒµ•“¦Ncü䩜þí,J»$‹ˆˆˆˆˆˆˆˆ# ð×Õ« 6‚µaaT­R™­aë7j^žžÓ dÉ2Š:8 W(8söÇú™ÇciÛº%¡KQ¢D‰w戈ˆˆˆˆˆÈLj8UÉlllðkß–µ+—3jø¢¢o³bÕ*ʸºP´¨.^àä©SZåÎ_¸@ÒÓ§@Îsk++ƒwÞÝʸâääHÄá#¨T*DäžnŸ@ßàÁ»sq¶9œG£P(¸|å ‘QÑx”׿‘‡±ötQÊÙ™re˰yë6ä YYY\¾r…DZ±ã’••…B‘óA©|õ·©888àëíÍ{÷¡T*9sîÄ©J""""""""""âT%‘\´ïÜ…'Oþý—#FsùÊ•wèчåmÕ/=#ƒ Át büä)ïÜž±ôöë<˜7oå[O^óúMŒm¿wA^ÛèmŬ Ú)¨|È~BDäߊ8pøÑ£ëç”qq}kúvíÙGïþéЕa£Çòàá÷¦ÛÞVýlml[ψ¡_¾{/Ù¶s!+W½s{áÛwЪƒ?Wþþ€´´4ÚùðõŒ™oÅÏ÷Éÿ]ù‹VüÙ±k·æ;cÛï] ¯ÞgÌ”*­:äì?rìþºzÕè²B~¾Ë:¤¥¥Ñ¹[OââsoÎ($ûÐÌœ;ŸîA}ñ èʨñ¹ù¡]´c¦R©Y¹ŠÀ~éøy7Æ}5™Ûwîj~ûT&cÒ×ÓéЕAÃFpóV”Q²S¿þÊð1ãhë ˜o¶ŸP9!{B:MáCöocÚ!2*ŠÖ~ضs—Ñzå =‚ú0gÁ"þ¸ô'¿þå%ù˜F8üÇhÚ¸ŽŽogƒ£Çäà‘#Lž0ž][7Ó§W *•ê­è6•·Y¿Ýž«Ki~=s€³çÇÙ¹ä[Ñû¾ùýâEKðû?´+Àûω…£ÇOP£z5œK:åIö¡iצ!‹²mS-š7gúw³Q«ÕÚ-­˜©ÕjÌÍ¥|;m ›×¯ÅÓÃofÎÒl̹bÕjìlíØ¹e#Í7bÖ¼y(_ôåB²"… Ó«G7š6nh´/†Ê ÙÒù_ÆP;¨ÕjÖ¬£\Ù²yÒ{çÎ]<<܈ŒŠÆËÓ3ß¾¾Dl¿/âËÑ™,™£?žàéÓ$F BTt4‹CB‰‹£Kg­ß>Žeñ²åܽÿ©TŠ_»¶ö`߃ì?ARÒSf};ÕªiÊݾs—…KCˆ‹£cûvì;xˆMk¿×\üÄ'$0yú @Ú¯Þ‡Ù¹{/}{R©b-Ù®½û8|ô8)©©¸”.ÅààT­\È™*Ñ¢y3.þy™Ä¤$zvëŠ_»6įű$$”èÛ·qttdØ—ƒ¨Zùƒå„êpøØqöž6­Z¢V«™³`1­[4gÏöp233ÉÊÊÒ” ß¾o/öîØÊ¦µ«©VõÕp@'ÂÃÖãææ¦e';;›9 Òò³æìݱ•ììl-³á\LÌ#­ çž={F\|¼fÐð&v¶¶Ìþv:ûØNÏnݘþÝlär¹FÇòÅ ˜óí7¬ß¸IcsÞ¢%x{{±{{8}zõä»9óHÏÈ0XN_ýNÿv–»v3uâöíÜÆÀ¾}5OF ù©!{£‡cûæ0¶o£}ÛÖÔ¬^¢‚ö|¼= [Ïç;ñY³&„‡­'äuöDGßF©TÒ50ˆ»÷îu;š >Þ&Ë„0¦Ü/²bé"‚{Ò´qƒ±25ÏŒé¯ Å35õ«×m`È `ÌÍÍ‘Ë嘙™¡T*ùéä)bcã°±±&S®”‹/ob¬=]:kÕ¨Aíš5;i2IO“µ.”õG¦b¨òEè|$„©åÂ6m¡sG?lmltÊuêÔ®EøÆõ8—rf÷öp5¬Ïד'²×<=Êì_ ñfû99:2°_¶nÚ@çŽ~ütòô ÄÑã?æI¯ÈûAœªTÀHNNA&“Q¿n]<Üݱyí`OIMÆÞ¾RssŠ-ª¹ Я·leøØqXZXÜ¿/M Ï=MNÑÖéàà ¥S/ï ?K{F‘"¹gÎgמ}È’e/~—ŽR©ÔÈílm5K¥æ<®@.ÏDjn®¥¯¨CQd2™`9C„¨èÛ¬XµŠ2®.-êÀ…‹—8yê”V¹ó.hæK¥æX[Y¼sëVÆ''G"A¥Rq ""×oâè.ýy™ÂE sxßnŽìßÑý{¨ûim~¿ðjº’®öÊùw•ƒ÷˜ ѾmkÖ®\‘ë3|Èàwê§,âÈQ233¹~ã&‘QÑÔzí¥a}}È“'‰üqéOÚ·ió¦:½2«B…(]ª¿üz†šÕ«¡T*¹wÿ!^&Ë„ü*÷86–C‡Orr2›Â·¢P<§|ùr‚:MÍ3Cýµ¾˜©Õjæ/^Š­-úõA¡P P(ÈÎÎÆªP!ê|Z‹Ý{÷“‘‘Á¾ƒ‡(\ØŽŠ|es¬P(ÈV«Q«Ô( Mnµ­¾r†ì éܱk¶6¶ üíÛ¶añ²£êyíç õ‘B¾è;B¨œ¾xúµkÉÃ5Ÿzuêзw/£n.DE߯ÇˋȨ(¼¼ÞÎjJúÚoÿƒœ8ù?ü;´gKØ:‚{Šƒ†ŒøÄ¡€S¹R%*WªDVVfffL™8e¡+Ù¶s>^^Z£ö[‘Q„„®F®PàäèȤqc4²à!ÃQ(ä$%=eÞ¢¥²´ _Po>kÖ”)'°`É2¶lÛAg?,,,˜½p¼¼ëóæ‹~íÛ‘žžÎ¬ù III¡œ›“Æ¡t©RtògÄØñ”(^o/O£—§œ8n ËBWÒ%0'ÇL›2 »×¦@èC¨~M5$%%•Yó˜ô÷re™:iÎŽ% úYÆÕ•¶­ZÒ/x0*•Ší[6bgk+hïôogyK·Þ}4zÖ¯ 5*. Ôçä©Óôì;©¹9Ö¬ÂÚÊJО©1b꤉¬Z³Ž þÁ¨T*¼¼<õÚãn}~ !ä§P^ëãK—¨_·Žf@ƒzuÙöÃ.†þÐß~B9ÿ.rÐÔ˜éÃÆÆFkãÛDÈO!Y_oú „e!KÆŽ®uâ×ׇ<|„fMéœî($óõõ!!!©TJ¥ŠHIM¡P¡Bù’ ù©¯œT*å·³çÙ´u;YYY¸•qeú”¯´æ|ëÓiJžI$ÁÜÕ³¤¤§œþí ³æÿKV,YŒ·'Ç|ÉüÅKéÚ«¥K93mò$Í1($;|ì+¿_§ÑwìÄOŒ:¿vmÛO¨œ=}:oFFrèðV¯X@Ïî]=~"ÑšŠ£}ý„úúH!_:ùuÐ{>2„P9¡xš‚J¥">!—Òìܳ—ºŸÖ6\Èôµ_—€ÎôìÞMO)‘‚†äèшì5r/'òßD.—ãßµGöï1úñ²ˆÈ¿1çóO`ß|3u >ÞÆß™Ì”Ë Ìâyss-Á($yÅë¹›¥T˜˜½‹ösâߨ~†Ë—/Q¸ˆþ){ºðòôÐ+»|ù’8UI®^»¦™âtäø ª|ò‰x%òQ#æü‡')1‰>:/"„dÿuôånAŠÙ»ð¥ ÕO$ïˆí÷ñ ž)Exðà!3çÌÀÍ­ cGÿÀ‰ˆ¼[Äœÿ𸺺àêª{ýw!Ù}¹[bö.|)HõÉ;bû}<ˆS•DDDDDDDDDD>2Ä©J"¹hß¹ O^¬)m _ŽÍå+WÞ¡GÖÞ¿™¾Áƒ¹qóV¾õä5'Þ$=#ƒ Át büä)ùö'/ä5_ÞVÌ ª=‘‚J^û‰‚~®ú/"ög"¦ þcôèú9eŒ\ƒÛ‘QQŒ8¿€®ôìÓŸ…KCÞ©=ClÛ¹‹•«Þ¹ðí;hÕÁŸ+ÿ @ZZíüøzÆL£Ê¿/?áÿ®üE«þìØµ[ó­ áaë1ôË÷î¾|yŸ1SªT´ê³#ïȱøëêU£Ë ùù.ë––Fçn=‰‹Ï½a’ìC3sî|ºõÅ/ +£ÆOäfdä‡v ÐŽ™J¥"då*û ¤ãçÝ÷Õdnß¹«ùíS™ŒI_O§C@W ÁÍ[QFÉNýú+ÃÇŒ£­€`^¼Ù~B:_Ek¿NlÛ™{¯]:Má]÷ïóÜñ.+ôêÀœ‹øãÒŸÖ¡·„¡ãaäØ ´êà¯ùÌ[´D#{þü9+V¯áóž½éøy7-[®‘å§ß}IAîë>&ÄÃŒ¦½4ªJ•ŠißÍ¢QƒzìÚÎÒ…óñp/÷Îì4\]Jóë™sœ=ÿ;ÎÎyÛѳ ðûÅ‹8:–à÷?.þñ{àcÍ—wÍÑã'¨Q½šÎMÇ„dšvmZ²h!Û6…Ñ¢ys¦7µÚ¸}ß%¯ÇL­Vcn.åÛiSؼ~-ž|3s–f]þ«VcgkÇÎ-iÖ¸³æÍCùb/!Y‘Â…éÕ£M74ÚC:!gφ5ëÃ(WVÿNï9'^òoï îܹ‹‡‡;‘QÑxy¾½>4†Ž€éS¾"bï."öîÒlJ °aÓnEF2oÖ·lZ¿†VŸ}öV}û7äõÇ€ørtG&Kæè'xú4‰C‡ÍâPbãâèÒÙ_ë·ccY¼l9wï?@*•â×®­f{ø}²ÿPIIO™õítÍκ·ïÜeáÒbãâèØ¾ûbÓÚï5v|B“§Ï`ðÀÔ©ó>L||<))©øûuÀÌÌ k++:½òÇT{}ƒÓ¢y3.þy™Ä¤$zvëªY‹z×Þ}>zœ”ÔT\J—bpðªV®Ì­ÈhfÍ›GzzJ•ŠK^`íªP¬­¬è<˜¯Æ¥‚oÎ&B]{³dþ\ÜÊä¬î4 ˜®>vœ”ÔªU®Âä‰ãÉ’ ý~ ]½Š­ÁýûÒ°~În±•+UâÚõ¨Õj~={–†õësçî=M›é*gÈO€+ÿÍÜE‹IKK§Ÿ MÝcãâXJôíÛ8::2ìËAT­ü €`Nâ‹Ò»gBBW!KN¡¨»ý µŸŸB±Ö—/¦ÆìçÿbÿÁd))T®X¥JÉÕk×ä`w IDAT™=cZ¾Oàºì ùùàAŒ`û ^ÚDݾKff½zt§iãF{ºŽ¿×Q©T:r„ÉÆ”öÀÜ™ßj­l’M@÷@ *ÄüÙ3ó,[·j…ÞÍš„ì­[µ‚š¯mWÒÉ‘äädž¥¥a_¤ˆ`˜šg†ú:]1³°°`ø—ƒ4ò.:±ÿ`Oe2lmm¹ðÇ%B–,ÄÎÎŽ.Ù±kׯßÄÛÛS¯¬JåJšº_º|µZ÷¦€oú"W(uûñ¾¾¾ÄÄ<2JgJj*CFŽaêÄñTªXYr CGŽæ›¯'óÈäãèCœ«ta¨/05—„γk³—‡Ïž=C­Vkõ­út ÅEßùÏB:Aÿ9GŸ=¡ãáåþ%R©TkÈÙÏ䨉ŸY4÷råwöÎ+oæõˆÃÄž6­Z¢V«™³`1­[4gÏöp233ÉÊÊÒ” ß¾o/öîØÊ¦µ«©VõU§ÐÉŸð°õ¸¹¹iÙÉÎÎf΂…´ü¬9{wl%;;[K'äð11´60*Q¼8ööE]½†¨èè\w óc/..Žå‹0çÛoX¿q“FngkËìo§³ÿ‡íôìÖéßÍF.—ããíIxØz>ï܉Ϛ5!Ï^°{{{¶o cú”I„„®$>!稙¹|½¹pñ"ÏRÓ(õÚ}åŒñóÚõ›¬YÂÔIãY¶QS÷y‹–àííÅîíáôéÕ“ïæÌ#=#Ã`Nóˆ¸øxš4l@Y77.ñ(ÝPûéóÓP¬õåK~bfaiÁ†Õ¡œûýwZ·lÁç:rêô¯ÂÜÌŒ1#†г{\]´WÑeOÈOcê––ÁòÅ ˜ùÍtBBWi–ÛÝÇßëœ9w*U¬`PæëãEdt´Öo=zŒµ +ø˜$ÚáUÈÞËr —†Ð=¯g|G³&M À´<3¦ïáxDß¹MñbÅp°·'..¥JE6n çYZ¥J•"æQŒ ÌXÞôÅΔÔTöŠ °{W£uÚ)ÂØ‘ÃX´l9r¹œå+WÒ®Mk|½½ÓŽ£u®Ò‡PLí³ôõó.^¢s·ž¬]ƾ‡èÕo ‰‰‰tîÖ“èÛwu ÅEßùÏB:Aÿi¬½×‡—¬ü~ A‚™9w±qq@Î`ŸìlÎýþ=zÑï‹/ùù§4e õ»†x3¯ë×­ƒµ•“¦Ncü䩜þí¬Ö“9ÓŒ¿®^eа¬ £j•Êl [ǸQ#ðòôäÁÃG$&&âס=t è¬UÖLbÆ“Ä$“°±±¡r¥Jí=xCBÂüýÚ#•JuÞ±v)]š‡Ò¬qcÍw… bñ¼¹Èr¦LŸAϾýÙ(â­ØkÚ$ÇN¹²nH$’žÊhצ5e\]133£QƒzXZHykЦ1tòk¯¹#TÆÕ•ظ8®_¿Á ý°´´¤¼»;5ª×ÐÜ-hÔ >+V¯¥^W»jSN¿öm±¶¶ÖÜLz*#99™7oÑýó¤R) ë×£¨ƒ=Wÿ¹f0'„8ÿÇE*øø`ccCÍêU¹pÑðt%¡öòóuÞŒu~Ñ3€RÎ%±¶¶ÆÁÁW—Ò”trB–œÌžýéØ;×g¸‰„¶­[P¯N­]€…ìå‡æÍšà\Ò oo/­|Ñuü½Î¾ƒ‡ðïh”Ì×Û‡èèÛ(•Jºq÷Þ=¢nGSÁÇÛd™Æ”òÅ@V,]DP`Oš6n`0V¦æ™1}¡x¦¦>cõº Œ¹¹9r¹333”J%?„m çÆÍ[/^œ?ÿï/’’ñõñÁ×ÛË$™Bö^bgg‡½zt£GŸþ¸—sœ£ljžÓ÷ÅS®P0cÖZ·h¡‘[YY¡V«±°°`ûæ0222±¶*$(3]¾é¼Í­¨hF’'/iצ "3zøPÍE è?Ž„øPçªn½û’üâØÙ¸v5.¥K ÖÁÔ\êç»öF!W R«éÔŒÌLο@@§{ZS„tõƒBq1tþÓWwC±Öן²§ëx€œ‹þ—Œ3ŠÎÝzòðáC¬¬ ¡R©èÖ%+++ªU­‚‡»;×®_ÇÕ¥t®X䡼–š›ã^®,îDß¾­™5 b:âÀ¡€Ñ¤QCj׬ÁÉS§Yºb%jµš6­ZòY³¦8Ø;žþêjZzºÖ IŋӼˆtú·³,Y¾‚Æ ^H:Ø;’’ŠR¥BjnŽ,9EK§1H$ªU­‚·—'÷îß8˜jO&Kfþ¢%,œ;_/zô";ûUÝÌ$èÔeiiR•Óáegg“‘‘©£æZÿwr,µ V¯Ì¿ðí;€œiáÜYšƒP9C~êÃÁÞ¥JEjê3Í N–,£¨ƒƒÁœÐGZz:ׯßàú›ìܽ€¬¬,þþçÍœb )j•¶.¡öòóuÞŒµ1ä5fúÈÎÎæÈ±ÙþCîÕfjÕ¬ÁÔIò¥_ÈO!ÙëO-d2F¼k°ïÀ!üÚ·Õ9ØÓ%óòòäÞ½û\¾r…þ}zsôø ÒÒŸÑ¿O=<=„¨èÛ¬XµŠ2®.-êÀ…‹—8yê”V¹ó.hæFK¥æX[Y¼ûìVÆ''G"A¥Rq "÷t£ø„úÖØ…œAØæp=~ŒB¡àò•+DFEãQ^ÿÆ!ÆÚÓE¦<©TJY·œ“îéßΖ–¦õ›bÅŠñàA ª7æ1º”váÆœµªÏœ;oÔSŠRÎΔ+[†Í[·!W(ÈÊÊâò•+ SNŸŸúpppÀ×Û›öîC©TræÜy’’žòI¥ŠsB—þ¼Lá"…9¼o7GöïáÈþ=Ôý´6¿_x5]ÉÕÅ…û’’šªùN¨ý„üÌ/y™íÛ¶fíʹ>Ç ~§~ É"Ž%33“ë7nM­×^ÖuüõêÔ¡oï^šAƒP¾ìص[[†þ‚ömÛ°xYˆÑu]ýć:WåSsÉP?}//"£¢ðò2~1}q1æü—WBÙ:’““9ýÛ’ž>%ééSV¯Û€££#ne\±²²¢fõªì;R©äú›Ü¾s—O*ž¦&„¾¼Þà 'NþÿíÙ¶Ž Àžâ á-!>q(àT®T‰Ê•*‘••…™™S&N`YèJ¶íÜ…——ÖûVd!¡«‘+89:2iÜ,xÈp 9IIO™·h)…,-èԛϚ5eÊÄ ,X²Œ-ÛvÐÙß $f¯:–—wµ^9S*•ŸÀØI“IKK§¤“#C¾ˆ·g¾íé¢t©RtògÄØñ”(^o/Ï\+i4lPŸ“§NÓ³ï¤æælX³ k++zõèÊÜEKøý‹Ô¨^Më±´S'MdÕšuõF¥RáååÉ(iÆ–Óç§ÇaYèJºáäX‚iS&a÷bz‹PNèãK—¨_·Žæ>@ƒzuÙöÃ.†þÈ™wÛ¶UKúF¥R±}ËFìlmÛOÈO!„òÅÔ˜éÃÆÆFk àÛDÈO!Y_oú „e!KÆŽ®u‚Óuü<|„fMéÌg!™¯¯ H¥R*U¬@JjŠfSeB~ê+'•Jùíìy6mÝNVVne\™>å+­¹Íútš’g‰D0wõÅ,)é)§;äì½ð’KããíÉð!_2ñRºöêCéRÎL›7##9tø«W, g÷®Œ?‘ƒ‡hMÕ‡P?ñ¾ÏU¦`jŸ¥¯ŸW©TÄ'ÄãâRš{öR÷ÓÚu½D_\Œ9ÿåU§Bö„އ%бkï>/[Ž™¹9||ønú×XXX0jø0-]Nçn=)Z¬(cF Ë÷4%}yÝ% 3=»ë"b:’£G#²kÔȽğȹ\Ž×ٿǨ“ѿ͞ÈÛEl¿üØwßL¢tC¦\NЀ`Ï››k©A!™È+^ÏÝ,¥²ÀÄì]´ßǘbß#ò1æõÛæòåK.’·eo½<õϹ|ù’8UI®^»¦y”yäø ª|òÉ;íˆß·=‘·‹Ø~ž¤Ä$úê–¼þ·!NUùȧ*‰ä¢}ç.ÈȱØ¿U+WÖÈå ý‚³3|s,¢Eóf|Z«¦^{÷î?dÄØq@Îr…ŸÖªIpÿ¾8ØÞ[àùóç¬Ù°‘_~ý¬çÏiܨ¡f ñ«×®±jÍ:b=ÆÕ¥4£‡ÇÇÛ•JEè÷k¸pñOÒž=ÃË˃¡ƒáQÞÈYõ%tõÆ<¢xñb|Ñ¿¯Ö&C‘QQŒ;>½5ëŠ ù"Ä©_eïþƒÜ¾s—6­Z0jØPƒe„|ª_ôí; •{•“Ù3¾¡v­<•ɘ¿x)׮ߠt)gÆŽ©YWÝ_À¸xêÂÔ¸è³g¨Ý?úú¥üæÄ›j÷™sçóÏõëd¤gP¾¼;Cãë³Û¶¾ãHÈÏüä™/†ú‘ñ‰ÃŒ¦½Œ›1Úó»¶n¦qƒ̘=—ôŒ Ã…€”ÔTÔÙÙÕ ÁXôÅlUÈR"öîÂÌÌŒ¥ æ5h0†;wîâá‘s1—§§QöïÛÍÂ9³H}öŒéßÎ2ÊÖ†M[¸ɼYß²iýZ}ös?kÞüÚµeÿÛiÙ¼3çÎE¥R¡V«17—òí´)l^¿O¾™9K³ŽüÜ‹©U£:ûØÎ—Á™=‘Öšñjµš5ëÃ(W¶¬Q¾¢HáÂôêѦ…w²Õ…._„êçQÞˆ½»4Ÿå‹aggGÕ*Ÿ°bÕjìlíØ¹e#Í7bÖ¼y(ÜËB_\ ÅS¦ÆEŸ=Cíþ!Ð×/å''ta¨ÝÛµiEÈ¢…lÛF‹æÍ™þÝlÔjµàq$äg~òLŸ/"""ÿMÄ'™,™£?žàéÓ$F BTt4‹CB‰‹£Kg­ß>Žeñ²åܽÿ©TŠ_»¶š-î÷8ÈþC$%=eÖ·Ó5»ܾs—…KCˆ‹£cûvì;xˆMk¿× 0â˜<}ƒ Nmí÷a$ VVVtìÐŽõ›6óoOd²dB¿_Ã_W¯bgkGpÿ¾4¬Ÿ³[ìøÉSxOVVA‚øú«¯ –L×€;NJj Õ*WaòÄq‚åú¦Eóf\üó2‰IIôìÖ¿v¯6‹9|ì8û")é)åʺñõ¤‰8:–0è‹©è‹Ù˵®¤R‹·2hˆŒŽÆËÃgÏž¡V«)úbWbCö,--)ëV†¯Æ¡k¯>ܸy‹ ¾>zí(•JŽø‰Eóq/W@³ûlÌ£Gd¤gЮMk:ûwdSø6¢n߯×Û›á_ÒèéÒ©ûFðT&£XÑ¢_ Õï%ç/üAáÂ…5ƒ3¹\Ž™™J¥’ŸNžâÓZµ°±±&S®Ð¥Úh_„âùòN6@£õX¾r%cc)ïnú{Bö^¢+.BmdªŸï¢Ýóëç›íþ’!_ $°{Wþ÷˯x¾˜jhìq$„)y¦Ë@oßãäèÈÀ~}èÔ‹ \äØ?±6,Œ^Ý»iÅDDDä߇8p(`$'§ “ɨ_·.îîšA@Jj2ööE4wmŠ-ŠD"ÑÈôëÃÆ-[>v––÷ïK“FÂsr“S´u:88hé4DÄÞ](9ón=zLÍêÕIx±ÊÓÐÑc5¿“Ë”w/«O €ÑåJ9;繜­­æo©ÔœçÏsNŠOž$R²dî÷,L­ƒ™™î׆^ÿ^WÌ„h׺%M6Èõ½e!KÁr]{£+P©ÕôêGFf&çÎ_  Ó=Í6c‘Ë3±µ±ü•U!T*ݺ`eeEµªUðpwçÚõ븶lÁ×_MdõÚõ¬ ÛDÝO?Å£¼v~Ë fÌšCë-hü¢¾é|=c&ƒƒТYSnEE1ý»Ù,[8ÌL·¢¢9lHÞ|q)­·Ý¡[ï¾$''°qíj\J—æVd´^_„ê÷:?þü3-š7­V¨Õj,,,ؾ9 €ŒŒL¬­^]tçÕ¡xº•)ÙsçÙµg²dÏÒÒQ*•&ÇÅ=¡¸µ‘!?uùbH§©äÇOÈÝî/±³³ÃÎÎŽ^=ºÑ£OÜ˹ã\ÒÉàqdSòLŸ/†š›ã^®,îDß¾M|B‚Ñ~ŠˆˆLÄC£I£†Ô®Yƒ“§N³tÅJÔj5mZµä³fMq°w =ýÕt¡´ôt­ ‹+¦Y5æôogY²|68Ø;’’ŠR¥BjnŽ,9%Ï/')R˜¡ƒ¿`ì¤É4oÚ'ÇXÛØ°aõÊ< BŒ-'‘hß©5Õ€£c âããMöåM¬ åLAP½öò Z­ÆÚZ{ Å›1³³³Ó«óȱÙþî\ßתYƒ©“&è-·gûVV¬^CE_\]\Ù¼u+s¾›at]^’‘‘Á£Ç±W»q.é„™™¯‡Kb&!›œ|ª\©«B–9w9{ôéÇ‹;¯J¥’™sæãéQ^³úÀãÇ‘Ëå´iÙ‰DÂ'+âêâÂ_W¯pçî]Úúh~þÂîݿϸQ#}Ñ×îÆ°këæ\ßEFGëõeê¤ zë÷’Ĥ$þïÿþbäÐWûÎÎNHÍÍyƒ—‡J¥’ظ8\_[-¯¾tû¼³Þx¶+ÌüEKX8wŽfE€½ÈÎ6.ÿuù"Ô~neÊÆE_ÉdÉýÔå‹Îü?uµû›H$ÔjÆà\ÒIð82„©y¦Ï}È Μ=ÇñŸ~æñãXÚ¶nIèÒE”(ñöæù0ˆ«*@lllðkß–µ+—3jø¢¢o³bÕ*ʸºP´¨.^àä©SZåÎ_¸@ÒÓ§@Î]uk++ƒ½ne\qrr$âðT*""rý&>!¾Áƒ5vuQ®¬||Øèðÿ³wæqQUíÿ3 €2˜(› ±©Hšåë¾Un "¸‚¢†¼š¹T®¹dš»â‚¢– ™¸”¹!jf™Y¦¹äÏ27-EY…‘™á÷9:ÊÜß<ßÏg>â}î9ÏóœçÜsî¹÷Üspqv¦v­|±~…*EEEœŽ¾G÷®ÌœM`&ÇòÏVñvoo/Þ“˜Æqg®M«–äææ1cÎ<²²oâQ»–î)¾¹yN7š%Ë?eÝÆM%o>‰sõj\¹zõ‘s,³‘£ÇêöUø`܇@É´&[[ÛÇšŠpFCFfnn®|¹e+MÿÓXOþî{”ªï]B{¢pp ñë¯1jäp“Þ¼¼7|Ñ‹–Ò+ Ç*Ž|0bîn%ÓDNýñ'›6MÑÝ»4~½Æ ;û&>”¬C¥ àëãÅ”‰ãY³vKW|ŠCåJ è×—þõËd‹TÜw}ó Ë>]¥Ëç›}ß1âÝ¡z+q=ÆüØ·ÿa½z>’vøÐw˜»`=ûöÇÕÅ™&Œ×»æ;[[Éò í̈Qc¨úRU|¼½ô–n6§\¤ôݸ‘%Y.†bäìT]ÒN)¤â.…”ïe±³´¸Ëd2~þåk×o¤¨¨ˆš5Ü™2ñCÝJS†®#cvÒ†ë™1[ Ñ#4„°Þ¾YÿûXìÙ“XܨÑëÆÏ¼ܳ»·o)Ó Š@  ü8yò•*;?ñ¼½<%óS•œ>sF7Åi÷Þ}¼R¿¾4@ ôw‡®\¹ÊôYs¨Y³£F/g‹Ï+ñ7”ujßþ‰îJ.àùB vîTæ½ /áaåm‚@ ‚rBLU<!=¸ñϦðΈ÷9yêÔS´¨|Iܳ—žá„ô ãÜù åmŽ@ ÁSC¼q}™ýÉ4|}¼{÷'In^Úâb1h@ðB =”Êö|»›7³ñîP.&'³ &–´ôtz„ë{=-‹—pùï+Èd2‚:Þ€m;ؾ3‘ìì›Ì˜6…F êÒ¥\ºÌüE1¤¥§Ó50€m ;Y»òS݇µ™™L˜2•!ƒ"iÒ¸d©à{kŠßC.—acc£gODd=CCÙõÍ^rórièÿ ÆfóÖmìÚ³—ܼ<Ü\]ID á­7Úqü·“degÖ«§nÍs)ÿÆL˜Èõ´ ŠŠŠˆˆŒ`ò‡âëãEZz: cbINIÁÉɉaï ÖÛw 4;¿Þˆí ‰(ssñ¯WµFÍé3g™9õ#¼½¼ Ú²#q©×®ЙZ5k”½@ @ üqú ‰{öðÛÿ¢E³¦t ìŒV«eÖ¼t ìL—€Î|¾.ž¢¢"]šø›ðõñ&zÎ, H¹|Y' íLh·`Þñ¾žžââbfÍ›O@§Nw `mü½<¡dùÔÔkF7œ+ý?þÈÜ™ÓqT8p55{;;fN›‚›«+¿9Ê”OfòUüZ*T¨@zz:KÌ㯿¯ðÞ˜qtjÿ&r¹\Ò¿èÙ³¸q#‹ƒß!>nµž s¢RßÏÙÓ§òë±ã|2këÖ¬ÄîMܶóBÒEäÖrÖ¬ˆ¥Wßþ|4ñC|¼½8pð'¼½¼ ÚÒ¼iwÃøIáîîFP@-š7EfeõØe'@ …ø8úç÷Ó§®..6>X1Ï?Ô yy·tƒeŽG…BÒNCÜ+C¶¨îÞåÐ/‡ÙûÝ÷\¿žFçŽí‰]MÕªb#6@ OñÆA€­­-AY¹l ï ÊÅä–._N w7=~€ýè¥;rô(Ù7o%Oê+V¨`ôíAÍîT«æDâ®Ýh4v$&>rNFf&¢†èô–…‚Âd2µj–L:øó!òóóMJkŽ …‚:>>|µujµšC‡}“ú~õÊä‡![¶ïH`ßþîȺ¸UD„‡‰Aƒ@ ‚§‚xã ÐÃßÏ??ŠŠŠ°´´d⸱,Ž]Ɔ/7ãëí­·²Ñ…¤‹ÄÄ® P¥¢š“ãG “E ŽJUHvöMæD/ÂÆZÎÀˆ~¼Ù®-ÇeÞÂŬ۰‰à är9–÷oÈÕj5iééf}ý0®..„v fĨ1T}©*>Þ^ºÕ›Œ!åŸãFÀâØeô šSU>š8{{û²¸aЖ¡!„õîU¦¼@ LÁbÏžÄâF^/o;/(………÷ìÃîí[ô%@ ó9yò•*;>Vo/OÉüÄT%Á3çô™3ºi7»÷îã•úõÅ A à9GÜ­ ž9W®\eú¬¹Ô¬YƒQ#‡—³E@ cˆƒà™ع;•·@ ‚Ç@LU<׆ôàÆ,“ÏgÄûœs†¡#ß'¨{/†Ž|Ÿ IÉ&åiO»0§(T©è1€Yó¢9vâ·§`™4åÙ~> žT_l ÿ+íü=žç¶ÕÄÀAð¯¢mëV&/·*Å'S&“¸u3‰[7#—ˉž=‹Ä­›Ù²iý°Ò™‰V«-o³ôÊL«Õbe%cÚGùbõJ¼<=ùxú ݦŽK—¯ÀÞΞ/×}N»Ö­˜1gj¦L2C¶H¥»{÷.3æÌ#( 3Û¿ÚHû7Ú1}öl4&äù ’ÙÓ§Q«f Ý9ÅÅÅ„öÇÆÆ†¹3§?¶lÕò¥7+”Ò·jùR^{õUÝñêÕœÈÉÉáV~>•+KÆÀÜzf¬ )­Ìär9Ã߬“÷èÖí ‰ÜT*±³³ãè±Ä,œ½½==BCØ´y gÏžÇÇÇË,Ù+þ~m)T© ¦³··åÎí;tê@HpWÖÆoàbJ u|| æ™›—ÇБ0iÜüêÕE™“Ë»#ßçãÉHM½föuT}@Rr2ÞžžÜºu ­V«kÏ”Êb?ýŒßOŸÆÞΞ¨·вy3Àp[þÇé3,ˆY«?ÓéŸ=oµèÓ³‡džRHÕ]©¶@JŸTYKñ4â Õ7êSµó¢†ðá˜Qº·ó=Âû±pîljÖ(iW ]ïæÆÈ¾Žv$î"õÚu‚:ëµwÏ3⃠\øãôfÎÏ ¡ÃHÏÈ S‡öhµZfÍ[@Ç·Þ`ËÆx (**Ò¥‰ß¸ _o¶nZÏÚ•+hØÀ_' íL|ÜjjÖ¬©§§¸¸˜YóæÓþÍ7غi=ÅÅÅzyBɆs©©×ÌÚpnÿ?2wæt¾Šÿ‚~á½°·³cæ´)lÿj#a½z1å“™êÒ¤§§³dÁn5ñq«ñõ)é€çD/ÄÇÇ›¯7ÆÓ¿oŸÌšÃí;ú¾”f§ÜZΚ±þõW:¶‹îݺrààOF}NM½FzFmZ¶ VÍš7áµ¾±8˜ëƒ¡¸ûúx·šî!Ýx³]]™Ý4œ9{žÏ–Æ0iüVÅ}®³ÇÜr±²´äƒÃëÝw77=yiú¤ì4Ňüü;,Y0éO!&v¹n™c0^¯>‚B¡À¯^]£²:¾Þ$%ëO_¹ví:mm©W××,™ÔçRú¿(†ÐÞáLžú íÚ´1:hóê™)mH—'@ò¥^ªR…ƒé陨5j¸¹ñùºxnåçãââBêµT³eR¶˜šîA®_O“ÌÓ¡reFFôâ%²dÙ2:uÔ 6Ì¹Žžupôø Bz…±ruÛvì¤ïÀAdeeÒ+Œä”KÌœ76®cÊÄñÄÄ.##óþô’ÒÚòú~uQ©T$§\àîÝ»=q‚V-šÍÓÆê®¡¶À>ce-ÅÓê‹ õ†úTSÚHc”Ö¯HÅÈX;oŒ‡¯£æM›P±BÆOúˆ1&qðç_J}ƒø Iã׉ÿ|5Î.Î|½1žV-›3yÂ8¶oÞ„-gÏžcpä@¬­­yÙÃF¯6Ò»ÆJkË---iÙ¢9‡àä©ßquqÆÍÕ•´ôt£y–†)u·´¶@JŸ±²–âiõņúFc}jYxøz7#cí¼1¾Žª991h`Ö¯]CH× ¾Ûÿ¢³gï·OÄ¿§˜ª$x¦äää¢T*iÞ´)žØÚÚêd¹y988TFfe@GG,,,tòÈýù|Ýz†µÜš¨·ЦUKi}¹úy* ½<ËŠ‹³ó#Ç>Âæ-ÛPæ”4z·òo£V«ur{;;Ýß2™wï–tÐæù—‹ÌÊŠÊ•+éŽ9*Q*õoJK³ÓÒÒR÷⦅Fc|žøÑãÇyµa^mÐsç¡V«%7ñ“ŠCY|( •+Ý×gm-×ÅÁP¹tlO›–Þ ZÛX—I_Y¨do¯÷·)?€óIIܸ‘EëRü)MV·Ž/qëâ9wþ/½ô¿ýßïdggQÇ×—:>ÞfɤÒw{{{ìííéÛ§}ú¿GmÉ9ÃæÖ3SÚ©ò,T©˜:cßzK'¯P¡Z­¹\ÎÆ/â¸s§€ŠlÌ–IÙ"•ÎÚښɎcÅÊÕ¬Š[KÓÿüÏ—õÛf)ÿ:ubGâ.Þþ.Vÿ”˜×¾<ë> Gx?T…*4Z-}"r§ €ÃGŽÚí/^ùgлïÒåUX¨âeZºÿjË[·hÁ’åËÑŸ9¬{ÛùÏŠERy–îƒñº[Z[ ¥ÏXYôê7€œÚ”ÏW®ÀÍÕxz}±¡ò4Ö§–…‡¯wscd RבÌÊ Úµðôô 9%Ť·På…8ž)mZµ¤ñkØà ‹–.C«ÕÒ©C{Þl×…ƒ‚Û·xõzû¶îCB€—ªTaÌû#8øó/,\²”Ö-[H rsóPk4Ȭ¬PæäêåYV,,¬ôþ¯Tæ07z!ógÏ¢Ž¯7¡}úR\l|°bž¨5òòné:eŽG…BÒNC+›üÛ·9{ögÏç˯·PTTÄþ©›Ë*—ËÐjôó‘ŠÃ“öáA,-Œûd ÅÅÅìþæ[6~µùÙë¯5bÒø±eÊ_ÊN)Ùƒo-”J% ¾5ضc'AKì•&óöö⯿þæä©S¼Ý¿{öî#ÿö-Þîß /OO³dRHé{ ´Z W¯¦JÌ­g¦´!†ÊS­V3}Ö\¼<_¦oŸû+²9;WCfeÅ•ÔT¼==Q«Õ¤¥§ãîæn¶LÊcéüýüX³(y;ѧÿÛxzxõO£Ñ°86–nA]X·aM›ü‡*ŽŽcð`¹j'že°eãz–®øŒzu|qwsç‹õë™õÉT ä›‚涬Y±ì±6Õ÷«K~þm.]¾Ì¯Ç޳dÁ<ª9U5šgéåb¼î–ÖHéûûÊUɲؼþ‹Rm|–}±)}ª¡6ÒÚZŽZS2À(..æÎ‚GÎyøz7%FæRÚuT¨Rqè—Ãìýî{®_O£sÇöÄ.Š–œÊYÞˆ©J‚gŽ­­-AY¹l ï ÊÅä–._N w7=~€ýè¥;rô¨nÞ¦LfEÅ Œ^Ø5k¸S­š‰»v£ÑhØ‘˜øÈ9™™ ˆ¢Ó[ ÉdÔªYÒ!üùùùù&¥5Ç?…BA¾Úº µZÍ¡ÃGÈξI}¿zesÄ'~;I¥Ê•صíkvoßÂîí[húŸÆüzôþt%w77þ¾z•ܼ<Ý1©8ce-ÅÓê‹KÔ>ÕPéæêƹs%ûç:|Ĥ·e‰»†®£í;Ø·ÿ‚»².náaÏõ ÄA9ãï燿ŸEEEXZZ2qÜXÇ.c×›ñõöÖ™_HºHLì U*ª991~ô:YÔÐá¨T…dgßdNô"l¬å ŒèÇ›íÚ2qÜXæ-\̺ › B.—cay¿‘»÷ôÍœ£ÆÕÅ…ÐnÁŒ5†ª/UÅÇÛËä%é¤ü“bÜèX»ŒáTsªÊGÇcÿÀkë'ɱ'hÞ´‰n@‹fMÙðÕfÞò_ džhçí5FÃÆuŸcog's}Š;@ËÍÙà a"‘YY±æ³åõá܃ØÚÚêMßx’HÙ)%«[LJþƒcmcͨ÷†ëu8†êu®ݴkÓJoÚƒ)²:u|ÉÌÌD&“áW¯.¹y¹º•ÆÌ•IÙi(L&ãç_ްvýFŠŠŠ¨YÃ)?Ô›kl(Osê™………dÝ5TfÙÙ79øó!ütÿ£à¥ àëãÅð¡ï0wÁ"zöí«‹3M¯kïÌ•IÅO*Ý©?þdÓæ¯)º{—Ư7b¸û+mÊó|R;wífÅÒÅ„õîÉûcÆ‘°k·Þ”CHµϪÐh4ddfàææÊ—[¶Òô?õlœ4~Ë?[EÄÛQh4¼½½xoØP£¾´nÙ‚±öðvDßÇÊÓP¹«»†ÚCúŒõ·R<­¾¸4LéS µ‘}ûôdvôB~=vœF¯6,õº(²Ä݆®£¡!„õ.¿ý¡ÌÁbÏžÄâF]ªO ø7RXXHpÏ>ìÞ¾ÅäFRðäq(;á"ùxÒDÝêZ¦PPXHDd æÌ~dé?)™à>ÖÝ"µú¹)³§¿cø7¶=æ´åÍ¿1†(ÏëèäÉTªlxú`ix{y”Oeö4lyžü+ ¢íy>xQãðo¹Žîñï˜à…çÊ•«LŸ5€š5k0jäðr¶èÅDÄ¡üqwwÓÍ]Ù‹Ž¡ºû<•ÙÓ°åyò¯,ˆ¶çùàEÿå:º‡˜ª$@ üËS•/!=¸ñϺʦðΈ÷9yêÔS´¨|Iܳ—žá„ô ãÜù åmŽ·ïÜ!"2ŠžáŒ™0ñ™ê~ܸˆòLËïYëžWʳeG ÿ*úôìN ·²ï*Mòóó éFzÆ£› IÉÊ›é³çÒ;bA¡=yoÌ8Î'%•·I€~™i4b–-'|à ºvïÅè'ré²îÜ›J%ã'O¡KhOÁù M’øé'†0šÎÁ¡’õâáøI¥“Ò'•§9”g;ñ$1%I/Ò1¨¾|tÿCªTô‰À¬yÑ;ñP¶öåÏó5-øßA ÿ*Ú¶neòò§R|2e2‰[7“¸u3r¹œèÙ³Hܺ™-›Ö?+Í#7/mq1ÎÎÕË͆{üzü8NNUùõØqã'?žTÜ_4öìÝG£W–ºq𔬼 èÔ˜èùlXÇ[o¼Á”Of¢Õßõüió`™iµZ¬¬dLûh"_¬^‰—§'OŸ¡Ûç`éòØÛÙóåºÏi׺3æÌAýÏ:ôR²Ê•*Ñ·O/Ú¶–Þ©÷áøI¥“Ò'•狌±8hµZ>[GíZ·ãð¥K—ñô,Ù|/éb2Þ^On¥$?Á“@|-(W”Êö|»›7³ñîP.&'³ &–´ôtz„ë{=-‹—pùï+Èd2‚:Þ€m;ؾ3‘ìì›Ì˜6E·‹1@Ê¥ËÌ_CZz:]Ø–°“µ+?ÕÝhfdf2aÊT† ФIã’ï}d2™Þjr¹Loíy äu{h(»¾ÙKn^. ý¨:Q IDATý_a¸ÑlÞº]{ö’›—‡›« C¢"iàï”LYyëvÿí$YÙÙ„õêIP@'£þ™0‘ëiÀä?Ä×Ç‹´ôtÆÄ’œ’‚““ÃÞLÿú’v6~½ÛQææâ_¯jšÓgÎ2sêG&uTÇŽÿF¿°>ÄÄ.G™“‹£ ;KÅÁ&Œm0î’’™1g·oßA­Ñpâ·“¬\«ÛáÔ0;zùù·y»Aøþ‡e*)JÓ'eç•+©’>„ˆ¤Y“Æ\L¹LAÁúöéMÛÖ­túJ«×¢Ñhع{7ÆŽ1* ÉìéÓôV)..&´w8666Ì9ý±e«–/5¸Ñ‘”¾UË—òÚÝU¯æDNN·òóq¨\Y2æÖ3cmHie&—ËþÎ`¼G·nlOHä¦R‰G fá|ìííé¦Í[8{ö<>>^e¯øûé|?qòZmé–[Cé U*I}†òÌÍËcèȘ4n ~õê¢ÌÉåÝ‘ïóñä ¤¦^{*בR™Cì§ŸñûéÓØÛÙõöZ6o&iKv}³—m ;ÉξIíZ5™<~œI ¥3‡o¾ÝG:uHM½¦w\ªHJNÆÛÓ“[·n¡ÕjMj[MááøíHÜEêµëtþ׬ö#x6ˆ7‚ráÓg˜9w>ƒ†#=#ƒNÚ£Õj™5oßzƒ-ã)(( ¨¨H—&~ã&|}¼Ùºi=kW® a,´[0ñq«©Y³¦žžââbfÍ›Oû7ß`ë¦õëå %›E¥¦^3k¸ý?þÈÜ™Óù*þ ú…÷ÀÞÎŽ™Ó¦°ý«„õêÅ”OfRXX¨K“žžÎ’ó˜5ícV¾Vg”ѳg3.r¹œø¸ÕÄÇ­Ö­Ù='z!>>Þ|½1žþ}ÃødÖnßÑ÷¥4;åÖrÖ¬ˆåð¯¿Ò±ý[tï֕©©×HÏÈ MËÔªY“ãÿ¼J—ÂXÌõÁPÜ}}¼ˆ[M÷n¼Ù®®ÌÜüíÌÙó|¶4†Iãǰ*îs=æ–‹•¥%Œ@X︻鯢Qš>);Mñ!?ÿKÌcúÇSˆ‰]®[êŒ×ëC‡ P(ð«Wר¬Ž¯7IÉÉzç\»vж¶Ô«ëk–LjwT)}÷ÒÍ_Chïp&Oý„vmÚ4€yõÌ”6¤Ë ùR /U©‚ÂÁôôLÔ 5ÜÜø|]<·òóqqq!õZª¤ÌTŒÙò ¦ê{8O‡Ê•5rÑ‹—PXXÈ’eËèÔQw£nîu$ÅÌyóqpp`ãÚ8¦LOLì2223Úrðç_øró×L7–m_n`Ѐ&í&onºÜ¼<¶ïL$¼wÏRå¥õGŸ ¤W+WDZmÇNúDVV!½ÂHN¹d´}1ÆÃñkÞ´ +T`ü¤3aþ¥Ô·LÁȃà™òûéÓ 6‚•qq4xÅŸõq«ýÞ¼½¼¸rõYYYu D.—Ó#4D/­¥…%7²²ÉÊÊÆÖÖ??Zîsåj*™™7 D&“=òÀÍÕ•}»h׺õcûÓ-(P÷D¨†{Éû€N©á¥%­Z4ÃZ.ÓÛ®¾m›=µkÕÄ‚ì›J³ýËÉÉáÜù ôîŠL&£eóf8*8ýç£vº8W§bÅŠ( ÜÝ\©^­Êœ£:;N]__lmmyíÕ=n|º’TÊâCY ìLÅŠuOïÅÁP¹lÙž@ð~üfÏ_”ì0ܹcš5i¢·“±”¾²ðF»68W¯†·î­¯×ÛvÜÕ$Y_’“SP«Õô àò_q1%™º¾>fˤ0%ÝÐÿbé¢h"ÂÃhÛº…Ѳ2·ž™Ò†+ϼ¼[¬Xµ†¡ƒ£°²²¢°°KKKÔj5ßí?@ZZ:¶¶)(TIÊLEÊ–‡1U_iy¾Þ¨_kĨñȾ™£w£lnûbˆ´ôtΞ=ÇàÈX[[ó²‡^m¤«óR¶ìÛÿ=¡Á]ñ|Ù+++^ñ÷3iʧ¹éâÖ®#¤kvvš/­hÒøuâ?_³‹3_oŒ§UËæLž0Ží›7áåù²ÑöÅǯš“ƒögýÚ5„t â»ý?0 j0{ö~ûXù ^<ÄT%Á3%''¥RIó¦MñôðÀö†57/‡ÊȬ¬¨â舅Åýíè#öçóuë>j4Örk¢Þ@›VÒó|srõóT(zy–gçGŽ:|„Í[¶¡Ì)¹1¼•µZ­“ÛÛÙéþ–ɬ¸{·¤ƒ6Ï¿\dVVzÛØ;*Q*õoJK³ÓÒÒR÷⦅Fc|žøÑãÇyµa^mÐsç¡V«%7ò‘ŠCY|( •+Ý×gm-×ÅÁP¹tlO›–Þ ZÛX—I_Y¨do¯÷·©7fç“’¸q#‹Ö¥øSš¬n_âÖÅsîü^zé%~û¿ßÉÎÎ¢Ž¯/u|¼Í’I!¥ïöööØÛÛÓ·O/úôÚ’s·Í­g¦´!RåY¨R1uÆ,:¾õ–N^¡B´Z-r¹œ_ÄpçN+ØHÊLAÊ–Ò0EŸTž:±#qï«ÊÌo_zõ@Î?uùó•+psu%óŸÕõÞ}”î¼ÂB/{Üÿ†À-7ndQ½ºáþÒô™’®4.$%sáb2#‡ 5xNi}@ð~¨ Uh´ZúD äNA‡%´Û_º)«æ"?™•µkáééArJ ™âÃi4bà x¦´iՒƯ5bÿƒ,Zº ­VK§íy³][ nß¾?m ÿöm݇„/U©Â˜÷G%¯.YJë–-$ ¹¹y¨5dVV(srõò,+VzÿW*s˜½ù³gQÇ×€Ð>}).6>X1Ï?Ô yy·t7DÊ%Ž …¤†0V6ù·osöì9Ξ;Ï—_o ¨¨ˆ?þüS÷}\.C«ÑÏG*OÚ‡±´0î“)³û›oÙøÕ£«£¼þZ#&[¦ü¥ì”’=øÖB©T¢0q>ô¶; ì\ê`¯4™··ýõ7'Oâíþýسwù·oñvÿfxyzš%“BJßÃXXX Õj¸z5Urà`n=3¥ 1Tžjµšé³æâåù2}ûÜ_‘ÍÙ¹2++®¤¦âíé‰Z­&-=w7wI™)HŶ4LÑg(OFÃâØXºua݆M4mòª8^3þÁr+­¸Çæõ_£^_ÜÝÜùbýzf}2õ±t¢´øªTúå0{¿ûžë×Óèܱ=±‹¢%§  ¦* Ê[[[‚;³rÙÞ>”‹É),]¾œîn8:*8züûÐKwäèQÝn™ÌŠŠ*}{P³†;Õª9‘¸k7†‰‰œ“‘™É€¨!:½e¡ °™LF­š%îÁŸ‘ŸŸoRZsüS(Ôññá«­ÛP«Õ:|„ìì›Ô÷«W6G pâ·“Tª\‰]Û¾f÷ö-ìÞ¾…¦ÿi̯GïOWrwsãï«WÉÍËÓ“ŠÃÓô¡J•*\¹’jÒ¼dcvîÈÊeKù :ä©Ú)%Kܽ‡‚‚Ξ;OÒÅd^à£aCõúÆ,ŽøÀNÎΠ¬‚ ®..üøÓ!^{µ!jµš¿þ¾Š·§§Ù2);¥Ò]OKcç®Ý¤gd““ÃÚøõ¨TwyùåÚ’yš[ÏŒµ!†ÊL«Õ2wÁ"ìíìˆØ•J…J¥¢¸¸˜ 664ùÏë|½u;wîÜa[ÂN*U²§^]_I”Ü «T*еZ´-*•JW7¤bk(1}RynÚ¼;[;Þò_;wbÁâ“ꥵR¸8;S»V ¾X¿B•Š¢¢"Nž:¥›*eK‡7ßdkÂN.]¾ŒV«åϳgIO7> Jg¨<ƒ:±oW‚î׬IôëkÒÃ…‹É)øz{“tñ"ÞÞOf5%CñÛ¾#}û ¸K ëâV& “o势Ÿþ~~aiiÉÄqcY»Œ _nÆ×Û[ï É…¤‹ÄÄ® P¥¢š“ãG “E ŽJUHvöMæD/ÂÆZÎÀˆ~¼Ù®-ÇeÞÂŬ۰‰à är9–÷oÈï=a3çãè‡quq!´[0#F¡êKUññö2y™P)ÿ¤7úÇ.£GxÕœªòÑÄñØ?0…åIrìÄ š7m¢›†Ð¢YS6|µ™w‡ü(™Þ¹C{F A£Ñ°qÝçØÛÙIÆÁ\¤âвEsö8HØ€HdVV¬ùl¹ÞÇŃ­­­ÞÔº'‰”R²ºu|è?h0Ö6ÖŒzo¸^Ço¨^'ìÚM»6­ô¦ì˜"«SÇ—ÌÌLd2~õê’›—«[iÌ\™”†ÒÉd2~þåk×o¤¨¨ˆš5Ü™2ñC½9߆ò4§žYXXHÖ]Ce–}“ƒ?JÖü¿ÇÒ… ðõñbøÐw˜»`=ûöÇÕÅ™&Œ×µwR²]ß|òOWéòûfßwŒxw(A$ã'•NJŸ¡<Ï'%±s×nV,] @Xïž¼?f »vëMÅ1„¡vBŠIãDZü³UD¼…F£ÁÛÛ‹÷† •´¥[PÚ´jInn3æÌ#+û&µk™t#/•Nª<ÍA£Ñ‘™››+_nÙJÓÿ46+Ÿ‡1¿¡!„õ.¿}‰ÿ»XìÙ“XܨѣKõ ÿF îÙ‡ÝÛ·˜ü*_ðäq(;á"ùxÒDÝêZ¦PPXHDd æÌ~d F)™à>ÖÝ"µú¹)³§?Q'þ·ñœk.5kÖ`ÔÈáålÑ‹‰ˆCùãî{éë¿KÉ^t ÕÝç©Ìž†-Ï“‚ÇGÄOð4S•@ ‚bª’à¹&0¤7þYkÛÞñ>'OzŠ•/‰{öÒ3<‚^aœ;¡¼Í@ (bª’Àdòóó‰ˆü/+–ÆH®—n*}zv§†‰k“K1qÊTNœü¿GŽW°±açÖG×Ýh4V®^êå±&í4*@ð¼#“Ù³w^møD m[·z"ù|2e²nóÐ>}™ýÉ4|}¼žèÑKn^Úâb1h@ð¯A z(•9ìùv7of3âÝ¡ºã†»w3aìݱ‹ÉÉ,ˆ‰%-=!Ázù\OKcÁâ%\þû 2™Œ €ÎD„÷`ÛŽ¶ïL$;û&3¦MÑí8 ré2óÅ–žN×À¶%ìdíÊOu{!ddf2aÊT† ФIã’osd2™ÞêCâž=üö§hѬ)];ëÉ>‚B¡À¯^] dWÔYóÐ5°3]:óùºxŠŠŠtçÇoÜ„¯7ÑsfQPP@ÊåË:Yh·`B»óΈ÷õt3kÞ|:u"¸Kkã7èå %;¥¦^3k³¶ý?þÈÜ™ÓqT8p55{;;fN›‚›«+¿9Ê”OfòUüZ*ü³ÁVzz:KÌ㯿¯ðÞ˜qtjÿ&r¹\Ò¿èÙ³¸q#‹ƒß!>nµž s¢RßÏÙÓ§òë±ã|2këÖ¬ÄîŶóBÒEäÖrÖ¬ˆ¥Wßþ|4ñC|¼½8pð'¼½¼ ÚÒ¼iwÃøIáîîFP@-š7EfeõØe'@ >Ž~ÁùýôiÁʸ8¼âÏú¸UŒ~oÞ^ú›JmKØIhpWÝÿ¯\½FVVA]‘Ëåô Ñ;ßÒÂ’YÙdeeckk‹¿ŸŸQ[®\M%3óÁAÈd²GÞb¸¹º²oWíZ·~l_»â¨pJv-èÔ‘îîXZZÒªE3¬å2®§¥éÒ´mS¢§v­šXXX}Si¶999œ;ÞÝC‘Éd´lÞ G…§ÿ)‰7²hݲ…îXn^•uO¯«8:ê}S9°?666 5šˆÈÿrðçCÆmÉÕÏS¡P<Ñï\œ9vèðFŽKDd‘QÜÊ¿Z­ÖÉííìtËdVܽ«Ìõ/™••+WÒsT8¢T*Úiii©û÷ÞO£Ñšd‹ÌÊ Úµðôô@£Ö‘™iÔV@ ‚‡S•^pÚ´jIã×±ÿÀA-]†V«¥S‡ö¼Ù®­î©÷¶; ì¬÷ÂAÁíÛ÷§ åß¾Mqq±îÿ/U©Â˜÷Gpðç_X¸d)­[¶(äææ¡ÖhYY¡ÌÉÕ˳¬XXèOÑQ*s˜½ù³gQÇ×(ù¸º¸Øø`Å<ÿPk4äåÝÒ ”9J I; q¯l Ù¢º{—C¿fïwßsýz;¶'vQ4U«V5)@ ‚oØÚÚØ™•Ë–ðÞð¡\LNaéòåܸ‘ű¿Ø©“^šîn8:*8züûГ9z”ì›7’'õ+T0úö f wªUs"q×n4 ;9'#3“QCtzËBAa2™ŒZ5K¦üùùùù&¥5Ç?…BA¾Úº µZÍ¡ÃGÈξI}¿zeòÃ-Ûw$°oÿw d]Ü*"ÂÃÄ A Ùˆ7=üýüð÷óÓ}”œ°k7íÚ´Ò›^%Sf&ŽËâØelør3¾ÞÞzo$.$]$&v…*Õœœ?ú,jèpTªB²³o2'z6ÖrFôãÍvm™8n,ó.f݆M„!—˱°¼C®V«IKO7ëãè‡quq!´[0#F¡êKUññöÒ­Þd )ÿ¤7úÇ.£GxÕœªòÑÄñØÛÛ—Å ƒ¶ô !¬w¯2å-@p‹={‹5z½¼í<‡Å‚9³Ëe9ÏÂÂB‚{öa÷ö-zƒ@ 4'Ož ReÇÇJãíå)™Ÿ˜ª$0HvV6ýÃßé áô™3ºi7»÷îã•úõÅ A à9@Ü‘ âî»Û3ÕyåÊU¦Ïš @Íš55rø3Õ/@ (1pÿïsòÔ©§hQùê{V ˆ¹óÊœÏãÆïanß¹CDd=Ã#3ab™íy7¶OªÌžW}ÁóJy¶‹¸n‚ÇC O•>=»SÃͽÌù\HJ¦C—`:t ¦W¿D/^BNnîSÓg ¾ÜL̲åzÇâ7n¢C—`Nýñùùù‡2yêt³ó,/þïÔïtè̦Í_ëŽÙÙÚ·šï¾óÌí1ÛgYfj†]Jv49j,¿Ÿ>mrZ);Ÿ¦ùùù„ô #=ãÑÿ¤dåÍôÙsé1€ Ðž¼7fç“’ÊÛ$@¿Ì4 1Ë–>p]»÷bô‡H¹tYwîM¥’ñ“§Ð%´'ƒ‡àü…‹&ÉüôÃ?MçàPÉzñpü¤ò¼GÒÅ‹t êÆ†/7›”§9”W;Q¨RÑ'b ³æEsìÄoOE±¸5V×_uèÌœè…:ÙÝ»wYºâ3º‡õ£k÷^D/^¢“•¥}¹Çó|M þˆƒà©Ò¶u+“—85…]Û¾fþ¬äݺŔi3žº>spwså§C‡øåȯ8;W/W{Ìå×ãÇqrªÊ¯ÇŽ—·)ÀóÛÿEöìÝG£Wâ\½ÚcÉÊ›€Nˆ‰žÏ†µq¼õÆLùd&Z­¶¼ÍÒ+3­V‹••ŒiMä‹Õ+ñòôäãé3t›3.]¾{;{¾\÷9íZ·bÆœ9¨5£²Ê•*Ñ·O/Ú¶ni²-ÆòÐjµ|¶:ŽÚµj™œçÿ—.]ÆÓÓ€¤‹Éx{y==Æâ0eâ‡$nÝLâÖͺ :Ö¬]Ç…¤$æÌ˜ÆÚÕŸÑáÍ7Ÿ¨mÿËñüo ¾qè¡Tæ°çÛ}ܼ™Íˆw‡r19™1±¤¥§Ó#$XïÜëii,X¼„Ë_A&“Йˆð>lÛ‘Àö‰dgßdÆ´)4jØP—.åÒeæ/Š!-=®lKØÉÚ•Ÿên 323™0e*CEÒ¤±þRÁÖÖÖÔªYƒG@Ͼý9wþuëøš­O©Ì!öÓÏøýôiìíì‰z{-›7“ôïBR23æÌáöí;¨5Nüv€•Ëc’½0Μ=‡V«å§_~¡eóæ\ºü—®|KÓ'•gÅ 8õÇÌŽ^@~þmÞîAP@É· iéé,Œ‰%9%'''†½3˜þõ$ãgŒcÇ£_Xbb—£ÌÉÕí$.…TYKÙ ”Lm e×7{ÉÍË¥¡ÿ+L7Ú`lÍ-³ï8Àö„D”¹¹ø×«‡Z£æô™³ÌœúQ™o4JÓ'eç•+©’>„ˆ¤Y“Æ\L¹LAÁúöéMÛÖ­tú¤®(y2ºs÷n&ŒcT> ’ÙÓ§é­¢V\\Lhïplll˜;súcËV-_jpÓA)}«–/åµW_Õ¯^͉œœnåçãP¹²d Ì­gÆÚ¥ÒÊL.—3üÁ:ynÝØžÈM¥;;;Ž;AÌÂùØÛÛÓ#4„M›·pöìy||¼ Ê^ñ÷Óù~âä)´Úû7þò°-…*•džß|»:uêšzͤn5ÝCºñf»6ÄÇ­&>nµîfÕÒÊ’ºu|8zü8·òòqyàƒ!}Æò8sö<Ÿ-aÒø1¬Šû\çÇœè…øøxóõÆxú÷ ã“Ys¸}çŽÑøI‘šzôŒ Ú´lA­š59nÂ+cemÈÎÙÿãÌ9¯â¿ _xoÀplËRfrk9kVÄrø×_éØþ-ºwëʃ?õÑÊÒ’F  ¬wÜÝôW+MŸ”¦øŸ‡% æ1ýã)ÄÄ.×-W Ư•C‡ P(ð«Wר¬Ž¯7IÉÉzç\»vж¶Ô«ëk–Lj§r)}÷ÒÍ_Chïp&Oý„vmÚ4€yõÌ”v ¤Ë ùR /U©‚ÂÁôôLÔ 5ÜÜø|]<·òóqqq!õZª¤ÌT¶ÅXž¹yylß™Hxïž&çéP¹2£F#zñ Y²l:RÇÇ0ÿ:’jwK|IgÉ‚yÌšö1«?_«‹ÅÁŸáËÍ_3iÜX¶}¹A Ñh8zü!½ÂX¹:Žm;vÒwà ²²²éFrÊ%ɾ ·övvÌœ6…í_m$¬W/¦|2“ÂÂÂGüy0î÷XöégDDF1}öÒÒÓ’A-ÅÅþõ¡}ú2ð¿ïðýtiŒµ/Æx8~Í›6¡b… ŒŸôc&LâàÏ¿è½ÌA ^p~?}šÁÃF°2.ޝø³>n£ß·—W®^#++‹ .Èårz„†è¥µ´°äFV6YYÙØÚÚâïçgTß•«©dfÞ 8(™LVêSp7WWöíJ ]ëÖ’yU¨P‘Ûwn›­/-=³gÏ18r ÖÖÖ¼ìáA£Wéžúšãß=ZµhÎÒ+iÖ¤±ÉúŒØ™Š+êžFfßT’““ùóèÝ=™LFËæÍpT8púÏ3Fã'Å‘cÇ©ëë‹­­-¯½Ú€£ÇOW’*k);¤[P î)a ÷²«RZ™¸8W§bÅŠ( ÜÝ\©^­Êœ¶lO Gx¿G~³ç—ÌQ¶°° sÇ4kÒD÷tј¾²ðF»68W¯†·^}1v­lKØIhpW“du||INNA­VÓ3<‚ËýÅÅ”dêúú˜-“”Á3ëa IDATtCÿ;ˆ¥‹¢‰£mëFËÊÜzfJ»d¬<óòn±bÕ†ŽÂÊÊŠÂÂB,--Q«Õ|·ÿiiéØÚV¤ P%)3•‡m1–gÜÚu„t ÂÎÖÖä<^oԈƯ5bÔø dßÌÑxºŽ¤0¥lÛ¦¤>×®U Ýu´oÿ÷„wÅóe¬¬¬xÅßgçê4iü:ñŸ¯ÆÙÅ™¯7ÆÓªes&OÇöÍ›ðò|Ùh[nèº èÔ‘îîXZZÒªE3¬å2®§¥é¥}8îPrÓ?ýã)L< °à£iÓQk4ܾ]@‘ZMFF&×®áýáï²hé2R¯]Œ·/Æx8~Õœœ4°?ë×®!¤kßíÿQƒÙ³÷ÛÇÊW x1Ué'''¥RIó¦MñôðÀöN%7/‡ÊÈþi «8:baa¡“GìÏçëÖ3|Ôh¬åÖD½=€6­¤çäæäêç©P(ôò| °³µ3[_æ?« ½ûþ¨òTñ²G-³ý»GƒW^ÁÍÅ…V-[rúÏ?MÒgŒÊ•*éþ¶¶–s÷®ŠÂÂdVVT®|_æ¨pD©TR±bÉøIqôøq^mØ€W4dÆÜy¨ÕjÉÍø¤Ê:'7× ââìl’}¦RZ™XZZêþ½÷Óh´tlO›–Þ ZÛX—I_Y¨do¯÷·±³{œOJâÆ,Z—âOi²ºu|‰[Ϲóx饗øíÿ~';;‹:¾¾Ôññ6K&…”¾{ØÛÛcooOß>½èÓÿm!'GG“õgHÒƒœ?ÿ7çÿ¾ÀÚõHKKãÌ_éÞ/°´T‘¡ÑËXYKç“ %9•Ó23D«ÕöÓϬþ1ël3•*ú2|È çŠßX:m{ò©E||<Žf¼k°iË6üeÛÙËn›·wq®_¿ÁÉS§èÔ¾;wí&éÁ}:µ¯Nq/¯gÚfŒ±ã=M¡P‘¡!<<ÂhÇáYÛ™9ç%C噞žÎè±(îUŒ¶­[ê>/X0?*¥’›x{y‘žžNdTn®nF·™#»´‹óÒ•+üsíƒtû>r„ë7nèÚµ¡üi4¦ÏžMÓ€OX¾j ÕªV1:.ÿÉrËîäü߸tù •žxiØÐwåîÝŽ?¿_ÖÅ mËemMáB…øý¨X¡<ééé\¿Ž·—×3o3–NcánGF²mGQÑÑ$$$°tÅJÔêTŠó0ç³¶3Sç%Ce–‘‘Á„)Ó°³µ¥sÇö¨ÕjÔj5Z­–\ÖÖT­R‰õ7“œœÌ¦­ÛȓǎÒï•4º 2oÖÕj5ÚŒ 24¨Õj]Û0V†â hìÇî[uÿªW­J‡vmucíeͺ ØÚØÒ£ëçø7òcÊôfwÔ³;O˜s4¤aýúlܺ®]###ƒ¿ÎŸ'**³#qùÊUJz{séòe¼½õ_Î~–kÕÔ‡¨T*Šºgvæöí?@RR`¼ÞØ·ÿ±qqÄÆÅ1oÁ"\\\p/âF®\¹¨Xá}6mÝNzz:çÿ¾ÀÕ®Q¦´ùÃ`³c¨þ6oÙÊî=¿ø‰?Ë/ $¸tÄs“'BOYÊúø––†……Ãbúì9¬Z»Ž’ÞÞz¿f\¼t™³ç‘¢V“ßÅ…!¾Ðm íÞ µ:…ØØ8ÆOž†µ•%CÚQ¿^]† ÄÄ©ÓY¾j Ͱ´´Dañø$þè—²ì^øü$¨ŽT®T‘þ}zéNþÏz¼áC3÷‡„t E£Ñàí]œ¾ÿ>Î7–?€š5>`ÏÞ}´éЕRÉ¢LÏÃoìx†â|òEÙì ðÓgÏ¡ypù]œ1lvÿo1V†=~œªUÕ C¨Q½«~\G®Ÿ™ãÂ5l@ÇЮh4V/_‚­­Ñ²6–NcŒÕí³–™!666zÃõ^$cé4¶í½R%hÿY¬¬­èß·—Þ…ßÐweëŽ0êÕ©¥7dÇœm¥J•äÎ;¨T*|J¿Gâ½DÝÌ0ϺÍX: …S©Tì?x˜¥+W“––†{7FûRoÌ·¡8Ÿ¥) £m×P™ÅÆÆ±oÿ sí…GfMBÉÅéÕ½¦L£EÛö.TC‡è¾ƒÆ¶íøé'æ|¿@ßO»¡wî4ö3ZÆâ4ÆPœ.]bÛŽ0æÍš@›V-è7p0[w„é '2ÄÐyÂÔyÐ:µj’˜xïÆO$&6O¢ 2FCôh\] ³vÃFªU©¬ÎÔ¹<;… "¨i ½ûÄ9Ÿ3%¼‹ëfo2VïÎÎyY·qS¦ÏÄB©ä½’%ùväWXZZзWO&O›I³–mpÊëĽ{>÷0%Cõ×<¨mZ~"ijPìܹ]ëë›u?!^…””[´&ló³.pÿµã½Ë¤¬Ÿ_p‡Î|=|%K˜?MìÔB:‡2eü¸,S0Û&{²í¦¥§¿1eö2êOÚěԟ0æäÉãä±ÏÙ”¿ÞŽ n;yò¸ U¯ÞÙsçtÃví¦\™2/õÆòUï]&eýúÅÆÄÒ>88Û›cÛÞu†Úî›Tf/#-oRþDÎIý‰WM®èâ•»y3œÑc'àî^„þ}z½UÇ{—IY¿~nn®¸¹e?ÿ»±mï:Cm÷M*³—‘–7)"ç¤þÄ«&C•„B!„xËÈP%ñÚù7kÎÝçá6G·Þý8yêÔKLÑë=Þ‹ò 9™Î¡´aàÐa¯;9:«Ö®cÆã/}ç´M¼ æ¤ó¿¨ChWþ¾p1Çá¦Î˜ÍŠÕk^BвzmwûÎ]´¡YË6Ï”ßÿrœÿU/ëœõ*Ûnvä÷r¼©×8‘s2TI¼T­[|J3ç&7æâ¥+ôî?Èœr±J¥Š„vꀣƒþœö/êxæXµv1±1ôíÙC÷ÙÂ%ˈ¸u›Q_ Õ}¶nÃ&Žž8Áäqc ÆekcÊŠù߃l {©éó˳’¯/)ê‡fÇ{áÒ%fÏûðˆ[äË——Ï;uÐ[ 鑤¤$B:μY3(X ?»õ "â¹P]­5èÚ •JÅÙsç˜ûÃ"nÝÆÍµ0ýzõÊò²ðÓé\±z §Ïþ¥Wæ –,ãÊÕ«Løî[³óó´ÁÃGPÙ×—Ÿš¿ 7@Ÿþƒ¸pé’îïëÖáË™ `ËûË0zÜþ:žäÉ+æI÷.¡”*ñxµæìÚµ±mÆÚî°‘£8~òÏ,ñä²¶fÛÆÌõ24 ó.bÁÜÙ,˜uq¯gaNœO¶Á¤¤$zôý‚¶­[Ò¡][ ³®kÕ¨A@c¿çJçº ›ØFBâ=ŠºaÈ€/p/b|<ú“mâ‘¡ƒqËh»6ô]¹rõzôÍ:“ИQ_³yÛ6ƒu´~õ >Íœ•Ç‚‚ ܪ ?ªoòœŤ©3¸xù2 ä§O÷n”¿œn{·ÞýøçÚ5ü4 ßW3¬ñésÏó’kÜ›y/tÄKU·v­ߎM뉌ŠfѲåŒüæ;fNôR—S•+UdçÏ»ÉÈÈÐMgzú¯³T©Tñµ¦ËS噓Ù|ÆMœB½:µ˜9eÇOþÉ·cdzjé"ìíõöÛ¹k7¾Êë]¸¿ØŸÚ5kpíÆ ¾=–ü.ÎøóÝø‰thLÃê³mG£ÇcÙÂùz+Ø>ÎæAÍØ¹k7GŸ J¥ŠÄÄİ=l'3&OÈQ~^¤‘þ¤rE_½´CöyÏiçÄ\ýòyçNØäÎ;ùíÖ._¢7ýî‹òíȯtëµn˸o¿¡d‰âzsè'Þ»G†VûÂ: æÆùd¼’”„R©ä×ßöÒ®Më,õó¬éܹëg¶†…1lÐ@<=ŠröÜy³×Ô¿µk| ûÛÒÒujªÁvššjð»âUÌ“í/lxíúM†}=Š÷Ë•¡BùrëèÑz óçÌ¢p¡‚œ¿pá_K©’%Lv~ÆOžŠGÑ¢ŒújûöàÛ±ãY±d!¶ÿNw{Ö¬zyzÈJóàvÜ 7š‡-Ûw0ûûùܸn0^CåùÇ‘£„t%¨Up–!@†Ú„V«ånL 5>¨Ž……U*UDDFê¯ôªÑhØFP`½Ï T*Þ^^T«\‰«×®qëÉ’iì÷1*•ŠfM¸wï>—¯^5šÎܹrÑ©};/[V«eÅêµ|X·6ž$Þ»GpÇÏ8wþïÌòKH¤MûNzOB:‡²mÇNºôìC«Œ›8Åhýœ9{Ž¡ú‹Ä›8E·ò6€J¥ÂÚÚZ·N©¼?rêÌÚ‡v!¨u[¶ïÜ¥û<<"‚ÞýÒ¤y+fÌ™KËv¸rõŸ,i»péÁ?ãÂÅËT¬P‚òcoŸ‡ù]HHHà~R’Ñvml›1Oæ2W¶¶¶ÆÊÊ €C‡Ñ다¥¥éâ¼xé ÙæW¬^KŸƒ îø™^Þ#£¢4ô+šµlC—ž}8}ö/Ý6cq>’]´T©(æYŒ#ÇŽñ4Cq^¸x‘–í:èuæ-X¨[ƒaíúthŒOé÷°±±¡jåJxzxÆÏŸ™e§Ô•µµ5FÛµ±ïŠB¡Ð‹kï¾}Ô­U+++“u`a¡ÀÊÊŠòåÊáäèÀ›FëýîÝþ¾p‘àÖØÛç!À¿¹mrsüÄÉÇåmi‰µµ5 #Ö§Û®¹ž¾Ž=’]½Ë5îí¾Æ‰O:ȼñ3aŸuïITt4~ ‘‘Á؉Søø£Ù°z>$--MfÅê5”,áÍÆ5+Y:åß/«ÛÔ4‹âîî®w­VËØ‰“hPÿC6®Y‰V«Õ‹2vŠˆ¸•ípØØØàZ¸îFéY7fâ$X½t1#‡ aÆì9Dß¹c4%KgÅâ…|Ú¬)õëÕaÅâ…¬X¼Ü¹r¡R*ñ-_ŽÓg2od.]ºŒ­­îfÁÎÖ–1ߌdó«iÓ²%#¿CЉ›/S åáƒjUÉ+C†`àÐáìÛt¿v>]žÕªVÑåñIÆÚ„B¡ i“ö8DZZ‡ÁÁÁý:9pè0ŽŽŽø”~/Û´ÄÇ'pêÌ\ ^éöíH£éøèÃzXX(X¶rû¦ã¿CPìíéß§'“§Ï$%%…™sæÐØïc½á:{~ÿ cFóãŠe´ ne0-e|ÞC­VëÊ/55•#ÇSë‰_Œç|ÿ!C=n<‘QQfçýÜù ü0kÇ dÁâ%ºòž8u:•|+°ií*ò;;“%¾ËW®0z܆H©’ÞºÏ'M›AP«`¾õ-õêÔÁÁÞÞh»6¶íyL7–“&`ii©‹óɧGQQQÌœ2‘±ß|ÍÂ%Kuy?y*%Jx³~õ Ú·m÷cÇó 9Ù¬8Áplì×°Ÿv›ÎR%K’;W.ÎüÛqÑjµì?x˜zujqÿþ}¢¢£ ¶scçOc µkC}WIKKã·ß§AýÍ:Þ#†S§Ïxï>Þ^ÅŒîqëÖÖÖ8ç˧û¬PÁ‚„?5üÊCmטì®cOzºÞå÷î\ãÄ‹#‡wÜé³géÒ³7ó/æýreY¹xúöÆ»xqn†ß"&&†€Oü±´´¤yþÐ …wcb‰‰‰ÅÆÆ†²>>&w3<‚;wîàJ¥¢y³À,û¸.Ìî[©W»¶Ñ¸råÊ̓äÏ|¼È¨(Οÿ›.;beeE1OO|+øê~{–üT®X‘ÓgÎpúÌÝ/1ý>¦ˆ›ÔªQ+K·## Ee’±<äwq᳎íY¹tÍšðËžßèÚ…»~Î6.óÊÓx›ø Z~ÿß~ü›5gܤ©ôëÝ“\O¬ °ië¶,OfΞG˶íiÛé3<ŠºÓ"¨)n®®ØØÚ°ã§]¨Õj6mÝNZz:jµÚdÙXXXÐ5´3«\O«æA8::ê¶Uòõ¥rE_úJl\Á­Zd ß4À'ÇÌñÅEÜŒ)¶°° f8pè'O¦p¡‚º@›VÍýõHF}5P0â›Ñz¸ìòþH€#rçÎMÅ ˆ‹'!!‹—.óiÓ@T*AM³ 5º~ãCGŽ¢W·.Yn^»þ³¦M&$¸ uk×0U”¯MÝ:™ç¢î( ]Þÿ¾p‘VŸ¡R©¨ùAuœ8û×9³ã5Ô+Wô%<"‚;wïšׇukóûÿöpþï (-,(ý^)âÿíÈå±ËºÂ3˜>¿LŸ5—æÁítÿýúj¨]›û]9|ä(yòäá½R%ÍÎch÷^4 bÈW#iÓ²…ÉáZjµZµµ•Ù7ÙµÝ ›·ê•Ç£ã&M5z{ÒÓõ.׸wë'^ yÇá—H||<T«†—§'6ÿŽ?H¼—€ƒƒ=ªÇûæurқܹc{–,_I¯þ°²´"´SêÔªiüx‰úq:::êÅ™))±µ±}æãÝù÷Qn~ýŸˆSM1ϢϜ?ȼ)¿h †?Ïœ% qcݶ‡³nÃ&â⸟ô€ôôôäZŸ©<¨”J<=ŠâååÉ•«W‰¾s¯bY§[3§<µ‰ÉÉ|5j4]C;óQ½º\¼|™‘ߎaú¤ñºñÐ.]âîÝj×Ìz³úY§Ô¬^ÏãO++KRSÕ¤¤Î»R‰½ýãrqrt">>Þ¬8µA Ôÿ]»1;õêÔæ‹ÁCéÝ£ÿ;pzÿvvœþ½¡¿Ÿt_/­˜:¿|Ö¡=Õ«VyœÇ¼§TÌ®][YY™õ]ùù×_ùèÜ=mX8o6n®®Ü àëÑcp-\˜:µ w8­­­IMMÕûL­NÅÖ&w¶û?} È®í6þ¸u²©3+k+N>cð:öHvõ.׸·÷'^é8¼ãêÔªI劾ìÙ»i³æ‘‘_ÃÔ¯WGGìÛ©3gQ»f £'IGGѠR*‰OHÔ‹Ó\ÉÉÉܺ‰W1O£û;^~grÛØ°hÞœlÓl* ²M»‹‹3...œ¿pK—¯à[!óâŸÀ„ÉS™4n¬îÑ{Pë¶hµmi©"C“5N++KÒ5™'_­VKròC“yHQ«9pð»~ù•Û·#iôqfO›Œ³³s–1ß9)OCmâöíÛ¤¤¤à×à# eJ—ÆÍÕ•ÓgÏé:›¶l#À¿Q¶3ÙÚÚè=x¤¬sgLËÌSJ ­ÛwÂËÓx:MÑh4LŸ=›¦Ÿ°|ÕªU­B^'ý¹®Ь cÊø¼GRÒþ¹v?Žc攉Ùî§R*±P(ÈÈx\φònˆ£ƒééé$=x€­-jµšädýaÍhѬ)Ÿ÷è;ý³½ÑS(ddhÐu µkSÛ µÝÍÑÁt†{÷ßÇ'ÄënÔM1Ö5üˆ/Iæ½í^¤ò»pâÏSì?t˜1£F'O\\œ9wþïl‡Ý™:¿ä±·ÃÅÅÙ¬4dÁëØ£§†ÙÕ»\ãÞÞkœxyd¨’ÀÆÆ†ÿFÌŸ3“¾½ºsùÊUfÍK7Wœœu/píÙ»W/Üá#Gˆ‹2 Ì+—É_VÜ‹¸‘?¿ Ûw„¡Ñhز}{–}¢ïÜ¡Ch×l_KMMåÆÍpÆO™†—§§ÉÇíÆŽW¨`A<ŠaÙÊU¤¨Õ¤¥¥qòÔ)ÝcUSùË›7/7oFd;KJåJùqý&Š{yêfyøï¯ÄEÝ3‡½ìÛ€¤¤$½pn®®Ü'ñÞ=½Ï] »ò÷ß™/Ž8tX÷ ޱ¨ÎÜù Èï⬻YLHH`ßþÄÆÅǼ‹pqqѕ˳ptt¤d o6oÝFzz:›·m'##Co•R‰ýz÷dö¼yÄÅÇs;2’m;ÂˆŠŽ&!!¥+V¢V§R¬˜‡.œ±vml›¡¶û¢9::RªD ~ܸ‰ôôt:Llle|J› kNtvvÆÓà /ÜçiõêÔaÁâ¥ØÚäÖ=EhõiËV­æüßxøð!ÇŽŸäÚõë€éóKzº~;3g6&Sß•_~ÝC äÏÙÓ¥ŒŒÌqó·8uú ®®…tÛ²«wgJ•,ÉÊ5k¹ÿ>;~ÚEJŠß åùãè1vü´‹ø„D¢¢¢ùóôY<Ÿú! »¶ëßècæÏ™•å_¯î™“ºŽáz—kÜÛ/žd0sX@H§P4 ÞÞÅéÛ³»ÉüÔ¬ñ{öî£M‡Î¨”Jý0W÷²h•J¾¬ß¸‰ÎBtû.Tˆ ¦ôî?ç|Δð.žåÅ"nn4jØ€Ž¡]Ñh4¬^¾;[[Ú¶nÁ¸ÉSùãè1|+”×ú`(̓šÑ¦UK£õm¨< 1Ö&lml9l‹–.gÖ¼ïq°ÏC‡vmy¿l¶î£^ZÙÛ0æÔ™¿X³n=i©©T®äËÐÁsþi.]bÛŽ0æÍš@›V-è7p0[w„Ñ4à“á/[Áâe+tתQC¿ vÍ º“N!_XÕdd°nã&¦LŸ‰…RÉ{%Kòíȯ°´´|®| î߉S§³~Óüý|bQµr%ªV©Â´™³éÝ£ûuòµ–IDATféÊÕ¤¥¥á^đþÔý* ÆÛµ±m†ÚîË0xÀLŸ=‡æÁ!äwqfİ!ºa[ƘÛýùñÇѬ³+R¯vM.YJû¶ÁzŸø7æÁƒ|7a‰‰‰x¸»3d`æyÄÔùeÒÔéLš:]÷÷ç:šœ¢×Ôwe÷ž½´i™õ}SºôìB¡ÀÉёիҬI€n›¡z2 “¦ÍÐÍ:5nô(ìlmqqvf݆MÌ_´++KjT«F=SŒ>ÙvG=Âì!ŠO^ÇÀp½Ë5îí¿Æ‰O±sçv­¯o¥×ñŽJII!°EkÂ6oxi ^½Îãý×­X½†{÷’èÙíóçŠçaJ !C™2~EÝÏÿþ,^T:ÿËZ¶ëÀØo¾¦¸‰oÞU/³ ªÕjZ´mÏÜÓps5<Ø»H£ÑðÝø h4|3bø3÷V/ûÜcŠ\ãÄëtòäqòØ;™Þñ Þų¾ùd|2TI¼rgÏÓ= Ûµ›reʼÔÜ«>ÞÛB£Ñð×ù¿)òœÃ‚bcbiüR.Ü/2âíõ2ÛàO»¡˜§‡t²¡T*:h -›½òN¼Üz7D®qâm&-K¼r7o†3zlæ ¾îîEèß§×[u¼·AûÐ.$Ä'PƧ4 ê×{îøÜÜ\Ÿû½„ì¼ètŠ·×KkƒŸuA¥R2dÀ€÷ÛÂÊÊŠ2¥M¿ƒò2¼¬z7F®qâm&C•„B!„xËÈP%‘#O/soJ·Þý²,eÿºlß¹‹Á!4kÙFoz!„BñzÈP¥ÿ°¤¤$B:μY3^ÈâM­[|J×gŸ"ò‘›áá„vÏ|TjmmMQ÷"„vì@ù÷Ë™^£Ñ0á"Ìmr…R!„BñjHÇá?lç®ÝøV(ÿÂV|­k`J¼gµmÃhµZ¶‡ýĨ1ãXµt‘n¾gcïÝ#C«•NƒB!ÄD:o¸øøvþ¼›¸¸Xz?±Ú§F£a[XC=ž§ûò•+L™1›È¨(š7 Ô‹çvd$S¦ÏäÚ›¨T*7"$¸5›¶leó¶íÄÆÆñÝ7#ñ-_^îê?ט4m‘QQ4ño̦­ÛX:ÿ{ÝÜÌÑwî0tä(º~Ö™ª•õß•Q(äÊ•‹&Ÿ4fáÒeDDܦd‰âÄÇ'0ûû8}ö,v¶v„vê@ͪ0pè0nGF“––FHçP¾úòK“áB:‡Ò"(ˆ?í"ñ^"åË–cèàFÃuíÊGÖãØ‰“ÄÄÆÒ¦e ?^$hÇO»Ø´u±±qxuç«!ƒqqq6ç–í;ˆ¸u›€Æ^ËÔB!„/‹tÞPgΞcûΜøó5ªW£‰#½íÆÑÑŸÒï‘‘Á؉Shâ߈O7bÉòºÅo sžû’%¼™<~,>äêµkºmAM jH·ÞýôŽ¡Õj;qýüü¤1KW¬Ò‹2²‰ˆ¸•íB6¹ æ/{öbeeE.Œ™8 ÷"EX½t1·n1dø¼‹{Q ~&ËÝ»1tìÒ‹êÅe,Ü#{~ÿ cFãäè@xD„Yᢢ¢˜9e"×oܤïÀÁø5¨¥¥%ûödíºõ|3â+<ŠºsîüÝ š†âü ZU¶‡ýÄá#pss% qcj|P •Ri¢Æ…B!ÞlòrôæôÙ³téÙ›ù‹ó~¹²¬\¼€}{ã]¼¸Þ~›¶n#(°‰îï›á·ˆ‰‰!à,--i¤¿Â¨…‚»1±ÄÄÄbccCY“i¹Á;w ðG¥ReyŠàZ¸0»wl¥^íÚY¶|Ú’O‚Z°~Óf¾1 G"£¢8þoºt•Å<=ñ­àËñ'¦ÅÜpMüqrt2W¨4'\Ý:™i÷(êŽB¡ 6.€Ý{~%(° ^ŨV /OOl²y'àÂ¥KܽCíš5tŸ%ÞKÀÁÁ^÷Ëv^''½Åv:wlÏ’å+éÕV–V„vê@Z5§%Q?NGGÇ-à³}ã:ÔêT¾?‘[·nS±Bîü;ËS~ýuû¥¤¨)æYÔh\æ†+T°`ŽÃÙÙÚêþ¯R)IMUp÷n  d}Ïœ8UJ%žEñòòäÊÕ«Dß¹c4B!„o:é8¼aêÔªI劾ìÙ»i³æ‘‘_ÃÔ¯WW÷Kú¦-Ûðo¤·2¤£ƒ#<.”ôàZ­V÷w¾¼yدûödêÌYÔ®YÃhGÀÑÁ‘ÄÄ{¤k4¨”Jâõâ4‡½}ztýœþC†òaÝ:äwq&· ‹æÍÉQ'ÄÜp …þ g=€‹‹3ÑÑÑ9JKŠZ̓‡Øõ˯ܾI£0{Údœstl!„Bˆ7 UzÙØØà߈ùsfÒ·Ww._¹Ê¬¹sÌ_Á?¿ŸŸ^˜"n®899räØqöìÝ«·ýð‘#º%éU*%¹så2y#í^Äüù]ؾ# FÖíÛ³ì}çB»êŽ›¢î¼W²$›·í PÁ‚x-²•«HQ«IKKãä©SÜŽŒ4š–W aýúlܺ®]###ƒ¿ÎŸ'**Úhœ›·le÷žßüÄŸå‹ÜF: B!„x+ȇ7\YÊúøè^JÞº#ŒzujaoŸGo? † ÄôÙsXµv%½½õžH\¼t™³ç‘¢V“ßÅ…!¾Ðm íÞ µ:…ØØ8ÆOž†µ•%CÚQ¿^]† ÄÄ©ÓY¾j Ͱ´´Dañ¸Ã‘žžNdT”Á—£ù´YÆL˜Lóf 2˜¹?, ¤S(oïâôíÙÝhxà•‡«S«&‰‰÷ønüDbbãðô(Êð!ƒŒÆÙ<¨mZµ4·B!ÄbçÎíZ_ßJ¦÷¯ÝÔB:‡2eü¸×2ÕgJJ -Z¶yƒ^§D!„B¼YNžªôKûààWÚi8{îœnˆSخݔ+SF: B!„ï yâ „B!„0Jž8!„B!Ì"!„B!„IÒqB!„B˜$!„B!„IÒqB!„B˜$!„B!„I*Èœ^I!„B! QDEEj_w"„B!„o6ª$„B!„0I:B!„B“¤ã „B!„0I:B!„B“rÜqˆ‰eüä©wüŒæÁíñÍhÒÒÒ^FÚ„¬Û¸‰˜ØØwTt4 ? $.>þ…Ç-„B!þ›T9Ù9!!þC†â× ÝB;£T*ùçúu,--_Vú^©%+Vq‹C‡¼î¤˜ô0%…-ÛÃðöòÂ9_¾×!„Bñ–ËQÇaÇO?ãíUŒàV-tŸ½_¶ì OÔë¢Iמ®yÝÉ0‹­ «—.zÝÉB!„ïˆuöîÛGHp³÷oÖ² ߎüŠ-æÆ›¬[µœôôtæÎ_Àñ¢R©ø°nm:†´Ó=µHNNfæÜï9~ò$NŽŽ”ñ)ͯ{ö²lÑ|Z‡tdù¢ù,P€Ý¿îaí†,þ~.÷ïß7ÿž½¿³úÇuDEß!¯“#Ó²yÃG}ñã'ðkÒŒŠ¾å3êkƒû¿¨ü¦¨Õ,Y¾‚C‡ð0å!%½‹Ó§g äÏo2/M>mÉ„1£Yýã:ò:9ñEŸ^º´|7~"öööôéÑÍd<Êü؉“äÎeMù÷ß7»Ž…B!ÄGŸ)\rr²ù­VKô»ì?x˜¥+VGáB…èLõªU³ “𖯬yßÓ³[WÜ\ “;wn†…­­- æÍ&åa #¾½½=­š À”3‰¾s— ßF¥RñÛ¾}¨SSÍÎÔ¸IS ÆÏ„)Óèß·5«W'éÁ3êk.YFxÄ-¾1 Àèþ/*¿³æÌãFx8£¿‹‹3;¯zd,/Oª]³& /¡FƒR©$55•£ÇOðíÈáfÅ3eÆl¢ïD3qÌã2B!„ 66–ãÇ›ÿrtŠZMjj*ù]ò1üËA,[øþ=v×oÜ4®ÛçŸñ~Ù2äË›—¨¨hŽŸü“>=ºáèà@Á‚ðkأǎŸÀþƒ‡éÓ£^Å<)ê^„F ˜)Sñß¿Ÿ@¹2e°³³£`”*QÂ`|9Ý?§ùMHHà—ßöòEï^xuÇÖÆ†ª•+¡T*M†}Rõª•IJJâÂÅKüyú ÖVV”ññ1+ zæ2B!„o¯¸¸8Ž?ŽV«5ÿ‰C.kk¬¬¬¨]³&Þ^^|ÒÈÃüÁþƒñ(êžm8¥R©ûdT­ÛwÒ}¦Ñh(\¸·o¡P(ð*V,ç¹2#þ"n®T«Z…^ýà×ð#‚›àììl0¾œî9ÍïmTJ%žEsœ—'ÙÙÙQ¡|y9ŠOé÷8ôÇjÖ¨ŽJ©4+ À3—¹B!„x;=ê4TªTÉüŽƒB¡ @~¢¢£x¯TIÝçNNy‰‰3+[[”J%Û6ü¨wƒ­£Í¥57Q9Œß‚o¾ÆG²yÛ:wíAnŸã× û_ØsºNÓ£ÍЂBB¡Èqاծù?nÜDçöí8|ä(Cö7+&ã¹Ê\!„B¼}ž|ÒP±bEœs¶ŽCõªU9~òO½ÏÂ#"(X ¿Yá (ˆV«åòÕ«Ùn/âæŠB¡àêÕkÙnW)3û9†Ö.0?dv€ªW­ÊÄ1£ íÔÅKWèm×j3r´¿1¦ÒS¸p!ÒÓÓ‰¸uë™òò¤êU«Å/¿ýFF††²e˘›kaƒe.„B!Þ-Ùu’““sÖqðoô1GŽçÀ¡Ã¤¨Õlû‰þ¹F½:uÌ ooŸ‡õ?dæœy\¿Njj*.^æôÙ¿ptt¤níÚÌ?Ÿ¨èh¦¤päØ1]ø!‘þÉØzz:û"))‰„ÄDnÝŽÄÞ>.¼““×®ßàÞ½ûܹ{×äþÏ›ß|yóR«Fuæ|¿€˜˜RRR8~ò$ñ ‰&Ãfw¬ ï—cñÒÔ¨ž9LÉÜ4T«ZE¯ÌO<Õ9B!„ï†ì: ±±±ìß¿åÀG™Q;;J–ðæÇõ˜¿h qññ êß/OÏl÷_ýã:ê׫«›:À·By""n³lÕJV¬Zé3§q-\˜ÞŨ\±.\ä‡EKض…‚7oÒ<¨)6¹sãááΦmÛY³v7#"(W¦ ×nÜ ð“ñÇÅűxÙ –®\ÉúM[HOO§o¯îº÷ò:9òÛïûX³~×oÜ |¹²F÷ù­TÑ—¿/\dÁ’¥¬Û°‰ë×oR¶LiòåÍk2ìšuëiPÿC\þMOjZ¿ÿo?;´£p¡Bf§¡Bù÷9xø0ß/XÌÏ¿üŠ]nÞ §i“rçÎmnóB!„o8;;;ƒÛ²ë4<ù™"**òÞ®ÕjQ(DEGÓþ³.¬]±”¼NN¯;Yoµäädlll^w2„B!ÄKbhS†Š+æl¨Ò«”Ý Ãâå’NƒB!ĻǜNCŽ_ŽB!„B¼=Ìí4ähåh!„B!ÄÛÃÜNã•£ßØw„B!„/Ö£wÌí4ÄÅÅqìØ1´Z­ UB!„â]’“NÃ3­-„B!„øoKNNÖ=A¨\¹2ùòå3û³ÿÚ2~éBIEND®B`‚mod_auth_mellon-0.16.0/doc/user_guide/images/saml-tracer.svg0000664002412700241270000051033313367654776024023 0ustar jhrozekjhrozek image/svg+xml SAML tab must be active List of HTTP messages,SAML messages areflagged with SAML icon mod_auth_mellon-0.16.0/doc/user_guide/images/saml-web-sso.svg0000664002412700241270000013476113367654776024131 0ustar jhrozekjhrozek image/svg+xml Browser Service Provider(e.g. Mellon) Identity Provider 1. User attempts to accessresource at SP Is there a session for this user?No, then create one. 2. SP determines IdP. Creates<AuthnRequest> message andembeds it in URL redirect to IdP. Login form sent to user User replies with userid & password 3. IdP authenticates principal. 4. IdP responds with <Assertion>embedded in form whichautomatically posts to SP. 6. SP replies with redirect fororiginal resource (RelayState) 7. SP accesses resource again 8. SP responds with resource 5. SP validates <Assertion>and creates user session. Is there a session for this user?Yes, validate & return resource.