Skip to content

Commit

Permalink
IDP Initiate POST SLO Implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
gurtzoo authored and thijskh committed Jan 12, 2024
1 parent 89201df commit dbe050a
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 32 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,9 @@ The following is an example of metadata for the example configuration:
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://example.com/secret/endpoint/logout" />
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://example.com/secret/endpoint/logout" />
<NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:transient
</NameIDFormat>
Expand Down
163 changes: 151 additions & 12 deletions auth_mellon_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ static char *am_generate_metadata(apr_pool_t *p, request_rec *r)
<SingleLogoutService\n\
Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n\
Location=\"%slogout\" />\n\
<SingleLogoutService\n\
Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n\
Location=\"%slogout\" />\n\
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>\n\
<AssertionConsumerService\n\
index=\"0\"\n\
Expand Down Expand Up @@ -556,26 +559,113 @@ static int am_save_lasso_profile_state(request_rec *r,
return ret;
}

/* Use Lasso Logout to set the HTTP content & headers for HTTP-POST binding.
*
* Parameters:
* request_rec *r The request we are processing.
* LassoProfile *profile The Lasso profile(logout) message.
* char *post_data The post data (if present for POST logout)
*
* Returns:
* OK on success, or an error on failure.
*/
static int am_set_logout_response_post_content(request_rec *r,
LassoProfile *profile,
char *post_data)
{
char *url;
char *message;
char *relay_state = NULL;
char *output;

url = am_htmlencode(r, profile->msg_url);
message = am_htmlencode(r, profile->msg_body);
if (profile->msg_relayState != NULL) {
relay_state = am_htmlencode(r, profile->msg_relayState);
} else {
/* Try to get relayState from post_data (lasso seems not to initialize relayState from post logout) */
int rc = 0;
relay_state = am_extract_query_parameter(r->pool, post_data,
"RelayState");
if (relay_state) {
rc = am_urldecode(relay_state);
if (rc != OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r,
"Could not urldecode RelayState value.");
relay_state = NULL;
}
}
}

if (relay_state) {
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=\"SAMLResponse\" 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);
} else {
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=\"SAMLResponse\" value=\"%s\">\n"
" <noscript>\n"
" <input type=\"submit\">\n"
" </noscript>\n"
" </form>\n"
" </body>\n"
"</html>\n",
url, message);
}

ap_set_content_type(r, "text/html");
ap_rputs(output, r);

return OK;
}

/* Returns a SAML response
*
* Parameters:
* request_rec *r The current request.
* LassoProfile *profile The profile object.
* char *post_data The post data (if present for POST logout)
*
* 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)
LassoProfile *profile,
char *post_data)
{
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;
return am_set_logout_response_post_content(r, profile, post_data);
} else if (profile->msg_url) {
/* HTTP-Redirect binding response */
apr_table_setn(r->headers_out, "Location",
Expand Down Expand Up @@ -655,12 +745,16 @@ static void am_restore_lasso_profile_state(request_rec *r,
* request_rec *r The logout request.
* LassoLogout *logout A LassoLogout object initiated with
* the current session.
* char *msg The logout message to process.
* char *post_data The post data (if present for POST logout)
*
* 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)
LassoLogout *logout,
char *msg,
char *post_data)
{
gint res = 0, rc = HTTP_OK;
am_cache_entry_t *session = NULL;
Expand Down Expand Up @@ -753,7 +847,7 @@ static int am_handle_logout_request(request_rec *r,
rc = HTTP_INTERNAL_SERVER_ERROR;
goto exit;
}
rc = am_return_logout_response(r, &logout->parent);
rc = am_return_logout_response(r, &logout->parent, post_data);

exit:
if (session != NULL) {
Expand All @@ -764,6 +858,43 @@ static int am_handle_logout_request(request_rec *r,
return rc;
}

/* This function handles an IdP initiated logout request HTTP-POST.
*
* Parameters:
* request_rec *r The logout request.
* LassoLogout *logout A LassoLogout object initiated with
* the current session.
* char *post_data The post data (if present for POST logout)
*
* Returns:
* OK on success, or an error if any of the steps fail.
*/
static int am_handle_logout_request_POST(request_rec *r,
LassoLogout *logout,
char *post_data)
{
int rc = 0;
char *saml_request;

saml_request = am_extract_query_parameter(r->pool, post_data,
"SAMLRequest");

if (saml_request == NULL) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r,
"Could not find SAMLRequest field in POST data.");
return HTTP_BAD_REQUEST;
}

rc = am_urldecode(saml_request);
if (rc != OK) {
AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, rc, r,
"Could not urldecode SAMLRequest value.");
return rc;
}

return am_handle_logout_request(r, logout, saml_request, post_data);
}

/* This function handles an invalidate request.
*
* Parameters:
Expand Down Expand Up @@ -1169,7 +1300,7 @@ static int am_handle_logout(request_rec *r)

/*
* First check for POST method, which could be either IdP-initiated SOAP
* logout request or POST logout response from IdP.
* or HTTP-POST logout request or POST logout response from IdP.
*/
if ((r->args == NULL) && (r->method_number == M_POST)) {
int rc;
Expand All @@ -1187,14 +1318,22 @@ static int am_handle_logout(request_rec *r)
}

if (content_type != NULL &&
am_has_header(r, content_type, "application/x-www-form-urlencoded"))
am_has_header(r, content_type, "application/x-www-form-urlencoded")) {
/* POST can be for a SAMLRequest (IDP request for SLO) or a SAMLResponse (IDP response for SLO) */
char *saml_param;
saml_param = am_extract_query_parameter(r->pool, post_data,
"SAMLResponse");
if (saml_param) {
return am_handle_logout_response_POST(r, logout, post_data);
else
return am_handle_logout_request(r, logout, post_data);
}
}

/* HTTP-POST Request */
return am_handle_logout_request_POST(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);
return am_handle_logout_request(r, logout, r->args, r->args);

} else if(am_extract_query_parameter(r->pool, r->args,
"SAMLResponse") != NULL) {
Expand Down
1 change: 1 addition & 0 deletions auth_mellon_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -2451,6 +2451,7 @@ int am_get_boolean_query_parameter(request_rec *r, const char *name,
* "AuthnRequestsSigned"
* "AssertionConsumerService PAOS 2"
* "SingleLogoutService HTTP-Redirect"
* "SingleLogoutService HTTP-POST"
* "SingleLogoutService SOAP"
* "AssertionConsumerService HTTP-Artifact 1"
* "NameIDFormat"
Expand Down
Loading

0 comments on commit dbe050a

Please sign in to comment.