lasso/lasso/saml-2.0/profile.c

1531 lines
48 KiB
C

/* $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 <xmlsec/base64.h>
#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 "../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,
LassoSignatureMethod method, const char *private_key);
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 gboolean has_signature(LassoNode *node, LassoSignatureMethod *signature_method, const char
**private_key_file);
#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)
{
int rc = 0;
LassoProvider *provider;
LassoServer *server;
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 0;
}
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)
**/
char*
lasso_saml20_profile_generate_artifact(LassoProfile *profile, int part)
{
lasso_assign_new_string(profile->private_data->artifact,
lasso_saml20_profile_build_artifact(&profile->server->parent));
if (part == 0) {
lasso_assign_new_string(profile->private_data->artifact_message,
lasso_node_dump(profile->request));
} else if (part == 1) {
lasso_assign_new_string(profile->private_data->artifact_message,
lasso_node_dump(profile->response));
} else {
/* XXX: RequestDenied here? */
}
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;
/* 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->remote_providerID, LASSO_SAMLP2_REQUEST_ABSTRACT(
profile->request)->Issuer->content);
remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID);
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;
}
lasso_assign_string(profile->private_data->artifact,
LASSO_SAMLP2_ARTIFACT_RESOLVE(profile->request)->Artifact);
return rc;
}
int
lasso_saml20_profile_build_artifact_response(LassoProfile *profile)
{
LassoSamlp2StatusResponse *response = NULL;
LassoNode *resp = NULL;
int rc = 0;
if ( ! LASSO_IS_SAMLP2_REQUEST_ABSTRACT(profile->request)) {
return LASSO_PROFILE_ERROR_MISSING_REQUEST;
}
response = LASSO_SAMLP2_STATUS_RESPONSE(lasso_samlp2_artifact_response_new());
if (profile->private_data->artifact_message) {
resp = lasso_node_new_from_dump(profile->private_data->artifact_message);
lasso_assign_new_gobject(LASSO_SAMLP2_ARTIFACT_RESPONSE(response)->any, resp);
}
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);
lasso_check_good_rc(lasso_profile_saml20_setup_message_signature(profile,
(LassoNode*)response));
lasso_assign_new_gobject(profile->response, LASSO_NODE(response));
if (resp == NULL) {
lasso_saml20_profile_set_response_status(profile,
LASSO_SAML2_STATUS_CODE_REQUESTER, NULL);
} else {
lasso_saml20_profile_set_response_status(profile,
LASSO_SAML2_STATUS_CODE_SUCCESS, NULL);
}
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) {
lasso_release_gobject(profile->response);
goto_cleanup_with_rc(LASSO_PROFILE_ERROR_MISSING_RESPONSE);
}
lasso_assign_gobject(profile->response, artifact_response->any);
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;
rc = lasso_saml2_encrypted_element_decrypt(assertion->Subject->EncryptedID, lasso_server_get_encryption_private_key(profile->server), (LassoNode**) &assertion->Subject->NameID);
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)
{
xmlSecKey *encryption_private_key = NULL;
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) {
encryption_private_key = profile->server->private_data->encryption_private_key;
if (! LASSO_IS_SAML2_ENCRYPTED_ELEMENT(*encrypted_id)) {
return LASSO_PROFILE_ERROR_MISSING_NAME_IDENTIFIER;
}
if (encrypted_id != NULL && encryption_private_key == NULL) {
return LASSO_PROFILE_ERROR_MISSING_ENCRYPTION_PRIVATE_KEY;
}
rc = lasso_saml2_encrypted_element_decrypt(*encrypted_id, encryption_private_key,
&profile->nameIdentifier);
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 error code
* otherwise.
*/
int
lasso_saml20_profile_process_any_request(LassoProfile *profile,
LassoNode *request_node,
const char *request_msg)
{
int rc = 0;
LassoSaml2NameID *name_id = NULL;
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);
lasso_extract_node_or_fail(name_id, request_abstract->Issuer, SAML2_NAME_ID,
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_MAYBE:
rc = profile->signature_status;
break;
case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE:
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));
}
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);
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;
LassoIdentity *identity = 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) {
lasso_extract_node_or_fail(identity, profile->identity, 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);
if (profile->signature_status) {
message(G_LOG_LEVEL_WARNING, "Request signature is invalid");
lasso_saml20_profile_set_response_status(profile,
LASSO_SAML2_STATUS_CODE_REQUESTER,
LASSO_LIB_STATUS_CODE_INVALID_SIGNATURE);
return profile->signature_status;
}
cleanup:
if (provider && provider_out)
*provider_out = provider;
return rc;
}
/**
* lasso_saml20_profile_export_to_query:
* @profile: a #LassoProfile
* @request_or_response: 0 to encode the request, 1 to encode the response
* @sign: TRUE if query must signed, FALSE otherwise
*
* 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: a newly allocated string containing the query string if successfull, NULL
* otherwise.
*/
static int
lasso_saml20_profile_export_to_query(LassoProfile *profile, LassoNode *msg, char **query,
LassoSignatureMethod signature_method, const char *private_key_file) {
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 (signature_method && private_key_file && lasso_flag_add_signature) {
result = lasso_query_sign(unsigned_query, signature_method, private_key_file);
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;
}
static gboolean
has_signature(LassoNode *node, LassoSignatureMethod *method, const char **private_key_file) {
LassoNodeClass *klass;
if (node == NULL)
return FALSE;
klass = LASSO_NODE_GET_CLASS(node);
/* follow the class parenting chain */
while (klass && LASSO_IS_NODE_CLASS(klass)) {
if (klass && klass->node_data && klass->node_data->sign_type_offset != 0) {
if (G_STRUCT_MEMBER(LassoSignatureType, node,
klass->node_data->sign_type_offset)
!= LASSO_SIGNATURE_TYPE_NONE) {
*method = G_STRUCT_MEMBER(LassoSignatureMethod, node,
klass->node_data->sign_method_offset);
*private_key_file = G_STRUCT_MEMBER(char*, node,
klass->node_data->private_key_file_offset);
return TRUE;
}
}
klass = g_type_class_peek_parent(klass);
}
return FALSE;
}
/**
* 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;
gboolean must_sign = FALSE;
LassoSignatureMethod signature_method = 0;
const char *private_key_file = NULL;
goto_cleanup_if_fail_with_rc (url != NULL, LASSO_PROFILE_ERROR_UNKNOWN_PROFILE_URL);
/* if message is signed, remove XML signature, add query signature */
if (has_signature(msg, &signature_method, (const char **)&private_key_file)) {
must_sign = TRUE;
lasso_node_remove_signature(msg);
}
lasso_check_good_rc(lasso_saml20_profile_export_to_query(profile, msg, &query,
signature_method, private_key_file));
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;
}
}
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 && g_strcmp0(name_id->Format, LASSO_SAML2_NAME_IDENTIFIER_FORMAT_ENTITY) != 0) {
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);
} else {
/* no issuer ? use implicit provider id */
missing_issuer = TRUE;
}
remote_provider = lasso_server_get_provider(server, profile->remote_providerID);
goto_cleanup_if_fail_with_rc(remote_provider != NULL, LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND);
/* 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;
}
/* propagate signature status ? */
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;
}
/* 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);
goto_cleanup_if_fail_with_rc (! rc && status_code1->Value != NULL,
LASSO_PROFILE_ERROR_MISSING_STATUS_CODE);
if (! rc && strcmp(status_code1->Value, LASSO_SAML2_STATUS_CODE_SUCCESS) != 0) {
rc = LASSO_PROFILE_ERROR_STATUS_NOT_SUCCESS;
}
cleanup:
lasso_release_doc(doc);
if (rc) {
return rc;
}
if (profile->signature_status) {
return LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE;
}
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;
}
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);
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;
}
if (! profile->server->private_key) {
return LASSO_DS_ERROR_PRIVATE_KEY_LOAD_FAILED;
}
if (LASSO_IS_SAMLP2_REQUEST_ABSTRACT(request_or_response)) {
LassoSamlp2RequestAbstract *request;
request = (LassoSamlp2RequestAbstract*)request_or_response;
if (profile->server->certificate) {
request->sign_type = LASSO_SIGNATURE_TYPE_WITHX509;
} else {
request->sign_type = LASSO_SIGNATURE_TYPE_SIMPLE;
}
request->sign_method = LASSO_SIGNATURE_METHOD_RSA_SHA1;
lasso_assign_string(request->private_key_file,
profile->server->private_key);
lasso_assign_string(request->certificate_file,
profile->server->certificate);
} else if (LASSO_IS_SAMLP2_STATUS_RESPONSE(request_or_response)) {
LassoSamlp2StatusResponse *response;
response = (LassoSamlp2StatusResponse*)request_or_response;
if (profile->server->certificate) {
response->sign_type = LASSO_SIGNATURE_TYPE_WITHX509;
} else {
response->sign_type = LASSO_SIGNATURE_TYPE_SIMPLE;
}
response->sign_method = LASSO_SIGNATURE_METHOD_RSA_SHA1;
lasso_assign_string(response->private_key_file,
profile->server->private_key);
lasso_assign_string(response->certificate_file,
profile->server->certificate);
} else {
return LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ;
}
return 0;
}
/**
* 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;
}