/* $Id$ * * Lasso - A free implementation of the Liberty Alliance specifications. * * Copyright (C) 2004 Entr'ouvert * http://lasso.entrouvert.org * * Authors: Nicolas Clapies * Valery Febvre * * 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 /*****************************************************************************/ /* public methods */ /*****************************************************************************/ /** * lasso_defederation_build_notification_msg: * @defederation: the federation termination object * * This method builds the federation termination notification message. * * It gets the federation termination notification protocol profile and: * * - if it is a SOAP method, then it builds the federation termination * notification SOAP message, optionaly signs the notification node, set the * msg_body attribute, gets the SoapEndpoint url and set the msg_url * attribute of the federation termination object. * * - if it is a HTTP-Redirect method, then it builds the federation termination * notification QUERY message (optionaly signs the notification message), * builds the federation termination notification url with federation * termination service url, set the msg_url attribute of the federation * termination object, set the msg_body to NULL * * Return value: O of OK else < 0 **/ gint lasso_defederation_build_notification_msg(LassoDefederation *defederation) { LassoProfile *profile; LassoProvider *remote_provider; gchar *url = NULL, *query = NULL; g_return_val_if_fail(LASSO_IS_DEFEDERATION(defederation), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); profile = LASSO_PROFILE(defederation); /* get the remote provider object */ remote_provider = g_hash_table_lookup(profile->server->providers, profile->remote_providerID); if (LASSO_IS_PROVIDER(remote_provider) == FALSE) { return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND, profile->remote_providerID); } /* get the protocol profile type */ /* build the federation termination notification message (SOAP or HTTP-Redirect) */ if (profile->http_request_method == LASSO_HTTP_METHOD_SOAP) { /* build the logout request message */ profile->msg_url = lasso_provider_get_metadata_one( remote_provider, "SoapEndpoint"); profile->msg_body = lasso_node_export_to_soap(profile->request, profile->server->private_key, profile->server->certificate); return 0; } if (profile->http_request_method == LASSO_HTTP_METHOD_REDIRECT) { /* build and optionaly sign the query message and build the * federation termination notification url */ url = lasso_provider_get_metadata_one(remote_provider, "FederationTerminationServiceURL"); if (url == NULL) { return critical_error(LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL); } query = lasso_node_export_to_query(profile->request, profile->server->signature_method, profile->server->private_key); if (query == NULL) { g_free(url); return critical_error(LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED); } profile->msg_url = g_strdup_printf("%s?%s", url, query); profile->msg_body = NULL; g_free(url); g_free(query); return 0; } return critical_error(LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD); } /** * lasso_defederation_destroy: * @defederation: the federation termination object * * This method destroys the federation termination object * **/ void lasso_defederation_destroy(LassoDefederation *defederation) { g_object_unref(G_OBJECT(defederation)); } /** * lasso_defederation_init_notification: * @defederation: the federation termination object * @remote_providerID: the provider id of the federation termination notified * provider. * * It sets a new federation termination notification to the remote provider id * with the provider id of the requester (from the server object ) * and the name identifier of the federated principal * * Return value: 0 if OK else < 0 **/ gint lasso_defederation_init_notification(LassoDefederation *defederation, gchar *remote_providerID, lassoHttpMethod http_method) { LassoProfile*profile; LassoProvider *remote_provider; LassoFederation *federation; LassoSamlNameIdentifier *nameIdentifier = NULL; g_return_val_if_fail(LASSO_IS_DEFEDERATION(defederation), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); if (remote_providerID == NULL) { return critical_error(LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID); } profile = LASSO_PROFILE(defederation); /* set the remote provider id */ profile->remote_providerID = g_strdup(remote_providerID); remote_provider = g_hash_table_lookup( profile->server->providers, profile->remote_providerID); if (LASSO_IS_PROVIDER(remote_provider) == FALSE) { return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND, profile->remote_providerID); } /* get federation */ federation = g_hash_table_lookup(profile->identity->federations, profile->remote_providerID); if (federation == NULL) { return critical_error(LASSO_PROFILE_ERROR_FEDERATION_NOT_FOUND); } /* get the nameIdentifier to send the federation termination notification */ nameIdentifier = lasso_profile_get_nameIdentifier(profile); if (nameIdentifier == NULL) { return critical_error(LASSO_PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND); } /* get / verify http method */ if (http_method == LASSO_HTTP_METHOD_ANY) { http_method = lasso_provider_get_first_http_method( LASSO_PROVIDER(profile->server), remote_provider, LASSO_MD_PROTOCOL_TYPE_FEDERATION_TERMINATION); } else { if (lasso_provider_accept_http_method(LASSO_PROVIDER(profile->server), remote_provider, LASSO_MD_PROTOCOL_TYPE_FEDERATION_TERMINATION, http_method, TRUE) == FALSE) { return critical_error(LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE); } } /* build the request */ if (http_method == LASSO_HTTP_METHOD_SOAP) { profile->request = lasso_lib_federation_termination_notification_new_full( LASSO_PROVIDER(profile->server)->ProviderID, nameIdentifier, LASSO_SIGNATURE_TYPE_WITHX509, LASSO_SIGNATURE_METHOD_RSA_SHA1); } if (http_method == LASSO_HTTP_METHOD_REDIRECT) { profile->request = lasso_lib_federation_termination_notification_new_full( LASSO_PROVIDER(profile->server)->ProviderID, nameIdentifier, LASSO_SIGNATURE_TYPE_NONE, 0); } if (LASSO_IS_LIB_FEDERATION_TERMINATION_NOTIFICATION(profile->request) == FALSE) { return critical_error(LASSO_PROFILE_ERROR_BUILDING_REQUEST_FAILED); } /* Set the nameIdentifier attribute from content local variable */ profile->nameIdentifier = g_object_ref(nameIdentifier); /* remove federation with remote provider id */ if (profile->identity == NULL) { return critical_error(LASSO_PROFILE_ERROR_IDENTITY_NOT_FOUND); } lasso_identity_remove_federation(profile->identity, profile->remote_providerID); /* remove assertion from session */ if (profile->session) lasso_session_remove_assertion(profile->session, profile->remote_providerID); /* Save notification method */ profile->http_request_method = http_method; return 0; } /** * lasso_defederation_process_notification_msg: * @defederation: the federation termination object * @notification_msg: the federation termination notification message * * Process the federation termination notification. * * - if it is a SOAP notification method then it builds the federation * termination object from the SOAP message and optionaly verify the * signature. * * - if it is a HTTP-Redirect notification method then it builds the * federation termination notication object from the QUERY message and * optionaly verify the signature. * * Set the msg_nameIdentifier attribute with the NameIdentifier content of the * notification object and optionaly set the msg_relayState attribute with the * RelayState content of the notification object * * Return value: 0 on success or a negative value otherwise. **/ gint lasso_defederation_process_notification_msg(LassoDefederation *defederation, char *request_msg) { LassoProfile *profile; LassoProvider *remote_provider; LassoMessageFormat format; g_return_val_if_fail(LASSO_IS_DEFEDERATION(defederation), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(request_msg != NULL, LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); profile = LASSO_PROFILE(defederation); profile->request = lasso_lib_federation_termination_notification_new(); format = lasso_node_init_from_message(profile->request, request_msg); if (format == LASSO_MESSAGE_FORMAT_UNKNOWN || format == LASSO_MESSAGE_FORMAT_ERROR) { return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG); } profile->remote_providerID = g_strdup(LASSO_LIB_FEDERATION_TERMINATION_NOTIFICATION( profile->request)->ProviderID); remote_provider = g_hash_table_lookup(profile->server->providers, profile->remote_providerID); if (LASSO_IS_PROVIDER(remote_provider) == FALSE) { return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND, profile->remote_providerID); } profile->signature_status = lasso_provider_verify_signature( remote_provider, request_msg, "RequestID", format); /* set the http request method */ if (format == LASSO_MESSAGE_FORMAT_SOAP) profile->http_request_method = LASSO_HTTP_METHOD_SOAP; if (format == LASSO_MESSAGE_FORMAT_QUERY) profile->http_request_method = LASSO_HTTP_METHOD_REDIRECT; profile->nameIdentifier = g_object_ref(LASSO_LIB_FEDERATION_TERMINATION_NOTIFICATION( profile->request)->NameIdentifier); /* get the RelayState (only available in redirect mode) */ if (LASSO_LIB_FEDERATION_TERMINATION_NOTIFICATION(profile->request)->RelayState) profile->msg_relayState = g_strdup( LASSO_LIB_FEDERATION_TERMINATION_NOTIFICATION( profile->request)->RelayState); return profile->signature_status; } /** * lasso_defederation_validate_notification: * @defederation: the federation termination object * * Validate the federation termination notification : * - verifies the ProviderID * - if HTTP-Redirect method, set msg_url with the federation termination * service return url * - verifies the federation * - verifies the authentication * * Return value: O if OK else < 0 **/ gint lasso_defederation_validate_notification(LassoDefederation *defederation) { LassoProfile *profile; LassoProvider *remote_provider; LassoFederation *federation = NULL; LassoSamlNameIdentifier *nameIdentifier; g_return_val_if_fail(LASSO_IS_DEFEDERATION(defederation), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); profile = LASSO_PROFILE(defederation); /* verify the federation termination notification */ if (LASSO_IS_LIB_FEDERATION_TERMINATION_NOTIFICATION(profile->request) == FALSE) return LASSO_PROFILE_ERROR_MISSING_REQUEST; /* If SOAP notification, then msg_url and msg_body are NULL */ /* if HTTP-Redirect notification, set msg_url with the federation * termination service return url, and set msg_body to NULL */ profile->msg_url = NULL; profile->msg_body = NULL; if (profile->http_request_method == LASSO_HTTP_METHOD_REDIRECT) { remote_provider = g_hash_table_lookup(profile->server->providers, profile->remote_providerID); if (LASSO_IS_PROVIDER(remote_provider) == FALSE) { return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND, profile->remote_providerID); } /* build the QUERY and the url. Dont need to sign the query, * only the relay state is optinaly added and it is crypted * by the notifier */ profile->msg_url = lasso_provider_get_metadata_one(remote_provider, "FederationTerminationServiceReturnURL"); if (profile->msg_url == NULL) { return critical_error(LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL); } /* if a relay state, then build the query part */ if (profile->msg_relayState) { gchar *url; url = g_strdup_printf("%s?RelayState=%s", profile->msg_url, profile->msg_relayState); g_free(profile->msg_url); profile->msg_url = url; } } /* get the name identifier */ nameIdentifier = LASSO_LIB_FEDERATION_TERMINATION_NOTIFICATION( profile->request)->NameIdentifier; if (nameIdentifier == NULL) { return critical_error(LASSO_DEFEDERATION_ERROR_MISSING_NAME_IDENTIFIER); } /* Verify federation */ if (profile->identity == NULL) { return critical_error(LASSO_PROFILE_ERROR_IDENTITY_NOT_FOUND); } federation = g_hash_table_lookup(profile->identity->federations, profile->remote_providerID); if (federation == NULL) { return critical_error(LASSO_PROFILE_ERROR_FEDERATION_NOT_FOUND); } if (lasso_federation_verify_nameIdentifier(federation, nameIdentifier) == FALSE) { return critical_error(LASSO_PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND); } /* remove federation of the remote provider */ lasso_identity_remove_federation(profile->identity, profile->remote_providerID); /* if defederation has a session and if there is an assertion for remote provider id, then remove assertion too */ if (profile->session != NULL) { lasso_session_remove_assertion(profile->session, profile->remote_providerID); } return 0; } /*****************************************************************************/ /* instance and class init functions */ /*****************************************************************************/ GType lasso_defederation_get_type() { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof (LassoDefederationClass), NULL, NULL, NULL, NULL, NULL, sizeof(LassoDefederation), 0, NULL, }; this_type = g_type_register_static(LASSO_TYPE_PROFILE, "LassoDefederation", &this_info, 0); } return this_type; } /** * lasso_defederation_new: * @server: the server object of the provider * @provider_type: the provider type (service provider or identity provider) * * This function build a new federation termination object to build * a notification message or to process a notification. * * If building a federation termination notification message then call : * lasso_defederation_init_notification() * lasso_defederation_build_notification_msg() * and get msg_url or msg_body. * * If processing a federation termination notification message then call : * lasso_defederation_process_notification_msg() * lasso_defederation_validate_notification() * and process the returned code. * * Return value: a new instance of federation termination object or NULL **/ LassoDefederation* lasso_defederation_new(LassoServer *server) { LassoDefederation *defederation; g_return_val_if_fail(LASSO_IS_SERVER(server), NULL); defederation = g_object_new(LASSO_TYPE_DEFEDERATION, NULL); LASSO_PROFILE(defederation)->server = server; return defederation; }