modmellon/auth_mellon_handler.c

3883 lines
128 KiB
C
Raw Permalink Normal View History

/*
*
* 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
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
/*
* 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</%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, "<Organization>%s%s%s</Organization>",
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;
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
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,
"<KeyDescriptor use=\"signing\">"
"<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">"
"<ds:X509Data>"
"<ds:X509Certificate>%s</ds:X509Certificate>"
"</ds:X509Data>"
"</ds:KeyInfo>"
"</KeyDescriptor>"
"<KeyDescriptor use=\"encryption\">"
"<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">"
"<ds:X509Data>"
"<ds:X509Certificate>%s</ds:X509Certificate>"
"</ds:X509Data>"
"</ds:KeyInfo>"
"</KeyDescriptor>",
sp_cert_file,
sp_cert_file);
}
return apr_psprintf(p,
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n\
<EntityDescriptor\n\
entityID=\"%s%s\"\n\
xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\">\n\
<SPSSODescriptor\n\
AuthnRequestsSigned=\"true\"\n\
WantAssertionsSigned=\"true\"\n\
protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n\
%s\
<SingleLogoutService\n\
Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:SOAP\"\n\
Location=\"%slogout\" />\n\
<SingleLogoutService\n\
Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n\
Location=\"%slogout\" />\n\
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>\n\
<AssertionConsumerService\n\
index=\"0\"\n\
isDefault=\"true\"\n\
Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n\
Location=\"%spostResponse\" />\n\
<AssertionConsumerService\n\
index=\"1\"\n\
Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\"\n\
Location=\"%sartifactResponse\" />\n\
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
<AssertionConsumerService\n\
index=\"2\"\n\
Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:PAOS\"\n\
Location=\"%spaosResponse\" />\n\
</SPSSODescriptor>\n\
%s\n\
</EntityDescriptor>",
sp_entity_id, cfg->sp_entity_id ? "" : "metadata",
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
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] );
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
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);
}
}
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
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);
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
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;
}
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
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);
}
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
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 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);
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
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 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) {
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
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;
}
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
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. */
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
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.
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
* 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,
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
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;
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
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,
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
"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;
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
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. */
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
return am_handle_reply_common(r, login, relay_state, saml_response, false);
}
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
/* 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;
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
am_diag_printf(r, "enter function %s\n", __func__);
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
/* Make sure that this is a POST request. */
if(r->method_number != M_POST) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
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
"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,
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
"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,
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
"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__);
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
if (rc != 0) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
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
"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));
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
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;
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
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. */
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
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,
" <input type=\"hidden\" name=\"%s\" value=\"%s\">\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,
" <input type=\"hidden\" name=\"%s\" value=\"%s\">\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;
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
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,
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
"<html>\n"
" <head>\n"
" <title>SAML rePOST request</title>\n"
" </head>\n"
" <body onload=\"document.getElementById('form').submit();\">\n"
" <noscript>\n"
" Your browser does not support Javascript, \n"
" you must click the button below to proceed.\n"
" </noscript>\n"
" <form id=\"form\" method=\"POST\" action=\"%s\" enctype=\"%s\"%s>\n%s"
" <noscript>\n"
" <input type=\"submit\">\n"
" </noscript>\n"
" </form>\n"
" </body>\n"
"</html>\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;
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
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
}
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
/* 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.
*/
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_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;
}
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
/* 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.
*/
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_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,
"<!DOCTYPE html>\n"
"<html>\n"
" <head>\n"
" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
" <title>POST data</title>\n"
" </head>\n"
" <body onload=\"document.forms[0].submit()\">\n"
" <noscript><p>\n"
" <strong>Note:</strong> Since your browser does not support JavaScript, you must press the button below once to proceed.\n"
" </p></noscript>\n"
" <form method=\"POST\" action=\"%s\">\n"
" <input type=\"hidden\" name=\"SAMLRequest\" value=\"%s\">\n"
" <input type=\"hidden\" name=\"RelayState\" value=\"%s\">\n"
" <noscript>\n"
" <input type=\"submit\">\n"
" </noscript>\n"
" </form>\n"
" </body>\n"
"</html>\n",
url, message, relay_state);
ap_set_content_type(r, "text/html");
ap_rputs(output, r);
return OK;
}
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
/* Use Lasso Login to set the HTTP content & headers for PAOS binding.
*
* Parameters:
* request_rec *r The request we are processing.
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
* LassoLogin *login The login message.
*
* Returns:
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
* 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);
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_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
*
*/
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_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)
{
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
gint ret;
am_dir_cfg_rec *dir_cfg;
LassoServer *server;
LassoLogin *login;
LassoSamlp2AuthnRequest *request;
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
const char *sp_name;
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
*login_return = NULL;
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
dir_cfg = am_get_dir_cfg(r);
server = am_get_lasso_server(r);
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
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;
}
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
*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);
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
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;
}
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
/*
* 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;
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
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);
}
}
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
LASSO_PROFILE(login)->msg_relayState = g_strdup(return_to_url);
2015-10-26 19:46:17 +01:00
#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,
2015-10-26 19:46:17 +01:00
"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);
2015-10-26 19:46:17 +01:00
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;
}
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
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)
{
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
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);
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
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
switch (login->http_method) {
case LASSO_HTTP_METHOD_REDIRECT:
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
return am_set_authn_request_redirect_content(r, login);
case LASSO_HTTP_METHOD_POST:
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
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;
}
}
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
#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 <AuthnRequest>
*
* 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,
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
"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,
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
"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;
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
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;
}
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
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;
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
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;
}
}
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
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);
}
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
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;
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
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);
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
#ifdef HAVE_ECP
am_req_cfg_rec *req_cfg = am_get_req_cfg(r);
#endif /* HAVE_ECP */
const char *endpoint;
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
/*
* 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);
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
} else if(!strcmp(endpoint, "paosResponse")) {
return am_handle_paos_reply(r);
} else if(!strcmp(endpoint, "auth")) {
return am_handle_auth(r);
} else if(!strcmp(endpoint, "logout")
|| !strcmp(endpoint, "logoutRequest")) {
/* logoutRequest is included for backwards-compatibility
* with version 0.0.6 and older.
*/
return am_handle_logout(r);
} else if(!strcmp(endpoint, "login")) {
return am_handle_login(r);
} else if(!strcmp(endpoint, "probeDisco")) {
return am_handle_probe_discovery(r);
} else {
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;
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
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;
}
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
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. */
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
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;
}
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
#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.
*/
2015-10-26 19:46:17 +01:00
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) {
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
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);
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
#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
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) {
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
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) {
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
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);
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
} 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;
}
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
am_diag_printf(r, "enter function %s\n", __func__);
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
#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,
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
"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. */
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
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) {
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
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) {
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
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) {
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
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;
}