diff --git a/docs/reference/lasso/lasso-sections.txt b/docs/reference/lasso/lasso-sections.txt index a73bc9ee..9c91d374 100644 --- a/docs/reference/lasso/lasso-sections.txt +++ b/docs/reference/lasso/lasso-sections.txt @@ -149,6 +149,13 @@ LassoEncryptionSymKeyType lasso_provider_verify_single_node_signature lasso_provider_get_default_name_id_format lasso_provider_get_sp_name_qualifier +lasso_provider_get_idp_supported_attributes +lasso_provider_get_valid_until +lasso_provider_get_cache_duration +lasso_provider_get_metadata_one_for_role +lasso_provider_get_metadata_list_for_role +lasso_provider_get_metadata_keys_for_role +lasso_provider_get_roles LASSO_PROVIDER LASSO_IS_PROVIDER diff --git a/lasso/id-ff/profile.h b/lasso/id-ff/profile.h index 5dc5be2f..270a3034 100644 --- a/lasso/id-ff/profile.h +++ b/lasso/id-ff/profile.h @@ -203,7 +203,8 @@ LASSO_EXPORT gint lasso_profile_set_soap_fault_response(LassoProfile *profile, c LASSO_EXPORT void lasso_profile_set_signature_verify_hint(LassoProfile *profile, LassoProfileSignatureVerifyHint signature_verify_hint); LASSO_EXPORT LassoProfileSignatureVerifyHint lasso_profile_get_signature_verify_hint(LassoProfile *profile); -LASSO_EXPORT LassoProviderRole lasso_profile_sso_role_with(LassoProfile *profile, const char *remote_provider_id); +LASSO_EXPORT LassoProviderRole lasso_profile_sso_role_with(LassoProfile *profile, + const char *remote_provider_id); #ifdef __cplusplus } diff --git a/lasso/id-ff/provider.c b/lasso/id-ff/provider.c index 0dcd9d5e..5e874100 100644 --- a/lasso/id-ff/provider.c +++ b/lasso/id-ff/provider.c @@ -26,7 +26,27 @@ * SECTION:provider * @short_description: Service or identity provider * - * It holds all the data about a provider. + * The #LassoProvider object holds metadata about a provider. Metadata are sorted into descriptors, + * each descriptor being assigned a role. We refer you to Liberty Metadata Description + * and Discovery +Specification and Metadata for the OASIS Security Assertion Markup Language +(SAML) V2.0. + +Roles are represented by the enumeration #LassoProviderRole, you can access descriptors +content using lasso_provider_get_metadata_list_by_role() and lasso_provider_get_metadata_by_role(). +Descriptors resources are flattened inside a simple hashtable. For example to get the URL(s) for the +SAML 2.0 single logout response endpoint using binding HTTP-POST of the SP descriptor of a provider +called x, you would call: + + +GList *urls = lasso_provider_get_metadata_list_by_role(x, LASSO_PROVIDER_ROLE_SP, "SingleLogoutService HTTP-POST ResponseLocation"); + + +A provider usually possess a default role stored in the #LassoProvider.role field, which is +initialized by the lasso_server_add_provider() method when registering a new remote provider to our +current provider. The methods lasso_provider_get_metadata() and lasso_provider_get_metadata_list() +use this default role to access descriptors. + **/ #include "../xml/private.h" @@ -62,14 +82,19 @@ static char *protocol_md_nodename[LASSO_MD_PROTOCOL_TYPE_LAST] = { "SingleSignOnProtocolProfile" }; static char *protocol_roles[LASSO_PROVIDER_ROLE_LAST] = { - NULL, "sp", "idp", + NULL, "idp", "sp", "authn-authority", "pdp", "attribute-authority" }; char *protocol_methods[LASSO_HTTP_METHOD_LAST] = { "", "", "", "", "", "-http", "-soap" }; -static gboolean lasso_provider_load_metadata_from_doc(LassoProvider *provider, xmlDoc *doc); + +static gboolean _lasso_provider_load_metadata_from_doc(LassoProvider *provider, xmlDoc *doc); +static int _lasso_provider_get_role_index(LassoProviderRole role); +void _lasso_provider_add_metadata_value_for_role(LassoProvider *provider, + LassoProviderRole role, const char *name, const char *value); +typedef int LassoProviderRoleIndex; /*****************************************************************************/ /* public methods */ @@ -87,28 +112,115 @@ static gboolean lasso_provider_load_metadata_from_doc(LassoProvider *provider, x * string must be freed by the caller. **/ gchar* -lasso_provider_get_assertion_consumer_service_url(const LassoProvider *provider, const char *service_id) +lasso_provider_get_assertion_consumer_service_url(LassoProvider *provider, const char *service_id) +{ + char *name = NULL; + char *assertion_consumer_service_url = NULL; + + if (service_id == NULL) + service_id = provider->private_data->default_assertion_consumer; + name = g_strdup_printf("AssertionConsumerServiceURL %s", service_id); + assertion_consumer_service_url = lasso_provider_get_metadata_one_for_role(provider, LASSO_PROVIDER_ROLE_SP, name); + g_free(name); + + return assertion_consumer_service_url; +} + +static LassoProviderRoleIndex +_lasso_provider_get_role_index(LassoProviderRole role) { + switch (role) { + case LASSO_PROVIDER_ROLE_IDP: + return 1; + case LASSO_PROVIDER_ROLE_SP: + return 2; + case LASSO_PROVIDER_ROLE_AUTHN_AUTHORITY: + return 3; + case LASSO_PROVIDER_ROLE_AUTHZ_AUTHORITY: + return 4; + case LASSO_PROVIDER_ROLE_ATTRIBUTE_AUTHORITY: + return 5; + default: + return 0; + } +} + +void +_lasso_provider_add_metadata_value_for_role(LassoProvider *provider, LassoProviderRole role, const char *name, const char *value) { - GHashTable *descriptor; GList *l; - char *sid = (char*)service_id; - char *name; + GHashTable *descriptor; + char *symbol; + LassoProviderRoleIndex role_index; - g_return_val_if_fail(LASSO_IS_PROVIDER(provider), NULL); - if (sid == NULL) - sid = provider->private_data->default_assertion_consumer; + g_return_if_fail(LASSO_IS_PROVIDER(provider) && name && value); + role_index = _lasso_provider_get_role_index(role); + g_return_if_fail ( role_index); + descriptor = provider->private_data->Descriptors; /* default to SP */ + g_return_if_fail (descriptor); + l = (GList*)lasso_provider_get_metadata_list_for_role(provider, role, name); + lasso_list_add_string(l, value); + symbol = g_strdup_printf("%s %s", protocol_roles[role_index], name); + g_hash_table_insert(descriptor, symbol, l); +} - descriptor = provider->private_data->Descriptors; +/** + * lasso_provider_get_metadata_list_for_role: + * @provider: a #LassoProvider + * @role: a #LassoProviderRole value + * @name: the element name + * + * Extracts zero to many elements from the @provider descriptor for the given @role. + * + * Return value:(transfer none)(element-type string): a #GList with the elements. This GList is internally + * allocated and points to internally allocated strings. It must + * not be freed, modified or stored. + **/ +GList* +lasso_provider_get_metadata_list_for_role(const LassoProvider *provider, LassoProviderRole role, const char *name) +{ + GList *l; + GHashTable *descriptor; + char *symbol; + LassoProviderRoleIndex role_index; + + g_return_val_if_fail(LASSO_IS_PROVIDER(provider) && name, NULL); + + role_index = _lasso_provider_get_role_index(role); + if (! role_index) + return NULL; + + descriptor = provider->private_data->Descriptors; /* default to SP */ if (descriptor == NULL) return NULL; - name = g_strdup_printf("AssertionConsumerServiceURL %s", sid); - l = g_hash_table_lookup(descriptor, name); - g_free(name); - if (l == NULL) - return NULL; + symbol = g_strdup_printf("%s %s", protocol_roles[role_index], name); + l = g_hash_table_lookup(descriptor, symbol); + g_free(symbol); - return g_strdup(l->data); + return l; +} + +/** + * lasso_provider_get_metadata_one_for_role: + * @provider: a #LassoProvider object + * @role: a #LassoProviderRole value + * @name: a metadata information name + * + * Return the given information extracted from the metadata of the given #LassoProvider for the + * given @role descriptor. + * + * Retun value: a newly allocated string or NULL. If non-NULL must be freed by the caller. + */ +char* +lasso_provider_get_metadata_one_for_role(LassoProvider *provider, LassoProviderRole role, const char *name) +{ + const GList *l; + + l = lasso_provider_get_metadata_list_for_role(provider, role, name); + + if (l) + return g_strdup(l->data); + return NULL; } /** @@ -122,24 +234,11 @@ lasso_provider_get_assertion_consumer_service_url(const LassoProvider *provider, * string must be freed by the caller. **/ gchar* -lasso_provider_get_metadata_one(const LassoProvider *provider, const char *name) +lasso_provider_get_metadata_one(LassoProvider *provider, const char *name) { - GList *l; - GHashTable *descriptor; - - g_return_val_if_fail(LASSO_IS_PROVIDER(provider), NULL); - - descriptor = provider->private_data->Descriptors; /* default to SP */ - if (descriptor == NULL) - return NULL; - l = g_hash_table_lookup(descriptor, name); - if (l) - return g_strdup(l->data); - - return NULL; + return lasso_provider_get_metadata_one_for_role(provider, provider->role, name); } - /** * lasso_provider_get_metadata_list: * @provider: a #LassoProvider @@ -151,18 +250,12 @@ lasso_provider_get_metadata_one(const LassoProvider *provider, const char *name) * allocated and points to internally allocated strings. It must * not be freed, modified or stored. **/ -const GList* -lasso_provider_get_metadata_list(const LassoProvider *provider, const char *name) +GList* +lasso_provider_get_metadata_list(LassoProvider *provider, const char *name) { - GHashTable *descriptor; - - g_return_val_if_fail(LASSO_IS_PROVIDER(provider), NULL); - descriptor = provider->private_data->Descriptors; - - return g_hash_table_lookup(descriptor, name); + return lasso_provider_get_metadata_list_for_role(provider, provider->role, name); } - /** * lasso_provider_get_first_http_method: * @provider: (transfer none): a #LassoProvider @@ -176,7 +269,7 @@ lasso_provider_get_metadata_list(const LassoProvider *provider, const char *name **/ LassoHttpMethod lasso_provider_get_first_http_method(LassoProvider *provider, - const LassoProvider *remote_provider, const LassoMdProtocolType protocol_type) + LassoProvider *remote_provider, LassoMdProtocolType protocol_type) { char *protocol_profile_prefix; const GList *local_supported_profiles; @@ -245,7 +338,7 @@ lasso_provider_get_first_http_method(LassoProvider *provider, * Return value: %TRUE if it is appropriate **/ gboolean -lasso_provider_accept_http_method(LassoProvider *provider, const LassoProvider *remote_provider, +lasso_provider_accept_http_method(LassoProvider *provider, LassoProvider *remote_provider, LassoMdProtocolType protocol_type, LassoHttpMethod http_method, gboolean initiate_profile) { @@ -302,7 +395,7 @@ lasso_provider_accept_http_method(LassoProvider *provider, const LassoProvider * * Return value: %TRUE if it is supported **/ gboolean -lasso_provider_has_protocol_profile(const LassoProvider *provider, +lasso_provider_has_protocol_profile(LassoProvider *provider, LassoMdProtocolType protocol_type, const char *protocol_profile) { const GList *supported; @@ -339,7 +432,6 @@ lasso_provider_get_base64_succinct_id(const LassoProvider *provider) return ret; } - /** * lasso_provider_get_organization * @provider: a #LassoProvider @@ -375,6 +467,14 @@ static struct XmlSnippet schema_snippets[] = { static LassoNodeClass *parent_class = NULL; +/** + * lasso_provider_get_public_key: + * @provider: a #LassoProvider object + * + * Return the public key associated with this provider. + * + * Return value: an #xmlSecKey object. + */ xmlSecKey* lasso_provider_get_public_key(const LassoProvider *provider) { @@ -403,56 +503,47 @@ lasso_provider_get_encryption_public_key(const LassoProvider *provider) } static void -load_descriptor(xmlNode *xmlnode, GHashTable *descriptor, LassoProvider *provider) +_lasso_provider_load_endpoint_type(LassoProvider *provider, xmlNode *endpoint, + LassoProviderRole role) +{ + char *name = (char*)endpoint->name; + xmlChar *value = NULL; + + if (strcmp(name, "AssertionConsumerServiceURL") == 0) { + char *isDefault = (char*)xmlGetProp(endpoint, (xmlChar*)"isDefault"); + char *id = (char*)xmlGetProp(endpoint, (xmlChar*)"id"); + name = g_strdup_printf("%s %s", name, id); + if (isDefault) { + if (strcmp(isDefault, "true") == 0 || strcmp(isDefault, "1") == 0) + lasso_assign_string(provider->private_data->default_assertion_consumer, + id); + xmlFree(isDefault); + } + xmlFree(id); + } else { + name = g_strdup_printf("%s", (char*)name); + } + value = xmlNodeGetContent(endpoint); + _lasso_provider_add_metadata_value_for_role(provider, role, name, (char*)value); + lasso_release_string(name); + xmlFree(value); +} + +static void +_lasso_provider_load_descriptor(LassoProvider *provider, xmlNode *xmlnode, LassoProviderRole role) { xmlNode *t; - GList *elements; - char *name; - xmlChar *value; - xmlChar *use; - t = xmlnode->children; + t = xmlSecGetNextElementNode(xmlnode->children); while (t) { - if (t->type != XML_ELEMENT_NODE) { - t = t->next; - continue; - } - if (strcmp((char*)t->name, "KeyDescriptor") == 0) { - use = xmlGetProp(t, (xmlChar*)"use"); - if (use == NULL || strcmp((char*)use, "signing") == 0) { - provider->private_data->signing_key_descriptor = xmlCopyNode(t, 1); - } - if (use == NULL || strcmp((char*)use, "encryption") == 0) { - provider->private_data->encryption_key_descriptor = - xmlCopyNode(t, 1); - } - if (use) { - xmlFree(use); - } - t = t->next; - continue; - } - if (strcmp((char*)t->name, "AssertionConsumerServiceURL") == 0) { - char *isDefault = (char*)xmlGetProp(t, (xmlChar*)"isDefault"); - char *id = (char*)xmlGetProp(t, (xmlChar*)"id"); - name = g_strdup_printf("%s %s", t->name, id); - if (isDefault) { - if (strcmp(isDefault, "true") == 0 || strcmp(isDefault, "1") == 0) - lasso_assign_string(provider->private_data->default_assertion_consumer, - id); - xmlFree(isDefault); - } - xmlFree(id); + if (xmlSecCheckNodeName(t, + BAD_CAST "KeyDescriptor", + BAD_CAST LASSO_METADATA_HREF)) { + _lasso_provider_load_key_descriptor(provider, t); } else { - name = g_strdup((char*)t->name); + _lasso_provider_load_endpoint_type(provider, t, role); } - elements = g_hash_table_lookup(descriptor, name); - value = xmlNodeGetContent(t); - elements = g_list_append(elements, g_strdup((char*)value)); - // Do not mix g_free strings with xmlFree strings - xmlFree(value); - g_hash_table_insert(descriptor, name, elements); - t = t->next; + t = xmlSecGetNextElementNode(t->next); } } @@ -461,8 +552,20 @@ get_xmlNode(LassoNode *node, gboolean lasso_dump) { xmlNode *xmlnode; LassoProvider *provider = LASSO_PROVIDER(node); - char *roles[LASSO_PROVIDER_ROLE_LAST] = { "None", "SP", "IdP", "AuthnAuthority", "PDP", "AttributeAuthority"}; - char *encryption_mode[] = { "None", "NameId", "Assertion", "Both" }; + char *roles[LASSO_PROVIDER_ROLE_LAST] = { + "None", + "SP", + "IdP", + "AuthnAuthority", + "PDP", + "AttributeAuthority" + }; + char *encryption_mode[] = { + "None", + "NameId", + "Assertion", + "Both" + }; xmlnode = parent_class->get_xmlNode(node, lasso_dump); @@ -479,12 +582,39 @@ get_xmlNode(LassoNode *node, gboolean lasso_dump) return xmlnode; } +void +_lasso_provider_load_key_descriptor(LassoProvider *provider, xmlNode *key_descriptor) +{ + LassoProviderPrivate *private_data; + xmlChar *use; + + g_return_if_fail(LASSO_IS_PROVIDER(provider)); + g_return_if_fail(provider->private_data); + + private_data = provider->private_data; + use = xmlGetProp(key_descriptor, (xmlChar*)"use"); + if (use == NULL || g_strcmp0((char*)use, "signing") == 0) { + lasso_assign_xml_node(private_data->signing_key_descriptor, key_descriptor); + } + if (use == NULL || strcmp((char*)use, "encryption") == 0) { + lasso_assign_xml_node(private_data->encryption_key_descriptor, key_descriptor); + } + lasso_release_xml_string(use); +} + static int init_from_xml(LassoNode *node, xmlNode *xmlnode) { LassoProvider *provider = LASSO_PROVIDER(node); - char *roles[LASSO_PROVIDER_ROLE_LAST] = { "None", "SP", "IdP", "AuthnAuthority", "PDP", "AttributeAuthority"}; + static char * const roles[LASSO_PROVIDER_ROLE_LAST] = { + "None", + "SP", + "IdP", + "AuthnAuthority", + "PDP", + "AttributeAuthority" + }; xmlChar *s; int i; @@ -497,16 +627,16 @@ init_from_xml(LassoNode *node, xmlNode *xmlnode) /* Load provider role */ s = xmlGetProp(xmlnode, (xmlChar*)"ProviderRole"); provider->role = LASSO_PROVIDER_ROLE_NONE; - i = LASSO_PROVIDER_ROLE_NONE; - while (i < LASSO_PROVIDER_ROLE_LAST) { - if (strcmp((char*)s, roles[i]) == 0) { - provider->role = i; - break; + if (s) { + i = LASSO_PROVIDER_ROLE_NONE; + while (i < LASSO_PROVIDER_ROLE_LAST) { + if (strcmp((char*)s, roles[i]) == 0) { + provider->role = i; + break; + } + i++; } - i++; - } - if (s != NULL) { - xmlFree(s); + lasso_release_xml_string(s); } /* Load encryption mode */ @@ -540,6 +670,67 @@ init_from_xml(LassoNode *node, xmlNode *xmlnode) return 0; } +static void* +_lasso_provider_get_pdata_thing(LassoProvider *provider, ptrdiff_t offset) +{ + LassoProviderPrivate *pdata; + + lasso_return_val_if_fail(LASSO_IS_PROVIDER(provider), NULL); + pdata = provider->private_data; + if (pdata) + return G_STRUCT_MEMBER_P(pdata, offset); + + return NULL; +} + +/** + * lasso_provider_get_idp_supported_attributes: + * @provider: a #LassoProvider object + * + * If the provider supports the IDP SSO role, then return the list of Attribute definition that this + * provider declared supporting. + * + * Return value:(transfer none)(element-type LassoNode): a list of #LassoSaml2Attribute or #LassoSamlAttribute + */ +GList* +lasso_provider_get_idp_supported_attributes(LassoProvider *provider) +{ + return _lasso_provider_get_pdata_thing(provider, G_STRUCT_OFFSET(LassoProviderPrivate, + attributes)); +} + +/** + * lasso_provider_get_valid_until: + * @provider: a #LassoProvider object + * + * Return the time after which the metadata for this provider will become invalid. This is an + * ISO-8601 formatted string. + * + * Return value:(transfer none): an internally allocated string, you can copy it but not store it. + */ +char* +lasso_provider_get_valid_until(LassoProvider *provider) +{ + return _lasso_provider_get_pdata_thing(provider, + G_STRUCT_OFFSET(LassoProviderPrivate, valid_until)); +} + +/** + * lasso_provider_get_cache_duration: + * @provider: a #LassoProvider object + * + * Return the time during which the metadata for this provider can be kept. + * + * Return value:(transfer none): an internally allocated string, you can copy it but not store it. + */ +char* +lasso_provider_get_cache_duration(LassoProvider *provider) +{ + return _lasso_provider_get_pdata_thing(provider, + G_STRUCT_OFFSET(LassoProviderPrivate, cache_duration)); +} + + /*****************************************************************************/ /* overridden parent class methods */ /*****************************************************************************/ @@ -727,7 +918,7 @@ _lasso_provider_load_metadata_from_buffer(LassoProvider *provider, const gchar * if (doc == NULL) { return FALSE; } - goto_cleanup_if_fail_with_rc (lasso_provider_load_metadata_from_doc(provider, doc), FALSE); + goto_cleanup_if_fail_with_rc (_lasso_provider_load_metadata_from_doc(provider, doc), FALSE); lasso_assign_string(provider->metadata_filename, metadata); cleanup: lasso_release_doc(doc); @@ -774,7 +965,7 @@ lasso_provider_load_metadata(LassoProvider *provider, const gchar *path) } static gboolean -lasso_provider_load_metadata_from_doc(LassoProvider *provider, xmlDoc *doc) +_lasso_provider_load_metadata_from_doc(LassoProvider *provider, xmlDoc *doc) { xmlXPathContext *xpathCtx; xmlXPathObject *xpathObj; @@ -833,8 +1024,8 @@ lasso_provider_load_metadata_from_doc(LassoProvider *provider, xmlDoc *doc) xpathObj = xmlXPathEvalExpression((xmlChar*)xpath_idp, xpathCtx); if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr == 1) { - load_descriptor(xpathObj->nodesetval->nodeTab[0], - provider->private_data->Descriptors, provider); + _lasso_provider_load_descriptor(provider, xpathObj->nodesetval->nodeTab[0], + LASSO_PROVIDER_ROLE_IDP); if (provider->private_data->conformance < LASSO_PROTOCOL_LIBERTY_1_2) { /* lookup ProviderID */ node = xpathObj->nodesetval->nodeTab[0]->children; @@ -853,8 +1044,8 @@ lasso_provider_load_metadata_from_doc(LassoProvider *provider, xmlDoc *doc) xpathObj = xmlXPathEvalExpression((xmlChar*)xpath_sp, xpathCtx); if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr == 1) { - load_descriptor(xpathObj->nodesetval->nodeTab[0], - provider->private_data->Descriptors, provider); + _lasso_provider_load_descriptor(provider, xpathObj->nodesetval->nodeTab[0], + LASSO_PROVIDER_ROLE_SP); if (provider->private_data->conformance < LASSO_PROTOCOL_LIBERTY_1_2) { /* lookup ProviderID */ node = xpathObj->nodesetval->nodeTab[0]->children; @@ -890,8 +1081,9 @@ lasso_provider_load_metadata_from_doc(LassoProvider *provider, xmlDoc *doc) * Help to factorize common code. */ static LassoProvider* -lasso_provider_new_helper(LassoProviderRole role, const char *metadata, - const char *public_key, const char *ca_cert_chain, gboolean (*loader)(LassoProvider *provider, const gchar *metadata)) +_lasso_provider_new_helper(LassoProviderRole role, const char *metadata, + const char *public_key, const char *ca_cert_chain, gboolean (*loader)( + LassoProvider *provider, const gchar *metadata)) { LassoProvider *provider; @@ -936,7 +1128,7 @@ LassoProvider* lasso_provider_new(LassoProviderRole role, const char *metadata, const char *public_key, const char *ca_cert_chain) { - return lasso_provider_new_helper(role, metadata, public_key, ca_cert_chain, + return _lasso_provider_new_helper(role, metadata, public_key, ca_cert_chain, lasso_provider_load_metadata); } @@ -955,7 +1147,7 @@ LassoProvider* lasso_provider_new_from_buffer(LassoProviderRole role, const char *metadata, const char *public_key, const char *ca_cert_chain) { - return lasso_provider_new_helper(role, metadata, public_key, ca_cert_chain, + return _lasso_provider_new_helper(role, metadata, public_key, ca_cert_chain, lasso_provider_load_metadata_from_buffer); } @@ -1321,7 +1513,7 @@ lasso_provider_verify_query_signature(LassoProvider *provider, const char *messa * Return value:(transfer full)(allow-none): a NameIDFormat URI or NULL, the returned value must be freed by the caller. */ gchar* -lasso_provider_get_default_name_id_format(const LassoProvider *provider) +lasso_provider_get_default_name_id_format(LassoProvider *provider) { return lasso_provider_get_metadata_one(provider, "NameIDFormat"); } @@ -1384,3 +1576,84 @@ lasso_provider_verify_single_node_signature (LassoProvider *provider, LassoNode } return lasso_verify_signature(xmlnode, NULL, id_attr_name, NULL, xmlseckey, NO_SINGLE_REFERENCE, NULL); } + +struct AddForRoleHelper { + GList *l; + LassoProviderRole role; +}; + + +static void +_add_for_role(gpointer key, G_GNUC_UNUSED gpointer data, struct AddForRoleHelper *helper) +{ + char role_prefix[64]; + int l; + + l = sprintf(role_prefix, "%s ", protocol_roles[helper->role]); + + if (key && strncmp(key, role_prefix, l) == 0) { + lasso_list_add_string(helper->l, ((char*)key) + l); + } +} + +/** + * lasso_provider_get_metadata_keys_for_role: + * @provider: a #LassoProvider object + * @role: a #LassoProviderRole value + * + * Returns the list of metadata keys existing for the given provider. + * + * Return value:(element-type utf8)(transfer full): a newly allocated list of strings + */ +GList* +lasso_provider_get_metadata_keys_for_role(LassoProvider *provider, LassoProviderRole role) +{ + struct AddForRoleHelper helper = { NULL, role }; + + lasso_return_val_if_fail(LASSO_IS_PROVIDER(provider), NULL); + lasso_return_val_if_fail(provider->private_data != NULL, NULL); + lasso_return_val_if_fail(role > LASSO_PROVIDER_ROLE_NONE && role < LASSO_PROVIDER_ROLE_LAST, NULL); + + g_hash_table_foreach(provider->private_data->Descriptors, (GHFunc)_add_for_role, &helper); + + return helper.l; +} + +/** + * lasso_provider_get_roles: + * @provider: a #LassoProvider object + * + * Return the bitmask of the supported roles. + * + * Return value: a #LassoProviderRole enumeration value. + */ +LassoProviderRole +lasso_provider_get_roles(LassoProvider *provider) +{ + lasso_return_val_if_fail(LASSO_IS_PROVIDER(provider) && provider->private_data, LASSO_PROVIDER_ROLE_NONE); + + return provider->private_data->roles; +} + +/** + * lasso_provider_match_conformance: + * @provider: a #LassoProvider object + * @another_provider: a #LassoProvider object + * + * Return whether the two provider support a same protocol. + * See also #LassoProtocolConformance. + * + * Return value: TRUE or FALSE. + */ +gboolean +lasso_provider_match_conformance(LassoProvider *provider, LassoProvider *another_provider) +{ + lasso_return_val_if_fail(LASSO_IS_PROVIDER(provider) + && LASSO_IS_PROVIDER(another_provider), + FALSE); + + int conformance1 = lasso_provider_get_protocol_conformance(provider); + int conformance2 = lasso_provider_get_protocol_conformance(another_provider); + + return (conformance1 & conformance2) != 0; +} diff --git a/lasso/id-ff/provider.h b/lasso/id-ff/provider.h index be30426f..f3124f2f 100644 --- a/lasso/id-ff/provider.h +++ b/lasso/id-ff/provider.h @@ -118,10 +118,12 @@ typedef enum { * assertion providing authorization about a principal acessing a resource, * @LASSO_PROVIDER_ROLE_ATTRIBUTE_AUTHORITY: an attribute authority, i.e. an endpoint able to return * attributes aboute a principal, - * @LASSO_PROVIDER_ROLE_LAST: all value in the enumeration are garanteed to be < to + * @LASSO_PROVIDER_ROLE_LAST: all values in the enumeration are guaranteed to be < to * @LASSO_PROVIDER_ROLE_LAST. * - * #LassoProviderRole is an enumeration allowing to precise the roles handled by a provider. + * #LassoProviderRole is an enumeration allowing to enumerate the roles handled by a provider, it + * can be used in a bitmask as each value is a power of 2 (except #LASSO_PROVIDER_ROLE_ANY which is + * the full bitmask and LASSO_PROVIDER_ROLE_NONE). **/ typedef enum { LASSO_PROVIDER_ROLE_ANY = -1, @@ -169,6 +171,22 @@ typedef enum { } LassoEncryptionMode; +/** + * LassoProvider: + * @ProviderID: the identifier URI of this provider + * @role: the role prescribed when this #LassoProvider was built + * @metadata_filename: file path or content of the metadata description for this provider. + * @public_key: file path or content of the public key file for this provider. + * @ca_cert_chain: file path or content of the CA cert chain used to validate signature of this + * provider (can be used instead of a public key to limit the need for metadata updates). + * + * Any kind of provider, identity provider, service provider, attribute authority, authorization + * authority will be represented by a #LassoProvider object. This object will holds public keys, + * certificate chains and metadata informations. The ID-FF 1.2 and SAML 2.0 metadata files are + * flattened inside a key-value map that you can access using the functions + * lasso_provider_get_metadata_one_for_role(), lasso_provider_get_metadata_list_for_role(), + * lasso_provider_get_metadata_keys_for_role(). + */ struct _LassoProvider { LassoNode parent; @@ -193,21 +211,21 @@ LASSO_EXPORT LassoProvider* lasso_provider_new(LassoProviderRole role, const cha const char *public_key, const char *ca_cert_chain); LASSO_EXPORT LassoProvider* lasso_provider_new_from_buffer(LassoProviderRole role, const char *metadata, const char *public_key, const char *ca_cert_chain); -LASSO_EXPORT gchar* lasso_provider_get_assertion_consumer_service_url(const LassoProvider *provider, +LASSO_EXPORT gchar* lasso_provider_get_assertion_consumer_service_url(LassoProvider *provider, const char *service_id); -LASSO_EXPORT gchar* lasso_provider_get_metadata_one(const LassoProvider *provider, const char *name); -LASSO_EXPORT const GList* lasso_provider_get_metadata_list(const LassoProvider *provider, const char *name); +LASSO_EXPORT gchar* lasso_provider_get_metadata_one(LassoProvider *provider, const char *name); +LASSO_EXPORT GList* lasso_provider_get_metadata_list(LassoProvider *provider, const char *name); LASSO_EXPORT LassoProvider* lasso_provider_new_from_dump(const gchar *dump); LASSO_EXPORT LassoHttpMethod lasso_provider_get_first_http_method(LassoProvider *provider, - const LassoProvider *remote_provider, LassoMdProtocolType protocol_type); + LassoProvider *remote_provider, LassoMdProtocolType protocol_type); LASSO_EXPORT gboolean lasso_provider_accept_http_method(LassoProvider *provider, - const LassoProvider *remote_provider, LassoMdProtocolType protocol_type, + LassoProvider *remote_provider, LassoMdProtocolType protocol_type, LassoHttpMethod http_method, gboolean initiate_profile); -LASSO_EXPORT gboolean lasso_provider_has_protocol_profile(const LassoProvider *provider, +LASSO_EXPORT gboolean lasso_provider_has_protocol_profile(LassoProvider *provider, LassoMdProtocolType protocol_type, const char *protocol_profile); LASSO_EXPORT gchar* lasso_provider_get_base64_succinct_id(const LassoProvider *provider); @@ -225,13 +243,32 @@ LASSO_EXPORT LassoEncryptionMode lasso_provider_get_encryption_mode(LassoProvide LASSO_EXPORT void lasso_provider_set_encryption_sym_key_type(LassoProvider *provider, LassoEncryptionSymKeyType encryption_sym_key_type); -LASSO_EXPORT gchar* lasso_provider_get_default_name_id_format(const LassoProvider *provider); +LASSO_EXPORT gchar* lasso_provider_get_default_name_id_format(LassoProvider *provider); LASSO_EXPORT const char* lasso_provider_get_sp_name_qualifier(LassoProvider *provider); LASSO_EXPORT int lasso_provider_verify_single_node_signature (LassoProvider *provider, LassoNode *node, const char *id_attr_name); +LASSO_EXPORT GList* lasso_provider_get_idp_supported_attributes(LassoProvider *provider); + +LASSO_EXPORT char* lasso_provider_get_valid_until(LassoProvider *provider); + +LASSO_EXPORT char* lasso_provider_get_cache_duration(LassoProvider *provider); + +LASSO_EXPORT char* lasso_provider_get_metadata_one_for_role(LassoProvider *provider, + LassoProviderRole role, const char *name); + +LASSO_EXPORT GList* lasso_provider_get_metadata_list_for_role(const LassoProvider *provider, + LassoProviderRole role, const char *name); + +LASSO_EXPORT GList *lasso_provider_get_metadata_keys_for_role(LassoProvider *provider, + LassoProviderRole role); + +LASSO_EXPORT LassoProviderRole lasso_provider_get_roles(LassoProvider *provider); + +LASSO_EXPORT gboolean lasso_provider_match_conformance(LassoProvider *provider, LassoProvider *another_provider); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/lasso/id-ff/providerprivate.h b/lasso/id-ff/providerprivate.h index 2382cf69..de3bc963 100644 --- a/lasso/id-ff/providerprivate.h +++ b/lasso/id-ff/providerprivate.h @@ -46,6 +46,7 @@ struct _LassoProviderPrivate { gboolean dispose_has_run; + LassoProviderRole roles; LassoProtocolConformance conformance; GHashTable *Descriptors; GList *attributes; /* of LassoSaml2Attribute */ @@ -62,6 +63,8 @@ struct _LassoProviderPrivate xmlSecKey *encryption_public_key; LassoEncryptionMode encryption_mode; LassoEncryptionSymKeyType encryption_sym_key_type; + char *valid_until; + char *cache_duration; }; @@ -76,6 +79,9 @@ xmlSecKey* lasso_provider_get_encryption_public_key(const LassoProvider *provide LassoEncryptionSymKeyType lasso_provider_get_encryption_sym_key_type(const LassoProvider* provider); int lasso_provider_verify_saml_signature(LassoProvider *provider, xmlNode *signed_node, xmlDoc *doc); int lasso_provider_verify_query_signature(LassoProvider *provider, const char *message); +void _lasso_provider_load_key_descriptor(LassoProvider *provider, xmlNode *key_descriptor); +void _lasso_provider_add_metadata_value_for_role(LassoProvider *provider, + LassoProviderRole role, const char *name, const char *value); #ifdef __cplusplus diff --git a/lasso/saml-2.0/assertion_query.c b/lasso/saml-2.0/assertion_query.c index de7ae6bc..ddf8ad7e 100644 --- a/lasso/saml-2.0/assertion_query.c +++ b/lasso/saml-2.0/assertion_query.c @@ -24,9 +24,9 @@ #include "../id-ff/session.h" #include "../xml/private.h" -#include "assertion_query.h" -#include "providerprivate.h" -#include "profileprivate.h" +#include "./assertion_query.h" +#include "./providerprivate.h" +#include "./profileprivate.h" #include "../id-ff/providerprivate.h" #include "../id-ff/profileprivate.h" #include "../id-ff/identityprivate.h" @@ -47,6 +47,23 @@ struct _LassoAssertionQueryPrivate LassoAssertionQueryRequestType query_request_type; }; +LassoMdProtocolType +_lasso_assertion_query_type_to_protocol_type(LassoAssertionQueryRequestType query_request_type) { + + LassoMdProtocolType types[4] = { + LASSO_MD_PROTOCOL_TYPE_ASSERTION_ID_REQUEST, + LASSO_MD_PROTOCOL_TYPE_AUTHN_QUERY, + LASSO_MD_PROTOCOL_TYPE_ATTRIBUTE, + LASSO_MD_PROTOCOL_TYPE_AUTHZ, }; + + if (query_request_type < LASSO_ASSERTION_QUERY_REQUEST_TYPE_ASSERTION_ID && + query_request_type > LASSO_ASSERTION_QUERY_REQUEST_TYPE_AUTHZ_DECISION) { + return -1; + } + + return types[query_request_type - LASSO_ASSERTION_QUERY_REQUEST_TYPE_ASSERTION_ID]; +} + /*****************************************************************************/ /* public methods */ @@ -74,24 +91,16 @@ lasso_assertion_query_init_request(LassoAssertionQuery *assertion_query, LassoAssertionQueryRequestType query_request_type) { LassoProfile *profile; - LassoProvider *remote_provider; - LassoFederation *federation; - LassoSamlp2RequestAbstract *request; - gint ret = 0; + LassoNode *request; + gint rc = 0; g_return_val_if_fail(http_method == LASSO_HTTP_METHOD_ANY || http_method == LASSO_HTTP_METHOD_SOAP, LASSO_PARAM_ERROR_INVALID_VALUE); g_return_val_if_fail(LASSO_IS_ASSERTION_QUERY(assertion_query), LASSO_PARAM_ERROR_INVALID_VALUE); - profile = LASSO_PROFILE(assertion_query); - /* verify the identity is set */ - if (LASSO_IS_IDENTITY(profile->identity) == FALSE) { - return critical_error(LASSO_PROFILE_ERROR_IDENTITY_NOT_FOUND); - } - /* set the remote provider id */ profile->remote_providerID = NULL; if (remote_provider_id) { @@ -106,7 +115,7 @@ lasso_assertion_query_init_request(LassoAssertionQuery *assertion_query, role = LASSO_PROVIDER_ROLE_ATTRIBUTE_AUTHORITY; break; case LASSO_ASSERTION_QUERY_REQUEST_TYPE_AUTHZ_DECISION: - role = LASSO_PROVIDER_ROLE_PDP; + role = LASSO_PROVIDER_ROLE_AUTHZ_AUTHORITY; break; /* other request types should not happen or should not go there */ default: @@ -119,81 +128,40 @@ lasso_assertion_query_init_request(LassoAssertionQuery *assertion_query, g_return_val_if_fail(profile->remote_providerID != NULL, LASSO_PARAM_ERROR_INVALID_VALUE); - remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID); - if (LASSO_IS_PROVIDER(remote_provider) == FALSE) { - return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND); - } - assertion_query->private_data->query_request_type = query_request_type; switch (query_request_type) { case LASSO_ASSERTION_QUERY_REQUEST_TYPE_ASSERTION_ID: - profile->request = lasso_samlp2_assertion_id_request_new(); + request = lasso_samlp2_assertion_id_request_new(); break; case LASSO_ASSERTION_QUERY_REQUEST_TYPE_AUTHN: - profile->request = lasso_samlp2_authn_query_new(); + request = lasso_samlp2_authn_query_new(); break; case LASSO_ASSERTION_QUERY_REQUEST_TYPE_ATTRIBUTE: - profile->request = lasso_samlp2_attribute_query_new(); + request = lasso_samlp2_attribute_query_new(); break; case LASSO_ASSERTION_QUERY_REQUEST_TYPE_AUTHZ_DECISION: - profile->request = lasso_samlp2_authz_decision_query_new(); + request = lasso_samlp2_authz_decision_query_new(); break; default: return critical_error(LASSO_PARAM_ERROR_INVALID_VALUE); } - if (query_request_type != LASSO_ASSERTION_QUERY_REQUEST_TYPE_ASSERTION_ID) { - LassoSaml2NameID *nameID = NULL; - /* fill */ - LassoSamlp2SubjectQueryAbstract *subject_query; - - /* Get federation */ - if (profile->session) { - GList *assertions; - LassoSaml2Assertion *assertion = NULL; - - assertions = lasso_session_get_assertions(profile->session, - (gchar*)profile->remote_providerID); - /* multiple assertions, take the first */ - if (assertions && LASSO_IS_SAML2_ASSERTION(assertions->data)) { - assertion = (LassoSaml2Assertion*)assertions->data; - } - if (assertion && assertion->Subject) { - nameID = assertion->Subject->NameID; - } - } - if (nameID == NULL) { - federation = g_hash_table_lookup(profile->identity->federations, - profile->remote_providerID); - if (LASSO_IS_FEDERATION(federation) == FALSE) { - return critical_error(LASSO_PROFILE_ERROR_FEDERATION_NOT_FOUND); - } /* XXX: should support looking up transient id */ - nameID = LASSO_SAML2_NAME_ID(lasso_profile_get_nameIdentifier(profile)); - } - subject_query = LASSO_SAMLP2_SUBJECT_QUERY_ABSTRACT(profile->request); - subject_query->Subject = LASSO_SAML2_SUBJECT(lasso_saml2_subject_new()); - subject_query->Subject->NameID = g_object_ref(nameID); - } /* Setup usual request attributes */ - request = LASSO_SAMLP2_REQUEST_ABSTRACT(profile->request); - request->ID = lasso_build_unique_id(32); - request->Version = g_strdup("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(); + if (LASSO_IS_SAMLP2_SUBJECT_QUERY_ABSTRACT(request)) { + LassoSamlp2SubjectQueryAbstract *sqa; - request->sign_method = LASSO_SIGNATURE_METHOD_RSA_SHA1; - if (profile->server->certificate) { - request->sign_type = LASSO_SIGNATURE_TYPE_WITHX509; - } else { - request->sign_type = LASSO_SIGNATURE_TYPE_SIMPLE; + sqa = (LassoSamlp2SubjectQueryAbstract*)request; + sqa->Subject = (LassoSaml2Subject*)lasso_saml2_subject_new(); } - request->private_key_file = g_strdup(profile->server->private_key); - request->certificate_file = g_strdup(profile->server->certificate); - - profile->http_request_method = http_method; - - return ret; + lasso_check_good_rc(lasso_saml20_profile_init_request(profile, + profile->remote_providerID, + TRUE, + (LassoSamlp2RequestAbstract*)request, + http_method, + _lasso_assertion_query_type_to_protocol_type(query_request_type))); +cleanup: + lasso_release_gobject(request); + return rc; } @@ -210,6 +178,7 @@ lasso_assertion_query_build_request_msg(LassoAssertionQuery *assertion_query) { LassoProfile *profile; LassoProvider *remote_provider; + gint rc = 0; g_return_val_if_fail(LASSO_IS_ASSERTION_QUERY(assertion_query), LASSO_PARAM_ERROR_INVALID_VALUE); @@ -221,8 +190,31 @@ lasso_assertion_query_build_request_msg(LassoAssertionQuery *assertion_query) if (LASSO_IS_PROVIDER(remote_provider) == FALSE) { return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND); } + + /* fill and encrypt if necessary */ + if (LASSO_IS_SAMLP2_SUBJECT_QUERY_ABSTRACT(profile->request)) do { + LassoSaml2NameID *nameID = NULL; + LassoSamlp2SubjectQueryAbstract *subject_query; + + subject_query = (LassoSamlp2SubjectQueryAbstract*)profile->request; + if (subject_query->Subject && + (subject_query->Subject->NameID || + subject_query->Subject->EncryptedID)) { + + nameID = (LassoSaml2NameID*)lasso_profile_get_nameIdentifier(profile); + if (! LASSO_IS_SAML2_NAME_ID(nameID)) + return LASSO_PROFILE_ERROR_MISSING_NAME_IDENTIFIER; + subject_query = LASSO_SAMLP2_SUBJECT_QUERY_ABSTRACT(profile->request); + subject_query->Subject->NameID = g_object_ref(nameID); + } + if (! subject_query->Subject) + return LASSO_PROFILE_ERROR_MISSING_SUBJECT; + lasso_check_good_rc(lasso_saml20_profile_setup_subject(profile, subject_query->Subject)); + } while(FALSE); + if (profile->http_request_method == LASSO_HTTP_METHOD_SOAP) { LassoAssertionQueryRequestType type; + const char *url; /* XXX: support only SOAP */ static const gchar *servicepoints[LASSO_ASSERTION_QUERY_REQUEST_TYPE_LAST] = { "AssertionIDRequestService SOAP", @@ -230,22 +222,31 @@ lasso_assertion_query_build_request_msg(LassoAssertionQuery *assertion_query) "AuthzService SOAP", "AttributeService SOAP" }; + static const LassoProviderRole roles[LASSO_ASSERTION_QUERY_REQUEST_TYPE_LAST] = { + LASSO_PROVIDER_ROLE_ANY, + LASSO_PROVIDER_ROLE_AUTHN_AUTHORITY, + LASSO_PROVIDER_ROLE_AUTHZ_AUTHORITY, + LASSO_PROVIDER_ROLE_ATTRIBUTE_AUTHORITY + }; + type = assertion_query->private_data->query_request_type; - if (type <= LASSO_ASSERTION_QUERY_REQUEST_TYPE_ASSERTION_ID || + if (type == LASSO_ASSERTION_QUERY_REQUEST_TYPE_ASSERTION_ID) { + return LASSO_ERROR_UNDEFINED; + } + if (type < LASSO_ASSERTION_QUERY_REQUEST_TYPE_ASSERTION_ID || type >= LASSO_ASSERTION_QUERY_REQUEST_TYPE_AUTHZ_DECISION) { return LASSO_PARAM_ERROR_INVALID_VALUE; } - profile->msg_url = lasso_provider_get_metadata_one(remote_provider, - servicepoints[type]); - lasso_saml20_profile_setup_request_signing(profile); - profile->msg_body = lasso_node_export_to_soap(profile->request); - return 0; + url = lasso_provider_get_metadata_one_for_role(remote_provider, roles[type], servicepoints[type]); + + return lasso_saml20_profile_build_request_msg(&assertion_query->parent, + NULL, + LASSO_HTTP_METHOD_SOAP, url); } - - return critical_error(LASSO_PROFILE_ERROR_INVALID_HTTP_METHOD); +cleanup: + return rc; } - /** * lasso_assertion_query_process_request_msg: * @assertion_query: a #LassoAssertionQuery @@ -302,63 +303,22 @@ lasso_assertion_query_validate_request(LassoAssertionQuery *assertion_query) { LassoProfile *profile; LassoProvider *remote_provider; - LassoFederation *federation; LassoSamlp2StatusResponse *response; + int rc; g_return_val_if_fail(LASSO_IS_ASSERTION_QUERY(assertion_query), LASSO_PARAM_ERROR_INVALID_VALUE); profile = LASSO_PROFILE(assertion_query); - if (profile->remote_providerID) { - g_free(profile->remote_providerID); - } + response = (LassoSamlp2StatusResponse*) lasso_samlp2_response_new(); + lasso_check_good_rc(lasso_saml20_profile_validate_request(profile, + FALSE, + response, + &remote_provider)); - profile->remote_providerID = g_strdup( - LASSO_SAMLP2_REQUEST_ABSTRACT(profile->request)->Issuer->content); - - /* get the provider */ - remote_provider = lasso_server_get_provider(profile->server, profile->remote_providerID); - if (LASSO_IS_PROVIDER(remote_provider) == FALSE) { - return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND); - } - - federation = g_hash_table_lookup(profile->identity->federations, - profile->remote_providerID); - if (LASSO_IS_FEDERATION(federation) == FALSE) { - return critical_error(LASSO_PROFILE_ERROR_FEDERATION_NOT_FOUND); - } - - if (profile->response) { - lasso_node_destroy(profile->response); - } - - profile->response = lasso_samlp2_response_new(); - response = LASSO_SAMLP2_STATUS_RESPONSE(profile->response); - response->ID = lasso_build_unique_id(32); - response->Version = g_strdup("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(); - response->InResponseTo = g_strdup(LASSO_SAMLP2_REQUEST_ABSTRACT(profile->request)->ID); - lasso_saml20_profile_set_response_status(profile, LASSO_SAML2_STATUS_CODE_SUCCESS, NULL); - - response->sign_method = LASSO_SIGNATURE_METHOD_RSA_SHA1; - if (profile->server->certificate) { - response->sign_type = LASSO_SIGNATURE_TYPE_WITHX509; - } else { - response->sign_type = LASSO_SIGNATURE_TYPE_SIMPLE; - } - response->private_key_file = g_strdup(profile->server->private_key); - response->certificate_file = g_strdup(profile->server->certificate); - - /* verify signature status */ - if (profile->signature_status != 0) { - lasso_saml20_profile_set_response_status_requester(profile, - LASSO_LIB_STATUS_CODE_INVALID_SIGNATURE); - return profile->signature_status; - } - - return 0; +cleanup: + lasso_release_gobject(response); + return rc; } @@ -375,6 +335,7 @@ lasso_assertion_query_build_response_msg(LassoAssertionQuery *assertion_query) { LassoProfile *profile; LassoSamlp2StatusResponse *response; + int rc; g_return_val_if_fail(LASSO_IS_ASSERTION_QUERY(assertion_query), LASSO_PARAM_ERROR_INVALID_VALUE); @@ -383,44 +344,23 @@ lasso_assertion_query_build_response_msg(LassoAssertionQuery *assertion_query) if (profile->response == NULL) { /* no response set here means request denied */ - profile->response = lasso_samlp2_response_new(); - response = LASSO_SAMLP2_STATUS_RESPONSE(profile->response); - response->ID = lasso_build_unique_id(32); - response->Version = g_strdup("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(); - response->InResponseTo = g_strdup( - LASSO_SAMLP2_REQUEST_ABSTRACT(profile->request)->ID); - lasso_saml20_profile_set_response_status_responder(profile, - LASSO_SAML2_STATUS_CODE_REQUEST_DENIED); + response = (LassoSamlp2StatusResponse*) lasso_samlp2_response_new(); - response->sign_method = LASSO_SIGNATURE_METHOD_RSA_SHA1; - if (profile->server->certificate) { - response->sign_type = LASSO_SIGNATURE_TYPE_WITHX509; - } else { - response->sign_type = LASSO_SIGNATURE_TYPE_SIMPLE; - } - response->private_key_file = g_strdup(profile->server->private_key); - response->certificate_file = g_strdup(profile->server->certificate); + lasso_check_good_rc(lasso_saml20_profile_init_response( + profile, + response, + LASSO_SAML2_STATUS_CODE_RESPONDER, + LASSO_SAML2_STATUS_CODE_REQUEST_DENIED)); return 0; } - if (profile->remote_providerID == NULL || profile->response == NULL) { - /* no remote provider id set or no response set, this means - * this function got called before validate_request, probably - * because there were no identity or federation */ - return critical_error(LASSO_SERVER_ERROR_PROVIDER_NOT_FOUND); - } - /* build logout response message */ - if (profile->http_request_method == LASSO_HTTP_METHOD_SOAP) { - profile->msg_url = NULL; - profile->msg_body = lasso_node_export_to_soap(profile->response); - return 0; - } - - return LASSO_PROFILE_ERROR_MISSING_REQUEST; + rc = lasso_saml20_profile_build_response_msg(profile, + NULL, + profile->http_request_method, + NULL); +cleanup: + return rc; } @@ -446,9 +386,10 @@ lasso_assertion_query_process_response_msg( profile = &assertion_query->parent; response = (LassoSamlp2StatusResponse*)lasso_samlp2_response_new(); - rc = lasso_saml20_profile_process_any_response(profile, response, NULL, response_msg); + lasso_check_good_rc(lasso_saml20_profile_process_any_response(profile, + response, NULL, response_msg)); -/* cleanup: */ +cleanup: lasso_release_gobject(response); return rc; } @@ -486,12 +427,6 @@ init_from_xml(LassoNode *node, xmlNode *xmlnode) /* overridden parent class methods */ /*****************************************************************************/ -static void -dispose(GObject *object) -{ - G_OBJECT_CLASS(parent_class)->dispose(object); -} - static void finalize(GObject *object) { @@ -501,8 +436,6 @@ finalize(GObject *object) G_OBJECT_CLASS(parent_class)->finalize(object); } - - /*****************************************************************************/ /* instance and class init functions */ /*****************************************************************************/ @@ -525,12 +458,9 @@ class_init(LassoAssertionQueryClass *klass) lasso_node_class_set_nodename(nclass, "AssertionQuery"); lasso_node_class_add_snippets(nclass, schema_snippets); - G_OBJECT_CLASS(klass)->dispose = dispose; G_OBJECT_CLASS(klass)->finalize = finalize; } - - GType lasso_assertion_query_get_type() { @@ -571,8 +501,7 @@ lasso_assertion_query_new(LassoServer *server) g_return_val_if_fail(LASSO_IS_SERVER(server), NULL); assertion_query = g_object_new(LASSO_TYPE_ASSERTION_QUERY, NULL); - LASSO_PROFILE(assertion_query)->server = g_object_ref(server); - + LASSO_PROFILE(assertion_query)->server = lasso_ref(server); return assertion_query; } @@ -585,5 +514,5 @@ lasso_assertion_query_new(LassoServer *server) void lasso_assertion_query_destroy(LassoAssertionQuery *assertion_query) { - lasso_node_destroy(LASSO_NODE(assertion_query)); + lasso_release_gobject(assertion_query); } diff --git a/lasso/saml-2.0/profile.c b/lasso/saml-2.0/profile.c index 7be10686..a27966bc 100644 --- a/lasso/saml-2.0/profile.c +++ b/lasso/saml-2.0/profile.c @@ -340,6 +340,8 @@ lasso_saml20_profile_process_artifact_resolve(LassoProfile *profile, const char 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); @@ -644,7 +646,6 @@ cleanup: return rc; } - int lasso_saml20_profile_process_soap_request(LassoProfile *profile, const char *request_msg) @@ -1165,7 +1166,7 @@ lasso_saml20_profile_build_response_msg(LassoProfile *profile, char *service, } /* if not explicitely given, automatically determine an URI from the metadatas */ - if (url == NULL && service) { + if (url == NULL && service && method != LASSO_HTTP_METHOD_SOAP) { made_url = url = get_response_url(provider, service, http_method_to_binding(method)); } @@ -1484,6 +1485,29 @@ lasso_profile_saml20_setup_message_signature(LassoProfile *profile, LassoNode *r 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) diff --git a/lasso/saml-2.0/profileprivate.h b/lasso/saml-2.0/profileprivate.h index 04677633..c3968aa3 100644 --- a/lasso/saml-2.0/profileprivate.h +++ b/lasso/saml-2.0/profileprivate.h @@ -34,6 +34,7 @@ extern "C" { #include "../xml/saml-2.0/saml2_encrypted_element.h" #include "../xml/saml-2.0/samlp2_status_response.h" #include "../xml/saml-2.0/samlp2_request_abstract.h" +#include "../xml/saml-2.0/saml2_subject.h" #include "../id-ff/provider.h" int lasso_saml20_profile_init_request(LassoProfile *profile, const char *remote_provider_id, @@ -77,6 +78,7 @@ gint lasso_profile_saml20_setup_message_signature(LassoProfile *profile, LassoNode *request_or_response); gint lasso_saml20_profile_setup_encrypted_node(LassoProvider *provider, LassoNode **node_to_encrypt, LassoNode **node_destination); +int lasso_saml20_profile_setup_subject(LassoProfile *profile, LassoSaml2Subject *subject); #ifdef __cplusplus } diff --git a/lasso/saml-2.0/provider.c b/lasso/saml-2.0/provider.c index 5e7f1082..a30250c4 100644 --- a/lasso/saml-2.0/provider.c +++ b/lasso/saml-2.0/provider.c @@ -22,14 +22,18 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define _POSIX_SOURCE + #include "../xml/private.h" #include +#include #include "providerprivate.h" #include "../id-ff/providerprivate.h" #include "../utils.h" #include "./provider.h" #include "../xml/saml-2.0/saml2_attribute.h" +#include "../xml/saml-2.0/saml2_xsd.h" const char *profile_names[LASSO_MD_PROTOCOL_TYPE_LAST] = { "", /* No fedterm in SAML 2.0 */ @@ -50,122 +54,179 @@ const char *profile_names[LASSO_MD_PROTOCOL_TYPE_LAST] = { static void add_assertion_consumer_url_to_list(gchar *key, G_GNUC_UNUSED gpointer value, GList **list); -static void -load_descriptor(xmlNode *xmlnode, GHashTable *descriptor, LassoProvider *provider) +static const char* +binding_uri_to_identifier(const char *uri) { - char *descriptor_attrs[] = {"AuthnRequestsSigned", "WantAuthnRequestsSigned", NULL}; + if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_SOAP) == 0) { + return "SOAP"; + } else if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_REDIRECT) == 0) { + return "HTTP-Redirect"; + } else if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_POST) == 0) { + return "HTTP-POST"; + } else if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_ARTIFACT) == 0) { + return "HTTP-Artifact"; + } else if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_PAOS) == 0) { + return "PAOS"; + } else if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_URI) == 0) { + return "URI"; + } + return NULL; +} + +static gboolean +checkSaml2MdNode(xmlNode *t, char *name) +{ + return xmlSecCheckNodeName(t, + BAD_CAST name, + BAD_CAST LASSO_SAML2_METADATA_HREF); +} + +static xmlChar* +getSaml2MdProp(xmlNode *t, char *name) { + return xmlGetProp(t, BAD_CAST name); +} + +static gboolean +hasSaml2MdProp(xmlNode *t, char *name) { + return xmlHasProp(t, BAD_CAST name) != NULL; +} + +static gboolean +xsdIsTrue(xmlChar *value) +{ + if (value && strcmp((char*)value, "true") == 0) + return TRUE; + return FALSE; +} + +static void +load_endpoint_type(xmlNode *xmlnode, LassoProvider *provider, LassoProviderRole role) +{ + xmlChar *binding = xmlGetProp(xmlnode, BAD_CAST "Binding"); + char *name = NULL; + char *response_name = NULL; + LassoProviderPrivate *private_data = provider->private_data; + const char *binding_s = NULL; + xmlChar *value = NULL; + xmlChar *response_value = NULL; + + + binding_s = binding_uri_to_identifier((char*)binding); + if (! binding_s) { + message(G_LOG_LEVEL_CRITICAL, "XXX: unknown binding: %s", binding); + goto cleanup; + } + + /* get endpoint location */ + value = getSaml2MdProp(xmlnode, LASSO_SAML2_METADATA_ATTRIBUTE_LOCATION); + + if (value == NULL) { + message(G_LOG_LEVEL_CRITICAL, "XXX: missing location for element %s", xmlnode->name); + goto cleanup; + } + /* special case of AssertionConsumerService */ + if (checkSaml2MdNode(xmlnode, LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE)) { + xmlChar *index = getSaml2MdProp(xmlnode, LASSO_SAML2_METADATA_ATTRIBUTE_INDEX); + xmlChar *is_default = getSaml2MdProp(xmlnode, LASSO_SAML2_METADATA_ATTRIBUTE_ISDEFAULT); + + if (xsdIsTrue(is_default)) { + lasso_assign_string(private_data->default_assertion_consumer, (char*)index); + } + name = g_strdup_printf(LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE + " %s %s", + binding_s, + index); + lasso_release_xml_string(index); + lasso_release_xml_string(is_default); + } else { + name = g_strdup_printf("%s %s", xmlnode->name, binding_s); + } + lasso_release_xml_string(binding); + + /* Response endpoint ? */ + response_value = getSaml2MdProp(xmlnode, LASSO_SAML2_METADATA_ATTRIBUTE_RESPONSE_LOCATION); + if (response_value) { + response_name = g_strdup_printf("%s " + LASSO_SAML2_METADATA_ATTRIBUTE_RESPONSE_LOCATION, + name); + _lasso_provider_add_metadata_value_for_role(provider, role, response_name, + (char*)response_value); + } + _lasso_provider_add_metadata_value_for_role(provider, role, name, (char*)value); + +cleanup: + lasso_release_xml_string(value); + lasso_release_xml_string(response_value); + lasso_release_string(name); + lasso_release_string(response_name); +} + +static gboolean +load_descriptor(xmlNode *xmlnode, LassoProvider *provider, LassoProviderRole role) +{ + static char * const descriptor_attrs[] = { + LASSO_SAML2_METADATA_ATTRIBUTE_VALID_UNTIL, + LASSO_SAML2_METADATA_ATTRIBUTE_CACHE_DURATION, + LASSO_SAML2_METADATA_ATTRIBUTE_AUTHN_REQUEST_SIGNED, + LASSO_SAML2_METADATA_ATTRIBUTE_WANT_AUTHN_REQUEST_SIGNED, + LASSO_SAML2_METADATA_ATTRIBUTE_ERROR_URL + }; int i; xmlNode *t; - GList *elements; - char *name, *binding, *response_name; xmlChar *value; - xmlChar *response_value; - xmlChar *use; + LassoProviderPrivate *pdata = provider->private_data; + char *token, *saveptr; + + /* check protocol support enumeration */ + value = getSaml2MdProp(xmlnode, + LASSO_SAML2_METADATA_ATTRIBUTE_PROTOCOL_SUPPORT_ENUMERATION); + token = strtok_r((char*) value, " ", &saveptr); + while (token) { + if (strcmp(token, LASSO_SAML2_PROTOCOL_HREF) == 0) + break; + token = strtok_r(NULL, " ", &saveptr); + } + if (g_strcmp0(token, LASSO_SAML2_PROTOCOL_HREF) != 0) { + lasso_release_xml_string(value); + message(G_LOG_LEVEL_WARNING, "%s descriptor does not support SAML 2.0 protocol", xmlnode->name); + return FALSE; + } + lasso_release_xml_string(value); - t = xmlnode->children; + /* add role to supported roles for the provider */ + pdata->roles |= role; + t = xmlSecGetNextElementNode(xmlnode->children); while (t) { - if (t->type != XML_ELEMENT_NODE) { - t = t->next; - continue; - } - if (strcmp((char*)t->name, "KeyDescriptor") == 0) { - use = xmlGetProp(t, (xmlChar*)"use"); - if (use == NULL || strcmp((char*)use, "signing") == 0) { - provider->private_data->signing_key_descriptor = xmlCopyNode(t, 1); - } - if (use == NULL || strcmp((char*)use, "encryption") == 0) { - provider->private_data->encryption_key_descriptor = - xmlCopyNode(t, 1); - } - if (use) { - xmlFree(use); - } - t = t->next; - continue; - } - if (strcmp((char*)t->name, "Attribute") == 0) { + if (checkSaml2MdNode(t, + LASSO_SAML2_METADATA_ELEMENT_KEY_DESCRIPTOR)) { + _lasso_provider_load_key_descriptor(provider, t); + } else if (checkSaml2MdNode(t, + LASSO_SAML2_ASSERTION_ELEMENT_ATTRIBUTE) && role == LASSO_PROVIDER_ROLE_IDP) { LassoSaml2Attribute *attribute; - attribute = LASSO_SAML2_ATTRIBUTE(lasso_node_new_from_xmlNode(t)); - if (attribute) { - provider->private_data->attributes = - g_list_append(provider->private_data->attributes, attribute); - } - continue; - } - binding = (char*)xmlGetProp(t, (xmlChar*)"Binding"); - if (binding) { - /* Endpoint type */ - char *binding_s = NULL; - if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_SOAP) == 0) { - binding_s = "SOAP"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_REDIRECT) == 0) { - binding_s = "HTTP-Redirect"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_POST) == 0) { - binding_s = "HTTP-POST"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_ARTIFACT) == 0) { - binding_s = "HTTP-Artifact"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_PAOS) == 0) { - binding_s = "PAOS"; - } else { - message(G_LOG_LEVEL_CRITICAL, "XXX: unknown binding: %s", binding); - xmlFree(binding); - t = t->next; - continue; - } - value = xmlGetProp(t, (xmlChar*)"Location"); - if (value == NULL) { - message(G_LOG_LEVEL_CRITICAL, "XXX: missing location"); - xmlFree(binding); - t = t->next; - continue; - } - if (strcmp((char*)t->name, "AssertionConsumerService") == 0) { - char *index = (char*)xmlGetProp(t, (xmlChar*)"index"); - char *is_default = (char*)xmlGetProp(t, (xmlChar*)"isDefault"); - if (is_default && strcmp(is_default, "true") == 0) { - provider->private_data->default_assertion_consumer = - g_strdup(index); - } - name = g_strdup_printf("%s %s %s", t->name, binding_s, index); - xmlFree(index); - xmlFree(is_default); - } - else { - name = g_strdup_printf("%s %s", t->name, binding_s); - } - xmlFree(binding); - - response_value = xmlGetProp(t, (xmlChar*)"ResponseLocation"); - if (response_value) { - response_name = g_strdup_printf("%s ResponseLocation", name); - elements = g_hash_table_lookup(descriptor, response_name); - elements = g_list_append(elements, g_strdup((char*)response_value)); - g_hash_table_insert(descriptor, response_name, elements); - xmlFree(response_value); - } + attribute = (LassoSaml2Attribute*) lasso_node_new_from_xmlNode(t); + lasso_list_add_new_gobject(pdata->attributes, + attribute); + } else if (hasSaml2MdProp(t, LASSO_SAML2_METADATA_ATTRIBUTE_BINDING)) { + load_endpoint_type(t, provider, role); } else { - name = g_strdup((char*)t->name); value = xmlNodeGetContent(t); + _lasso_provider_add_metadata_value_for_role(provider, role, (char*)t->name, + (char*)value); + lasso_release_xml_string(value); } - elements = g_hash_table_lookup(descriptor, name); - elements = g_list_append(elements, g_strdup((char*)value)); - xmlFree(value); - g_hash_table_insert(descriptor, name, elements); - t = t->next; + t = xmlSecGetNextElementNode(t->next); } for (i = 0; descriptor_attrs[i]; i++) { - value = xmlGetProp(xmlnode, (xmlChar*)descriptor_attrs[i]); + value = getSaml2MdProp(xmlnode, descriptor_attrs[i]); if (value == NULL) { continue; } - name = g_strdup(descriptor_attrs[i]); - elements = g_hash_table_lookup(descriptor, name); - elements = g_list_append(elements, g_strdup((char*)value)); - xmlFree(value); - g_hash_table_insert(descriptor, name, elements); + _lasso_provider_add_metadata_value_for_role(provider, role, descriptor_attrs[i], + (char*)value); + lasso_release_xml_string(value); } - + return TRUE; } gboolean @@ -173,103 +234,104 @@ lasso_saml20_provider_load_metadata(LassoProvider *provider, xmlNode *root_node) { xmlNode *node, *descriptor_node; xmlChar *providerID; + LassoProviderPrivate *pdata = provider->private_data; + static const struct { + char *name; + LassoProviderRole role; + } descriptors[] = { + { LASSO_SAML2_METADATA_ELEMENT_IDP_SSO_DESCRIPTOR, + LASSO_PROVIDER_ROLE_IDP }, + { LASSO_SAML2_METADATA_ELEMENT_SP_SSO_DESCRIPTOR, + LASSO_PROVIDER_ROLE_SP }, + { LASSO_SAML2_METADATA_ELEMENT_ATTRIBUTE_AUTHORITY_DESCRIPTOR, + LASSO_PROVIDER_ROLE_ATTRIBUTE_AUTHORITY }, + { LASSO_SAML2_METADATA_ELEMENT_PDP_DESCRIPTOR, + LASSO_PROVIDER_ROLE_AUTHZ_AUTHORITY }, + { LASSO_SAML2_METADATA_ELEMENT_AUTHN_DESCRIPTOR, + LASSO_PROVIDER_ROLE_AUTHN_AUTHORITY }, + { NULL, 0 } + }; - if (strcmp((char*)root_node->name, "EntityDescriptor") == 0) { + /* find a root node for the metadata file */ + if (xmlSecCheckNodeName(root_node, + BAD_CAST LASSO_SAML2_METADATA_ELEMENT_ENTITY_DESCRIPTOR, + BAD_CAST LASSO_SAML2_METADATA_HREF)) { node = root_node; - } else if (strcmp((char*)root_node->name, "EntitiesDescriptor") == 0) { - /* XXX: take the first entity; would it be possible to have an - * optional argument to take another one ? */ - node = root_node->children; - while (node && strcmp((char*)node->name, "EntityDescriptor") != 0) { - node = node->next; - } - if (node == NULL) { - message (G_LOG_LEVEL_CRITICAL, "lasso_saml20_provider_load_metadata_from_doc: no EntityDescriptor"); - return FALSE; - } - } else { - message (G_LOG_LEVEL_CRITICAL, "lasso_saml20_provider_load_metadata_from_doc: no EntityDescriptor"); - /* what? */ - return FALSE; + } else if (xmlSecCheckNodeName(root_node, + BAD_CAST LASSO_SAML2_METADATA_ELEMENT_ENTITIES_DESCRIPTOR, + BAD_CAST LASSO_SAML2_METADATA_HREF)) { + node = xmlSecFindChild(root_node, + BAD_CAST LASSO_SAML2_METADATA_ELEMENT_ENTITY_DESCRIPTOR, + BAD_CAST LASSO_SAML2_METADATA_HREF); } + g_return_val_if_fail (node, FALSE); providerID = xmlGetProp(node, (xmlChar*)"entityID"); + g_return_val_if_fail(providerID, FALSE); lasso_assign_string(provider->ProviderID, (char*)providerID); lasso_release_xml_string(providerID); - if (provider->ProviderID == NULL) { - message (G_LOG_LEVEL_CRITICAL, "lasso_saml20_provider_load_metadata_from_doc: no entityID attribute"); - return FALSE; + /* initialize roles */ + pdata->roles = LASSO_PROVIDER_ROLE_NONE; + lasso_set_string_from_prop(&pdata->valid_until, node, + BAD_CAST LASSO_SAML2_METADATA_ATTRIBUTE_VALID_UNTIL, + BAD_CAST LASSO_SAML2_METADATA_HREF); + lasso_set_string_from_prop(&pdata->cache_duration, node, + BAD_CAST LASSO_SAML2_METADATA_ATTRIBUTE_CACHE_DURATION, + BAD_CAST LASSO_SAML2_METADATA_HREF); + + descriptor_node = xmlSecGetNextElementNode(node->children); + while (descriptor_node) { + int i = 0; + + while (descriptors[i].name) { + char *name = descriptors[i].name; + LassoProviderRole role = descriptors[i].role; + + if (checkSaml2MdNode(descriptor_node, name)) { + load_descriptor(descriptor_node, + provider, + role); + } + i++; + } + + if (checkSaml2MdNode(descriptor_node, + LASSO_SAML2_METADATA_ELEMENT_ORGANIZATION)) { + lasso_assign_xml_node(pdata->organization, descriptor_node); } + descriptor_node = xmlSecGetNextElementNode(descriptor_node->next); } - for (descriptor_node = node->children; descriptor_node != NULL; - descriptor_node = descriptor_node->next) { - if (descriptor_node->type != XML_ELEMENT_NODE) - continue; - - if (strcmp((char*)descriptor_node->name, "IDPSSODescriptor") == 0) { - load_descriptor(descriptor_node, - provider->private_data->Descriptors, provider); - provider->role = LASSO_PROVIDER_ROLE_IDP; - continue; - } - - if (strcmp((char*)descriptor_node->name, "SPSSODescriptor") == 0) { - load_descriptor(descriptor_node, - provider->private_data->Descriptors, provider); - provider->role = LASSO_PROVIDER_ROLE_SP; - continue; - } - - if (strcmp((char*)descriptor_node->name, "AttributeAuthorityDescriptor") == 0) { - load_descriptor(descriptor_node, - provider->private_data->Descriptors, provider); - provider->role = LASSO_PROVIDER_ROLE_ATTRIBUTE_AUTHORITY; - continue; - } - - if (strcmp((char*)descriptor_node->name, "PDPDescriptor") == 0) { - load_descriptor(descriptor_node, - provider->private_data->Descriptors, provider); - provider->role = LASSO_PROVIDER_ROLE_PDP; - continue; - } - - if (strcmp((char*)descriptor_node->name, "AuthnAuthorityDescriptor") == 0) { - load_descriptor(descriptor_node, - provider->private_data->Descriptors, provider); - provider->role = LASSO_PROVIDER_ROLE_AUTHN_AUTHORITY; - continue; - } - - if (strcmp((char*)descriptor_node->name, "Organization") == 0) { - provider->private_data->organization = xmlCopyNode( - descriptor_node, 1); - continue; - } - } - - - return TRUE; } LassoHttpMethod lasso_saml20_provider_get_first_http_method(LassoProvider *provider, - const LassoProvider *remote_provider, LassoMdProtocolType protocol_type) + LassoProvider *remote_provider, LassoMdProtocolType protocol_type) { LassoHttpMethod method = LASSO_HTTP_METHOD_NONE; int i; const char *possible_bindings[] = { - "HTTP-Redirect", "HTTP-Post", "SOAP", "HTTP-Artifact", NULL + "HTTP-POST", + "HTTP-Redirect", + "HTTP-Artifact", + "SOAP", + "PAOS", + NULL }; LassoHttpMethod method_bindings[] = { - LASSO_HTTP_METHOD_SOAP, LASSO_HTTP_METHOD_REDIRECT, LASSO_HTTP_METHOD_POST + LASSO_HTTP_METHOD_POST, + LASSO_HTTP_METHOD_REDIRECT, + LASSO_HTTP_METHOD_ARTIFACT_GET, + LASSO_HTTP_METHOD_SOAP, + LASSO_HTTP_METHOD_PAOS }; for (i=0; possible_bindings[i] && method == LASSO_HTTP_METHOD_NONE; i++) { char *s; const GList *l1, *l2; - s = g_strdup_printf("%s %s", profile_names[protocol_type], possible_bindings[i]); + s = g_strdup_printf("%s %s", + profile_names[protocol_type], + possible_bindings[i]); l1 = lasso_provider_get_metadata_list(provider, s); l2 = lasso_provider_get_metadata_list(remote_provider, s); if (l1 && l2) { @@ -286,37 +348,29 @@ lasso_saml20_provider_check_assertion_consumer_service_url(LassoProvider *provid GHashTable *descriptor; GList *l = NULL, *r = NULL, *candidate = NULL; char *name; - char *binding_s = NULL; + const char *binding_s = NULL; int lname; - descriptor = provider->private_data->SPDescriptor; + descriptor = provider->private_data->Descriptors; if (descriptor == NULL || url == NULL || binding == NULL) return FALSE; - if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_SOAP) == 0) { - binding_s = "SOAP"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_REDIRECT) == 0) { - binding_s = "HTTP-Redirect"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_POST) == 0) { - binding_s = "HTTP-POST"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_ARTIFACT) == 0) { - binding_s = "HTTP-Artifact"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_PAOS) == 0) { - binding_s = "PAOS"; - } - + binding_s = binding_uri_to_identifier(binding); if (binding_s == NULL) { return FALSE; } - g_hash_table_foreach(descriptor, (GHFunc)add_assertion_consumer_url_to_list, &r); + g_hash_table_foreach(descriptor, + (GHFunc)add_assertion_consumer_url_to_list, + &r); - name = g_strdup_printf("AssertionConsumerService %s ", binding_s); + name = g_strdup_printf(LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE + " %s ", binding_s); lname = strlen(name); for (l = r; l; l = g_list_next(l)) { char *b = l->data; if (strncmp(name, b, lname) == 0) { - candidate = g_hash_table_lookup(descriptor, b); + candidate = lasso_provider_get_metadata_list_for_role(provider, LASSO_PROVIDER_ROLE_SP, b); if (candidate && candidate->data && strcmp(candidate->data, url) == 0) break; else @@ -333,15 +387,17 @@ lasso_saml20_provider_check_assertion_consumer_service_url(LassoProvider *provid } gchar* -lasso_saml20_provider_get_assertion_consumer_service_url(const LassoProvider *provider, +lasso_saml20_provider_get_assertion_consumer_service_url(LassoProvider *provider, int service_id) { - GHashTable *descriptor; GList *l = NULL; char *sid; char *name; const char *possible_bindings[] = { - "HTTP-Artifact", "HTTP-Post", "HTTP-POST", "HTTP-Redirect", "SOAP", NULL + "HTTP-Artifact", + "HTTP-POST", + "HTTP-Redirect", + NULL }; int i; @@ -351,19 +407,18 @@ lasso_saml20_provider_get_assertion_consumer_service_url(const LassoProvider *pr sid = g_strdup_printf("%d", service_id); } - descriptor = provider->private_data->Descriptors; - if (descriptor == NULL) - return NULL; - for (i=0; possible_bindings[i]; i++) { - name = g_strdup_printf("AssertionConsumerService %s %s", + name = g_strdup_printf(LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE + " %s %s", possible_bindings[i], sid); - l = g_hash_table_lookup(descriptor, name); - g_free(name); + l = lasso_provider_get_metadata_list_for_role(provider, + LASSO_PROVIDER_ROLE_SP, + name); + lasso_release_string(name); if (l != NULL) break; } - g_free(sid); + lasso_release_string(sid); if (l) return g_strdup(l->data); return NULL; @@ -372,45 +427,37 @@ lasso_saml20_provider_get_assertion_consumer_service_url(const LassoProvider *pr static void add_assertion_consumer_url_to_list(gchar *key, G_GNUC_UNUSED gpointer value, GList **list) { - if (strncmp(key, "AssertionConsumerService", 24) == 0) { - *list = g_list_append(*list, key); + if (strncmp(key, "sp AssertionConsumerService", 24) == 0) { + lasso_list_add_new_string(*list, key); } } - gchar* -lasso_saml20_provider_get_assertion_consumer_service_url_by_binding(const LassoProvider *provider, - gchar *binding) +lasso_saml20_provider_get_assertion_consumer_service_url_by_binding(LassoProvider *provider, + const gchar *binding) { GHashTable *descriptor; GList *l = NULL, *r = NULL; char *name; - char *binding_s = NULL; + const char *binding_s = NULL; int lname; descriptor = provider->private_data->Descriptors; if (descriptor == NULL) return NULL; - if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_SOAP) == 0) { - binding_s = "SOAP"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_REDIRECT) == 0) { - binding_s = "HTTP-Redirect"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_POST) == 0) { - binding_s = "HTTP-POST"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_ARTIFACT) == 0) { - binding_s = "HTTP-Artifact"; - } else if (strcmp(binding, LASSO_SAML2_METADATA_BINDING_PAOS) == 0) { - binding_s = "PAOS"; - } - + binding_s = binding_uri_to_identifier(binding); if (binding_s == NULL) { return NULL; } - g_hash_table_foreach(descriptor, (GHFunc)add_assertion_consumer_url_to_list, &r); + g_hash_table_foreach(descriptor, + (GHFunc)add_assertion_consumer_url_to_list, + &r); - name = g_strdup_printf("AssertionConsumerService %s ", binding_s); + name = g_strdup_printf("sp " + LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE + " %s ", binding_s); lname = strlen(name); for (l = r; l; l = g_list_next(l)) { char *b = l->data; @@ -419,8 +466,8 @@ lasso_saml20_provider_get_assertion_consumer_service_url_by_binding(const LassoP break; } } - g_free(name); - g_list_free(r); + lasso_release_string(name); + lasso_release_list(r); if (l) { return g_strdup(l->data); @@ -429,10 +476,8 @@ lasso_saml20_provider_get_assertion_consumer_service_url_by_binding(const LassoP return NULL; } - - gchar* -lasso_saml20_provider_get_assertion_consumer_service_binding(const LassoProvider *provider, +lasso_saml20_provider_get_assertion_consumer_service_binding(LassoProvider *provider, int service_id) { GHashTable *descriptor; @@ -441,7 +486,11 @@ lasso_saml20_provider_get_assertion_consumer_service_binding(const LassoProvider char *name; char *binding = NULL; const char *possible_bindings[] = { - "HTTP-Artifact", "HTTP-Post", "HTTP-POST", "HTTP-Redirect", "SOAP", NULL + "HTTP-POST", + "HTTP-Redirect", + "HTTP-Artifact", + "SOAP", + NULL }; int i; @@ -450,32 +499,32 @@ lasso_saml20_provider_get_assertion_consumer_service_binding(const LassoProvider } else { sid = g_strdup_printf("%d", service_id); } - descriptor = provider->private_data->Descriptors; if (descriptor == NULL) return NULL; for (i=0; possible_bindings[i]; i++) { - name = g_strdup_printf("AssertionConsumerService %s %s", + name = g_strdup_printf(LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE + " %s %s", possible_bindings[i], sid); - l = g_hash_table_lookup(descriptor, name); - g_free(name); + l = lasso_provider_get_metadata_list_for_role(provider, LASSO_PROVIDER_ROLE_SP, name); + lasso_release_string(name); if (l != NULL) { binding = g_strdup(possible_bindings[i]); break; } } - g_free(sid); + lasso_release_string(sid); return binding; } gboolean -lasso_saml20_provider_accept_http_method(LassoProvider *provider, const LassoProvider *remote_provider, +lasso_saml20_provider_accept_http_method(LassoProvider *provider, LassoProvider *remote_provider, LassoMdProtocolType protocol_type, LassoHttpMethod http_method, gboolean initiate_profile) { char *protocol_profile; - const static char *http_methods[] = { + static const char *http_methods[] = { NULL, NULL, NULL, @@ -488,7 +537,7 @@ lasso_saml20_provider_accept_http_method(LassoProvider *provider, const LassoPro NULL }; gboolean rc = FALSE; - + LassoProviderRole initiating_role; initiating_role = remote_provider->role; if (remote_provider->role == LASSO_PROVIDER_ROLE_SP) { @@ -511,7 +560,7 @@ lasso_saml20_provider_accept_http_method(LassoProvider *provider, const LassoPro /* special hack for single sign on */ if (protocol_type == LASSO_MD_PROTOCOL_TYPE_SINGLE_SIGN_ON) { /* no need to check for the response, it uses another canal - * (AssertionConsumingService) */ + * (AssertionConsumerService) */ rc = (lasso_provider_get_metadata_list(remote_provider, protocol_profile) != NULL); } else { diff --git a/lasso/saml-2.0/providerprivate.h b/lasso/saml-2.0/providerprivate.h index 5c08fe5c..2c217ab5 100644 --- a/lasso/saml-2.0/providerprivate.h +++ b/lasso/saml-2.0/providerprivate.h @@ -35,20 +35,20 @@ extern "C" { gboolean lasso_saml20_provider_load_metadata(LassoProvider *provider, xmlNode *root_node); LassoHttpMethod lasso_saml20_provider_get_first_http_method(LassoProvider *provider, - const LassoProvider *remote_provider, LassoMdProtocolType protocol_type); + LassoProvider *remote_provider, LassoMdProtocolType protocol_type); gboolean lasso_saml20_provider_accept_http_method(LassoProvider *provider, - const LassoProvider *remote_provider, LassoMdProtocolType protocol_type, + LassoProvider *remote_provider, LassoMdProtocolType protocol_type, LassoHttpMethod http_method, gboolean initiate_profile); -char* lasso_saml20_provider_build_artifact(const LassoProvider *provider); +char* lasso_saml20_provider_build_artifact(LassoProvider *provider); -gchar* lasso_saml20_provider_get_assertion_consumer_service_url(const LassoProvider *provider, +gchar* lasso_saml20_provider_get_assertion_consumer_service_url(LassoProvider *provider, int service_id); -gchar* lasso_saml20_provider_get_assertion_consumer_service_binding(const LassoProvider *provider, +gchar* lasso_saml20_provider_get_assertion_consumer_service_binding(LassoProvider *provider, int service_id); -gchar* lasso_saml20_provider_get_assertion_consumer_service_url_by_binding(const LassoProvider *provider, - gchar *binding); +gchar* lasso_saml20_provider_get_assertion_consumer_service_url_by_binding(LassoProvider *provider, + const gchar *binding); gboolean lasso_saml20_provider_check_assertion_consumer_service_url(LassoProvider *provider, const gchar *url, const gchar *binding); #ifdef __cplusplus } diff --git a/lasso/xml/saml-2.0/saml2_strings.h b/lasso/xml/saml-2.0/saml2_strings.h index f4f0470d..00443d62 100644 --- a/lasso/xml/saml-2.0/saml2_strings.h +++ b/lasso/xml/saml-2.0/saml2_strings.h @@ -116,6 +116,13 @@ */ #define LASSO_SAML2_METADATA_BINDING_PAOS "urn:oasis:names:tc:SAML:2.0:bindings:PAOS" +/** + * LASSO_SAML2_METADATA_BINDING_URI: + * + * URI for the URI special binding. + */ +#define LASSO_SAML2_METADATA_BINDING_URI "urn:oasis:names:tc:SAML:2.0:bindings:URI" + /** * LASSO_SAML2_DEFLATE_ENCODING: * diff --git a/lasso/xml/saml-2.0/saml2_xsd.h b/lasso/xml/saml-2.0/saml2_xsd.h index 365a4955..d9b9b7cc 100644 --- a/lasso/xml/saml-2.0/saml2_xsd.h +++ b/lasso/xml/saml-2.0/saml2_xsd.h @@ -33,7 +33,7 @@ #define LASSO_SAML2_METADATA_ELEMENT_SP_SSO_DESCRIPTOR "SPSSODescriptor" #define LASSO_SAML2_METADATA_ELEMENT_ATTRIBUTE_AUTHORITY_DESCRIPTOR "AttributeAuthorityDescriptor" #define LASSO_SAML2_METADATA_ELEMENT_PDP_DESCRIPTOR "PDPDescriptor" -#define LASSO_SAML2_METADATA_ELEMENT_AUTHN_DESCRIPTOR "AuthnAuhtorityDescriptor" +#define LASSO_SAML2_METADATA_ELEMENT_AUTHN_DESCRIPTOR "AuthnAuthorityDescriptor" #define LASSO_SAML2_METADATA_ELEMENT_ORGANIZATION "Organization" #define LASSO_SAML2_METADATA_ELEMENT_KEY_DESCRIPTOR "KeyDescriptor" #define LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE "AssertionConsumerService" diff --git a/tests/Makefile.am b/tests/Makefile.am index 62ee098c..2d1f6f4a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -20,7 +20,7 @@ INCLUDES = \ $(CHECK_CFLAGS) -tests_SOURCES = tests.c login_tests.c basic_tests.c random_tests.c metadata_tests.c login_tests_saml2.c $(WSF_TESTS) +tests_SOURCES = tests.c login_tests.c basic_tests.c random_tests.c metadata_tests.c login_tests_saml2.c assertion_query_saml2.c $(WSF_TESTS) tests_LDADD = \ $(top_builddir)/lasso/liblasso.la \ diff --git a/tests/assertion_query_saml2.c b/tests/assertion_query_saml2.c new file mode 100644 index 00000000..12e4b345 --- /dev/null +++ b/tests/assertion_query_saml2.c @@ -0,0 +1,95 @@ +/* + * Lasso library C unit tests + * + * 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 +#include + +#include +#include + +#include "../lasso/lasso.h" +#include "../lasso/utils.h" +#include "../lasso/backward_comp.h" +#include "../lasso/xml/saml-2.0/saml2_xsd.h" + +#include "./tests.h" + +inline static char* +generateIdentityProviderContextDump() +{ + LassoServer *serverContext; + GList *providers; + char *ret; + + serverContext = lasso_server_new( + TESTSDATADIR "/idp6-saml2/metadata.xml", + TESTSDATADIR "/idp6-saml2/private-key.pem", + NULL, /* Secret key to unlock private key */ + NULL); + lasso_server_add_provider( + serverContext, + LASSO_PROVIDER_ROLE_SP, + TESTSDATADIR "/sp5-saml2/metadata.xml", + NULL, + NULL); + providers = g_hash_table_get_values(serverContext->providers); + lasso_provider_set_encryption_mode(LASSO_PROVIDER(providers->data), LASSO_ENCRYPTION_MODE_ASSERTION | LASSO_ENCRYPTION_MODE_NAMEID); + ret = lasso_server_dump(serverContext); + + g_object_unref(serverContext); + + return ret; +} + +inline static char* +generateServiceProviderContextDump() +{ + LassoServer *serverContext; + char *ret; + + serverContext = lasso_server_new( + TESTSDATADIR "/sp5-saml2/metadata.xml", + TESTSDATADIR "/sp5-saml2/private-key.pem", + NULL, /* Secret key to unlock private key */ + NULL); + lasso_server_add_provider( + serverContext, + LASSO_PROVIDER_ROLE_IDP, + TESTSDATADIR "/idp6-saml2/metadata.xml", + NULL, + NULL); + + ret = lasso_server_dump(serverContext); + g_object_unref(serverContext); + return ret; +} + +Suite* +assertion_query_suite() +{ + Suite *s = suite_create("Assertion Query"); + TCase *tc_metadata_access = tcase_create("Extended metadata access"); + suite_add_tcase(s, tc_metadata_access); + + return s; +} diff --git a/tests/metadata_tests.c b/tests/metadata_tests.c index 6daf31f0..c6653121 100644 --- a/tests/metadata_tests.c +++ b/tests/metadata_tests.c @@ -28,6 +28,9 @@ #include <../lasso/lasso.h> #include <../lasso/id-ff/provider.h> +#include "../lasso/utils.h" +#include "./tests.h" +#include "../lasso/xml/saml-2.0/saml2_xsd.h" START_TEST(test01_metadata_load_der_certificate_from_x509_cert) { @@ -83,6 +86,29 @@ START_TEST(test06_metadata_load_public_key_from_rsa_keyvalue) } END_TEST +START_TEST(test07_metadata_role_descriptors) +{ + LassoProvider *provider = (LassoProvider*)lasso_provider_new(LASSO_PROVIDER_ROLE_IDP, TESTSDATADIR "/idp6-saml2/metadata.xml", + NULL, NULL); + GList *l, *q; + int i = 0; + + check_not_null(provider); + for (i = LASSO_PROVIDER_ROLE_ANY+1; i < LASSO_PROVIDER_ROLE_LAST; i++) { + l = lasso_provider_get_metadata_keys_for_role(provider, i); + lasso_foreach(q, l) { + printf("%i %s\n", i, (char*)q->data); + } + } + l = lasso_provider_get_metadata_list_for_role(provider, LASSO_PROVIDER_ROLE_IDP, + LASSO_SAML2_METADATA_ATTRIBUTE_WANT_AUTHN_REQUEST_SIGNED); + check_not_null(l); + check_null(l->next); + check_str_equals(l->data, "true"); + lasso_release_gobject(provider); +} +END_TEST + Suite* metadata_suite() { @@ -99,12 +125,16 @@ metadata_suite() tcase_create("Load DER public key from "); TCase *tc_metadata_load_public_key_from_rsa_keyvalue = tcase_create("Load RSAKeyValue public key"); + TCase *tc_metadata_role_descriptors = + tcase_create("Lookup different role descriptors datas"); + suite_add_tcase(s, tc_metadata_load_der_certificate_from_x509_cert); suite_add_tcase(s, tc_metadata_load_pem_certificate_from_x509_cert); suite_add_tcase(s, tc_metadata_load_der_public_key_from_keyvalue); suite_add_tcase(s, tc_metadata_load_pem_public_key_from_keyvalue); suite_add_tcase(s, tc_metadata_load_public_key_from_x509_cert); suite_add_tcase(s, tc_metadata_load_public_key_from_rsa_keyvalue); + suite_add_tcase(s, tc_metadata_role_descriptors); tcase_add_test(tc_metadata_load_der_certificate_from_x509_cert, test01_metadata_load_der_certificate_from_x509_cert); tcase_add_test(tc_metadata_load_pem_certificate_from_x509_cert, @@ -117,5 +147,7 @@ metadata_suite() test05_metadata_load_public_key_from_x509_cert); tcase_add_test(tc_metadata_load_public_key_from_rsa_keyvalue, test06_metadata_load_public_key_from_rsa_keyvalue); + tcase_add_test(tc_metadata_role_descriptors, + test07_metadata_role_descriptors); return s; } diff --git a/tests/tests.c b/tests/tests.c index 806a6097..5217bbb8 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -34,6 +34,7 @@ extern Suite* login_suite(); extern Suite* login_saml2_suite(); extern Suite* random_suite(); extern Suite* metadata_suite(); +extern Suite* assertion_query_suite(); #ifdef LASSO_WSF_ENABLED extern Suite* idwsf2_suite(); #endif @@ -46,6 +47,7 @@ SuiteFunction suites[] = { login_saml2_suite, random_suite, metadata_suite, + assertion_query_suite, #ifdef LASSO_WSF_ENABLED idwsf2_suite, #endif