diff options
-rw-r--r-- | docs/reference/lasso/lasso-sections.txt | 15 | ||||
-rw-r--r-- | lasso/saml-2.0/Makefile.am | 3 | ||||
-rw-r--r-- | lasso/saml-2.0/saml2_helper.c | 711 | ||||
-rw-r--r-- | lasso/saml-2.0/saml2_helper.h | 98 |
4 files changed, 826 insertions, 1 deletions
diff --git a/docs/reference/lasso/lasso-sections.txt b/docs/reference/lasso/lasso-sections.txt index adc68924..0467d918 100644 --- a/docs/reference/lasso/lasso-sections.txt +++ b/docs/reference/lasso/lasso-sections.txt @@ -13,6 +13,7 @@ lasso_server_dump lasso_server_get_provider lasso_server_set_encryption_private_key lasso_server_load_affiliation +lasso_server_saml2_assertion_setup_signature <SUBSECTION Standard> LASSO_SERVER LASSO_IS_SERVER @@ -2691,6 +2692,8 @@ LASSO_SAML2_SUBJECT_GET_CLASS LassoSaml2NameID lasso_saml2_name_id_new lasso_saml2_name_id_new_with_string +lasso_saml2_name_id_build_persistent +lasso_saml2_name_id_build_encrypted_persistent <SUBSECTION Standard> LASSO_SAML2_NAME_ID LASSO_IS_SAML2_NAME_ID @@ -2887,6 +2890,7 @@ LASSO_SAMLP2_LOGOUT_RESPONSE_GET_CLASS LassoSaml2EncryptedElement lasso_saml2_encrypted_element_new lasso_saml2_encrypted_element_decrypt +lasso_saml2_encrypted_element_build_encrypted_persistent_name_id <SUBSECTION Standard> LASSO_SAML2_ENCRYPTED_ELEMENT LASSO_IS_SAML2_ENCRYPTED_ELEMENT @@ -2962,6 +2966,17 @@ LASSO_SAMLP2_AUTHN_REQUEST_GET_CLASS <TITLE>LassoSaml2Assertion</TITLE> LassoSaml2Assertion lasso_saml2_assertion_new +lasso_saml2_assertion_has_audience_restriction +lasso_saml2_assertion_is_audience_restricted +lasso_saml2_assertion_set_subject_name_id +lasso_saml2_assertion_set_subject_confirmation_name_id +lasso_saml2_assertion_set_subject_confirmation_data +lasso_saml2_assertion_set_basic_conditions +lasso_saml2_assertion_add_audience_restriction +lasso_saml2_assertion_add_proxy_limit +lasso_saml2_assertion_validate_conditions +lasso_saml2_assertion_get_issuer_provider +lasso_saml2_assertion_add_attribute_with_node <SUBSECTION Standard> LASSO_SAML2_ASSERTION LASSO_IS_SAML2_ASSERTION diff --git a/lasso/saml-2.0/Makefile.am b/lasso/saml-2.0/Makefile.am index 768b946c..a778462a 100644 --- a/lasso/saml-2.0/Makefile.am +++ b/lasso/saml-2.0/Makefile.am @@ -18,7 +18,8 @@ liblasso_saml_20_la_SOURCES = \ login.c \ logout.c \ name_id_management.c \ - server.c + server.c \ + saml2_helper.c liblassoinclude_HEADERS = \ assertion_query.h \ diff --git a/lasso/saml-2.0/saml2_helper.c b/lasso/saml-2.0/saml2_helper.c new file mode 100644 index 00000000..fe830f25 --- /dev/null +++ b/lasso/saml-2.0/saml2_helper.c @@ -0,0 +1,711 @@ +/* $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 "./saml2_helper.h" + +#include "../xml/saml-2.0/saml2_audience_restriction.h" +#include "../xml/saml-2.0/saml2_one_time_use.h" +#include "../xml/saml-2.0/saml2_proxy_restriction.h" +#include "../xml/saml-2.0/saml2_attribute.h" +#include "../xml/saml-2.0/saml2_attribute_statement.h" +#include "../xml/saml-2.0/saml2_attribute_value.h" +#include "../xml/private.h" +#include "../utils.h" +#include "./provider.h" +#include <time.h> + +/** + * lasso_saml2_assertion_has_audience_restriction: + * @saml2_assertion: a #LassoSaml2Assertion object + * + * Verify that a #LassoSaml2AudienceRestriction is present in the assertion. + * + * Return value: TRUE if a #LassoSaml2AudienceRestriction is present in the Conditions of the + * #LassoSaml2Assertion. + */ +gboolean +lasso_saml2_assertion_has_audience_restriction(LassoSaml2Assertion *saml2_assertion) +{ + GList *it; + + g_return_val_if_fail (LASSO_IS_SAML2_ASSERTION(saml2_assertion), FALSE); + if (! LASSO_IS_SAML2_CONDITIONS(saml2_assertion->Conditions)) + return FALSE; + + lasso_foreach(it, saml2_assertion->Conditions->Condition) + { + if (LASSO_IS_SAML2_AUDIENCE_RESTRICTION(it->data)) { + return TRUE; + } + } + return FALSE; +} + +/** + * lasso_saml2_assertion_is_audience_restricted: + * @saml2_assertion: a #LassoSaml2Assertion object + * @providerID: the providerID that will be compared to the audience restriction declarations. + * + * Verify that the assertion is restricted to the given providerID. + * + * Return value: TRUE if @providerID is part of a #LassoSaml2AudienceRestriction element in the + * assertion, FALSE otherwise. + */ +gboolean +lasso_saml2_assertion_is_audience_restricted(LassoSaml2Assertion *saml2_assertion, char* providerID) +{ + GList *it; + + g_return_val_if_fail (LASSO_IS_SAML2_ASSERTION(saml2_assertion), FALSE); + if (! LASSO_IS_SAML2_CONDITIONS(saml2_assertion->Conditions)) + return FALSE; + lasso_foreach(it, saml2_assertion->Conditions->Condition) + { + if (LASSO_IS_SAML2_AUDIENCE_RESTRICTION(it->data)) { + LassoSaml2AudienceRestriction *saml2_audience_restriction; + saml2_audience_restriction = (LassoSaml2AudienceRestriction*)it->data; + if (g_strcmp0(saml2_audience_restriction->Audience, providerID) == 0) + return TRUE; + } + } + return FALSE; +} + +/** + * lasso_saml2_name_id_build_persistent: + * @id: the identifier for the princiapl + * @idpID: the entity ID of the IdP + * @providerID: the entity ID of the provider + * + * Create a new #LassoSaml2NameID object, which the #LASSO_SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT + * format, @id as content, @idpID as NameQualifier and @providerID as SPNameQualifier. + * + * Return value: a newly created #LassoSaml2NameID + */ +LassoSaml2NameID* +lasso_saml2_name_id_build_persistent(const char *id, const char *idpID, const char *providerID) +{ + LassoSaml2NameID *saml2_name_id; + + saml2_name_id = (LassoSaml2NameID*)lasso_saml2_name_id_new(); + saml2_name_id->content = g_strdup(id); + saml2_name_id->Format = g_strdup(LASSO_SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT); + saml2_name_id->NameQualifier = g_strdup(idpID); + saml2_name_id->SPNameQualifier = g_strdup(providerID); + + return saml2_name_id; +} + +/** + * lasso_saml2_name_id_build_encrypted_persistent + * @id: the identifier for the principal + * @idpID: the entity ID of the IdP + * @provider: the provider for which the NameID is created. + * + * Create a new #LassoSaml2NameID issued by @idpID and targeted at @provider, and encrypt it using + * @provider public key. + * + * Return value: a newly created #LassoSaml2EncryptedElement object. + */ +LassoSaml2EncryptedElement* lasso_saml2_encrypted_element_build_encrypted_persistent_name_id( + const char *id, const char *idpID, const LassoProvider *provider) +{ + LassoSaml2NameID *saml2_name_id; + LassoSaml2EncryptedElement *encrypted_element; + + saml2_name_id = lasso_saml2_name_id_build_persistent(id, idpID, provider->ProviderID); + encrypted_element = lasso_provider_saml2_node_encrypt(provider, (LassoNode*)saml2_name_id); + lasso_release_gobject(saml2_name_id); + + return encrypted_element; +} + +static LassoSaml2Conditions* +lasso_saml2_assertion_get_conditions(LassoSaml2Assertion *saml2_assertion, gboolean create) +{ + if (! LASSO_IS_SAML2_CONDITIONS(saml2_assertion->Conditions) && create) { + lasso_assign_new_gobject (saml2_assertion->Conditions, + (LassoSaml2Conditions*)lasso_saml2_conditions_new()); + } + return saml2_assertion->Conditions; +} + +static LassoSaml2Subject* +lasso_saml2_assertion_get_subject(LassoSaml2Assertion *saml2_assertion, gboolean create) +{ + if (! LASSO_IS_SAML2_SUBJECT(saml2_assertion->Subject) && create) { + lasso_assign_new_gobject(saml2_assertion->Subject, + (LassoSaml2Subject*)lasso_saml2_subject_new()); + } + return saml2_assertion->Subject; +} + +static LassoSaml2SubjectConfirmation* +lasso_saml2_assertion_get_subject_confirmation(LassoSaml2Assertion *saml2_assertion, gboolean create) +{ + LassoSaml2Subject *subject; + + subject = lasso_saml2_assertion_get_subject (saml2_assertion, create); + if (subject == NULL) + return NULL; + + if (! LASSO_IS_SAML2_SUBJECT_CONFIRMATION(subject->SubjectConfirmation) && create) { + lasso_assign_new_gobject(subject->SubjectConfirmation, + (LassoSaml2SubjectConfirmation*)lasso_saml2_subject_confirmation_new()); + } + + return subject->SubjectConfirmation; +} + +static LassoSaml2SubjectConfirmationData* +lasso_saml2_assertion_get_subject_confirmation_data(LassoSaml2Assertion *saml2_assertion, gboolean create) +{ + LassoSaml2SubjectConfirmation *subject_confirmation; + + subject_confirmation = lasso_saml2_assertion_get_subject_confirmation (saml2_assertion, create); + if (subject_confirmation == NULL) + return NULL; + + if (! LASSO_IS_SAML2_SUBJECT_CONFIRMATION_DATA(subject_confirmation->SubjectConfirmationData) && create) { + lasso_assign_new_gobject(subject_confirmation->SubjectConfirmationData, + (LassoSaml2SubjectConfirmationData*)lasso_saml2_subject_confirmation_data_new()); + } + + return subject_confirmation->SubjectConfirmationData; +} + +/** + * lasso_saml2_assertion_set_subject_name_id: + * @saml2_assertion: a #LassoSaml2Assertion object + * @node: a #LassoSaml2NameID or #LassoSaml2EncryptedElement + * + * Set the subject NameID, which can be a simple #LassoSaml2NameID object or an encrypted + * #LassoSaml2NameID as a #LassoSaml2EncryptedElement. + */ +void +lasso_saml2_assertion_set_subject_name_id(LassoSaml2Assertion *saml2_assertion, LassoNode *node) +{ + LassoSaml2Subject *saml2_subject; + + g_return_if_fail (LASSO_IS_SAML2_ASSERTION (saml2_assertion)); + + saml2_subject = lasso_saml2_assertion_get_subject (saml2_assertion, TRUE); + if (LASSO_IS_SAML2_NAME_ID(node)) { + lasso_assign_gobject (saml2_subject->NameID, node); + } else if (LASSO_IS_SAML2_ENCRYPTED_ELEMENT(node)) { + lasso_assign_gobject(saml2_subject->EncryptedID, node) + } else { + g_warning("Cannot set subject name id, since node is neither an EncryptedElement or a NameID"); + } +} + +/** + * lasso_saml2_assertion_set_subject_confirmation_name_id: + * @saml2_assertion: a #LassoSaml2Assertion object + * @node: a #LassoSaml2NameID or #LassoSaml2EncryptedElement + * + * Set the subject NameID, which can be a simple #LassoSaml2NameID object or an encrypted + * #LassoSaml2NameID as a #LassoSaml2EncryptedElement. + */ +void +lasso_saml2_assertion_set_subject_confirmation_name_id(LassoSaml2Assertion *saml2_assertion, LassoNode *node) +{ + LassoSaml2SubjectConfirmation *saml2_subject_confirmation; + + g_return_if_fail (LASSO_IS_SAML2_ASSERTION (saml2_assertion)); + + saml2_subject_confirmation = lasso_saml2_assertion_get_subject_confirmation (saml2_assertion, TRUE); + if (LASSO_IS_SAML2_NAME_ID(node)) { + lasso_assign_gobject (saml2_subject_confirmation->NameID, node); + } else if (LASSO_IS_SAML2_ENCRYPTED_ELEMENT(node)) { + lasso_assign_gobject(saml2_subject_confirmation->EncryptedID, node) + } else { + g_warning("Cannot set subject name id, since node is neither an EncryptedElement or a NameID"); + } +} + +#define set_notbefore_and_notonorafter(node, tolerance, length) \ + if (tolerance != -1 && length != -1) \ + { \ + time_t now, a, b; \ + now = time(NULL); \ + a = now - tolerance; \ + b = now + length + tolerance; \ + lasso_assign_new_string (node->NotBefore, \ + lasso_time_to_iso_8601_gmt (a)); \ + lasso_assign_new_string (node->NotOnOrAfter, \ + lasso_time_to_iso_8601_gmt (b)); \ + } + +/** + * lasso_saml2_set_subject_confirmation_data: + * @saml2_assertion: a #LassoSaml2Assertion object + * @tolerance: tolerance to the range of time when the subject can be confirmed + * @length: length of the range of time when the subject can be confirmed + * @Recipient: the URL where the assertion can be consumed + * @InResponseTo: the identifier of the request which resulted in this assertion + * @Address: the address IP from which the subject should submit this assertion. + * + */ +void +lasso_saml2_assertion_set_subject_confirmation_data(LassoSaml2Assertion *saml2_assertion, + const time_t tolerance, const time_t length, const char *Recipient, + const char *InResponseTo, const char *Address) +{ + LassoSaml2SubjectConfirmationData *saml2_subject_confirmation_data; + + g_return_if_fail(LASSO_IS_SAML2_ASSERTION (saml2_assertion)); + + saml2_subject_confirmation_data = lasso_saml2_assertion_get_subject_confirmation_data (saml2_assertion, TRUE); + set_notbefore_and_notonorafter (saml2_subject_confirmation_data, tolerance, length); + lasso_assign_string (saml2_subject_confirmation_data->Recipient, Recipient); + lasso_assign_string (saml2_subject_confirmation_data->InResponseTo, InResponseTo); + lasso_assign_string (saml2_subject_confirmation_data->Address, Address); +} + +/** + * lasso_saml2_set_conditions: + * @tolerance:(default -1): tolerance to the range of time when the assertion is valid + * @length:(default -1): length of the range of time when the assertion is valid + * @audience_restriction:(allow-none): a providerID to restrict the assertion audience to + * @one_time_use:(default FALSE): can the assertion be kept or should it be used immediately + * @proxy_count:(default -1): number of hops of proxies + * + * Set conditions limiting usage of the assertion. + * + * @tolerance and @length are time quantity measured in seconds, it defines the time range in which + * the assertion is valid, it is computed as [now()-tolerance, now()+length+tolerance]. + * @audience_restriction allows to limit the target of the assertion. + * @one_time_use allows the issuer to limit caching of the assertion. + * @proxy_count specify how many proxy hop can be traversed before this assertion should lose any trust. + * + */ +void +lasso_saml2_assertion_set_basic_conditions(LassoSaml2Assertion *saml2_assertion, time_t tolerance, + time_t length, gboolean one_time_use) +{ + LassoSaml2Conditions *saml2_conditions; + + g_return_if_fail (LASSO_IS_SAML2_ASSERTION (saml2_assertion)); + + saml2_conditions = lasso_saml2_assertion_get_conditions (saml2_assertion, TRUE); + set_notbefore_and_notonorafter (saml2_assertion->Conditions, tolerance, length); + if (one_time_use) { + lasso_list_add_new_gobject (saml2_conditions->OneTimeUse, lasso_saml2_one_time_use_new()); + } +} + +/** + * lasso_saml2_assertion_add_audience_restriction: + * @saml2_assertion: a #LassoSaml2Assertion object + * @providerId: the provider id to restrict audience to + * + * Add an audience restriction to a #LassoSaml2Assertion. + * + */ +void +lasso_saml2_assertion_add_audience_restriction(LassoSaml2Assertion *saml2_assertion, const char *providerID) +{ + LassoSaml2AudienceRestriction *audience_restriction; + LassoSaml2Conditions *conditions; + + g_return_if_fail (LASSO_IS_SAML2_ASSERTION(saml2_assertion)); + + conditions = lasso_saml2_assertion_get_conditions (saml2_assertion, TRUE); + audience_restriction = (LassoSaml2AudienceRestriction*) + lasso_saml2_audience_restriction_new(); + lasso_assign_string(audience_restriction->Audience, providerID); + lasso_list_add_new_gobject(conditions->AudienceRestriction, audience_restriction); +} + +/** + * lasso_saml2_assertion_add_proxy_limit: + * @saml2_assertion: a #LassoSaml2Assertion object + * @proxy_count:(default -1): the number of hops in the proxy chain, a negative value means no limitation + * @proxy_audiences:(allow-none)(element-type string): a list of audience restriction for newly issued assertion + * based on the @saml2_assertion assertion. An empty list means no audience restriction. + * + * A #LassoSaml2ProxyRestriction to the conditions of consumption of @saml2_assertion. + */ +void +lasso_saml2_assertion_add_proxy_limit (LassoSaml2Assertion *saml2_assertion, int proxy_count, + GList *proxy_audiences) +{ + LassoSaml2Conditions *saml2_conditions; + LassoSaml2ProxyRestriction *saml2_proxy_restriction; + + g_return_if_fail (LASSO_IS_SAML2_ASSERTION (saml2_assertion)); + saml2_conditions = lasso_saml2_assertion_get_conditions (saml2_assertion, TRUE); + saml2_proxy_restriction = (LassoSaml2ProxyRestriction*)lasso_saml2_proxy_restriction_new (); + if (proxy_count >= 0) { + saml2_proxy_restriction->Count = g_strdup_printf("%i", proxy_count); + } + if (proxy_audiences) { + lasso_assign_string (saml2_proxy_restriction->Audience, proxy_audiences->data); + if (proxy_audiences->next) { + g_warning ("Trying to set multiple proxy_audience restriction is not possible with currrent version of Lasso"); + } + } +} + +/** + * lasso_saml2_validation_conditions: + * @saml2_assertion: a #LassoSaml2Assertion object + * @relaying_party_providerID:(allow-none): the providerID of the current relaying party, use to + * check for audience restrictions. + * + * Check the validation of the assertion with respect to the conditions of consumption that it + * contains. System functions are used for getting current time and checking eventual time + * constraints. + * + * Return value: LASSO_SAML2_ASSERTION_VALID if the assertion is valid, + * LASSO_SAML2_ASSERTION_INVALID is some check failed, LASSO_SAML2_ASSERTION_INDETERMINATE if + * somehting was impossible to eveluate. + */ +LassoSaml2AssertionValidationState +lasso_saml2_assertion_validate_conditions(LassoSaml2Assertion *saml2_assertion, const char *relaying_party_providerID) +{ + /* FIXME: should allow to give a tolerance for date matching */ + LassoSaml2Conditions *saml2_conditions; + time_t a, b, now; + gboolean did_audience = FALSE; + gboolean found_audience = FALSE; + + g_return_val_if_fail (LASSO_SAML2_ASSERTION (saml2_assertion), LASSO_SAML2_ASSERTION_INDETERMINATE); + saml2_conditions = lasso_saml2_assertion_get_conditions(saml2_assertion, FALSE); + + if (saml2_conditions == NULL) + return LASSO_SAML2_ASSERTION_VALID; + + now = time(NULL); + + if (saml2_conditions->NotBefore) { + a = lasso_iso_8601_gmt_to_time_t (saml2_conditions->NotBefore); + if (a == -1) + return LASSO_SAML2_ASSERTION_INDETERMINATE; + if (now < a) { + return LASSO_SAML2_ASSERTION_INVALID; + } + } + if (saml2_conditions->NotOnOrAfter) { + b = lasso_iso_8601_gmt_to_time_t (saml2_conditions->NotOnOrAfter); + if (b == -1) + return LASSO_SAML2_ASSERTION_INDETERMINATE; + if (now >= b) { + return LASSO_SAML2_ASSERTION_INVALID; + } + } + /* FIXME: treat other kinds of conditions when they appear */ + if (saml2_conditions->Condition) { + return LASSO_SAML2_ASSERTION_INDETERMINATE; + } + lasso_foreach_full_begin (LassoSaml2AudienceRestriction*, saml2_audience_restriction, it, + saml2_conditions->AudienceRestriction) + did_audience = TRUE; + if (g_strcmp0(saml2_audience_restriction->Audience, relaying_party_providerID)) { + found_audience = TRUE; + } + lasso_foreach_full_end() + if (did_audience ^ found_audience) { + return LASSO_SAML2_ASSERTION_INVALID; + } + + return LASSO_SAML2_ASSERTION_VALID; +} + +/** + * lasso_saml2_assertion_get_issuer_provider: + * @saml2_assertion: a #LassoSaml2 assertion + * @server: a #LassoServer object + * + * Return the #LassoProvider object for the provider who created this assertion. + * + * Return value: a #LassoProvider object, or NULL if the Issuer element is missing, or the given + * provider unknown to the #LassoServer object. + */ +LassoProvider* +lasso_saml2_assertion_get_issuer_provider(const LassoSaml2Assertion *saml2_assertion, const LassoServer *server) +{ + LassoSaml2NameID *issuer; + + g_return_val_if_fail (LASSO_IS_SAML2_ASSERTION (saml2_assertion), NULL); + issuer = saml2_assertion->Issuer; + g_return_val_if_fail (LASSO_IS_SAML2_NAME_ID (issuer), NULL); + g_return_val_if_fail (g_strcmp0(issuer->Format, LASSO_SAML2_NAME_IDENTIFIER_FORMAT_ENTITY) == 0, NULL); + return lasso_server_get_provider (server, issuer->content); +} + + +/** + * lasso_server_saml2_assertion_setup_signature: + * @server: a #LassoServer object + * @saml2_assertion: a #LassoSaml2Assertion object + * + * Configure signature on a saml2:Assertion element. + * + * Return value: 0 if successfull, an error code otherwise. + */ +int +lasso_server_saml2_assertion_setup_signature(LassoServer *server, + LassoSaml2Assertion *saml2_assertion) +{ + lasso_bad_param(SERVER, server); + lasso_bad_param(SAML2_ASSERTION, saml2_assertion); + + if (server->certificate) { + saml2_assertion->sign_type = LASSO_SIGNATURE_TYPE_WITHX509; + } else { + saml2_assertion->sign_type = LASSO_SIGNATURE_TYPE_SIMPLE; + } + saml2_assertion->sign_method = server->signature_method; + lasso_assign_string(saml2_assertion->private_key_file, + server->private_key); + lasso_assign_string(saml2_assertion->certificate_file, + server->certificate); + + return 0; +} + +#if 0 +lasso_saml2_assertion(LassoL + const char *authenticationMethod, + const char *authenticationInstant, + const char *notBefore, + const char *notOnOrAfter) +{ + LassoProfile *profile = LASSO_PROFILE(login); + LassoFederation *federation; + LassoSaml2Assertion *assertion; + LassoSaml2AudienceRestriction *audience_restriction; + LassoSamlp2NameIDPolicy *name_id_policy; + LassoSaml2NameID *name_id = NULL; + LassoSaml2AuthnStatement *authentication_statement; + LassoProvider *provider = NULL; + LassoSaml2EncryptedElement *encrypted_element = NULL; + LassoSamlp2Response *response = NULL; + LassoSamlp2RequestAbstract *request_abstract = NULL; + + provider = lasso_server_get_provider(profile->server, profile->remote_providerID); + + if (! LASSO_IS_PROVIDER(provider)) { + return LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND; + } + + if (profile->request && LASSO_IS_SAMLP2_REQUEST_ABSTRACT(profile->request)) { + request_abstract = LASSO_SAMLP2_REQUEST_ABSTRACT(profile->request); + } + + if (profile->identity && strcmp(login->nameIDPolicy, + LASSO_SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT) != 0) { + char *name_id_sp_name_qualifier; + name_id_sp_name_qualifier = lasso_provider_get_sp_name_qualifier(provider); + + if (name_id_sp_name_qualifier != NULL) { + federation = g_hash_table_lookup(profile->identity->federations, + name_id_sp_name_qualifier); + lasso_release_string(name_id_sp_name_qualifier); + } + if (federation == NULL) { + message(G_LOG_LEVEL_WARNING, "can't find federation for identity"); + } + } else { + federation = NULL; + } + + assertion = LASSO_SAML2_ASSERTION(lasso_saml2_assertion_new()); + assertion->ID = lasso_build_unique_id(32); + lasso_assign_string(assertion->Version, "2.0"); + assertion->IssueInstant = lasso_get_current_time(); + assertion->Issuer = LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string( + LASSO_PROVIDER(profile->server)->ProviderID)); + assertion->Conditions = LASSO_SAML2_CONDITIONS(lasso_saml2_conditions_new()); + + audience_restriction = LASSO_SAML2_AUDIENCE_RESTRICTION( + lasso_saml2_audience_restriction_new()); + lasso_assign_string(audience_restriction->Audience, profile->remote_providerID); + lasso_list_add_new_gobject(assertion->Conditions->AudienceRestriction, audience_restriction); + + name_id_policy = LASSO_SAMLP2_AUTHN_REQUEST(profile->request)->NameIDPolicy; + assertion->Subject = LASSO_SAML2_SUBJECT(lasso_saml2_subject_new()); + assertion->Subject->SubjectConfirmation = LASSO_SAML2_SUBJECT_CONFIRMATION( + lasso_saml2_subject_confirmation_new()); + assertion->Subject->SubjectConfirmation->Method = g_strdup( + LASSO_SAML2_CONFIRMATION_METHOD_BEARER); + assertion->Subject->SubjectConfirmation->SubjectConfirmationData = + LASSO_SAML2_SUBJECT_CONFIRMATION_DATA( + lasso_saml2_subject_confirmation_data_new()); + assertion->Subject->SubjectConfirmation->SubjectConfirmationData->NotBefore = g_strdup( + notBefore); + assertion->Subject->SubjectConfirmation->SubjectConfirmationData->NotOnOrAfter = g_strdup( + notOnOrAfter); + if (request_abstract) { + lasso_assign_string(assertion->Subject->SubjectConfirmation->SubjectConfirmationData->InResponseTo, + request_abstract->ID); + if (request_abstract->ID) { + /* + * It MUST NOT contain a NotBefore attribute. If + * the containing message is in response to an <AuthnRequest>, + * then the InResponseTo attribute MUST match the request's ID. + */ + lasso_release_string(assertion->Subject->SubjectConfirmation->SubjectConfirmationData->NotBefore); + } + } + + if (name_id_policy && (strcmp(name_id_policy->Format, + LASSO_SAML2_NAME_IDENTIFIER_FORMAT_EMAIL) == 0 || + strcmp(name_id_policy->Format, + LASSO_SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED) == 0)) { + /* caller must set the name identifier content afterwards */ + name_id = LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new()); + lasso_assign_string(name_id->NameQualifier, + LASSO_PROVIDER(profile->server)->ProviderID); + lasso_assign_string(name_id->Format, name_id_policy->Format); + assertion->Subject->NameID = name_id; + } else if (federation == NULL || + (name_id_policy && strcmp(name_id_policy->Format, + LASSO_SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT) == 0)) { + /* transient -> don't use a federation */ + name_id = LASSO_SAML2_NAME_ID(lasso_saml2_name_id_new_with_string( + lasso_build_unique_id(32))); + lasso_assign_string(name_id->NameQualifier, + LASSO_PROVIDER(profile->server)->ProviderID); + lasso_assign_string(name_id->Format, LASSO_SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT); + assertion->Subject->NameID = name_id; + } else { + if (provider && name_id_policy && strcmp(name_id_policy->Format, + LASSO_SAML2_NAME_IDENTIFIER_FORMAT_ENCRYPTED) == 0) { + provider->private_data->encryption_mode |= LASSO_ENCRYPTION_MODE_NAMEID; + } + lasso_assign_gobject(assertion->Subject->NameID, + federation->local_nameIdentifier); + } + + /* Encrypt NameID */ + if (provider && provider->private_data->encryption_mode & LASSO_ENCRYPTION_MODE_NAMEID + && provider->private_data->encryption_public_key != NULL + && assertion->Subject->NameID->content != NULL) { + encrypted_element = LASSO_SAML2_ENCRYPTED_ELEMENT(lasso_node_encrypt( + LASSO_NODE(assertion->Subject->NameID), + provider->private_data->encryption_public_key, + provider->private_data->encryption_sym_key_type)); + if (encrypted_element != NULL) { + assertion->Subject->EncryptedID = encrypted_element; + g_object_unref(assertion->Subject->NameID); + assertion->Subject->NameID = NULL; + } + } + + authentication_statement = LASSO_SAML2_AUTHN_STATEMENT(lasso_saml2_authn_statement_new()); + authentication_statement->AuthnInstant = g_strdup(authenticationInstant); + authentication_statement->SessionNotOnOrAfter = g_strdup(notOnOrAfter); + authentication_statement->AuthnContext = LASSO_SAML2_AUTHN_CONTEXT( + lasso_saml2_authn_context_new()); + authentication_statement->AuthnContext->AuthnContextClassRef = g_strdup( + authenticationMethod); + + assertion->AuthnStatement = g_list_append(NULL, authentication_statement); + + /* Save signing material in assertion private datas to be able to sign later */ + if (profile->server->certificate) { + assertion->sign_type = LASSO_SIGNATURE_TYPE_WITHX509; + } else { + assertion->sign_type = LASSO_SIGNATURE_TYPE_SIMPLE; + } + assertion->sign_method = profile->server->signature_method; + assertion->private_key_file = g_strdup(profile->server->private_key); + assertion->certificate_file = g_strdup(profile->server->certificate); + + /* Save encryption material in assertion private datas to be able to encrypt later */ + if (provider && provider->private_data->encryption_mode & LASSO_ENCRYPTION_MODE_ASSERTION + && provider->private_data->encryption_public_key != NULL) { + assertion->encryption_activated = TRUE; + assertion->encryption_public_key_str = g_strdup( + provider->private_data->encryption_public_key_str); + assertion->encryption_sym_key_type = + provider->private_data->encryption_sym_key_type; + } + +#ifdef LASSO_WSF_ENABLED + lasso_saml20_login_assertion_add_discovery(login, assertion); +#endif + + /* store assertion in session object */ + if (profile->session == NULL) { + profile->session = lasso_session_new(); + } + + lasso_session_add_assertion(profile->session, profile->remote_providerID, + LASSO_NODE(assertion)); + + response = LASSO_SAMLP2_RESPONSE(profile->response); + lasso_list_add_new_gobject(response->Assertion, assertion); + + lasso_assign_gobject(login->private_data->saml2_assertion, assertion); + + return 0; +} +#endif + +/** + * lasso_saml2_assertion_add_attribute_with_node: + * @assertion: a #LassoSaml2Assertion object + * @name: the attribute name + * @name_format: the attribute name format (the namespace of the name) + * @content: a #LassoNode object to put as content of the attribute + * + * Add a new attribute declaration and set this node as the content. + * + * Return value: 0 if successful, an error code otherwise. + */ +int +lasso_saml2_assertion_add_attribute_with_node(LassoSaml2Assertion *assertion, const char *name, + const char *name_format, LassoNode *content) +{ + LassoSaml2AttributeValue *attribute_value = NULL; + LassoSaml2Attribute *attribute = NULL; + LassoSaml2AttributeStatement *attribute_statement = NULL; + int rc = 0; + + lasso_bad_param(SAML2_ASSERTION, assertion); + lasso_check_non_empty_string(name); + lasso_check_non_empty_string(name_format); + lasso_bad_param(NODE, content); + + attribute_value = lasso_saml2_attribute_value_new(); + lasso_list_add_gobject(attribute_value->any, content); + + attribute = LASSO_SAML2_ATTRIBUTE(lasso_saml2_attribute_new()); + lasso_assign_string(attribute->Name, name); + lasso_assign_string(attribute->NameFormat, LASSO_SAML2_ATTRIBUTE_NAME_FORMAT_URI); + lasso_list_add_new_gobject(attribute->AttributeValue, attribute_value); + + attribute_statement = LASSO_SAML2_ATTRIBUTE_STATEMENT(lasso_saml2_attribute_statement_new()); + lasso_list_add_new_gobject(attribute_statement->Attribute, attribute); + + lasso_list_add_new_gobject(assertion->AttributeStatement, attribute_statement); +cleanup: + return rc; +} + + diff --git a/lasso/saml-2.0/saml2_helper.h b/lasso/saml-2.0/saml2_helper.h new file mode 100644 index 00000000..1b99185a --- /dev/null +++ b/lasso/saml-2.0/saml2_helper.h @@ -0,0 +1,98 @@ +/* $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 + */ + +#ifndef __LASSO_SAML20_SAML2_HELPER_H__ +#define __LASSO_SAML20_SAML2_HELPER_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "../export.h" + +#include "../xml/saml-2.0/saml2_assertion.h" +#include "../xml/saml-2.0/saml2_name_id.h" +#include "../xml/saml-2.0/saml2_encrypted_element.h" +#include "../id-ff/provider.h" +#include "../id-ff/server.h" + +typedef enum { + LASSO_SAML2_ASSERTION_VALID, + LASSO_SAML2_ASSERTION_INVALID, + LASSO_SAML2_ASSERTION_INDETERMINATE +} LassoSaml2AssertionValidationState; + +#define LASSO_DURATION_MINUTE 60 +#define LASSO_DURATION_HOUR 3600 +#define LASSO_DURATION_DAY 24*2600 +#define LASSO_DURATION_WEEK LASSO_DURATION_DAY*7 + +LASSO_EXPORT gboolean lasso_saml2_assertion_has_audience_restriction( + LassoSaml2Assertion *saml2_assertion); + +LASSO_EXPORT gboolean lasso_saml2_assertion_is_audience_restricted( + LassoSaml2Assertion *saml2_assertion, char* providerID); + +LASSO_EXPORT LassoSaml2NameID* lasso_saml2_name_id_build_persistent(const char *id, + const char *idpID, const char *providerID); + +LASSO_EXPORT LassoSaml2EncryptedElement* lasso_saml2_encrypted_element_build_encrypted_persistent_name_id( + const char *id, const char *idpID, const LassoProvider *provider); + +LASSO_EXPORT void lasso_saml2_assertion_set_subject_name_id(LassoSaml2Assertion *saml2_assertion, + LassoNode *node); + +LASSO_EXPORT void lasso_saml2_assertion_set_subject_confirmation_name_id( + LassoSaml2Assertion *saml2_assertion, LassoNode *node); + +LASSO_EXPORT void lasso_saml2_assertion_set_subject_confirmation_data( + LassoSaml2Assertion *saml2_assertion, const time_t tolerance, const time_t length, + const char *Recipient, const char *InResponseTo, const char *Address); + +LASSO_EXPORT void lasso_saml2_assertion_set_basic_conditions(LassoSaml2Assertion *saml2_assertion, + time_t tolerance, time_t length, gboolean one_time_use); + +LASSO_EXPORT void lasso_saml2_assertion_add_audience_restriction( + LassoSaml2Assertion *saml2_assertion, const char *providerID); + +LASSO_EXPORT void lasso_saml2_assertion_add_proxy_limit (LassoSaml2Assertion *saml2_assertion, + int proxy_count, GList *proxy_audiences); + +LASSO_EXPORT LassoSaml2AssertionValidationState lasso_saml2_assertion_validate_conditions( + LassoSaml2Assertion *saml2_assertion, const char *relaying_party_providerID); + +LASSO_EXPORT LassoProvider* lasso_saml2_assertion_get_issuer_provider( + const LassoSaml2Assertion *saml2_assertion, const LassoServer *server); + +LASSO_EXPORT int lasso_server_saml2_assertion_setup_signature(LassoServer *server, + LassoSaml2Assertion *saml2_assertion); + +LASSO_EXPORT int lasso_saml2_assertion_add_attribute_with_node(LassoSaml2Assertion *assertion, const char *name, const char *nameformat, LassoNode *content); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __LASSO_SAML20_SAML2_HELPER_H__ */ |