lasso/lasso/id-ff/logout.c

1199 lines
38 KiB
C

/* $Id$
*
* Lasso - A free implementation of the Liberty Alliance specifications.
*
* Copyright (C) 2004 Entr'ouvert
* http://lasso.entrouvert.org
*
* Authors: Nicolas Clapies <nclapies@entrouvert.com>
* Valery Febvre <vfebvre@easter-eggs.com>
*
* 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 <string.h>
#include <glib/gprintf.h>
#include <lasso/environs/logout.h>
#include <lasso/xml/errors.h>
#define LASSO_LOGOUT_NODE "LassoLogout"
#define LASSO_REMOTE_PROVIDERID_NODE "RemoteProviderID"
static GObjectClass *parent_class = NULL;
struct _LassoLogoutPrivate
{
gboolean dispose_has_run;
};
/*****************************************************************************/
/* public methods */
/*****************************************************************************/
/**
* lasso_logout_dump:
* @logout: the logout object
*
* This method dumps the logout object in string xml form.
*
* Return value: a newly allocated string or NULL
**/
gchar *
lasso_logout_dump(LassoLogout *logout)
{
LassoNode *initial_node = NULL, *child_node = NULL;
gchar *dump = NULL, *parent_dump = NULL;
LassoNode *node = NULL;
g_return_val_if_fail(LASSO_IS_LOGOUT(logout), NULL);
parent_dump = lasso_profile_dump(LASSO_PROFILE(logout), "Logout");
node = lasso_node_new_from_dump(parent_dump);
g_free(parent_dump);
if (logout->initial_request != NULL) {
initial_node = lasso_node_new();
LASSO_NODE_GET_CLASS(initial_node)->set_name(initial_node, "InitialLogoutResquest");
child_node = lasso_node_copy(logout->initial_request);
LASSO_NODE_GET_CLASS(initial_node)->add_child(initial_node, child_node, FALSE);
lasso_node_destroy(child_node);
LASSO_NODE_GET_CLASS(node)->add_child(node, initial_node, FALSE);
}
if (logout->initial_response != NULL) {
initial_node = lasso_node_new();
LASSO_NODE_GET_CLASS(initial_node)->set_name(initial_node, "InitialLogoutResponse");
child_node = lasso_node_copy(logout->initial_response);
LASSO_NODE_GET_CLASS(initial_node)->add_child(initial_node, child_node, FALSE);
lasso_node_destroy(child_node);
LASSO_NODE_GET_CLASS(node)->add_child(node, initial_node, FALSE);
}
if (logout->initial_remote_providerID != NULL) {
LASSO_NODE_GET_CLASS(node)->new_child(node, "InitialRemoteProviderID",
logout->initial_remote_providerID, FALSE);
}
dump = lasso_node_export(node);
lasso_node_destroy(node);
return dump;
}
/**
* lasso_logout_build_request_msg:
* @logout: the logout object
*
* This method builds the logout request message.
*
* It gets the single logout protocol profile of the remote provider and :
* if it is a SOAP method, then it builds the logout request SOAP message,
* set the msg_body attribute, get the single logout service url
* and set the msg_url attribute of the logout object.
*
* if it is a HTTP-Redirect method, then it builds the logout request QUERY message,
* builds the logout request url, set the msg_url to the logout request url,
* set the msg_body to NULL
*
* HTTP-GET : TODO
*
* Return value: a newly allocated string or NULL
**/
gint
lasso_logout_build_request_msg(LassoLogout *logout)
{
LassoProfile *profile;
LassoProvider *provider;
xmlChar *protocolProfile = NULL;
GError *err = NULL;
gchar *url = NULL, *query = NULL;
lassoProviderType remote_provider_type;
gint ret = 0;
g_return_val_if_fail(LASSO_IS_LOGOUT(logout), -1);
profile = LASSO_PROFILE(logout);
/* get the remote provider type and get the remote provider object */
if (profile->provider_type == lassoProviderTypeSp) {
remote_provider_type = lassoProviderTypeIdp;
}
else if (profile->provider_type == lassoProviderTypeIdp) {
remote_provider_type = lassoProviderTypeSp;
}
else {
message(G_LOG_LEVEL_CRITICAL, "Invalid provider type\n");
ret = -1;
goto done;
}
provider = lasso_server_get_provider_ref(profile->server, profile->remote_providerID, &err);
if (provider == NULL) {
message(G_LOG_LEVEL_CRITICAL, err->message);
ret = err->code;
g_error_free(err);
goto done;
}
/* get the prototocol profile of the logout request */
protocolProfile = lasso_provider_get_singleLogoutProtocolProfile(provider,
remote_provider_type,
NULL);
if (protocolProfile == NULL) {
message(G_LOG_LEVEL_CRITICAL, "Single logout protocol profile not found\n");
ret = -1;
goto done;
}
/* build the logout request message */
if (xmlStrEqual(protocolProfile, lassoLibProtocolProfileSloSpSoap) || \
xmlStrEqual(protocolProfile, lassoLibProtocolProfileSloIdpSoap)) {
/* sign the request message */
lasso_samlp_request_abstract_sign_signature_tmpl(LASSO_SAMLP_REQUEST_ABSTRACT(profile->request),
profile->server->private_key,
profile->server->certificate);
/* build the logout request message */
profile->msg_url = lasso_provider_get_soapEndpoint(provider,
remote_provider_type,
NULL);
profile->msg_body = lasso_node_export_to_soap(profile->request);
}
else if (xmlStrEqual(protocolProfile,lassoLibProtocolProfileSloSpHttp) || \
xmlStrEqual(protocolProfile,lassoLibProtocolProfileSloIdpHttp)) {
/* build and optionaly sign the logout request QUERY message */
url = lasso_provider_get_singleLogoutServiceURL(provider, remote_provider_type, NULL);
query = lasso_node_export_to_query(profile->request,
profile->server->signature_method,
profile->server->private_key);
if ( (url == NULL) || (query == NULL) ) {
message(G_LOG_LEVEL_CRITICAL, "Error while building url and query\n");
ret = -1;
goto done;
}
/* build the msg_url */
profile->msg_url = g_new(gchar, strlen(url)+strlen(query)+1+1);
g_sprintf(profile->msg_url, "%s?%s", url, query);
profile->msg_body = NULL;
}
else {
message(G_LOG_LEVEL_CRITICAL, "Invalid logout protocol profile\n");
ret = -1;
goto done;
}
done:
if (protocolProfile != NULL) {
xmlFree(protocolProfile);
}
if (url != NULL) {
xmlFree(url);
}
if (query != NULL) {
xmlFree(query);
}
return ret;
}
/**
* lasso_logout_build_response_msg:
* @logout: the logout object
*
* This method builds the logout response message.
*
* It gets the request message method and :
* if it is a SOAP method, then it builds the logout response SOAP message,
* set the msg_body attribute, get the single logout service return url
* and set the msg_url attribute of the logout object.
*
* if it is a HTTP-Redirect method, then it builds the logout response QUERY message,
* builds the logout response url, set the msg_url to the logout response url,
* set the msg_body to NULL
*
* HTTP-GET : TODO
*
* Return value:
**/
gint
lasso_logout_build_response_msg(LassoLogout *logout)
{
LassoProfile *profile;
LassoProvider *provider;
gchar *url = NULL, *query = NULL;
GError *err = NULL;
gint ret = 0;
gint remote_provider_type;
g_return_val_if_fail(LASSO_IS_LOGOUT(logout), -1);
profile = LASSO_PROFILE(logout);
/* get the provider */
provider = lasso_server_get_provider_ref(profile->server, profile->remote_providerID, &err);
if (provider == NULL) {
message(G_LOG_LEVEL_CRITICAL, err->message);
ret = err->code;
g_error_free(err);
goto done;
}
/* get the remote provider type */
if (profile->provider_type == lassoProviderTypeSp) {
remote_provider_type = lassoProviderTypeIdp;
}
else if (profile->provider_type == lassoProviderTypeIdp) {
remote_provider_type = lassoProviderTypeSp;
}
else {
message(G_LOG_LEVEL_CRITICAL, "Invalid provider type\n");
ret = -1;
goto done;
}
/* build a SOAP or HTTP-Redirect logout response message */
switch (profile->http_request_method) {
case lassoHttpMethodSoap:
/* optionaly sign the response message */
if (profile->server->private_key) {
lasso_samlp_response_abstract_set_signature(LASSO_SAMLP_RESPONSE_ABSTRACT(profile->response),
profile->server->signature_method,
profile->server->private_key,
profile->server->certificate);
}
/* build the logout response messsage */
profile->msg_url = NULL;
profile->msg_body = lasso_node_export_to_soap(profile->response);
break;
case lassoHttpMethodRedirect:
url = lasso_provider_get_singleLogoutServiceReturnURL(provider, remote_provider_type, NULL);
query = lasso_node_export_to_query(profile->response,
profile->server->signature_method,
profile->server->private_key);
if ( (url == NULL) || (query == NULL) ) {
message(G_LOG_LEVEL_CRITICAL, "Url %s or query %s not found\n", url, query);
ret = -1;
goto done;
}
profile->msg_url = g_new(gchar, strlen(url)+strlen(query)+1+1);
g_sprintf(profile->msg_url, "%s?%s", url, query);
profile->msg_body = NULL;
break;
default:
ret = LASSO_PROFILE_ERROR_MISSING_REQUEST;
goto done;
}
done:
if (url != NULL) {
xmlFree(url);
}
if (query != NULL) {
xmlFree(query);
}
return ret;
}
/**
* lasso_logout_destroy:
* @logout: the logout object
*
* destroy the logout object
*
**/
void
lasso_logout_destroy(LassoLogout *logout)
{
g_object_unref(G_OBJECT(logout));
}
/**
* lasso_logout_get_next_providerID:
* @logout: the logout object
*
* This method returns the next logout request service provider id
* excepted the initial service provider id.
*
* This method returns the next provider id to send a logout request.
* get the current provider id with index_remote_providerID as index in session->providerIDs.
*
* Return value: a newly allocated string or NULL
**/
gchar*
lasso_logout_get_next_providerID(LassoLogout *logout)
{
LassoProfile *profile;
gchar *provider_id;
g_return_val_if_fail(LASSO_IS_LOGOUT(logout), NULL);
profile = LASSO_PROFILE(logout);
g_return_val_if_fail(LASSO_IS_SESSION(profile->session), NULL);
provider_id = lasso_session_get_next_providerID(profile->session);
/* if initial provider id, then get a next provider id */
if (xmlStrEqual(logout->initial_remote_providerID, provider_id)) {
provider_id = lasso_session_get_next_providerID(profile->session);
}
return provider_id;
}
/**
* lasso_logout_init_request:
* @logout: the logout object
* @remote_providerID: The provider id of the logout requested provider.
* If it is set to NULL, then gets the default first remote provider id.
*
* It sets a new logout request to the remote provider id
* with the provider id of the requester (from the server object )
* and name identifier of the federated principal
*
* Return value: 0 if OK else < 0
**/
gint
lasso_logout_init_request(LassoLogout *logout,
gchar *remote_providerID,
lassoHttpMethod request_method) /* FIXME : support this param to allow the user to choose the request method */
{
LassoProfile *profile = NULL;
LassoProvider *provider = NULL;
LassoNode *nameIdentifier = NULL;
LassoFederation *federation = NULL;
xmlChar *content = NULL, *nameQualifier = NULL, *format = NULL;
xmlChar *singleLogoutProtocolProfile = NULL;
GError *err = NULL;
lassoSignatureType signature_type = lassoSignatureTypeNone;
gint ret = 0;
gboolean is_http_redirect_get_method = FALSE; /* tell if the logout protocol profile is HTTP Redirect / GET */
g_return_val_if_fail(LASSO_IS_LOGOUT(logout), -1);
profile = LASSO_PROFILE(logout);
/* verify if the identity and session exist */
if (profile->identity == NULL) {
message(G_LOG_LEVEL_CRITICAL, "Identity not found\n");
ret = -1;
goto done;
}
if (profile->session == NULL) {
message(G_LOG_LEVEL_CRITICAL, "Session not found\n");
ret = -1;
goto done;
}
/* get the remote provider id */
if (remote_providerID == NULL) {
/* No remote provider id, get the next assertion peer provider id */
profile->remote_providerID = lasso_session_get_first_providerID(profile->session);
}
else {
/* "A remote provider id for logout request */
profile->remote_providerID = g_strdup(remote_providerID);
}
if (profile->remote_providerID == NULL) {
message(G_LOG_LEVEL_CRITICAL, "No remote provider id to send the logout request\n");
ret = -1;
goto done;
}
/* get federation */
federation = lasso_identity_get_federation(profile->identity, profile->remote_providerID);
if (federation == NULL) {
message(G_LOG_LEVEL_CRITICAL, "Federation not found\n");
ret = -1;
goto done;
}
/* get the name identifier (!!! depend on the provider type : SP or IDP !!!)*/
switch (profile->provider_type) {
case lassoProviderTypeSp:
nameIdentifier = lasso_federation_get_local_nameIdentifier(federation);
if (nameIdentifier == NULL) {
nameIdentifier = lasso_federation_get_remote_nameIdentifier(federation);
}
break;
case lassoProviderTypeIdp:
nameIdentifier = lasso_federation_get_remote_nameIdentifier(federation);
if (nameIdentifier == NULL) {
nameIdentifier = lasso_federation_get_local_nameIdentifier(federation);
}
break;
default:
message(G_LOG_LEVEL_CRITICAL, "Invalid provider type\n");
ret = -1;
goto done;
}
if (nameIdentifier == NULL) {
message(G_LOG_LEVEL_CRITICAL, "Name identifier not found for %s\n",
profile->remote_providerID);
ret = -1;
goto done;
}
/* build the request */
content = lasso_node_get_content(nameIdentifier, NULL);
nameQualifier = lasso_node_get_attr_value(nameIdentifier, "NameQualifier", NULL);
format = lasso_node_get_attr_value(nameIdentifier, "Format", NULL);
/* get the single logout protocol profile and set a new logout request object */
provider = lasso_server_get_provider_ref(profile->server, profile->remote_providerID, &err);
if (provider == NULL) {
message(G_LOG_LEVEL_CRITICAL, err->message);
ret = err->code;
g_error_free(err);
goto done;
}
if (profile->provider_type == lassoProviderTypeIdp) {
singleLogoutProtocolProfile = lasso_provider_get_singleLogoutProtocolProfile(provider, lassoProviderTypeSp, NULL);
}
else if (profile->provider_type == lassoProviderTypeSp) {
singleLogoutProtocolProfile = lasso_provider_get_singleLogoutProtocolProfile(provider, lassoProviderTypeIdp, NULL);
}
else {
message(G_LOG_LEVEL_CRITICAL, "Invalid provider type\n");
ret = -1;
goto done;
}
if (singleLogoutProtocolProfile == NULL) {
message(G_LOG_LEVEL_CRITICAL, "Single logout protocol profile not found\n");
ret = -1;
goto done;
}
if (xmlStrEqual(singleLogoutProtocolProfile, lassoLibProtocolProfileSloSpSoap) || \
xmlStrEqual(singleLogoutProtocolProfile, lassoLibProtocolProfileSloIdpSoap)) {
profile->request = lasso_logout_request_new(profile->server->providerID,
content,
nameQualifier,
format,
lassoSignatureTypeWithX509,
lassoSignatureMethodRsaSha1);
}
else if (xmlStrEqual(singleLogoutProtocolProfile, lassoLibProtocolProfileSloSpHttp) || \
xmlStrEqual(singleLogoutProtocolProfile, lassoLibProtocolProfileSloIdpHttp)) {
/* later tell it is a HTTP Redirect / GET method */
is_http_redirect_get_method = TRUE;
profile->request = lasso_logout_request_new(profile->server->providerID,
content,
nameQualifier,
format,
lassoSignatureTypeNone,
0);
}
else {
message(G_LOG_LEVEL_CRITICAL, "Invalid single logout protocol profile : %s\n", singleLogoutProtocolProfile);
ret = -1;
goto done;
}
if (LASSO_IS_LOGOUT_REQUEST(profile->request) == FALSE) {
message(G_LOG_LEVEL_CRITICAL, "Error while building the request\n");
ret = -1;
goto done;
}
/* set the name identifier in logout object */
profile->nameIdentifier = content;
/* if logout request from a SP and if an HTTP Redirect / GET method, then remove assertion */
if ( (profile->provider_type == lassoProviderTypeSp) && (is_http_redirect_get_method == TRUE) ) {
lasso_session_remove_assertion(profile->session, profile->remote_providerID);
}
done:
if (federation != NULL) {
lasso_federation_destroy(federation);
}
if (nameIdentifier != NULL ) {
lasso_node_destroy(nameIdentifier);
}
if (nameQualifier != NULL) {
xmlFree(nameQualifier);
}
if (format != NULL) {
xmlFree(format);
}
if (singleLogoutProtocolProfile != NULL) {
xmlFree(singleLogoutProtocolProfile);
}
return ret;
}
/**
* lasso_logout_process_request_msg:
* @logout: the logout object
* @request_msg: the logout request message
* @request_method: the logout request method
*
* Process a logout request.
* if it is a SOAP request method then it builds the logout request object
* from the SOAP message and optionaly verify the signature of the logout request.
*
* if it is a HTTP-Redirect request method then it builds the logout request object
* from the QUERY message and verify the signature
*
* Set the msg_nameIdentifier attribute with the NameIdentifier content of the logout object and
* optionaly set the msg_relayState attribute with the RelayState of the logout request
*
* Return value: 0 if OK else < 0
**/
gint lasso_logout_process_request_msg(LassoLogout *logout,
gchar *request_msg,
lassoHttpMethod request_method)
{
LassoProfile *profile;
LassoProvider *provider;
gchar *remote_providerID = NULL;
gint ret = 0;
GError *err = NULL;
g_return_val_if_fail(LASSO_IS_LOGOUT(logout), -1);
g_return_val_if_fail(request_msg != NULL, -1);
profile = LASSO_PROFILE(logout);
/* build and process the logout request depending on the logout request type and optionaly verify the signature */
switch (request_method) {
case lassoHttpMethodSoap:
profile->request = lasso_logout_request_new_from_export(request_msg,
lassoNodeExportTypeSoap);
/* verify requets is a LogoutRequest */
if (LASSO_IS_LOGOUT_REQUEST(profile->request) == FALSE) {
message(G_LOG_LEVEL_CRITICAL, "Message is not a LogoutRequest\n");
ret = -1;
goto done;
}
/* signature verification */
remote_providerID = lasso_node_get_child_content(profile->request, "ProviderID", NULL, NULL);
if (remote_providerID == NULL) {
message(G_LOG_LEVEL_CRITICAL, "ProviderID not found\n");
ret = -1;
goto done;
}
provider = lasso_server_get_provider_ref(profile->server, remote_providerID, &err);
if (provider == NULL) {
message(G_LOG_LEVEL_CRITICAL, err->message);
ret = err->code;
g_error_free(err);
goto done;
}
if (provider->ca_certificate != NULL) {
ret = lasso_node_verify_x509_signature(profile->request, provider->ca_certificate);
/* ret = lasso_node_verify_signature(profile->request, provider->public_key); */
}
break;
case lassoHttpMethodRedirect:
profile->request = lasso_logout_request_new_from_export(request_msg,
lassoNodeExportTypeQuery);
/* if problem while rebuilding the response, then return invalid query code error */
if (LASSO_IS_LOGOUT_REQUEST(profile->request) == FALSE) {
ret = LASSO_PROFILE_ERROR_INVALID_QUERY;
goto done;
}
break;
default:
message(G_LOG_LEVEL_CRITICAL, "Invalid request method\n");
ret = -1;
goto done;
}
if(LASSO_IS_LOGOUT_REQUEST(profile->request) == FALSE) {
message(G_LOG_LEVEL_CRITICAL, "Error while building the request from msg\n");
ret = -1;
goto done;
}
/* set the http request method */
profile->http_request_method = request_method;
/* Set the NameIdentifier */
profile->nameIdentifier = lasso_node_get_child_content(profile->request,
"NameIdentifier",
NULL, NULL);
done:
if (remote_providerID != NULL ) {
xmlFree(remote_providerID);
}
return ret;
}
/**
* lasso_logout_process_response_msg:
* @logout: the logout object
* @response_msg: the response message
* @response_method: the response method
*
* Process the response method :
* build the logout response object
* verify the status code value
*
* Return value: 0 if OK else < 0
**/
gint
lasso_logout_process_response_msg(LassoLogout *logout,
gchar *response_msg,
lassoHttpMethod response_method)
{
LassoProfile *profile;
xmlChar *statusCodeValue;
LassoNode *statusCode;
GError *err = NULL;
gint ret = 0;
g_return_val_if_fail(LASSO_IS_LOGOUT(logout), -1);
g_return_val_if_fail(response_msg != NULL, -1);
profile = LASSO_PROFILE(logout);
/* build logout response object */
switch (response_method) {
case lassoHttpMethodSoap:
profile->response = lasso_logout_response_new_from_export(response_msg, lassoNodeExportTypeSoap);
break;
case lassoHttpMethodRedirect:
profile->response = lasso_logout_response_new_from_export(response_msg, lassoNodeExportTypeQuery);
/* if problem while rebuilding the response, then return invalid query code error */
if (LASSO_IS_LOGOUT_RESPONSE(profile->response) == FALSE) {
ret = LASSO_PROFILE_ERROR_INVALID_QUERY;
goto done;
}
break;
default:
message(G_LOG_LEVEL_CRITICAL, "Invalid response method\n");
ret = -1;
goto done;
}
if (LASSO_IS_LOGOUT_RESPONSE(profile->response) == FALSE) {
message(G_LOG_LEVEL_CRITICAL, "Message is not a LogoutResponse\n");
ret = -1;
goto done;
}
/* get the status code */
statusCode = lasso_node_get_child(profile->response, "StatusCode", NULL, NULL);
if (statusCode == NULL) {
message(G_LOG_LEVEL_CRITICAL, "StatusCode node not found\n");
ret = -1;
goto done;
}
statusCodeValue = lasso_node_get_attr_value(statusCode, "Value", NULL);
if (!xmlStrEqual(statusCodeValue, lassoSamlStatusCodeSuccess)) {
/* At SP, if the request method was a SOAP type, if at IDP, then rebuild the request message with HTTP method */
if (profile->provider_type == lassoProviderTypeSp && profile->http_request_method == lassoHttpMethodSoap) {
/* temporary vars */
LassoProvider *provider;
gchar *url, *query;
provider = lasso_server_get_provider_ref(profile->server, profile->remote_providerID, &err);
if (provider == NULL) {
message(G_LOG_LEVEL_CRITICAL, err->message);
ret = err->code;
g_error_free(err);
goto done;
}
/* FIXME : verify the IDP support a HTTP method */
/* Build and optionaly sign the logout request QUERY message */
url = lasso_provider_get_singleLogoutServiceURL(provider, lassoProviderTypeIdp, NULL);
query = lasso_node_export_to_query(profile->request,
profile->server->signature_method,
profile->server->private_key);
profile->msg_url = g_new(gchar, strlen(url)+strlen(query)+1+1);
g_sprintf(profile->msg_url, "%s?%s", url, query);
profile->msg_body = NULL;
/* send a HTTP Redirect / GET method, so first remove session */
lasso_session_remove_assertion(profile->session, profile->remote_providerID);
}
message(G_LOG_LEVEL_WARNING, "Status code value is not Success\n");
ret = LASSO_LOGOUT_ERROR_UNSUPPORTED_PROFILE;
goto done;
}
/* LogoutResponse status code value is ok, so remove assertion */
profile->remote_providerID = lasso_node_get_child_content(profile->response,
"ProviderID",
lassoLibHRef,
NULL);
/* set the msg_relayState */
profile->msg_relayState = lasso_node_get_child_content(profile->request, "RelayState", lassoLibHRef, NULL);
/* Only if SOAP method, then remove assertion */
if (response_method == lassoHttpMethodSoap) {
lasso_session_remove_assertion(profile->session, profile->remote_providerID);
}
switch (profile->provider_type) {
case lassoProviderTypeSp:
/* */
break;
case lassoProviderTypeIdp:
/* At IDP, if no more assertion for other providers and if initial remote provider id is set,
then remove his assertion and restore his original requester infos */
if(logout->initial_remote_providerID && profile->session->providerIDs->len == 1){
lasso_session_remove_assertion(profile->session, logout->initial_remote_providerID);
profile->remote_providerID = logout->initial_remote_providerID;
profile->request = logout->initial_request;
profile->response = logout->initial_response;
}
break;
default:
message(G_LOG_LEVEL_CRITICAL, "Invalid provider type\n");
}
done:
return ret;
}
/**
* lasso_logout_reset_session_index:
* @logout: the logout object
*
* Call the reset of the index provider id in session object
*
* Return value: 0 if OK else < 0
**/
gint lasso_logout_reset_session_index(LassoLogout *logout)
{
LassoProfile *profile;
g_return_val_if_fail(LASSO_IS_LOGOUT(logout), -1);
profile = LASSO_PROFILE(logout);
g_return_val_if_fail(LASSO_IS_SESSION(profile->session), -1);
lasso_session_reset_index_providerID(profile->session);
return 0;
}
/**
* lasso_logout_validate_request:
* @logout: the logout object
*
* Validate the logout request :
* sets the logout response
* verifies the ProviderID
* verifies the federation with the NameIdentifier
* verifies the authentication with the NameIdentifier
* if SOAP method at identity provider, verify all the remote service providers support SOAP method.
*
* Return value: O if OK else < 0
**/
gint
lasso_logout_validate_request(LassoLogout *logout)
{
LassoProfile *profile;
LassoFederation *federation;
LassoNode *nameIdentifier, *assertion;
LassoNode *statusCode;
LassoNodeClass *statusCode_class;
xmlChar *remote_providerID;
gint ret = 0;
g_return_val_if_fail(LASSO_IS_LOGOUT(logout), -1);
profile = LASSO_PROFILE(logout);
/* verify logout request */
if (profile->request == NULL) {
ret = LASSO_PROFILE_ERROR_MISSING_REQUEST;
goto done;
}
/* Set the remote provider id from the request */
remote_providerID = lasso_node_get_child_content(profile->request, "ProviderID",
NULL, NULL);
if (remote_providerID == NULL) {
message(G_LOG_LEVEL_CRITICAL, "ProviderID in LogoutRequest not found\n");
ret = -1;
goto done;
}
profile->remote_providerID = remote_providerID;
/* Set LogoutResponse */
switch (profile->http_request_method) {
case lassoHttpMethodSoap:
profile->response = lasso_logout_response_new(profile->server->providerID,
lassoSamlStatusCodeSuccess,
profile->request,
lassoSignatureTypeWithX509,
lassoSignatureMethodRsaSha1);
break;
case lassoHttpMethodRedirect:
profile->response = lasso_logout_response_new(profile->server->providerID,
lassoSamlStatusCodeSuccess,
profile->request,
lassoSignatureTypeNone,
0);
break;
default:
message(G_LOG_LEVEL_CRITICAL, "Invalid HTTP request method\n");
ret = -1;
goto done;
}
if (LASSO_IS_LOGOUT_RESPONSE(profile->response) == FALSE) {
message(G_LOG_LEVEL_CRITICAL, "Error while building response\n");
ret = -1;
goto done;
}
statusCode = lasso_node_get_child(profile->response, "StatusCode", NULL, NULL);
statusCode_class = LASSO_NODE_GET_CLASS(statusCode);
nameIdentifier = lasso_node_get_child(profile->request, "NameIdentifier",
NULL, NULL);
if (nameIdentifier == NULL) {
message(G_LOG_LEVEL_CRITICAL, "Name identifier not found in logout request\n");
statusCode_class->set_prop(statusCode, "Value", lassoLibStatusCodeFederationDoesNotExist);
ret = -1;
goto done;
}
remote_providerID = lasso_node_get_child_content(profile->request, "ProviderID",
NULL, NULL);
if (remote_providerID == NULL) {
message(G_LOG_LEVEL_CRITICAL, "Provider id not found in logout request\n");
ret = -1;
goto done;
}
/* verify authentication */
if (profile->identity == NULL) {
message(G_LOG_LEVEL_WARNING, "Identity not found\n");
statusCode_class->set_prop(statusCode, "Value", lassoSamlStatusCodeRequestDenied);
ret = -1;
goto done;
}
assertion = lasso_session_get_assertion(profile->session, remote_providerID);
if (assertion == NULL) {
message(G_LOG_LEVEL_WARNING, "%s has no assertion\n", remote_providerID);
statusCode_class->set_prop(statusCode, "Value", lassoSamlStatusCodeRequestDenied);
ret = -1;
goto done;
}
lasso_node_destroy(assertion);
/* Verify federation */
federation = lasso_identity_get_federation(profile->identity, remote_providerID);
if (federation == NULL) {
message(G_LOG_LEVEL_WARNING, "No federation for %s\n", remote_providerID);
statusCode_class->set_prop(statusCode, "Value", lassoLibStatusCodeFederationDoesNotExist);
ret = -1;
goto done;
}
if (lasso_federation_verify_nameIdentifier(federation, nameIdentifier) == FALSE) {
message(G_LOG_LEVEL_WARNING, "No name identifier for %s\n", remote_providerID);
statusCode_class->set_prop(statusCode, "Value", lassoLibStatusCodeFederationDoesNotExist);
ret = -1;
goto done;
}
/* if SOAP request method at IDP then verify all the remote service providers support SOAP protocol profile.
If one remote authenticated principal service provider doesn't support SOAP
then return UnsupportedProfile to original service provider */
if (profile->provider_type==lassoProviderTypeIdp && profile->http_request_method==lassoHttpMethodSoap) {
gboolean all_http_soap;
LassoProvider *provider;
gchar *providerID, *protocolProfile;
int i;
all_http_soap = TRUE;
for (i = 0; i<profile->server->providers->len; i++) {
provider = g_ptr_array_index(profile->server->providers, i);
providerID = lasso_provider_get_providerID(provider);
/* if the original service provider then continue */
if (xmlStrEqual(remote_providerID, providerID)) {
continue;
}
/* if principal is not authenticated with this remote service provider, continue */
assertion = lasso_session_get_assertion(profile->session, providerID);
if (assertion == NULL) {
continue;
}
/* if protocolProfile is SOAP continue else break */
protocolProfile = lasso_provider_get_singleLogoutProtocolProfile(provider, lassoProviderTypeIdp, NULL);
if (protocolProfile == NULL || !xmlStrEqual(protocolProfile, lassoLibProtocolProfileSloSpSoap)) {
all_http_soap = FALSE;
break;
}
if (protocolProfile != NULL) {
xmlFree(protocolProfile);
}
if (providerID != NULL) {
xmlFree(providerID);
}
}
if (all_http_soap==FALSE) {
statusCode_class->set_prop(statusCode, "Value", lassoLibStatusCodeUnsupportedProfile);
ret = -1;
goto done;
}
}
lasso_federation_destroy(federation);
/* authentication is ok, federation is ok, propagation support is ok, remove federation */
/* verification is ok, save name identifier in logout object */
lasso_session_remove_assertion(profile->session, profile->remote_providerID);
if (profile->provider_type == lassoProviderTypeIdp) {
logout->initial_remote_providerID = g_strdup(profile->remote_providerID);
if (profile->session->providerIDs->len>1) {
logout->initial_request = profile->request;
profile->request = NULL;
logout->initial_response = profile->response;
profile->response = NULL;
profile->remote_providerID = NULL;
}
}
done:
return ret;
}
/*****************************************************************************/
/* overrided parent class methods */
/*****************************************************************************/
static void
lasso_logout_dispose(LassoLogout *logout)
{
if (logout->private->dispose_has_run) {
return;
}
logout->private->dispose_has_run = TRUE;
debug("Logout object 0x%x disposed ...\n", logout);
/* unref reference counted objects */
lasso_node_destroy(logout->initial_request);
lasso_node_destroy(logout->initial_response);
parent_class->dispose(G_OBJECT(logout));
}
static void
lasso_logout_finalize(LassoLogout *logout)
{
debug("Logout object 0x%x finalized ...\n", logout);
g_free(logout->initial_remote_providerID);
g_free(logout->private);
parent_class->finalize(G_OBJECT(logout));
}
/*****************************************************************************/
/* instance and class init functions */
/*****************************************************************************/
static void
lasso_logout_instance_init(GTypeInstance *instance,
gpointer g_class)
{
LassoLogout *logout = LASSO_LOGOUT(instance);
logout->private = g_new (LassoLogoutPrivate, 1);
logout->private->dispose_has_run = FALSE;
logout->initial_request = NULL;
logout->initial_response = NULL;
logout->initial_remote_providerID = NULL;
}
static void
lasso_logout_class_init(LassoLogoutClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(class);
parent_class = g_type_class_peek_parent(class);
/* override parent class methods */
gobject_class->dispose = (void *)lasso_logout_dispose;
gobject_class->finalize = (void *)lasso_logout_finalize;
}
GType lasso_logout_get_type() {
static GType this_type = 0;
if (!this_type) {
static const GTypeInfo this_info = {
sizeof (LassoLogoutClass),
NULL,
NULL,
(GClassInitFunc) lasso_logout_class_init,
NULL,
NULL,
sizeof(LassoLogout),
0,
(GInstanceInitFunc) lasso_logout_instance_init,
};
this_type = g_type_register_static(LASSO_TYPE_PROFILE,
"LassoLogout",
&this_info, 0);
}
return this_type;
}
/**
* lasso_logout_new:
* @server: the logout object
* @provider_type: the provider type (service provider or identity provider)
*
* initialises a new logout object
*
* Return value: a new instance of logout object or NULL
**/
LassoLogout*
lasso_logout_new(LassoServer *server,
lassoProviderType provider_type)
{
LassoLogout *logout;
g_return_val_if_fail(LASSO_IS_SERVER(server), NULL);
/* set the logout object */
logout = g_object_new(LASSO_TYPE_LOGOUT,
"server", lasso_server_copy(server),
"provider_type", provider_type,
NULL);
return logout;
}
LassoLogout*
lasso_logout_new_from_dump(LassoServer *server,
gchar *dump)
{
LassoLogout *logout;
LassoProfile *profile;
LassoNode *node_dump, *request_node, *response_node;
LassoNode *initial_request_node, *initial_response_node;
gchar *type, *export, *initial_remote_providerID;
g_return_val_if_fail(LASSO_IS_SERVER(server), NULL);
g_return_val_if_fail(dump != NULL, NULL);
logout = LASSO_LOGOUT(g_object_new(LASSO_TYPE_LOGOUT,
"server", lasso_server_copy(server),
NULL));
profile = LASSO_PROFILE(logout);
node_dump = lasso_node_new_from_dump(dump);
/* profile attributes */
profile->nameIdentifier = lasso_node_get_child_content(node_dump, "NameIdentifier",
lassoLassoHRef, NULL);
profile->remote_providerID = lasso_node_get_child_content(node_dump, "RemoteProviderID",
lassoLassoHRef, NULL);
profile->msg_url = lasso_node_get_child_content(node_dump, "MsgUrl",
lassoLassoHRef, NULL);
profile->msg_body = lasso_node_get_child_content(node_dump, "MsgBody",
lassoLassoHRef, NULL);
profile->msg_relayState = lasso_node_get_child_content(node_dump, "MsgRelayState",
lassoLassoHRef, NULL);
/* rebuild request */
request_node = lasso_node_get_child(node_dump, "LogoutRequest", lassoLibHRef, NULL);
if (LASSO_IS_NODE(request_node) == TRUE) {
export = lasso_node_export(request_node);
profile->request = lasso_logout_request_new_from_export(export,
lassoNodeExportTypeXml);
g_free(export);
lasso_node_destroy(request_node);
}
/* rebuild response */
response_node = lasso_node_get_child(node_dump, "LogoutResponse", lassoLibHRef, NULL);
if (response_node != NULL) {
export = lasso_node_export(response_node);
profile->response = lasso_logout_response_new_from_export(export,
lassoNodeExportTypeXml);
g_free(export);
lasso_node_destroy(response_node);
}
/* provider type */
type = lasso_node_get_child_content(node_dump, "ProviderType", lassoLassoHRef, NULL);
profile->provider_type = atoi(type);
xmlFree(type);
/* logout attributes */
/* Initial logout request */
initial_request_node = lasso_node_get_child(node_dump, "InitialRequest", lassoLassoHRef, NULL);
if (initial_request_node != NULL) {
request_node = lasso_node_get_child(node_dump, "LogoutRequest", lassoLibHRef, NULL);
export = lasso_node_export(request_node);
profile->request = lasso_logout_request_new_from_export(export,
lassoNodeExportTypeXml);
g_free(export);
lasso_node_destroy(request_node);
}
/* Initial logout response */
initial_response_node = lasso_node_get_child(node_dump, "InitialResponse", lassoLassoHRef, NULL);
if (initial_response_node != NULL) {
response_node = lasso_node_get_child(node_dump, "LogoutResponse", lassoLibHRef, NULL);
export = lasso_node_export(response_node);
profile->response = lasso_logout_response_new_from_export(export,
lassoNodeExportTypeXml);
g_free(export);
lasso_node_destroy(response_node);
}
/* Initial logout remote provider id */
logout->initial_remote_providerID = lasso_node_get_child_content(node_dump, "InitialRemoteProviderID", lassoLassoHRef, NULL);
return logout;
}