diff options
author | Benjamin Dauvergne <bdauvergne@entrouvert.com> | 2010-08-25 18:57:25 +0200 |
---|---|---|
committer | Benjamin Dauvergne <bdauvergne@entrouvert.com> | 2010-08-25 18:57:25 +0200 |
commit | 908d4288a0a7df793c104118dc921ba4b57a2bb8 (patch) | |
tree | 55658e5112a7a72b86a756367f8e3df5649c5778 | |
parent | 5c85507ffd906e9a3f15b1206f9cc0e56bc8a207 (diff) | |
download | lasso-908d4288a0a7df793c104118dc921ba4b57a2bb8.tar.gz lasso-908d4288a0a7df793c104118dc921ba4b57a2bb8.tar.xz lasso-908d4288a0a7df793c104118dc921ba4b57a2bb8.zip |
[SAMLv2] rebuild specialized LassoProvider methods upon new endpoints storage
The new way of storing endpoints allows to keep ordering between
endpoints with respect to the order of the index and isDefault field for
indexed endpoint type, and to the XML node orders for other endpoints.
It also simplifies the code.
-rw-r--r-- | lasso/saml-2.0/provider.c | 517 |
1 files changed, 239 insertions, 278 deletions
diff --git a/lasso/saml-2.0/provider.c b/lasso/saml-2.0/provider.c index 65bd579b..b532259e 100644 --- a/lasso/saml-2.0/provider.c +++ b/lasso/saml-2.0/provider.c @@ -53,8 +53,6 @@ const char *profile_names[LASSO_MD_PROTOCOL_TYPE_LAST] = { "AttributeService" /*AttributeAuthorityDescriptor*/ }; -static void add_assertion_consumer_url_to_list(gchar *key, G_GNUC_UNUSED gpointer value, GList **list); - static const char* binding_uri_to_identifier(const char *uri) { @@ -74,23 +72,21 @@ binding_uri_to_identifier(const char *uri) return NULL; } -static const char* -identifier_to_binding_uri(const char *identifier) +static LassoHttpMethod +binding_uri_to_http_method(const char *uri) { - if (strcmp(identifier, "SOAP") == 0) { - return LASSO_SAML2_METADATA_BINDING_SOAP; - } else if (strcmp(identifier, "HTTP-Redirect") == 0) { - return LASSO_SAML2_METADATA_BINDING_REDIRECT; - } else if (strcmp(identifier, "HTTP-POST") == 0) { - return LASSO_SAML2_METADATA_BINDING_POST; - } else if (strcmp(identifier, "HTTP-Artifact") == 0) { - return LASSO_SAML2_METADATA_BINDING_ARTIFACT; - } else if (strcmp(identifier, "PAOS") == 0) { - return LASSO_SAML2_METADATA_BINDING_PAOS; - } else if (strcmp(identifier, "URI") == 0) { - return LASSO_SAML2_METADATA_BINDING_URI; + if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_SOAP) == 0) { + return LASSO_HTTP_METHOD_SOAP; + } else if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_REDIRECT) == 0) { + return LASSO_HTTP_METHOD_REDIRECT; + } else if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_POST) == 0) { + return LASSO_HTTP_METHOD_NONE; + } else if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_ARTIFACT) == 0) { + return LASSO_HTTP_METHOD_ARTIFACT_GET; + } else if (strcmp(uri, LASSO_SAML2_METADATA_BINDING_PAOS) == 0) { + return LASSO_HTTP_METHOD_PAOS; } - return NULL; + return LASSO_HTTP_METHOD_NONE; } static gboolean @@ -127,6 +123,87 @@ xsdIsFalse(xmlChar *value) return FALSE; } +static gboolean +xsdUnsignedShortParse(xmlChar *value, int *out) { + int l = strtol((char*)value, NULL, 10); + + if (((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || + errno == EINVAL || l < 0 || l >= 65535) { + return FALSE; + } + *out = l; + return TRUE; +} + +static void +load_endpoint_type2(xmlNode *xmlnode, LassoProvider *provider, LassoProviderRole role, int *counter) +{ + xmlChar *binding = getSaml2MdProp(xmlnode, LASSO_SAML2_METADATA_ATTRIBUTE_BINDING); + xmlChar *location = getSaml2MdProp(xmlnode, LASSO_SAML2_METADATA_ATTRIBUTE_LOCATION); + xmlChar *response_location = getSaml2MdProp(xmlnode, LASSO_SAML2_METADATA_ATTRIBUTE_RESPONSE_LOCATION); + xmlChar *index = getSaml2MdProp(xmlnode, LASSO_SAML2_METADATA_ATTRIBUTE_INDEX); + xmlChar *isDefault = getSaml2MdProp(xmlnode, LASSO_SAML2_METADATA_ATTRIBUTE_ISDEFAULT); + gboolean indexed_endpoint = FALSE; + int idx = *counter++; + gboolean is_default = FALSE; + EndpointType *endpoint_type; + + if (! binding || ! location) { + warning("Invalid endpoint node %s", (char*) xmlnode->name); + goto cleanup; + } + indexed_endpoint = checkSaml2MdNode(xmlnode, LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE); + if (indexed_endpoint) { + if (! xsdUnsignedShortParse(index, &idx)) { + warning("Invalid AssertionConsumerService, no index set"); + goto cleanup; + } + is_default = xsdIsTrue(isDefault); + } + endpoint_type = g_new0(EndpointType, 1); + endpoint_type->kind = g_strdup((char*)xmlnode->name); + endpoint_type->binding = g_strdup((char*)binding); + endpoint_type->url = g_strdup((char*)location); + endpoint_type->return_url = g_strdup((char*)response_location); + endpoint_type->role = role; + endpoint_type->index = idx; + endpoint_type->is_default = is_default; + lasso_list_add(provider->private_data->endpoints, (void*)endpoint_type); + +cleanup: + lasso_release_xml_string(binding); + lasso_release_xml_string(location); + lasso_release_xml_string(response_location); + lasso_release_xml_string(isDefault); + lasso_release_xml_string(index); +} + +static gint +compare_endpoint_type(const EndpointType *a, const EndpointType *b) { + int c; + + if (a->role < b->role) + return -1; + if (a->role > b->role) + return +1; + c = g_strcmp0(a->kind, b->kind); + if (c != 0) + return c; + c = g_strcmp0(a->binding, b->binding); + if (c != 0) + return c; + if (a->is_default && ! b->is_default) + return -1; + if (! a->is_default && b->is_default) + return +1; + if (a->index < b->index) + return -1; + if (a->index > b->index) + return +1; + return 0; +} + + static void load_endpoint_type(xmlNode *xmlnode, LassoProvider *provider, LassoProviderRole role) { @@ -141,7 +218,7 @@ load_endpoint_type(xmlNode *xmlnode, LassoProvider *provider, LassoProviderRole binding_s = binding_uri_to_identifier((char*)binding); if (! binding_s) { - message(G_LOG_LEVEL_CRITICAL, "XXX: unknown binding: %s", binding); + critical("XXX: unknown binding: %s", binding); goto cleanup; } @@ -259,6 +336,7 @@ load_descriptor(xmlNode *xmlnode, LassoProvider *provider, LassoProviderRole rol xmlChar *value; LassoProviderPrivate *pdata = provider->private_data; char *token, *saveptr; + int counter = 0; /* check protocol support enumeration */ value = getSaml2MdProp(xmlnode, @@ -290,6 +368,7 @@ load_descriptor(xmlNode *xmlnode, LassoProvider *provider, LassoProviderRole rol attribute); } else if (hasSaml2MdProp(t, LASSO_SAML2_METADATA_ATTRIBUTE_BINDING)) { load_endpoint_type(t, provider, role); + load_endpoint_type2(t, provider, role, &counter); } else { value = xmlNodeGetContent(t); _lasso_provider_add_metadata_value_for_role(provider, role, (char*)t->name, @@ -298,6 +377,8 @@ load_descriptor(xmlNode *xmlnode, LassoProvider *provider, LassoProviderRole rol } t = xmlSecGetNextElementNode(t->next); } + provider->private_data->endpoints = g_list_sort(provider->private_data->endpoints, + (GCompareFunc) compare_endpoint_type); for (i = 0; descriptor_attrs[i]; i++) { value = getSaml2MdProp(xmlnode, descriptor_attrs[i]); if (value == NULL) { @@ -403,226 +484,141 @@ lasso_saml20_provider_load_metadata(LassoProvider *provider, xmlNode *root_node) } LassoHttpMethod -lasso_saml20_provider_get_first_http_method(LassoProvider *provider, +lasso_saml20_provider_get_first_http_method(G_GNUC_UNUSED LassoProvider *provider, LassoProvider *remote_provider, LassoMdProtocolType protocol_type) { - LassoHttpMethod method = LASSO_HTTP_METHOD_NONE; - LassoProviderRole our_role = LASSO_PROVIDER_ROLE_SP; - int i; - const char *possible_bindings[] = { - "HTTP-POST", - "HTTP-Redirect", - "HTTP-Artifact", - "SOAP", - "PAOS", - NULL - }; - LassoHttpMethod method_bindings[] = { - LASSO_HTTP_METHOD_POST, - LASSO_HTTP_METHOD_REDIRECT, - LASSO_HTTP_METHOD_ARTIFACT_GET, - LASSO_HTTP_METHOD_SOAP, - LASSO_HTTP_METHOD_PAOS - }; + GList *t = NULL; + const char *kind = NULL; + LassoHttpMethod result = LASSO_HTTP_METHOD_NONE; + + if (protocol_type < LASSO_MD_PROTOCOL_TYPE_LAST) { + kind = profile_names[protocol_type]; + } + if (! kind) { + warning("Could not find a first http method for protocol type %u", protocol_type); + return LASSO_HTTP_METHOD_NONE; + } - switch (remote_provider->role) { - case LASSO_PROVIDER_ROLE_IDP: - our_role = LASSO_PROVIDER_ROLE_SP; - break; - case LASSO_PROVIDER_ROLE_SP: - our_role = LASSO_PROVIDER_ROLE_IDP; - break; - default: - return LASSO_HTTP_METHOD_NONE; - } - 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]); - l1 = lasso_provider_get_metadata_list_for_role(provider, our_role, s); - l2 = lasso_provider_get_metadata_list(remote_provider, s); - if (l1 && l2) { - method = method_bindings[i]; + lasso_foreach(t, remote_provider->private_data->endpoints) { + EndpointType *endpoint_type = (EndpointType*)t->data; + if (endpoint_type && g_strcmp0(endpoint_type->kind, kind) == 0) { + result = binding_uri_to_http_method(endpoint_type->binding); + if (result) break; } } - return method; + return result; } gboolean -lasso_saml20_provider_check_assertion_consumer_service_url(LassoProvider *provider, const gchar *url, const gchar *binding) +lasso_saml20_provider_accept_http_method(G_GNUC_UNUSED LassoProvider *provider, LassoProvider *remote_provider, + LassoMdProtocolType protocol_type, LassoHttpMethod http_method, + G_GNUC_UNUSED gboolean initiate_profile) { - GHashTable *descriptor; - GList *l = NULL, *r = NULL, *candidate = NULL; - char *name; - const char *binding_s = NULL; - int lname; - - descriptor = provider->private_data->Descriptors; - if (descriptor == NULL || url == NULL || binding == NULL) - return FALSE; - - binding_s = binding_uri_to_identifier(binding); - if (binding_s == NULL) { - return FALSE; + GList *t = NULL; + const char *kind = NULL; + + if (protocol_type < LASSO_MD_PROTOCOL_TYPE_LAST) { + kind = profile_names[protocol_type]; + } + if (! kind) { + warning("Could not find a first http method for protocol type %u", protocol_type); + return LASSO_HTTP_METHOD_NONE; } - g_hash_table_foreach(descriptor, - (GHFunc)add_assertion_consumer_url_to_list, - &r); - - 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 = lasso_provider_get_metadata_list_for_role(provider, LASSO_PROVIDER_ROLE_SP, b); - if (candidate && candidate->data && strcmp(candidate->data, url) == 0) - break; - else - candidate = NULL; + lasso_foreach(t, remote_provider->private_data->endpoints) { + EndpointType *endpoint_type = (EndpointType*)t->data; + if (endpoint_type && endpoint_type->role == remote_provider->role && + g_strcmp0(endpoint_type->kind, kind) == 0) { + if (binding_uri_to_http_method(endpoint_type->binding) == http_method) { + return TRUE; + } } } - lasso_release(name); - lasso_release_list(r); - if (candidate) - return TRUE; - else - return FALSE; + return FALSE; } -gchar* -lasso_saml20_provider_get_assertion_consumer_service_url(LassoProvider *provider, - int service_id) +gboolean +lasso_saml20_provider_check_assertion_consumer_service_url(LassoProvider *provider, const gchar *url, const gchar *binding) { - GList *l = NULL; - char *sid; - char *name; - const char *possible_bindings[] = { - "HTTP-Artifact", - "HTTP-POST", - NULL - }; - int i; - - if (service_id == -1) { - sid = g_strdup(provider->private_data->default_assertion_consumer); - } else { - sid = g_strdup_printf("%d", service_id); - } - - for (i=0; possible_bindings[i]; i++) { - name = g_strdup_printf(LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE - " %s %s", - possible_bindings[i], sid); - l = lasso_provider_get_metadata_list_for_role(provider, - LASSO_PROVIDER_ROLE_SP, - name); - lasso_release_string(name); - if (l != NULL) - break; + GList *t = NULL; + + lasso_foreach (provider->private_data->endpoints, t) { + EndpointType *endpoint_type = (EndpointType*) t->data; + if (endpoint_type && endpoint_type->role == LASSO_PROVIDER_ROLE_SP + && g_strcmp0(endpoint_type->url, url) == 0 + && g_strcmp0(endpoint_type->binding, binding) == 0) + { + return TRUE; + } } - lasso_release_string(sid); - if (l) - return g_strdup(l->data); - return NULL; + return FALSE; } -#define ACS_KEY "sp " LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE +static const char *supported_assertion_consumer_bindings[] = { LASSO_SAML2_METADATA_BINDING_POST, + LASSO_SAML2_METADATA_BINDING_ARTIFACT, NULL }; -static void -add_assertion_consumer_url_to_list(gchar *key, G_GNUC_UNUSED gpointer value, GList **list) -{ - if (strncmp(key, ACS_KEY, sizeof(ACS_KEY)-1) == 0) { - lasso_list_add_new_string(*list, key); +static gboolean match_any(const char *key, const char *array[]) { + const char **t = array; + + while (*t) { + if (g_strcmp0(key, *t) == 0) { + return TRUE; + } + t++; } + return FALSE; } -struct HelperBindingByUrl { - const char *binding; - const char *url; -}; - -void -helper_binding_by_url(char *key, GList *value, struct HelperBindingByUrl *data) +static EndpointType * +lasso_saml20_provider_get_assertion_consumer_service(LassoProvider *provider, int service_id) { - if (strncmp(key, ACS_KEY, sizeof(ACS_KEY)-1) != 0) { - return; - } - - if (data->binding == NULL && g_list_find_custom(value, data->url, (GCompareFunc)g_strcmp0) != NULL) { - char *end; - // URL was found for the first time - key += sizeof(ACS_KEY); - end = strchr(key, ' '); - if (end) { - key = g_strndup(key, (ptrdiff_t)(end-key)); - data->binding = identifier_to_binding_uri(key); - lasso_release(key); - } else { - data->binding = identifier_to_binding_uri(key); + const char *kind = LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE; + GList *t = NULL; + EndpointType *result = NULL; + + if (service_id != -1) { + lasso_foreach(t, provider->private_data->endpoints) { + EndpointType *endpoint_type = (EndpointType*) t->data; + if (! endpoint_type) + continue; + if (endpoint_type->role == LASSO_PROVIDER_ROLE_SP && + g_strcmp0(endpoint_type->kind, kind) == 0 && + endpoint_type->index == service_id) + { + result = endpoint_type; + break; + } + } + } else { /* lookup a default supported endpoint type */ + lasso_foreach(t, provider->private_data->endpoints) { + EndpointType *endpoint_type = (EndpointType*) t->data; + if (! endpoint_type) + continue; + if (endpoint_type->role == LASSO_PROVIDER_ROLE_SP && + g_strcmp0(endpoint_type->kind, kind) == 0 && + match_any(endpoint_type->binding, + supported_assertion_consumer_bindings)) + { + result = endpoint_type; + break; + } } } - + return result; } -const gchar* -lasso_saml20_provider_get_assertion_consumer_service_binding_by_url(LassoProvider *provider, const char *url) -{ - struct HelperBindingByUrl _helper_binding_by_url = { .binding = NULL, .url = url }; - - g_hash_table_foreach(provider->private_data->Descriptors, (GHFunc)helper_binding_by_url, - &_helper_binding_by_url); - - return _helper_binding_by_url.binding; -} gchar* -lasso_saml20_provider_get_assertion_consumer_service_url_by_binding(LassoProvider *provider, - const gchar *binding) +lasso_saml20_provider_get_assertion_consumer_service_url(LassoProvider *provider, + int service_id) { - GHashTable *descriptor; - GList *l = NULL, *r = NULL; - char *name; - const char *binding_s = NULL; - int lname; - - descriptor = provider->private_data->Descriptors; - if (descriptor == NULL) - return NULL; - - 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); - - 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; - if (strncmp(name, b, lname) == 0) { - l = g_hash_table_lookup(descriptor, b); - break; - } - } - lasso_release_string(name); - lasso_release_list(r); - - if (l) { - return g_strdup(l->data); + EndpointType *endpoint_type = lasso_saml20_provider_get_assertion_consumer_service(provider, service_id); + if (endpoint_type) + { + return g_strdup(endpoint_type->url); } - return NULL; } @@ -630,88 +626,53 @@ gchar* lasso_saml20_provider_get_assertion_consumer_service_binding(LassoProvider *provider, int service_id) { - GHashTable *descriptor; - GList *l = NULL; - char *sid; - char *name; - char *binding = NULL; - const char *possible_bindings[] = { - "HTTP-POST", - "HTTP-Redirect", - "HTTP-Artifact", - "SOAP", - NULL - }; - int i; - - if (service_id == -1) { - sid = g_strdup(provider->private_data->default_assertion_consumer); - } 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(LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE - " %s %s", - possible_bindings[i], sid); - 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; - } + EndpointType *endpoint_type = lasso_saml20_provider_get_assertion_consumer_service(provider, service_id); + if (endpoint_type) + { + return g_strdup(binding_uri_to_identifier(endpoint_type->binding)); } - lasso_release_string(sid); - return binding; + return NULL; } -gboolean -lasso_saml20_provider_accept_http_method(LassoProvider *provider, LassoProvider *remote_provider, - LassoMdProtocolType protocol_type, LassoHttpMethod http_method, - gboolean initiate_profile) +const gchar* +lasso_saml20_provider_get_assertion_consumer_service_binding_by_url(LassoProvider *provider, const char *url) { - char *protocol_profile; - static const char *http_methods[] = { - NULL, - NULL, - NULL, - NULL, - "HTTP-POST", - "HTTP-Redirect", - "SOAP", - "HTTP-Artifact", - "HTTP-Artifact", - NULL - }; - gboolean rc = FALSE; - LassoProviderRole initiating_role; + const char *kind = LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE; + GList *t = NULL; - initiating_role = remote_provider->role; - if (remote_provider->role == LASSO_PROVIDER_ROLE_SP) { - provider->role = LASSO_PROVIDER_ROLE_IDP; - } - if (remote_provider->role == LASSO_PROVIDER_ROLE_IDP) { - provider->role = LASSO_PROVIDER_ROLE_SP; - } - if (initiate_profile) - initiating_role = provider->role; - - /* exclude bad input */ - if (http_method > (int)G_N_ELEMENTS(http_methods) || http_method < 0 || http_methods[http_method+1] == NULL) { - return FALSE; + lasso_foreach(t, provider->private_data->endpoints) { + EndpointType *endpoint_type = (EndpointType*) t->data; + if (! endpoint_type) + continue; + if (endpoint_type->role == LASSO_PROVIDER_ROLE_SP && + g_strcmp0(endpoint_type->kind, kind) == 0 && + g_strcmp0(endpoint_type->url, url) == 0) + { + return endpoint_type->binding; + } } + return NULL; +} - protocol_profile = g_strdup_printf("%s %s", profile_names[protocol_type], - http_methods[http_method+1]); +gchar* +lasso_saml20_provider_get_assertion_consumer_service_url_by_binding(LassoProvider *provider, + const gchar *binding) +{ + const char *kind = LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE; + GList *t = NULL; - /* just check if remote provider can receive the request, remote provider will have to check - * how to return the response itself */ - rc = (lasso_provider_get_metadata_list(remote_provider, protocol_profile) != NULL); - lasso_release_string(protocol_profile); - return rc; + lasso_foreach(t, provider->private_data->endpoints) { + EndpointType *endpoint_type = (EndpointType*) t->data; + if (! endpoint_type) + continue; + if (endpoint_type->role == LASSO_PROVIDER_ROLE_SP && + g_strcmp0(endpoint_type->kind, kind) == 0 && + g_strcmp0(endpoint_type->binding, binding) == 0) + { + return endpoint_type->url; + } + } + return NULL; } /** |