modmellon/mod_auth_mellon.c

251 lines
8.5 KiB
C
Raw Normal View History

/*
*
* mod_auth_mellon.c: an authentication apache module
* Copyright <EFBFBD> 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 <curl/curl.h>
#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;
}
Add support for SAML ECP. The modifications in this commit address the changes necessary to support the SP component of SAML ECP. The Lasso library needs additional modifications before SAML ECP will be fully functional, those fixes have been submitted to upstream Lasso, mod_auth_mellon will continue to operate correctly without the Lasso upgrade, it just won't properly support ECP without the Lasso fixes. Below are the major logical changes in the commit and the rationale behind them. * Allow compilation against older versions of Lasso by conditionally compiling. Add the following CPP symbols set by configure: * HAVE_ECP * HAVE_LASSO_UTILS_H * Add lasso_compat.h If we can't include lasso utils.h than pull in our own local definitions so we can use some of the valuable utilities. * Add ECP specific documentation file Documentation specific to ECP is now contained in ECP.rst (using reStructuredText formatting). Information on general ECP concepts, mod_auth_mellon user information, and internal mod_auth_mellon coding issues are covered. * Add am_get_boolean_query_parameter() utility * Add am_validate_paos_header() utility This utility routine validates the PAOS HTTP header. It is used in conjunction with am_header_has_media_type() to determine if a client is ECP capable. * Add am_is_paos_request() utility This utility checks to see if the request is PAOS based on the required HTTP header content. * Add utility function am_header_has_media_type() to check if an HTTP Accept header includes a specific media type. This is necessary because the SP detects an ECP client by the presence of a application/vnd.paos+xml media type in the Accept header. Unfortunately neither Apache nor mod_auth_mellon already had a function to check Accept media types so this was custom written and added to mod_auth_mellon. * Add utility function am_get_assertion_consumer_service_by_binding() because Lasso does not expose that in it's public API. It's necessary to get the URL of the PAOS AssertionConsumerService. * Add MellonECPSendIDPList config option This option controls whether to include a list of IDP's when sending an ECP PAOS <AuthnRequest> message to an ECP client. * We need to do some bookkeeping during the processing of a request. Some Apache modules call this "adding a note". mod_auth_mellon was already doing this but because it only needed to track one value (the cookie value) took a shortcut and stuffed the cookie value into the per module request slot rather than defining a struct that could hold a variety of per-request values. To accommodate multiple per request bookkeeping values we define a new struct, am_req_cfg_rec, that holds the previously used cookie value and adds a new ECP specific value. This struct is now the bookkeeping data item attached to each request. To support the new am_req_cfg_rec struct the am_get_req_cfg macro was added (mirrors the existing am_get_srv_cfg, am_get_mod_cfg and am_get_dir_cfg macros). The am_create_request() Apache hook was added to initialize the am_req_cfg_rec at the beginning of the request pipeline. * A new endpoint was added to handle PAOS responses from the ECP client. The endpoint is called "paosResponse" and lives along side of the existing endpoints (e.g. postResponse, artifactResponse, metadata, auth, logout, etc.). The new endpoint is handled by am_handle_paos_reply(). The metadata generation implemented in am_generate_metadata() was augmented to add the paosResponse endpoint and bind it to the SAML2 PAOS binding. * am_handle_reply_common() was being called by am_handle_post_reply() and am_handle_artifact_reply() because replies share a fair amount of common logic. The new am_handle_paos_reply() also needs to utilize the same common logic in am_handle_reply_common() but ECP has slightly different behavior that has to be accounted for. With ECP there is no SP generated cookie because the SP did not initiate the process and has no state to track. Also the RelayState is optional with ECP and is carried in the PAOS header as opposed to an HTTP query/post parameter. The boolean flag is_paos was added as a parameter to am_handle_reply_common() so as to be able to distinguish between the PAOS and non-PAOS logic. * Add PAOS AssertionConsumerService to automatically generated metadata. Note, am_get_assertion_consumer_service_by_binding() should be able to locate this endpoint. * Refactor code to send <AuthnRequest>, now also supports PAOS The creation and initialization of a LassoLogin object is different for the ECP case. We want to share as much common code as possible, the following refactoring was done to achieve that goal. The function am_send_authn_request() was removed and it's logic moved to am_init_authn_request_common(), am_send_login_authn_request() and am_set_authn_request_content(). This allows the logic used to create and initialize a LassoLogin object to be shared between the PAOS and non-PAOS cases. am_send_paos_authn_request() also calls am_init_authn_request_common() and am_set_authn_request_content(). The function am_set_authn_request_content() replaces the logic at the end of am_send_authn_request(), it is responsible for setting the HTTP headers and body content based on the LassoLogin. Signed-off-by: John Dennis <jdennis@redhat.com>
2015-07-06 22:04:55 +02:00
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 */
Add diagnostic logging Field experience with Mellon has demonstrated there are many opportunities for deployment problems. Although there are tools such as browser plugins which can capture SAML messages it's onerous for site personnel to install and capture the relevant information. The problem with this approach is further compounded by the fact the external SAML messages are not correlated to Mellon's requests/responses. Mellon currently can dump the Lasso session and SAML Response messages and place them in Apache environment variables, however these do not appear in the log file. To get them into the log you have to add custom logging to the Apache config. Another issue is the dumps are not human readable, they are base64 encoded, anyone looking at the logs after setting up the custom logging will have to find the base64 text and then manually copy the text into an external base64 decoder. At that point you'll discover the XML is not pretty printed making human interpretation difficult. The Mellon debug messages written to the Apache error are often insufficient to diagnose problems. And since the Mellon log messages are written to the Apache error log they are interspersed with a lot of non-Mellon message. Compounding the problem of writing Mellon debug messages to the Apache error log is the fact Apache log messages have a fixed maximum length (currently 8192) which is insufficient to completely write out such things as SAML Assertions, metadata, etc. Apache logging also escapes all control characters with the consequence line breaks are not preserved and what was a nicely formatted human readable piece of text becomes a single line with escape characters and may be truncated. It would be really nice if we could capture diagnostic information with these properties: * All relevant data is collected in exactly one file. * Only information relevant to Mellon appears in the file. * All information is human readable (pretty printed, decrypted) with no need to rely on other tools. * The diagnostic information is grouped by requests. * The requests can be cross correlated with other Apache logs because they utilize the same unique request identifier. This patch adds diagnostic logging to a independent Mellon diagnostics log file. Every piece of relevant information is captured, including: * Request information which includes: - Request method - Request URL (raw and processed) - Scheme - Port - Request query parameters - Server name - Unique request ID - process ID, thread ID - Request headers * Mellon per directory configuration A complete dump of the entire am_dir_cfg_rec structure keyed using both the Mellon directive it is associated with and it's internal name. This is emitted once on first use for a given URL. The per directory dump includes the pathname of each file read as well as the file contents. This includes: - IdP metadata - SP metadata - SP cert - SP key - IdP public key file - IdP CA file * All session management operations - cookie - session lookup - session creation - session deletion - cache management - cache entry information * All SAML messages Each SAML message is decrypted, decoded and XML pretty printed in human readable form. * Request pipeline operations What operations Mellon performs, what decisions it makes and what data is being used to make those decisions. * Response - response status - response headers - Apache user name - auth_type - all Apache environment variables Diagnostics can be enabled/disabled both at compile time and run time. Compile time inclusion of diagnostics is managed with the ENABLE_DIAGNOSTICS preprocssor symbol. The configure script now accepts the --enable-diagnostics and --disable-diagnostics option. Building with diagnostics is disabled by default, you must specify --enable-diagnostics to enable the run time option of generating diagnostics. The following server config directives have been added (e.g. may be specified in the main server config area or within a <VirtualHost> directive). If Mellon was not built with diagnostics enabled then these config directives are no-ops and their use will generated a warning in the log file indicating they have been ignored and to be effective you must builld Mellon with diagnostics enabled. MellonDiagnosticsFile: The name of the diagnostics file or pipe, (default is logs/mellon_diagnostics) MellonDiagnosticsEnable: Currently either On or Off but it is designed so it can take other flags in the future to control what type of information is reported. Signed-off-by: John Dennis <jdennis@redhat.com>
2017-06-14 19:56:18 +02:00
#ifdef ENABLE_DIAGNOSTICS
req_cfg->diag_emitted = false;
#endif
Add support for SAML ECP. The modifications in this commit address the changes necessary to support the SP component of SAML ECP. The Lasso library needs additional modifications before SAML ECP will be fully functional, those fixes have been submitted to upstream Lasso, mod_auth_mellon will continue to operate correctly without the Lasso upgrade, it just won't properly support ECP without the Lasso fixes. Below are the major logical changes in the commit and the rationale behind them. * Allow compilation against older versions of Lasso by conditionally compiling. Add the following CPP symbols set by configure: * HAVE_ECP * HAVE_LASSO_UTILS_H * Add lasso_compat.h If we can't include lasso utils.h than pull in our own local definitions so we can use some of the valuable utilities. * Add ECP specific documentation file Documentation specific to ECP is now contained in ECP.rst (using reStructuredText formatting). Information on general ECP concepts, mod_auth_mellon user information, and internal mod_auth_mellon coding issues are covered. * Add am_get_boolean_query_parameter() utility * Add am_validate_paos_header() utility This utility routine validates the PAOS HTTP header. It is used in conjunction with am_header_has_media_type() to determine if a client is ECP capable. * Add am_is_paos_request() utility This utility checks to see if the request is PAOS based on the required HTTP header content. * Add utility function am_header_has_media_type() to check if an HTTP Accept header includes a specific media type. This is necessary because the SP detects an ECP client by the presence of a application/vnd.paos+xml media type in the Accept header. Unfortunately neither Apache nor mod_auth_mellon already had a function to check Accept media types so this was custom written and added to mod_auth_mellon. * Add utility function am_get_assertion_consumer_service_by_binding() because Lasso does not expose that in it's public API. It's necessary to get the URL of the PAOS AssertionConsumerService. * Add MellonECPSendIDPList config option This option controls whether to include a list of IDP's when sending an ECP PAOS <AuthnRequest> message to an ECP client. * We need to do some bookkeeping during the processing of a request. Some Apache modules call this "adding a note". mod_auth_mellon was already doing this but because it only needed to track one value (the cookie value) took a shortcut and stuffed the cookie value into the per module request slot rather than defining a struct that could hold a variety of per-request values. To accommodate multiple per request bookkeeping values we define a new struct, am_req_cfg_rec, that holds the previously used cookie value and adds a new ECP specific value. This struct is now the bookkeeping data item attached to each request. To support the new am_req_cfg_rec struct the am_get_req_cfg macro was added (mirrors the existing am_get_srv_cfg, am_get_mod_cfg and am_get_dir_cfg macros). The am_create_request() Apache hook was added to initialize the am_req_cfg_rec at the beginning of the request pipeline. * A new endpoint was added to handle PAOS responses from the ECP client. The endpoint is called "paosResponse" and lives along side of the existing endpoints (e.g. postResponse, artifactResponse, metadata, auth, logout, etc.). The new endpoint is handled by am_handle_paos_reply(). The metadata generation implemented in am_generate_metadata() was augmented to add the paosResponse endpoint and bind it to the SAML2 PAOS binding. * am_handle_reply_common() was being called by am_handle_post_reply() and am_handle_artifact_reply() because replies share a fair amount of common logic. The new am_handle_paos_reply() also needs to utilize the same common logic in am_handle_reply_common() but ECP has slightly different behavior that has to be accounted for. With ECP there is no SP generated cookie because the SP did not initiate the process and has no state to track. Also the RelayState is optional with ECP and is carried in the PAOS header as opposed to an HTTP query/post parameter. The boolean flag is_paos was added as a parameter to am_handle_reply_common() so as to be able to distinguish between the PAOS and non-PAOS logic. * Add PAOS AssertionConsumerService to automatically generated metadata. Note, am_get_assertion_consumer_service_by_binding() should be able to locate this endpoint. * Refactor code to send <AuthnRequest>, now also supports PAOS The creation and initialization of a LassoLogin object is different for the ECP case. We want to share as much common code as possible, the following refactoring was done to achieve that goal. The function am_send_authn_request() was removed and it's logic moved to am_init_authn_request_common(), am_send_login_authn_request() and am_set_authn_request_content(). This allows the logic used to create and initialize a LassoLogin object to be shared between the PAOS and non-PAOS cases. am_send_paos_authn_request() also calls am_init_authn_request_common() and am_set_authn_request_content(). The function am_set_authn_request_content() replaces the logic at the end of am_send_authn_request(), it is responsible for setting the HTTP headers and body content based on the LassoLogin. Signed-off-by: John Dennis <jdennis@redhat.com>
2015-07-06 22:04:55 +02:00
ap_set_module_config(r->request_config, &auth_mellon_module, req_cfg);
return OK;
}
static void register_hooks(apr_pool_t *p)
{
Modify am_handler setup to run before mod_proxy The way the ECP flow works is that when a client initiates the flow, the SP's response is HTTP 200, but not the requested content, but a signed XML document that contains the "samlp:AuthnRequest" element. The idea is that the ECP client would then determine the IDP and send the document to the IDP, get a samlp:Response and convey that to the SP to get access to the protected resource. Internally, the auth check which is normally done with am_check_uid() set to apache's ap_hook_check_user_id() hook, just responds with OK, so it pretends to authenticate the user. Then in the usual flow, the request reaches the ap_hook_handler which handles the request. There in the pipeline, mellon registers functions am_handler() which should run first (APR_HOOK_FIRST), determine that this request is an ECP one and return the ECP AuthnRequest document. But in case the proxy module is also in the picture, the proxy module "races" for who gets to be the first to handle the request in the pipeline and wins. Therefore, the request reaches the protected resource via mod_proxy and returns it. This fix modifies the ap_hook_handler() call to explicitly run before handlers from mod_proxy.c To reproduce the bug: 0) Have a SP with mellon connected to a Keycloak IDP (or any other IDP I guess). In the example below, my SAML SP is saml.federation.test 1) Set a Location protected by mellon that proxies requests to another URL. For example: ProxyPass /sp-proxy http://app.federation.test/example_app/ <Location /sp-proxy> AuthType Mellon MellonEnable auth Require valid-user </Location> 2) call: curl -L -H "Accept: application/vnd.paos+xml" \ -H 'PAOS: ver="urn:liberty:paos:2003-08";"urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp"' \ http://saml.federation.test/sp-proxy Before the patch, you would see whatever is served from the proxied page. With the patch, you should get back a XML document with a samlp:AuthnRequest.
2019-03-05 10:15:48 +01:00
/* 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);
Add support for SAML ECP. The modifications in this commit address the changes necessary to support the SP component of SAML ECP. The Lasso library needs additional modifications before SAML ECP will be fully functional, those fixes have been submitted to upstream Lasso, mod_auth_mellon will continue to operate correctly without the Lasso upgrade, it just won't properly support ECP without the Lasso fixes. Below are the major logical changes in the commit and the rationale behind them. * Allow compilation against older versions of Lasso by conditionally compiling. Add the following CPP symbols set by configure: * HAVE_ECP * HAVE_LASSO_UTILS_H * Add lasso_compat.h If we can't include lasso utils.h than pull in our own local definitions so we can use some of the valuable utilities. * Add ECP specific documentation file Documentation specific to ECP is now contained in ECP.rst (using reStructuredText formatting). Information on general ECP concepts, mod_auth_mellon user information, and internal mod_auth_mellon coding issues are covered. * Add am_get_boolean_query_parameter() utility * Add am_validate_paos_header() utility This utility routine validates the PAOS HTTP header. It is used in conjunction with am_header_has_media_type() to determine if a client is ECP capable. * Add am_is_paos_request() utility This utility checks to see if the request is PAOS based on the required HTTP header content. * Add utility function am_header_has_media_type() to check if an HTTP Accept header includes a specific media type. This is necessary because the SP detects an ECP client by the presence of a application/vnd.paos+xml media type in the Accept header. Unfortunately neither Apache nor mod_auth_mellon already had a function to check Accept media types so this was custom written and added to mod_auth_mellon. * Add utility function am_get_assertion_consumer_service_by_binding() because Lasso does not expose that in it's public API. It's necessary to get the URL of the PAOS AssertionConsumerService. * Add MellonECPSendIDPList config option This option controls whether to include a list of IDP's when sending an ECP PAOS <AuthnRequest> message to an ECP client. * We need to do some bookkeeping during the processing of a request. Some Apache modules call this "adding a note". mod_auth_mellon was already doing this but because it only needed to track one value (the cookie value) took a shortcut and stuffed the cookie value into the per module request slot rather than defining a struct that could hold a variety of per-request values. To accommodate multiple per request bookkeeping values we define a new struct, am_req_cfg_rec, that holds the previously used cookie value and adds a new ECP specific value. This struct is now the bookkeeping data item attached to each request. To support the new am_req_cfg_rec struct the am_get_req_cfg macro was added (mirrors the existing am_get_srv_cfg, am_get_mod_cfg and am_get_dir_cfg macros). The am_create_request() Apache hook was added to initialize the am_req_cfg_rec at the beginning of the request pipeline. * A new endpoint was added to handle PAOS responses from the ECP client. The endpoint is called "paosResponse" and lives along side of the existing endpoints (e.g. postResponse, artifactResponse, metadata, auth, logout, etc.). The new endpoint is handled by am_handle_paos_reply(). The metadata generation implemented in am_generate_metadata() was augmented to add the paosResponse endpoint and bind it to the SAML2 PAOS binding. * am_handle_reply_common() was being called by am_handle_post_reply() and am_handle_artifact_reply() because replies share a fair amount of common logic. The new am_handle_paos_reply() also needs to utilize the same common logic in am_handle_reply_common() but ECP has slightly different behavior that has to be accounted for. With ECP there is no SP generated cookie because the SP did not initiate the process and has no state to track. Also the RelayState is optional with ECP and is carried in the PAOS header as opposed to an HTTP query/post parameter. The boolean flag is_paos was added as a parameter to am_handle_reply_common() so as to be able to distinguish between the PAOS and non-PAOS logic. * Add PAOS AssertionConsumerService to automatically generated metadata. Note, am_get_assertion_consumer_service_by_binding() should be able to locate this endpoint. * Refactor code to send <AuthnRequest>, now also supports PAOS The creation and initialization of a LassoLogin object is different for the ECP case. We want to share as much common code as possible, the following refactoring was done to achieve that goal. The function am_send_authn_request() was removed and it's logic moved to am_init_authn_request_common(), am_send_login_authn_request() and am_set_authn_request_content(). This allows the logic used to create and initialize a LassoLogin object to be shared between the PAOS and non-PAOS cases. am_send_paos_authn_request() also calls am_init_authn_request_common() and am_set_authn_request_content(). The function am_set_authn_request_content() replaces the logic at the end of am_send_authn_request(), it is responsible for setting the HTTP headers and body content based on the LassoLogin. Signed-off-by: John Dennis <jdennis@redhat.com>
2015-07-06 22:04:55 +02:00
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.
*/
Modify am_handler setup to run before mod_proxy The way the ECP flow works is that when a client initiates the flow, the SP's response is HTTP 200, but not the requested content, but a signed XML document that contains the "samlp:AuthnRequest" element. The idea is that the ECP client would then determine the IDP and send the document to the IDP, get a samlp:Response and convey that to the SP to get access to the protected resource. Internally, the auth check which is normally done with am_check_uid() set to apache's ap_hook_check_user_id() hook, just responds with OK, so it pretends to authenticate the user. Then in the usual flow, the request reaches the ap_hook_handler which handles the request. There in the pipeline, mellon registers functions am_handler() which should run first (APR_HOOK_FIRST), determine that this request is an ECP one and return the ECP AuthnRequest document. But in case the proxy module is also in the picture, the proxy module "races" for who gets to be the first to handle the request in the pipeline and wins. Therefore, the request reaches the protected resource via mod_proxy and returns it. This fix modifies the ap_hook_handler() call to explicitly run before handlers from mod_proxy.c To reproduce the bug: 0) Have a SP with mellon connected to a Keycloak IDP (or any other IDP I guess). In the example below, my SAML SP is saml.federation.test 1) Set a Location protected by mellon that proxies requests to another URL. For example: ProxyPass /sp-proxy http://app.federation.test/example_app/ <Location /sp-proxy> AuthType Mellon MellonEnable auth Require valid-user </Location> 2) call: curl -L -H "Accept: application/vnd.paos+xml" \ -H 'PAOS: ver="urn:liberty:paos:2003-08";"urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp"' \ http://saml.federation.test/sp-proxy Before the patch, you would see whatever is served from the proxied page. With the patch, you should get back a XML document with a samlp:AuthnRequest.
2019-03-05 10:15:48 +01:00
ap_hook_handler(am_handler, NULL, run_handler_before, APR_HOOK_FIRST);
Add diagnostic logging Field experience with Mellon has demonstrated there are many opportunities for deployment problems. Although there are tools such as browser plugins which can capture SAML messages it's onerous for site personnel to install and capture the relevant information. The problem with this approach is further compounded by the fact the external SAML messages are not correlated to Mellon's requests/responses. Mellon currently can dump the Lasso session and SAML Response messages and place them in Apache environment variables, however these do not appear in the log file. To get them into the log you have to add custom logging to the Apache config. Another issue is the dumps are not human readable, they are base64 encoded, anyone looking at the logs after setting up the custom logging will have to find the base64 text and then manually copy the text into an external base64 decoder. At that point you'll discover the XML is not pretty printed making human interpretation difficult. The Mellon debug messages written to the Apache error are often insufficient to diagnose problems. And since the Mellon log messages are written to the Apache error log they are interspersed with a lot of non-Mellon message. Compounding the problem of writing Mellon debug messages to the Apache error log is the fact Apache log messages have a fixed maximum length (currently 8192) which is insufficient to completely write out such things as SAML Assertions, metadata, etc. Apache logging also escapes all control characters with the consequence line breaks are not preserved and what was a nicely formatted human readable piece of text becomes a single line with escape characters and may be truncated. It would be really nice if we could capture diagnostic information with these properties: * All relevant data is collected in exactly one file. * Only information relevant to Mellon appears in the file. * All information is human readable (pretty printed, decrypted) with no need to rely on other tools. * The diagnostic information is grouped by requests. * The requests can be cross correlated with other Apache logs because they utilize the same unique request identifier. This patch adds diagnostic logging to a independent Mellon diagnostics log file. Every piece of relevant information is captured, including: * Request information which includes: - Request method - Request URL (raw and processed) - Scheme - Port - Request query parameters - Server name - Unique request ID - process ID, thread ID - Request headers * Mellon per directory configuration A complete dump of the entire am_dir_cfg_rec structure keyed using both the Mellon directive it is associated with and it's internal name. This is emitted once on first use for a given URL. The per directory dump includes the pathname of each file read as well as the file contents. This includes: - IdP metadata - SP metadata - SP cert - SP key - IdP public key file - IdP CA file * All session management operations - cookie - session lookup - session creation - session deletion - cache management - cache entry information * All SAML messages Each SAML message is decrypted, decoded and XML pretty printed in human readable form. * Request pipeline operations What operations Mellon performs, what decisions it makes and what data is being used to make those decisions. * Response - response status - response headers - Apache user name - auth_type - all Apache environment variables Diagnostics can be enabled/disabled both at compile time and run time. Compile time inclusion of diagnostics is managed with the ENABLE_DIAGNOSTICS preprocssor symbol. The configure script now accepts the --enable-diagnostics and --disable-diagnostics option. Building with diagnostics is disabled by default, you must specify --enable-diagnostics to enable the run time option of generating diagnostics. The following server config directives have been added (e.g. may be specified in the main server config area or within a <VirtualHost> directive). If Mellon was not built with diagnostics enabled then these config directives are no-ops and their use will generated a warning in the log file indicating they have been ignored and to be effective you must builld Mellon with diagnostics enabled. MellonDiagnosticsFile: The name of the diagnostics file or pipe, (default is logs/mellon_diagnostics) MellonDiagnosticsEnable: Currently either On or Off but it is designed so it can take other flags in the future to control what type of information is reported. Signed-off-by: John Dennis <jdennis@redhat.com>
2017-06-14 19:56:18 +02:00
#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,
Add diagnostic logging Field experience with Mellon has demonstrated there are many opportunities for deployment problems. Although there are tools such as browser plugins which can capture SAML messages it's onerous for site personnel to install and capture the relevant information. The problem with this approach is further compounded by the fact the external SAML messages are not correlated to Mellon's requests/responses. Mellon currently can dump the Lasso session and SAML Response messages and place them in Apache environment variables, however these do not appear in the log file. To get them into the log you have to add custom logging to the Apache config. Another issue is the dumps are not human readable, they are base64 encoded, anyone looking at the logs after setting up the custom logging will have to find the base64 text and then manually copy the text into an external base64 decoder. At that point you'll discover the XML is not pretty printed making human interpretation difficult. The Mellon debug messages written to the Apache error are often insufficient to diagnose problems. And since the Mellon log messages are written to the Apache error log they are interspersed with a lot of non-Mellon message. Compounding the problem of writing Mellon debug messages to the Apache error log is the fact Apache log messages have a fixed maximum length (currently 8192) which is insufficient to completely write out such things as SAML Assertions, metadata, etc. Apache logging also escapes all control characters with the consequence line breaks are not preserved and what was a nicely formatted human readable piece of text becomes a single line with escape characters and may be truncated. It would be really nice if we could capture diagnostic information with these properties: * All relevant data is collected in exactly one file. * Only information relevant to Mellon appears in the file. * All information is human readable (pretty printed, decrypted) with no need to rely on other tools. * The diagnostic information is grouped by requests. * The requests can be cross correlated with other Apache logs because they utilize the same unique request identifier. This patch adds diagnostic logging to a independent Mellon diagnostics log file. Every piece of relevant information is captured, including: * Request information which includes: - Request method - Request URL (raw and processed) - Scheme - Port - Request query parameters - Server name - Unique request ID - process ID, thread ID - Request headers * Mellon per directory configuration A complete dump of the entire am_dir_cfg_rec structure keyed using both the Mellon directive it is associated with and it's internal name. This is emitted once on first use for a given URL. The per directory dump includes the pathname of each file read as well as the file contents. This includes: - IdP metadata - SP metadata - SP cert - SP key - IdP public key file - IdP CA file * All session management operations - cookie - session lookup - session creation - session deletion - cache management - cache entry information * All SAML messages Each SAML message is decrypted, decoded and XML pretty printed in human readable form. * Request pipeline operations What operations Mellon performs, what decisions it makes and what data is being used to make those decisions. * Response - response status - response headers - Apache user name - auth_type - all Apache environment variables Diagnostics can be enabled/disabled both at compile time and run time. Compile time inclusion of diagnostics is managed with the ENABLE_DIAGNOSTICS preprocssor symbol. The configure script now accepts the --enable-diagnostics and --disable-diagnostics option. Building with diagnostics is disabled by default, you must specify --enable-diagnostics to enable the run time option of generating diagnostics. The following server config directives have been added (e.g. may be specified in the main server config area or within a <VirtualHost> directive). If Mellon was not built with diagnostics enabled then these config directives are no-ops and their use will generated a warning in the log file indicating they have been ignored and to be effective you must builld Mellon with diagnostics enabled. MellonDiagnosticsFile: The name of the diagnostics file or pipe, (default is logs/mellon_diagnostics) MellonDiagnosticsEnable: Currently either On or Off but it is designed so it can take other flags in the future to control what type of information is reported. Signed-off-by: John Dennis <jdennis@redhat.com>
2017-06-14 19:56:18 +02:00
auth_mellon_srv_merge,
auth_mellon_commands,
register_hooks
};