Add support for IdP initiated SOAP single logout.

git-svn-id: https://modmellon.googlecode.com/svn/trunk@49 a716ebb1-153a-0410-b759-cfb97c6a1b53
This commit is contained in:
manu@netbsd.org 2009-05-15 08:57:03 +00:00
parent 4bbb403f59
commit 1803631503
4 changed files with 138 additions and 24 deletions

View File

@ -189,7 +189,10 @@ typedef struct am_cache_entry_t {
am_cache_env_t env[AM_CACHE_ENVSIZE];
} am_cache_entry_t;
typedef enum {
AM_CACHE_SESSION,
AM_CACHE_NAMEID
} am_cache_key_t;
extern const command_rec auth_mellon_commands[];
@ -203,7 +206,8 @@ void am_cookie_set(request_rec *r, const char *id);
void am_cookie_delete(request_rec *r);
am_cache_entry_t *am_cache_lock(server_rec *s, const char *key);
am_cache_entry_t *am_cache_lock(server_rec *s,
am_cache_key_t type, const char *key);
am_cache_entry_t *am_cache_new(server_rec *s, const char *key);
void am_cache_unlock(server_rec *s, am_cache_entry_t *entry);
@ -212,6 +216,8 @@ void am_cache_update_expires(am_cache_entry_t *t, apr_time_t expires);
void am_cache_env_populate(request_rec *r, am_cache_entry_t *session);
int am_cache_env_append(am_cache_entry_t *session,
const char *var, const char *val);
const char *am_cache_env_fetch_first(am_cache_entry_t *t,
const char *var);
void am_cache_delete(server_rec *s, am_cache_entry_t *session);
int am_cache_set_lasso_state(am_cache_entry_t *session,
@ -222,6 +228,8 @@ const char *am_cache_get_lasso_session(am_cache_entry_t *session);
am_cache_entry_t *am_get_request_session(request_rec *r);
am_cache_entry_t *am_get_request_session_by_nameid(request_rec *r,
char *nameid);
am_cache_entry_t *am_new_request_session(request_rec *r);
void am_release_request_session(request_rec *r, am_cache_entry_t *session);
void am_delete_request_session(request_rec *r, am_cache_entry_t *session);

View File

@ -28,12 +28,15 @@
*
* Parameters:
* server_rec *s The current server.
* const char *key The session key.
* const char *key The session key or user
* am_cache_key_t type AM_CACHE_SESSION or AM_CACHE_NAMEID
*
* Returns:
* The session entry on success or NULL on failure.
*/
am_cache_entry_t *am_cache_lock(server_rec *s, const char *key)
am_cache_entry_t *am_cache_lock(server_rec *s,
am_cache_key_t type,
const char *key)
{
am_mod_cfg_rec *mod_cfg;
am_cache_entry_t *table;
@ -43,10 +46,22 @@ am_cache_entry_t *am_cache_lock(server_rec *s, const char *key)
/* Check if we have a valid session key. We abort if we don't. */
if(key == NULL || strlen(key) != AM_SESSION_ID_LENGTH) {
if (key == NULL)
return NULL;
}
switch (type) {
case AM_CACHE_SESSION:
if (strlen(key) != AM_SESSION_ID_LENGTH)
return NULL;
break;
case AM_CACHE_NAMEID:
if (strlen(key) > AM_CACHE_MAX_LASSO_IDENTITY_SIZE)
return NULL;
break;
default:
return NULL;
break;
}
mod_cfg = am_get_mod_cfg(s);
@ -63,7 +78,25 @@ am_cache_entry_t *am_cache_lock(server_rec *s, const char *key)
for(i = 0; i < mod_cfg->init_cache_size; i++) {
if(strcmp(table[i].key, key) == 0) {
const char *tablekey;
switch (type) {
case AM_CACHE_SESSION:
tablekey = table[i].key;
break;
case AM_CACHE_NAMEID:
/* tablekey may be NULL */
tablekey = am_cache_env_fetch_first(&table[i], "NAME_ID");
break;
default:
tablekey = NULL;
break;
}
if (tablekey == NULL)
continue;
if(strcmp(tablekey, key) == 0) {
/* We found the entry. */
if(table[i].expires > apr_time_now()) {
/* And it hasn't expired. */
@ -113,7 +146,7 @@ am_cache_entry_t *am_cache_new(server_rec *s, const char *key)
/* First we try to find another session with the given key. */
t = am_cache_lock(s, key);
t = am_cache_lock(s, AM_CACHE_SESSION, key);
if(t == NULL) {
@ -288,6 +321,29 @@ int am_cache_env_append(am_cache_entry_t *t,
return OK;
}
/* This function fetches a value from a session.
* If multiple values are available, the first one is returned.
*
* Parameters:
* am_cache_entry_t *t The current session.
* const char *var The name of the value to be stored.
*
* Returns:
* The first value, NULL if it does not exist.
*/
const char *am_cache_env_fetch_first(am_cache_entry_t *t,
const char *var)
{
int i;
for (i = 0; t->size; i++) {
if (strcmp(t->env[i].varname, var) == 0)
return t->env[i].value;
}
return NULL;
}
/* This function populates the subprocess environment with data received
* from the IdP.

View File

@ -112,6 +112,9 @@ static char *am_generate_metadata(apr_pool_t *p, request_rec *r)
"WantAssertionsSigned=\"true\" "
"protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">"
"%s"
"<SingleLogoutService "
"Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:SOAP\" "
"Location=\"%slogout\" />"
"<SingleLogoutService "
"Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" "
"Location=\"%slogout\" />"
@ -128,7 +131,7 @@ static char *am_generate_metadata(apr_pool_t *p, request_rec *r)
"Location=\"%spostResponse\" />"
"</SPSSODescriptor>"
"</EntityDescriptor>",
url, cert, url, url, url);
url, cert, url, url, url, url);
}
#endif /* HAVE_lasso_server_new_from_buffers */
@ -532,19 +535,20 @@ static int am_restore_lasso_profile_state(request_rec *r,
*
* Parameters:
* request_rec *r The logout request.
* LassoLogout *logout A LassoLogout object initiatet with
* 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)
static int am_handle_logout_request(request_rec *r,
LassoLogout *logout, char *msg)
{
gint res;
am_cache_entry_t *session;
/* Process the logout message. Ignore missing signature. */
res = lasso_logout_process_request_msg(logout, r->args);
res = lasso_logout_process_request_msg(logout, msg);
if(res != 0 && res != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Error processing logout request message."
@ -556,7 +560,9 @@ static int am_handle_logout_request(request_rec *r, LassoLogout *logout)
/* Validate the logout message. Ignore missing signature. */
res = lasso_logout_validate_request(logout);
if(res != 0 && res != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) {
if(res != 0 &&
res != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND &&
res != LASSO_PROFILE_ERROR_SESSION_NOT_FOUND) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
"Error validating logout request."
" Lasso error: [%i] %s", res, lasso_strerror(res));
@ -566,12 +572,23 @@ static int am_handle_logout_request(request_rec *r, LassoLogout *logout)
}
/* Delete the session. */
/* Search session using cookie */
session = am_get_request_session(r);
if(session != NULL) {
am_delete_request_session(r, session);
/* If no session found, search by NameID, for IdP initiated SOAP SLO */
if (session == NULL) {
LassoSaml2NameID *nameid;
nameid = LASSO_SAML2_NAME_ID(LASSO_PROFILE(logout)->nameIdentifier);
if (nameid != NULL)
session = am_get_request_session_by_nameid(r, nameid->content);
}
/* Delete the session. */
if (session != NULL)
am_delete_request_session(r, session);
/* Create response message. */
res = lasso_logout_build_response_msg(logout);
@ -824,21 +841,40 @@ static int am_handle_logout(request_rec *r)
/* 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.
*/
if(am_extract_query_parameter(r->pool, r->args, "SAMLRequest") != NULL) {
/* First check for IdP-initiated SOAP logout request */
if ((r->args == NULL) && (r->method_number == M_POST)) {
int rc;
char *post_data;
rc = am_read_post_data(r, &post_data, NULL);
if (rc != OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
"Error reading POST data.");
return HTTP_INTERNAL_SERVER_ERROR;
}
return am_handle_logout_request(r, logout, post_data);
} else if(am_extract_query_parameter(r->pool, r->args,
"SAMLRequest") != NULL) {
/* SAMLRequest - logout request from the IdP. */
return am_handle_logout_request(r, logout);
} else if(am_extract_query_parameter(r->pool, r->args, "SAMLResponse")
!= NULL) {
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) {
} else if(am_extract_query_parameter(r->pool, r->args,
"ReturnTo") != NULL) {
/* RedirectTo - SP initiated logout. */
return am_init_logout_request(r, logout);
} else {
/* Unknown request to the logout handler. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,

View File

@ -22,7 +22,7 @@
#include "auth_mellon.h"
/* This function gets the session associated with a user.
/* This function gets the session associated with a user, using a cookie
*
* Parameters:
* request_rec *r The request we received from the user.
@ -42,9 +42,23 @@ am_cache_entry_t *am_get_request_session(request_rec *r)
return NULL;
}
return am_cache_lock(r->server, session_id);
return am_cache_lock(r->server, AM_CACHE_SESSION, session_id);
}
/* This function gets the session associated with a user, using a NameID
*
* Parameters:
* request_rec *r The request we received from the user.
* char *nameid The NameID
*
* Returns:
* The session associated with the user who places the request, or
* NULL if we don't have a session yet.
*/
am_cache_entry_t *am_get_request_session_by_nameid(request_rec *r, char *nameid)
{
return am_cache_lock(r->server, AM_CACHE_NAMEID, nameid);
}
/* This function creates a new session.
*