/* $Id$ * * Lasso - A free implementation of the Liberty Alliance specifications. * * Copyright (C) 2004-2007 Entr'ouvert * http://lasso.entrouvert.org * * Authors: See AUTHORS file in top-level directory. * * 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 "../xml/private.h" #include #include "../utils.h" #include "providerprivate.h" #include "profileprivate.h" #include "profile.h" #include "provider.h" #include "../id-ff/providerprivate.h" #include "../id-ff/profile.h" #include "../id-ff/profileprivate.h" #include "../id-ff/serverprivate.h" #include "../id-ff/sessionprivate.h" #include "../id-ff/login.h" #include "../xml/private.h" #include "../xml/saml-2.0/samlp2_request_abstract.h" #include "../xml/saml-2.0/samlp2_artifact_resolve.h" #include "../xml/saml-2.0/samlp2_artifact_response.h" #include "../xml/saml-2.0/samlp2_name_id_mapping_response.h" #include "../xml/saml-2.0/samlp2_status_response.h" #include "../xml/saml-2.0/samlp2_response.h" #include "../xml/saml-2.0/saml2_assertion.h" #include "../xml/misc_text_node.h" #include "../utils.h" #include "../debug.h" static char* lasso_saml20_profile_build_artifact(LassoProvider *provider); static int lasso_saml20_profile_export_to_query(LassoProfile *profile, LassoNode *msg, char **query, LassoSignatureContext context); static gint lasso_profile_saml20_build_artifact_get_request_msg(LassoProfile *profile, const char *service); static gint lasso_profile_saml20_build_artifact_post_request_msg(LassoProfile *profile, const char *service); static gint lasso_profile_saml20_build_artifact_get_response_msg(LassoProfile *profile, const char *service); static gint lasso_profile_saml20_build_artifact_post_response_msg(LassoProfile *profile, const char *service); static char* lasso_saml20_profile_generate_artifact(LassoProfile *profile, int part); #define check_msg_body \ if (! profile->msg_body) { \ return critical_error(LASSO_PROFILE_ERROR_BUILDING_MESSAGE_FAILED); \ } /* * Helper functions */ static int get_provider(LassoProfile *profile, LassoProvider **provider_out) { LassoProvider *provider; LassoServer *server; int rc = 0; lasso_bad_param(PROFILE, profile); lasso_extract_node_or_fail(server, profile->server, SERVER, LASSO_PROFILE_ERROR_MISSING_SERVER); provider = lasso_server_get_provider(server, profile->remote_providerID); if (! provider) { return LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND; } *provider_out = provider; cleanup: return rc; } static char * get_url(LassoProvider *provider, const char *service, const char *binding) { char *meta; char *result; meta = g_strdup_printf("%s %s", service, binding); result = lasso_provider_get_metadata_one(provider, meta); lasso_release_string(meta); return result; } static char * get_response_url(LassoProvider *provider, const char *service, const char *binding) { char *meta; char *result; meta = g_strdup_printf("%s %s ResponseLocation", service, binding); result = lasso_provider_get_metadata_one(provider, meta); lasso_release_string(meta); if (! result) { result = get_url(provider, service, binding); } return result; } static const char* http_method_to_binding(LassoHttpMethod method) { switch (method) { case LASSO_HTTP_METHOD_POST: return "HTTP-POST"; case LASSO_HTTP_METHOD_REDIRECT: return "HTTP-Redirect"; case LASSO_HTTP_METHOD_SOAP: return "SOAP"; case LASSO_HTTP_METHOD_ARTIFACT_GET: case LASSO_HTTP_METHOD_ARTIFACT_POST: return "HTTP-Artifact"; case LASSO_HTTP_METHOD_PAOS: return "PAOS"; default: return ""; } } /* * Artifact Handling functions */ /** * lasso_saml20_profile_generate_artifact * @profile: a #LassoProfile * @part: 0 for request, 1 for response * * Generates an artifact for current request or response and sets @profile * attributes accordingly. * * Return value: the generated artifact (internally allocated, don't free) **/ static char* lasso_saml20_profile_generate_artifact(LassoProfile *profile, int part) { LassoNode *what = NULL; lasso_assign_new_string(profile->private_data->artifact, lasso_saml20_profile_build_artifact(&profile->server->parent)); if (part == 0) { what = profile->request; } else if (part == 1) { what = profile->response; } else { /* XXX: RequestDenied here? */ } /* Remove signature at the response level, if needed if will be on the ArtifactResponse */ lasso_node_remove_signature(what); /* Keep an XML copy of the response for later retrieval */ lasso_assign_new_string(profile->private_data->artifact_message, lasso_node_export_to_xml(what)); return profile->private_data->artifact; } static char* lasso_saml20_profile_build_artifact(LassoProvider *provider) { xmlSecByte samlArt[44], *b64_samlArt; char *source_succinct_id; char *ret; source_succinct_id = lasso_sha1(provider->ProviderID); /* Artifact Format is described in saml-bindings-2.0-os, 3.6.4.2. */ memcpy(samlArt, "\000\004", 2); /* type code */ memcpy(samlArt+2, "\000\000", 2); /* XXX: Endpoint index */ memcpy(samlArt+4, source_succinct_id, 20); lasso_build_random_sequence((char*)samlArt+24, 20); xmlFree(source_succinct_id); b64_samlArt = xmlSecBase64Encode(samlArt, 44, 0); ret = g_strdup((char*)b64_samlArt); xmlFree(b64_samlArt); return ret; } /* * this function factorize all case for producing SAML artifact messages */ static gint lasso_profile_saml20_build_artifact_msg(LassoProfile *profile, const char *url, int request_or_response, int get_or_post) { char *artifact = lasso_saml20_profile_generate_artifact(profile, request_or_response); if (artifact == NULL) { return critical_error(LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED); } /* hack... */ if (LASSO_IS_LOGIN(profile)) { LassoLogin *login = (LassoLogin*)profile; lasso_assign_string(login->assertionArtifact, artifact); } if (get_or_post == 0) { char *query; if (profile->msg_relayState) { query = lasso_url_add_parameters(NULL, 0, LASSO_SAML2_FIELD_ARTIFACT, artifact, "RelayState", profile->msg_relayState, NULL); } else { query = lasso_url_add_parameters(NULL, 0, LASSO_SAML2_FIELD_ARTIFACT, artifact, NULL); } lasso_assign_new_string(profile->msg_url, lasso_concat_url_query(url, query)); lasso_release_string(query); } else { lasso_assign_string(profile->msg_url, url); lasso_assign_string(profile->msg_body, artifact); } return 0; } enum { REQUEST = 0, RESPONSE = 1, GET = 0, POST = 1 }; static gint lasso_profile_saml20_build_artifact_get_request_msg(LassoProfile *profile, const char *url) { return lasso_profile_saml20_build_artifact_msg(profile, url, REQUEST, GET); } static gint lasso_profile_saml20_build_artifact_post_request_msg(LassoProfile *profile, const char *url) { return lasso_profile_saml20_build_artifact_msg(profile, url, REQUEST, POST); } static gint lasso_profile_saml20_build_artifact_get_response_msg(LassoProfile *profile, const char *url) { return lasso_profile_saml20_build_artifact_msg(profile, url, RESPONSE, GET); } static gint lasso_profile_saml20_build_artifact_post_response_msg(LassoProfile *profile, const char *url) { return lasso_profile_saml20_build_artifact_msg(profile, url, RESPONSE, POST); } int lasso_saml20_profile_init_artifact_resolve(LassoProfile *profile, const char *msg, LassoHttpMethod method) { char **query_fields; char *artifact_b64 = NULL; xmlChar *provider_succinct_id_b64 = NULL; char *provider_succinct_id[21]; char artifact[45]; LassoSamlp2RequestAbstract *request = NULL; int i = 0; int rc = 0; if (method == LASSO_HTTP_METHOD_ARTIFACT_GET) { query_fields = urlencoded_to_strings(msg); for (i=0; query_fields[i]; i++) { if (strncmp((char*)query_fields[i], LASSO_SAML2_FIELD_ARTIFACT "=", 8) == 0) { lasso_assign_string(artifact_b64, query_fields[i]+8); } xmlFree(query_fields[i]); } lasso_release(query_fields); if (artifact_b64 == NULL) { return LASSO_PROFILE_ERROR_MISSING_ARTIFACT; } } else if (method == LASSO_HTTP_METHOD_ARTIFACT_POST) { artifact_b64 = g_strdup(msg); } else { return critical_error(LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD); } i = xmlSecBase64Decode((xmlChar*)artifact_b64, (xmlChar*)artifact, 45); if (i < 0 || i > 44) { lasso_release_string(artifact_b64); return LASSO_PROFILE_ERROR_INVALID_ARTIFACT; } if (artifact[0] != 0 || artifact[1] != 4) { /* wrong type code */ lasso_release_string(artifact_b64); return LASSO_PROFILE_ERROR_INVALID_ARTIFACT; } /* XXX: index endpoint */ memcpy(provider_succinct_id, artifact+4, 20); provider_succinct_id[20] = 0; provider_succinct_id_b64 = xmlSecBase64Encode((xmlChar*)provider_succinct_id, 20, 0); lasso_assign_new_string(profile->remote_providerID, lasso_server_get_providerID_from_hash( profile->server, (char*)provider_succinct_id_b64)); lasso_release_xml_string(provider_succinct_id_b64); if (profile->remote_providerID == NULL) { return critical_error(LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID); } lasso_assign_new_gobject(profile->request, lasso_samlp2_artifact_resolve_new()); request = LASSO_SAMLP2_REQUEST_ABSTRACT(profile->request); lasso_assign_new_string(LASSO_SAMLP2_ARTIFACT_RESOLVE(request)->Artifact, artifact_b64); request->ID = lasso_build_unique_id(32); lasso_assign_string(request->Version, "2.0"); request->Issuer = LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string( LASSO_PROVIDER(profile->server)->ProviderID)); request->IssueInstant = lasso_get_current_time(); lasso_check_good_rc(lasso_profile_saml20_setup_message_signature(profile, (LassoNode*)request)); cleanup: return rc; } int lasso_saml20_profile_process_artifact_resolve(LassoProfile *profile, const char *msg) { LassoProvider *remote_provider; int rc = 0; LassoProfileSignatureVerifyHint sig_verify_hint; /* FIXME: parse only one time the message, reuse the parsed document for signature * validation */ lasso_assign_new_gobject(profile->request, lasso_node_new_from_soap(msg)); if (profile->request == NULL) { return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG); } if (! LASSO_IS_SAMLP2_ARTIFACT_RESOLVE(profile->request)) { return critical_error(LASSO_PROFILE_ERROR_INVALID_MSG); } lasso_assign_string(profile->private_data->artifact, LASSO_SAMLP2_ARTIFACT_RESOLVE(profile->request)->Artifact); sig_verify_hint = lasso_profile_get_signature_verify_hint(profile); lasso_assign_string(profile->remote_providerID, LASSO_SAMLP2_REQUEST_ABSTRACT( profile->request)->Issuer->content); remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID); goto_cleanup_if_fail_with_rc(remote_provider, LASSO_PROFILE_ERROR_UNKNOWN_PROVIDER); if (sig_verify_hint != LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE) { profile->signature_status = lasso_provider_verify_signature(remote_provider, msg, "ID", LASSO_MESSAGE_FORMAT_SOAP); } switch (lasso_profile_get_signature_verify_hint(profile)) { case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE: rc = profile->signature_status; break; case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE: break; default: g_assert(0); break; } cleanup: return rc; } int lasso_saml20_profile_build_artifact_response(LassoProfile *profile) { LassoSamlp2StatusResponse *response = NULL; int rc = 0; if ( ! LASSO_IS_SAMLP2_REQUEST_ABSTRACT(profile->request)) { return LASSO_PROFILE_ERROR_MISSING_REQUEST; } /* Setup the response */ response = LASSO_SAMLP2_STATUS_RESPONSE(lasso_samlp2_artifact_response_new()); lasso_assign_new_gobject(profile->response, response); response->ID = lasso_build_unique_id(32); lasso_assign_string(response->Version, "2.0"); response->Issuer = LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string( LASSO_PROVIDER(profile->server)->ProviderID)); response->IssueInstant = lasso_get_current_time(); lasso_assign_string(response->InResponseTo, LASSO_SAMLP2_REQUEST_ABSTRACT(profile->request)->ID); /* Add content */ if (profile->private_data->artifact_message) { xmlDoc *doc; xmlNode *node; char *content = profile->private_data->artifact_message; doc = lasso_xml_parse_memory(content, strlen(content)); if (doc) { node = xmlDocGetRootElement(doc); lasso_assign_new_gobject(LASSO_SAMLP2_ARTIFACT_RESPONSE(response)->any, lasso_misc_text_node_new_with_xml_node(node)); lasso_release_doc(doc); lasso_saml20_profile_set_response_status(profile, LASSO_SAML2_STATUS_CODE_SUCCESS, NULL); } else { lasso_saml20_profile_set_response_status(profile, LASSO_SAML2_STATUS_CODE_RESPONDER, LASSO_PRIVATE_STATUS_CODE_FAILED_TO_RESTORE_ARTIFACT); } } else { /* if no artifact is present, it is a success anyway */ lasso_saml20_profile_set_response_status(profile, LASSO_SAML2_STATUS_CODE_SUCCESS, NULL); } /* Setup the signature */ lasso_check_good_rc(lasso_profile_saml20_setup_message_signature(profile, (LassoNode*)response)); /* Serialize the message */ lasso_assign_new_string(profile->msg_body, lasso_node_export_to_soap(profile->response)); cleanup: return rc; } int lasso_saml20_profile_process_artifact_response(LassoProfile *profile, const char *msg) { LassoSamlp2ArtifactResponse *artifact_response; int rc = 0; artifact_response = (LassoSamlp2ArtifactResponse*)lasso_samlp2_artifact_response_new(); lasso_check_good_rc(lasso_saml20_profile_process_any_response(profile, &artifact_response->parent, NULL, msg)); /* XXX: check signature status */ goto_cleanup_if_fail_with_rc(profile->response != NULL, critical_error(LASSO_PROFILE_ERROR_INVALID_RESPONSE)); if (artifact_response->any == NULL) { rc = LASSO_PROFILE_ERROR_MISSING_RESPONSE; } else { if (LASSO_IS_SAMLP2_REQUEST_ABSTRACT(artifact_response->any)) { lasso_assign_gobject(profile->request, artifact_response->any); } else if (LASSO_IS_SAMLP2_STATUS_RESPONSE(artifact_response->any)) { lasso_assign_gobject(profile->response, artifact_response->any); } else { rc = LASSO_PROFILE_ERROR_INVALID_RESPONSE; } } cleanup: lasso_release_gobject(artifact_response); return rc; } /** * lasso_saml20_profile_is_saml_query: * @query: HTTP query string * * Tests the query string to know if the URL is called as the result of a * SAML redirect (action initiated elsewhere) or not. * * Return value: TRUE if SAML query, FALSE otherwise **/ gboolean lasso_profile_is_saml_query(const gchar *query) { gchar *parameters[] = { LASSO_SAML2_FIELD_REQUEST "=", LASSO_SAML2_FIELD_RESPONSE "=", LASSO_SAML2_FIELD_ARTIFACT "=", NULL }; gint i; g_return_val_if_fail(query, FALSE); for (i=0; parameters[i]; i++) { if (strstr(query, parameters[i])) return TRUE; } return FALSE; } static void lasso_saml20_profile_set_session_from_dump_decrypt( LassoSaml2Assertion *assertion, LassoProfile *profile) { if (LASSO_IS_SAML2_ASSERTION(assertion) == FALSE) { return; } if (assertion->Subject != NULL && ! assertion->Subject->NameID && assertion->Subject->EncryptedID != NULL) { if (assertion->Subject->EncryptedID->original_data) { /* already decrypted */ lasso_assign_gobject(assertion->Subject->NameID, assertion->Subject->EncryptedID->original_data); lasso_release_gobject(assertion->Subject->EncryptedID); } else { /* decrypt */ int rc; GList *encryption_private_keys = lasso_server_get_encryption_private_keys(profile->server); rc = LASSO_PROFILE_ERROR_MISSING_ENCRYPTION_PRIVATE_KEY; lasso_foreach_full_begin(xmlSecKey*, encryption_private_key, it, encryption_private_keys); { rc = lasso_saml2_encrypted_element_decrypt( assertion->Subject->EncryptedID, encryption_private_key, (LassoNode**)&assertion->Subject->NameID); if (rc == 0) break; } lasso_foreach_full_end(); if (rc == 0) { lasso_release_gobject(assertion->Subject->EncryptedID); } else { message(G_LOG_LEVEL_WARNING, "Could not decrypt EncrypteID from" " assertion in session dump: %s", lasso_strerror(rc)); } } } } gint lasso_saml20_profile_set_session_from_dump(LassoProfile *profile) { GList *assertions = NULL; lasso_bad_param(PROFILE, profile); if (lasso_session_count_assertions(profile->session) > 0) { assertions = lasso_session_get_assertions(profile->session, NULL); g_list_foreach(assertions, (GFunc)lasso_saml20_profile_set_session_from_dump_decrypt, profile); lasso_release_list(assertions); } return 0; } /** * lasso_saml20_profile_process_name_identifier_decryption: * @profile: the #LassoProfile object * @name_id: the field containing the #LassoSaml2NameID object * @encrypted_id: the field containing an encrypted #LassoSaml2NameID as a * #LassoSaml2EncryptedElement * * Place content of the NameID in the profile nameIdentifier field, if no NameID is present but an * EncryptedElement is, then decrypt it, store it in place of the name_id field and in the * nameIdentifier field of the profile. * * Return value: 0 if successful, * LASSO_PROFILE_ERROR_MISSING_NAME_IDENTIFIER if no NameID can be found, * LASSO_PROFILE_ERROR_MISSING_ENCRYPTION_PRIVATE_KEY if an encryption element is present but no no * decryption key. */ gint lasso_saml20_profile_process_name_identifier_decryption(LassoProfile *profile, LassoSaml2NameID **name_id, LassoSaml2EncryptedElement **encrypted_id) { int rc = 0; lasso_bad_param(PROFILE, profile); lasso_null_param(name_id); lasso_null_param(encrypted_id); if (*name_id == NULL && *encrypted_id != NULL) { if (! LASSO_IS_SAML2_ENCRYPTED_ELEMENT(*encrypted_id)) { return LASSO_PROFILE_ERROR_MISSING_NAME_IDENTIFIER; } rc = LASSO_PROFILE_ERROR_MISSING_ENCRYPTION_PRIVATE_KEY; lasso_foreach_full_begin(xmlSecKey*, encryption_private_key, it, lasso_server_get_encryption_private_keys(profile->server)); { rc = lasso_saml2_encrypted_element_decrypt(*encrypted_id, encryption_private_key, &profile->nameIdentifier); if (rc == 0) break; } lasso_foreach_full_end(); if (rc) goto cleanup; if (! LASSO_IS_SAML2_NAME_ID(profile->nameIdentifier)) { rc = LASSO_PROFILE_ERROR_MISSING_NAME_IDENTIFIER; goto cleanup; } // swap the node contents lasso_assign_gobject(*name_id, LASSO_SAML2_NAME_ID(profile->nameIdentifier)); lasso_release_gobject(*encrypted_id); } else { lasso_assign_gobject(profile->nameIdentifier, (LassoNode*)*name_id); } cleanup: return rc; } /* * Request handling functions */ /** * lasso_saml20_profile_process_any_request: * @profile: a #LassoProfile object * @request_node: a #LassoNode object which will be initialized with the content of @request_msg * @request_msg: a string containing the request message as a SOAP XML message, a query string of * the content of SAMLRequest POST field. * * Parse a request message, initialize the given node object with it, try to extract basic SAML * profile information like the remote_provider_id or the name_id and validate the signature. * * Signature validation status is accessible in profile->signature_status, beware that if signature * validation fails no error code will be returned, you must explicitely verify the * profile->signature_status code. * * Return value: 0 if parsing is successful (even if signature validation fails), and otherwise, * LASSO_PROFILE_ERROR_INVALID_MSG, LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE, * * LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND. */ int lasso_saml20_profile_process_any_request(LassoProfile *profile, LassoNode *request_node, const char *request_msg) { int rc = 0; LassoProvider *remote_provider = NULL; LassoSamlp2RequestAbstract *request_abstract = NULL; LassoMessageFormat format; xmlDoc *doc = NULL; xmlNode *content = NULL; lasso_bad_param(PROFILE, profile); /* reset signature_status */ profile->signature_status = 0; format = lasso_node_init_from_message_with_format(request_node, request_msg, LASSO_MESSAGE_FORMAT_UNKNOWN, &doc, &content); if (format <= LASSO_MESSAGE_FORMAT_UNKNOWN) { rc = LASSO_PROFILE_ERROR_INVALID_MSG; goto cleanup; } switch (format) { case LASSO_MESSAGE_FORMAT_BASE64: profile->http_request_method = LASSO_HTTP_METHOD_POST; break; case LASSO_MESSAGE_FORMAT_SOAP: profile->http_request_method = LASSO_HTTP_METHOD_SOAP; break; case LASSO_MESSAGE_FORMAT_QUERY: profile->http_request_method = LASSO_HTTP_METHOD_REDIRECT; break; default: rc = LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE; goto cleanup; } lasso_assign_gobject(profile->request, request_node); if (format == LASSO_MESSAGE_FORMAT_QUERY) { lasso_assign_new_string(profile->msg_relayState, lasso_get_relaystate_from_query(request_msg)); } lasso_extract_node_or_fail(request_abstract, profile->request, SAMLP2_REQUEST_ABSTRACT, LASSO_PROFILE_ERROR_INVALID_MSG); goto_cleanup_if_fail_with_rc(LASSO_IS_SAML2_NAME_ID(request_abstract->Issuer), LASSO_PROFILE_ERROR_MISSING_ISSUER); lasso_assign_string(profile->remote_providerID, request_abstract->Issuer->content); rc = get_provider(profile, &remote_provider); goto_cleanup_if_fail(rc == 0); /* verify the signature at the request level */ if (content && doc && format != LASSO_MESSAGE_FORMAT_QUERY) { profile->signature_status = lasso_provider_verify_saml_signature(remote_provider, content, doc); } else if (format == LASSO_MESSAGE_FORMAT_QUERY) { profile->signature_status = lasso_provider_verify_query_signature(remote_provider, request_msg); } else { profile->signature_status = LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE; } cleanup: lasso_release_doc(doc); return rc; } int lasso_saml20_profile_process_soap_request(LassoProfile *profile, const char *request_msg) { int rc = 0; LassoSaml2NameID *issuer = NULL; LassoProvider *remote_provider = NULL; LassoSamlp2RequestAbstract *request_abstract = NULL; lasso_bad_param(PROFILE, profile); profile->signature_status = 0; lasso_assign_new_gobject(profile->request, lasso_node_new_from_soap(request_msg)); profile->http_request_method = LASSO_HTTP_METHOD_SOAP; lasso_extract_node_or_fail(request_abstract, profile->request, SAMLP2_REQUEST_ABSTRACT, LASSO_PROFILE_ERROR_INVALID_MSG); lasso_extract_node_or_fail(issuer, request_abstract->Issuer, SAML2_NAME_ID, LASSO_PROFILE_ERROR_MISSING_ISSUER); lasso_assign_string(profile->remote_providerID, issuer->content); rc = get_provider(profile, &remote_provider); goto_cleanup_if_fail(rc == 0); profile->signature_status = lasso_provider_verify_signature( remote_provider, request_msg, "ID", LASSO_MESSAGE_FORMAT_SOAP); switch (lasso_profile_get_signature_verify_hint(profile)) { case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE: case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE: rc = profile->signature_status; break; case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE: break; case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST: g_assert_not_reached(); break; } cleanup: return rc; } int lasso_saml20_profile_init_request(LassoProfile *profile, const char *remote_provider_id, gboolean first_in_session, LassoSamlp2RequestAbstract *request_abstract, LassoHttpMethod http_method, LassoMdProtocolType protocol_type) { LassoServer *server = NULL; LassoSession *session = NULL; LassoProvider *remote_provider = NULL; LassoSaml2NameID *name_id = NULL; char *remote_provider_id_auto = NULL; int rc = 0; lasso_bad_param(PROFILE, profile); lasso_bad_param(SAMLP2_REQUEST_ABSTRACT, request_abstract); if (http_method != LASSO_HTTP_METHOD_ANY && http_method != LASSO_HTTP_METHOD_REDIRECT && http_method != LASSO_HTTP_METHOD_POST && http_method != LASSO_HTTP_METHOD_ARTIFACT_GET && http_method != LASSO_HTTP_METHOD_ARTIFACT_POST && http_method != LASSO_HTTP_METHOD_SOAP && http_method != LASSO_HTTP_METHOD_PAOS) { return critical_error(LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD); } /* verify server and session object */ lasso_extract_node_or_fail(server, profile->server, SERVER, LASSO_PROFILE_ERROR_MISSING_SERVER); if (LASSO_IS_SESSION(profile->session)) { session = profile->session; } /* set remote provider Id */ if (! remote_provider_id) { if (first_in_session) { if (! session) { return LASSO_PROFILE_ERROR_SESSION_NOT_FOUND; } remote_provider_id_auto = lasso_session_get_provider_index(session, 0); } else { remote_provider_id_auto = lasso_server_get_first_providerID(server); } } if (! remote_provider_id && ! remote_provider_id_auto) { rc = LASSO_PROFILE_ERROR_CANNOT_FIND_A_PROVIDER; goto cleanup; } if (remote_provider_id) { lasso_assign_string(profile->remote_providerID, remote_provider_id); } else { lasso_assign_new_string(profile->remote_providerID, remote_provider_id_auto); } rc = get_provider(profile, &remote_provider); if (rc) goto cleanup; /* set the name identifier */ name_id = (LassoSaml2NameID*)lasso_profile_get_nameIdentifier(profile); if (LASSO_IS_SAML2_NAME_ID(name_id)) { lasso_assign_gobject(profile->nameIdentifier, (LassoNode*)name_id); } /* verify that this provider supports the current http method */ if (http_method == LASSO_HTTP_METHOD_ANY) { http_method = lasso_saml20_provider_get_first_http_method((LassoProvider*)server, remote_provider, protocol_type); } if (http_method == LASSO_HTTP_METHOD_NONE) { rc = LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE; goto cleanup; } if (! lasso_saml20_provider_accept_http_method( (LassoProvider*)server, remote_provider, protocol_type, http_method, TRUE)) { rc = LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE; } profile->http_request_method = http_method; /* initialize request fields */ lasso_assign_new_string(request_abstract->ID, lasso_build_unique_id(32)); lasso_assign_string(request_abstract->Version, "2.0"); lasso_assign_new_gobject(request_abstract->Issuer, LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string( LASSO_PROVIDER(profile->server)->ProviderID))); lasso_assign_new_string(request_abstract->IssueInstant, lasso_get_current_time()); lasso_assign_gobject(profile->request, LASSO_NODE(request_abstract)); /* set signature */ lasso_check_good_rc(lasso_profile_saml20_setup_message_signature(profile, profile->request)); cleanup: return rc; } static int lasso_saml20_profile_build_redirect_request_msg(LassoProfile *profile, const char *url) { return lasso_saml20_profile_build_http_redirect(profile, profile->request, url); } static int lasso_saml20_profile_build_post_request_msg(LassoProfile *profile, const char *url) { lasso_assign_string(profile->msg_url, url); lasso_assign_new_string(profile->msg_body, lasso_node_export_to_base64(profile->request)); check_msg_body; return 0; } static int lasso_saml20_profile_build_soap_request_msg(LassoProfile *profile, const char *url) { lasso_assign_string(profile->msg_url, url); lasso_assign_new_string(profile->msg_body, lasso_node_export_to_soap(profile->request)); check_msg_body; return 0; } /* * the url parameters is special for this function, it does not give the destination of the message * (it's implicit for the caller of this function) but where response should be posted later). */ static int lasso_profile_saml20_build_paos_request_msg(LassoProfile *profile, const char *url) { lasso_assign_new_string(profile->msg_body, lasso_node_export_to_paos_request(profile->request, profile->server->parent.ProviderID, url, profile->msg_relayState)); check_msg_body; return 0; } int lasso_saml20_profile_build_request_msg(LassoProfile *profile, const char *service, LassoHttpMethod method, const char *_url) { LassoProvider *provider; char *made_url = NULL, *url; int rc = 0; lasso_bad_param(PROFILE, profile); lasso_profile_clean_msg_info(profile); lasso_check_good_rc(get_provider(profile, &provider)); url = (char*)_url; /* check presence of a request */ if (! LASSO_IS_SAMLP2_REQUEST_ABSTRACT(profile->request)) { return critical_error(LASSO_PROFILE_ERROR_MISSING_REQUEST); } /* if not explicitely given, automatically determine an URI from the metadatas */ if (url == NULL) { made_url = url = get_url(provider, service, http_method_to_binding(method)); } if (url) { lasso_assign_string(((LassoSamlp2RequestAbstract*)profile->request)->Destination, url); } switch (method) { case LASSO_HTTP_METHOD_SOAP: rc = lasso_saml20_profile_build_soap_request_msg(profile, url); break; case LASSO_HTTP_METHOD_POST: rc = lasso_saml20_profile_build_post_request_msg(profile, url); break; case LASSO_HTTP_METHOD_REDIRECT: rc = lasso_saml20_profile_build_redirect_request_msg(profile, url); break; case LASSO_HTTP_METHOD_ARTIFACT_GET: rc = lasso_profile_saml20_build_artifact_get_request_msg(profile, url); break; case LASSO_HTTP_METHOD_ARTIFACT_POST: rc = lasso_profile_saml20_build_artifact_post_request_msg(profile, url); break; case LASSO_HTTP_METHOD_PAOS: rc = lasso_profile_saml20_build_paos_request_msg(profile, url); break; default: rc = LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD; break; } cleanup: lasso_release_string(made_url); return rc; } /* * Response handling functions */ int lasso_saml20_profile_set_response_status(LassoProfile *profile, const char *code1, const char *code2) { LassoSamlp2StatusResponse *status_response = NULL; LassoSamlp2Status *status = NULL; LassoSamlp2StatusCode *status_code1 = NULL; LassoSamlp2StatusCode *status_code2 = NULL; int rc = 0; lasso_bad_param(PROFILE, profile); lasso_null_param(code1); lasso_extract_node_or_fail(status_response, profile->response, SAMLP2_STATUS_RESPONSE, LASSO_PROFILE_ERROR_MISSING_RESPONSE); if (! LASSO_IS_SAMLP2_STATUS(status_response->Status)) { lasso_assign_new_gobject(status_response->Status, (LassoSamlp2Status*)lasso_samlp2_status_new()); } status = status_response->Status; if (! LASSO_IS_SAMLP2_STATUS_CODE(status->StatusCode)) { lasso_assign_new_gobject(status->StatusCode, (LassoSamlp2StatusCode*)lasso_samlp2_status_code_new()); } status_code1 = status->StatusCode; lasso_assign_string(status_code1->Value, code1); if (code2) { if (! LASSO_IS_SAMLP2_STATUS_CODE(status_code1->StatusCode)) { lasso_assign_new_gobject(status_code1->StatusCode, (LassoSamlp2StatusCode*)lasso_samlp2_status_code_new()); } status_code2 = status_code1->StatusCode; lasso_assign_string(status_code2->Value, code2); } cleanup: return rc; } int lasso_saml20_profile_init_response(LassoProfile *profile, LassoSamlp2StatusResponse *status_response, const char *status_code1, const char *status_code2) { int rc = 0; lasso_bad_param(PROFILE, profile); if (! LASSO_IS_SAMLP2_STATUS_RESPONSE(status_response)) return LASSO_PROFILE_ERROR_MISSING_RESPONSE; lasso_assign_gobject(profile->response, status_response); lasso_assign_new_string(status_response->ID, lasso_build_unique_id(32)); lasso_assign_string(status_response->Version, "2.0"); if (LASSO_IS_SERVER(profile->server)) { lasso_assign_new_gobject(status_response->Issuer, LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string( profile->server->parent.ProviderID))); } lasso_assign_new_string(status_response->IssueInstant, lasso_get_current_time()); if (LASSO_IS_SAMLP2_REQUEST_ABSTRACT(profile->request)) { lasso_assign_string(status_response->InResponseTo, ((LassoSamlp2RequestAbstract*)profile->request)->ID); } lasso_check_good_rc(lasso_profile_saml20_setup_message_signature(profile, profile->response)); if (status_code1) { lasso_saml20_profile_set_response_status(profile, status_code1, status_code2); } cleanup: return rc; } int lasso_saml20_profile_validate_request(LassoProfile *profile, gboolean needs_identity, LassoSamlp2StatusResponse *status_response, LassoProvider **provider_out) { int rc = 0; LassoSamlp2RequestAbstract *request_abstract = NULL; LassoSaml2NameID *issuer = NULL; LassoProvider *provider = NULL; lasso_bad_param(PROFILE, profile); lasso_bad_param(SAMLP2_STATUS_RESPONSE, status_response); /* verify request presence */ lasso_extract_node_or_fail(request_abstract, profile->request, SAMLP2_REQUEST_ABSTRACT, LASSO_PROFILE_ERROR_MISSING_REQUEST); /* look for identity object */ if (needs_identity) { goto_cleanup_if_fail_with_rc(LASSO_IS_IDENTITY(profile->identity), LASSO_PROFILE_ERROR_IDENTITY_NOT_FOUND); } /* extract provider */ lasso_extract_node_or_fail(issuer, request_abstract->Issuer, SAML2_NAME_ID, LASSO_PROFILE_ERROR_MISSING_ISSUER); lasso_assign_string(profile->remote_providerID, issuer->content); rc = get_provider(profile, &provider); if (rc) goto cleanup; /* init the response */ lasso_saml20_profile_init_response(profile, status_response, LASSO_SAML2_STATUS_CODE_SUCCESS, NULL); switch (lasso_profile_get_signature_verify_hint(profile)) { case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE: case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE: if (profile->signature_status) { lasso_saml20_profile_set_response_status(profile, LASSO_SAML2_STATUS_CODE_REQUESTER, LASSO_LIB_STATUS_CODE_INVALID_SIGNATURE); return profile->signature_status; } case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE: break; case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST: g_assert_not_reached(); } cleanup: if (provider && provider_out) *provider_out = provider; return rc; } /** * lasso_saml20_profile_export_to_query: * @profile: a #LassoProfile * @msg: a #LassoNode to export as a query * @query: an ouput variable to store the result * @signature_method: the signature method for signing the query * @private_key_file:(allow-none): the private key to eventually sign the query * @private_key_password:(allow-none): the password of the private key if there is one * * Create a query following the DEFLATE encoding of the SAML 2.0 HTTP * Redirect binding. If the root message node has an XML signature, signature is removed and query * is signed. * * Return value: 0 if successful, an error code otherwise. */ static int lasso_saml20_profile_export_to_query(LassoProfile *profile, LassoNode *msg, char **query, LassoSignatureContext context) { char *unsigned_query = NULL; char *result = NULL; int rc = 0; unsigned_query = lasso_node_build_query(msg); goto_cleanup_if_fail_with_rc(unsigned_query != NULL, LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED); if (profile->msg_relayState) { unsigned_query = lasso_url_add_parameters(unsigned_query, 1, "RelayState", profile->msg_relayState, NULL); if (strlen(profile->msg_relayState) > 80) { message(G_LOG_LEVEL_WARNING, "Encoded a RelayState of more than 80 bytes, " "see #3.4.3 of saml-bindings-2.0-os"); } } if (lasso_validate_signature_method(context.signature_method)) { result = lasso_query_sign(unsigned_query, context); goto_cleanup_if_fail_with_rc(result != NULL, LASSO_PROFILE_ERROR_BUILDING_QUERY_FAILED); lasso_transfer_string(*query, result); } else { lasso_transfer_string(*query, unsigned_query); } cleanup: lasso_release_string(unsigned_query); lasso_release_string(result); return rc; } /** * lasso_saml20_profile_build_http_redirect: * @profile: a #LassoProfile object * @msg: a #LassoNode object representing a SAML 2.0 message * @must_sign: wheter to sign the query message using query signatures * @url: the URL where the query is targeted * * Build an HTTP URL with a query-string following the SAML 2.0 HTTP-Redirect binding rules, * eventually sign it. Any signature at the message level is removed. * * Return value: 0 if successful, an error code otherwise. */ gint lasso_saml20_profile_build_http_redirect(LassoProfile *profile, LassoNode *msg, const char *url) { char *query = NULL; int rc = 0; LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE; goto_cleanup_if_fail_with_rc (url != NULL, LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL); /* if message is signed, remove XML signature, add query signature */ context = lasso_node_get_signature(msg); /* We must duplicate the key since lasso_node_remove_signature will free it. */ context.signature_key = xmlSecKeyDuplicate(context.signature_key); if (lasso_validate_signature_method(context.signature_method)) { lasso_node_remove_signature(msg); } lasso_check_good_rc(lasso_saml20_profile_export_to_query(profile, msg, &query, context)); lasso_assign_new_string(profile->msg_url, lasso_concat_url_query(url, query)); lasso_release(profile->msg_body); lasso_release(query); cleanup: return rc; } static int lasso_saml20_profile_build_redirect_response_msg(LassoProfile *profile, const char *url) { return lasso_saml20_profile_build_http_redirect(profile, profile->response, url); } static int lasso_saml20_profile_build_post_response_msg(LassoProfile *profile, const char *url) { lasso_assign_string(profile->msg_url, url); lasso_assign_new_string(profile->msg_body, lasso_node_export_to_base64(profile->response)); check_msg_body; return 0; } static int lasso_saml20_profile_build_soap_response_msg(LassoProfile *profile) { lasso_release_string(profile->msg_url); lasso_assign_new_string(profile->msg_body, lasso_node_export_to_soap(profile->response)); check_msg_body; return 0; } int lasso_saml20_profile_build_response_msg(LassoProfile *profile, char *service, LassoHttpMethod method, const char *_url) { LassoProvider *provider; char *made_url = NULL, *url; int rc = 0; lasso_bad_param(PROFILE, profile); lasso_profile_clean_msg_info(profile); lasso_check_good_rc(get_provider(profile, &provider)); url = (char*)_url; /* check presence of a request */ if (! LASSO_IS_SAMLP2_STATUS_RESPONSE(profile->response)) { return critical_error(LASSO_PROFILE_ERROR_MISSING_RESPONSE); } /* if not explicitely given, automatically determine an URI from the metadatas */ if (url == NULL && service && method != LASSO_HTTP_METHOD_SOAP) { made_url = url = get_response_url(provider, service, http_method_to_binding(method)); } /* only asynchronous bindings needs an URL for the response, SOAP does not need it, and PAOS * is special (response is a SOAP request !?! ) */ if (! url) { switch (method) { case LASSO_HTTP_METHOD_POST: case LASSO_HTTP_METHOD_REDIRECT: case LASSO_HTTP_METHOD_ARTIFACT_GET: case LASSO_HTTP_METHOD_ARTIFACT_POST: case LASSO_HTTP_METHOD_PAOS: goto_cleanup_with_rc(critical_error(LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL)); default: break; } } if (url) { lasso_assign_string(((LassoSamlp2StatusResponse*)profile->response)->Destination, url); } switch (method) { case LASSO_HTTP_METHOD_POST: rc = lasso_saml20_profile_build_post_response_msg(profile, url); break; case LASSO_HTTP_METHOD_REDIRECT: rc = lasso_saml20_profile_build_redirect_response_msg(profile, url); break; case LASSO_HTTP_METHOD_SOAP: rc = lasso_saml20_profile_build_soap_response_msg(profile); break; case LASSO_HTTP_METHOD_ARTIFACT_GET: rc = lasso_profile_saml20_build_artifact_get_response_msg(profile, url); break; case LASSO_HTTP_METHOD_ARTIFACT_POST: rc = lasso_profile_saml20_build_artifact_post_response_msg(profile, url); break; default: rc= LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE; break; } cleanup: lasso_release_string(made_url); return rc; } static gboolean _lasso_saml20_is_valid_issuer(LassoSaml2NameID *name_id) { if (! LASSO_IS_SAML2_NAME_ID(name_id)) return FALSE; if (name_id->Format && lasso_strisnotequal(name_id->Format,LASSO_SAML2_NAME_IDENTIFIER_FORMAT_ENTITY)) { return FALSE; } return TRUE; } /** * lasso_saml20_profile_process_any_response: * @profile: the SAML 2.0 #LassoProfile object * @status_response: the prototype for the response object * @response_msg: the content of the response message * * Generic method for SAML 2.0 protocol message handling. * * It tries to validate a signature on the response msg, the result of this operation is kept inside * profile->signature_status. Use it afterward in your specific profile. Beware that it does not * return an error code if signature validation failed. It let's specific profile accept unsigned * messages. * * Return value: 0 if successful, an error code otherwise. */ int lasso_saml20_profile_process_any_response(LassoProfile *profile, LassoSamlp2StatusResponse *status_response, LassoHttpMethod *response_method, const char *response_msg) { int rc = 0; LassoProvider *remote_provider = NULL; LassoServer *server = NULL; LassoSamlp2StatusResponse *response_abstract = NULL; LassoSamlp2Status *status = NULL; LassoSamlp2StatusCode *status_code1 = NULL; LassoMessageFormat format; gboolean missing_issuer = FALSE; xmlDoc *doc = NULL; xmlNode *content = NULL; lasso_bad_param(PROFILE, profile); lasso_bad_param(SAMLP2_STATUS_RESPONSE, status_response); /* reset signature_status */ profile->signature_status = 0; format = lasso_node_init_from_message_with_format((LassoNode*)status_response, response_msg, LASSO_MESSAGE_FORMAT_UNKNOWN, &doc, &content); if (format <= LASSO_MESSAGE_FORMAT_UNKNOWN) { rc = LASSO_PROFILE_ERROR_INVALID_MSG; goto cleanup; } if (response_method) { switch (format) { case LASSO_MESSAGE_FORMAT_SOAP: *response_method = LASSO_HTTP_METHOD_SOAP; break; case LASSO_MESSAGE_FORMAT_QUERY: *response_method = LASSO_HTTP_METHOD_REDIRECT; break; case LASSO_MESSAGE_FORMAT_BASE64: *response_method = LASSO_HTTP_METHOD_POST; break; default: return LASSO_PROFILE_ERROR_UNSUPPORTED_PROFILE; } } lasso_assign_gobject(profile->response, (LassoNode*)status_response); lasso_extract_node_or_fail(response_abstract, profile->response, SAMLP2_STATUS_RESPONSE, LASSO_PROFILE_ERROR_INVALID_MSG); lasso_extract_node_or_fail(server, profile->server, SERVER, LASSO_PROFILE_ERROR_MISSING_SERVER); if (_lasso_saml20_is_valid_issuer(response_abstract->Issuer)) { lasso_assign_string(profile->remote_providerID, response_abstract->Issuer->content); remote_provider = lasso_server_get_provider(server, profile->remote_providerID); } else { missing_issuer = TRUE; } if (remote_provider) { /* verify the signature at the message level */ if (content && doc && format != LASSO_MESSAGE_FORMAT_QUERY) { profile->signature_status = lasso_provider_verify_saml_signature(remote_provider, content, doc); } else if (format == LASSO_MESSAGE_FORMAT_QUERY) { profile->signature_status = lasso_provider_verify_query_signature(remote_provider, response_msg); } else { profile->signature_status = LASSO_DS_ERROR_SIGNATURE_VERIFICATION_FAILED; } } else { rc = LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND; profile->signature_status = LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND; goto cleanup; } /* verify status code */ lasso_extract_node_or_fail(status, status_response->Status, SAMLP2_STATUS, LASSO_PROFILE_ERROR_MISSING_STATUS_CODE); lasso_extract_node_or_fail(status_code1, status->StatusCode, SAMLP2_STATUS_CODE, LASSO_PROFILE_ERROR_MISSING_STATUS_CODE); if (lasso_strisnotequal(status_code1->Value,LASSO_SAML2_STATUS_CODE_SUCCESS)) { LassoSamlp2StatusCode *status_code2 = status_code1->StatusCode; rc = LASSO_PROFILE_ERROR_STATUS_NOT_SUCCESS; if (!status_code2) goto cleanup; if (!status_code2->Value) goto cleanup; /* FIXME: what to do with secondary status code ? */ if (lasso_strisequal(status_code2->Value, LASSO_SAML2_STATUS_CODE_REQUEST_DENIED)) { rc = LASSO_PROFILE_ERROR_REQUEST_DENIED; } } cleanup: lasso_release_doc(doc); if (rc) { return rc; } switch (lasso_profile_get_signature_verify_hint(profile)) { case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE: case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE: if (profile->signature_status) { return LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE; } case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE: break; case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST: g_assert_not_reached(); } if (missing_issuer) { return LASSO_PROFILE_ERROR_MISSING_ISSUER; } return 0; } /** * lasso_saml20_profile_process_soap_response: * * Generic method for processing SAML 2.0 protocol message as a SOAP response. * * Return value: 0 if successful; an error code otherwise. */ int lasso_saml20_profile_process_soap_response(LassoProfile *profile, const char *response_msg) { int rc = 0; LassoSaml2NameID *issuer = NULL; LassoProvider *remote_provider = NULL; LassoServer *server = NULL; LassoSamlp2StatusResponse *response_abstract = NULL; lasso_bad_param(PROFILE, profile); lasso_null_param(response_msg); profile->signature_status = 0; lasso_assign_new_gobject(profile->response, lasso_node_new_from_soap(response_msg)); lasso_extract_node_or_fail(response_abstract, profile->response, SAMLP2_STATUS_RESPONSE, LASSO_PROFILE_ERROR_INVALID_MSG); lasso_extract_node_or_fail(server, profile->server, SERVER, LASSO_PROFILE_ERROR_MISSING_SERVER); lasso_extract_node_or_fail(issuer, response_abstract->Issuer, SAML2_NAME_ID, LASSO_PROFILE_ERROR_MISSING_ISSUER); lasso_assign_string(profile->remote_providerID, issuer->content); remote_provider = lasso_server_get_provider(server, profile->remote_providerID); if (remote_provider == NULL) { rc = LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND; goto cleanup; } profile->signature_status = lasso_provider_verify_signature( remote_provider, response_msg, "ID", LASSO_MESSAGE_FORMAT_SOAP); switch (lasso_profile_get_signature_verify_hint(profile)) { case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE: rc = profile->signature_status; break; case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE: break; default: g_assert(0); break; } cleanup: return rc; } gint lasso_saml20_profile_build_http_redirect_query_simple(LassoProfile *profile, LassoNode *msg, const char *profile_name, gboolean is_response) { char *idx = NULL; char *url = NULL; LassoProvider *remote_provider = NULL; int rc = 0; goto_cleanup_if_fail_with_rc(profile->remote_providerID != NULL, LASSO_PROFILE_ERROR_MISSING_REMOTE_PROVIDERID); remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID); goto_cleanup_if_fail_with_rc(LASSO_IS_PROVIDER(remote_provider), LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND); if (is_response) { idx = g_strdup_printf("%s HTTP-Redirect ResponseLocation", profile_name); url = lasso_provider_get_metadata_one(remote_provider, idx); lasso_release(idx); } if (url == NULL) { idx = g_strdup_printf("%s HTTP-Redirect", profile_name); url = lasso_provider_get_metadata_one(remote_provider, idx); lasso_release(idx); } /* remove signature at the message level */ rc = lasso_saml20_profile_build_http_redirect(profile, msg, url); cleanup: lasso_release(url); return rc; } gint lasso_profile_saml20_setup_message_signature(LassoProfile *profile, LassoNode *request_or_response) { lasso_bad_param(PROFILE, profile); LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE; lasso_error_t rc = 0; switch (lasso_profile_get_signature_hint(profile)) { case LASSO_PROFILE_SIGNATURE_HINT_MAYBE: if (! lasso_flag_sign_messages) { message(G_LOG_LEVEL_WARNING, "message should be signed but no-sign-messages flag is " \ "activated, so it won't be"); return 0; } break; case LASSO_PROFILE_SIGNATURE_HINT_FORBID: return 0; default: break; } if (! LASSO_IS_SERVER(profile->server)) { return LASSO_PROFILE_ERROR_MISSING_SERVER; } lasso_check_good_rc(lasso_server_get_signature_context_for_provider_by_name(profile->server, profile->remote_providerID, &context)); lasso_check_good_rc(lasso_node_set_signature(request_or_response, context)); cleanup: return rc; } /** * lasso_saml20_profile_setup_subject: * @profile: a #LassoProfile object * @subject: a #LassoSaml2Subject object * * Encrypt subject if necessary. */ int lasso_saml20_profile_setup_subject(LassoProfile *profile, LassoSaml2Subject *subject) { LassoProvider *remote_provider; remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID); g_return_val_if_fail (LASSO_IS_PROVIDER(remote_provider), LASSO_ERROR_CAST_FAILED); if (! (lasso_provider_get_encryption_mode(remote_provider) & LASSO_ENCRYPTION_MODE_NAMEID)) { return 0; } return lasso_saml20_profile_setup_encrypted_node(remote_provider, (LassoNode**)subject->NameID, (LassoNode**)subject->EncryptedID); } gint lasso_saml20_profile_setup_encrypted_node(LassoProvider *provider, LassoNode **node_to_encrypt, LassoNode **node_destination) { LassoNode *encrypted_node; if (! LASSO_IS_PROVIDER(provider)) { return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND); } encrypted_node = (LassoNode*)lasso_node_encrypt(*node_to_encrypt, lasso_provider_get_encryption_public_key(provider), lasso_provider_get_encryption_sym_key_type(provider), provider->ProviderID); if (! encrypted_node) { return LASSO_DS_ERROR_ENCRYPTION_FAILED; } lasso_assign_new_gobject(*node_destination, encrypted_node); lasso_release_gobject(*node_to_encrypt); return 0; } /** * Check the profile->signature_status flag, if signature validation is activated, report it as an * error, if not not return 0. */ int lasso_saml20_profile_check_signature_status(LassoProfile *profile) { int rc = 0; if (profile->signature_status) { switch (lasso_profile_get_signature_verify_hint(profile)) { case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE: case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE: rc = profile->signature_status; break; case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE: break; case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST: g_assert_not_reached(); break; } } return rc; }