diff --git a/lasso/id-ff/profile.c b/lasso/id-ff/profile.c index 9518b9a5..f2614681 100644 --- a/lasso/id-ff/profile.c +++ b/lasso/id-ff/profile.c @@ -54,6 +54,8 @@ #endif #include "../lasso_config.h" +#include + /*****************************************************************************/ /* public functions */ /*****************************************************************************/ @@ -734,6 +736,119 @@ lasso_profile_get_signature_status(LassoProfile *profile) return profile->signature_status; } +static xmlChar * +extract_issuer(xmlTextReader *reader) +{ + const xmlChar *name; + const xmlChar *ns_uri; + xmlNode *node; + + name = xmlTextReaderConstLocalName(reader); + ns_uri = xmlTextReaderConstNamespaceUri(reader); + + if (strcmp((const char*)name, "Issuer")) + return NULL; + if (strcmp((const char*)ns_uri, LASSO_SAML2_ASSERTION_HREF)) + return NULL; + node = xmlTextReaderExpand(reader); + return xmlNodeGetContent(node); +} + + +/** + * lasso_profile_get_issuer: + * @message: the HTTP query, POST content or SOAP message + * + * Extract the issuer of a message. + * + * Return value:(transfer full): Returns the issuer of the given message. + */ +char* +lasso_profile_get_issuer(const char *message) +{ + xmlTextReader *reader; + char *result = NULL; + int count = 0, ret; + xmlChar *xml_result = NULL; + xmlChar *to_free = NULL; + + + reader = lasso_xmltextreader_from_message(message, &to_free); + if (! reader) + goto cleanup; + ret = xmlTextReaderRead(reader); + while (ret == 1) { + int node_type = xmlTextReaderNodeType(reader); + if (node_type == 1) { + count += 1; + xml_result = extract_issuer(reader); + if (xml_result) + break; + } + if (count == 3) { + break; + } + ret = xmlTextReaderRead(reader); + } + if (! xml_result) + goto cleanup; + result = g_strdup((char *)xml_result); +cleanup: + if (xml_result) + lasso_release_xml_string(xml_result); + if (reader) + xmlFreeTextReader(reader); + if (to_free) + lasso_release_xml_string(to_free); + return result; +} + +/** + * lasso_profile_get_request_id: + * @message: the HTTP query, POST content or SOAP message + * + * Extract the issuer of a message. + * + * Return value:(transfer full): Returns the issuer of the given message. + */ +char* +lasso_profile_get_in_response_to(const char *message) +{ + xmlTextReader *reader; + char *result = NULL; + int ret; + int node_type = 0; + xmlChar *xml_result = NULL; + xmlChar *to_free = NULL; + + + reader = lasso_xmltextreader_from_message(message, &to_free); + if (! reader) + goto cleanup; + ret = xmlTextReaderRead(reader); + while (ret == 1) { + node_type = xmlTextReaderNodeType(reader); + if (node_type == 1) { + break; + } + ret = xmlTextReaderRead(reader); + } + if (node_type != 1) + goto cleanup; + xml_result = xmlTextReaderGetAttribute(reader, BAD_CAST "InResponseTo"); + if (! xml_result) + goto cleanup; + result = g_strdup((char*)xml_result); +cleanup: + if (reader) + xmlFreeTextReader(reader); + if (xml_result) + lasso_release_xml_string(xml_result); + if (to_free) + lasso_release_xml_string(to_free); + return result; +} + /*****************************************************************************/ /* overridden parent class methods */ /*****************************************************************************/ diff --git a/lasso/id-ff/profile.h b/lasso/id-ff/profile.h index 7fb3e729..14a37fb8 100644 --- a/lasso/id-ff/profile.h +++ b/lasso/id-ff/profile.h @@ -211,6 +211,8 @@ LASSO_EXPORT LassoProfileSignatureVerifyHint lasso_profile_get_signature_verify_ LASSO_EXPORT LassoProviderRole lasso_profile_sso_role_with(LassoProfile *profile, const char *remote_provider_id); LASSO_EXPORT lasso_error_t lasso_profile_get_signature_status(LassoProfile *profile); +LASSO_EXPORT char* lasso_profile_get_issuer(const char *message); +LASSO_EXPORT char* lasso_profile_get_in_response_to(const char *message); #ifdef __cplusplus } diff --git a/lasso/xml/private.h b/lasso/xml/private.h index 1d3042ae..6f7d911d 100644 --- a/lasso/xml/private.h +++ b/lasso/xml/private.h @@ -335,6 +335,8 @@ void lasso_xmlnode_add_saml2_signature_template(xmlNode *node, LassoSignatureCon gchar* lasso_xmlnode_build_deflated_query(xmlNode *xmlnode); +xmlTextReader *lasso_xmltextreader_from_message(const char *message, xmlChar **to_free); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/lasso/xml/tools.c b/lasso/xml/tools.c index 337b2c6a..bb434a56 100644 --- a/lasso/xml/tools.c +++ b/lasso/xml/tools.c @@ -1310,13 +1310,51 @@ lasso_get_query_string_param_value(const char *qs, const char *param_key, const } } +unsigned char* +lasso_inflate(unsigned char *input, size_t len) +{ + z_stream zstr; + unsigned char *output; + int z_err; + + zstr.zalloc = NULL; + zstr.zfree = NULL; + zstr.opaque = NULL; + + output = g_malloc(len*10); + zstr.avail_in = len; + zstr.next_in = (unsigned char*)input; + zstr.total_in = 0; + zstr.avail_out = len*10; + zstr.total_out = 0; + zstr.next_out = output; + + z_err = inflateInit2(&zstr, -MAX_WBITS); + if (z_err != Z_OK) { + message(G_LOG_LEVEL_CRITICAL, "Failed to inflateInit"); + lasso_release_string(output); + return FALSE; + } + + z_err = inflate(&zstr, Z_FINISH); + if (z_err != Z_STREAM_END) { + message(G_LOG_LEVEL_CRITICAL, "Failed to inflate"); + inflateEnd(&zstr); + lasso_release_string(output); + return NULL; + } + output[zstr.total_out] = 0; + inflateEnd(&zstr); + + return output; +} + + gboolean lasso_node_init_from_deflated_query_part(LassoNode *node, char *deflate_string) { int len; xmlChar *b64_zre, *zre, *re; - z_stream zstr; - int z_err; xmlDoc *doc; xmlNode *root; @@ -1326,40 +1364,15 @@ lasso_node_init_from_deflated_query_part(LassoNode *node, char *deflate_string) len = xmlSecBase64Decode(b64_zre, zre, len*4); xmlFree(b64_zre); - zstr.zalloc = NULL; - zstr.zfree = NULL; - zstr.opaque = NULL; - - zstr.avail_in = len; - re = xmlMalloc(len*10); - zstr.next_in = (xmlChar*)zre; - zstr.total_in = 0; - zstr.avail_out = len*10; - zstr.total_out = 0; - zstr.next_out = re; - - z_err = inflateInit2(&zstr, -MAX_WBITS); - if (z_err != Z_OK) { - message(G_LOG_LEVEL_CRITICAL, "Failed to inflateInit"); - xmlFree(zre); - xmlFree(re); - return FALSE; - } - - z_err = inflate(&zstr, Z_FINISH); - if (z_err != Z_STREAM_END) { - message(G_LOG_LEVEL_CRITICAL, "Failed to inflate"); - inflateEnd(&zstr); - xmlFree(zre); - xmlFree(re); - return FALSE; - } - re[zstr.total_out] = 0; - inflateEnd(&zstr); + re = lasso_inflate(zre, len); xmlFree(zre); + if (! re) + return FALSE; + doc = lasso_xml_parse_memory((char*)re, strlen((char*)re)); - xmlFree(re); + lasso_release_string(re); + root = xmlDocGetRootElement(doc); lasso_node_init_from_xml(node, root); lasso_release_doc(doc); @@ -2826,3 +2839,75 @@ lasso_xmlnode_add_saml2_signature_template(xmlNode *node, LassoSignatureContext g_assert_not_reached(); } } + +static char* +get_saml_message(char **query_fields) { + int i; + char *field, *t; + + for (i=0; (field=query_fields[i]); i++) { + t = strchr(field, '='); + if (t == NULL) + continue; + *t = 0; + if (strcmp(field, LASSO_SAML2_FIELD_ENCODING) == 0) { + return t+1; + } + if (strcmp(field, LASSO_SAML2_FIELD_REQUEST) == 0 || strcmp(field, LASSO_SAML2_FIELD_RESPONSE) == 0) { + return t+1; + } + } + return NULL; +} + +/** + * lasso_xmltextreader_from_message: + * @message: the HTTP query, POST content or SOAP message + * + * Try to parse the passed message and create an xmlTextReader from it. + */ +xmlTextReader * +lasso_xmltextreader_from_message(const char *message, xmlChar **to_free) { + size_t len = strlen(message); + char *needle; + char **query_fields = NULL; + char *decoded_message = NULL; + xmlTextReader *reader = NULL; + + if (message[0] != '<') { + needle = strchr(message, '='); + if (needle) { + ptrdiff_t needle_pos = (needle-message); + if (len - needle_pos > 2) { // query + query_fields = urlencoded_to_strings(needle+1); + message = get_saml_message(query_fields); + if (! message) + goto cleanup; + len = strlen(message); + } + } + if (is_base64(message)) { + int rc; + decoded_message = g_malloc(len); + rc = xmlSecBase64Decode((xmlChar*)message, (xmlChar*)decoded_message, len); + if (rc < 0) + goto cleanup; + len = rc; + message = (char*)lasso_inflate((unsigned char*) decoded_message, rc); + lasso_release_string(decoded_message); + if (! message) + goto cleanup; + } + } + + if (message[0] == '<') // XML case + reader = xmlReaderForMemory(message, len, "", NULL, XML_PARSE_NOENT | + XML_PARSE_NONET); + +cleanup: + if (query_fields) + lasso_release(query_fields); + if (decoded_message && to_free) + *to_free = BAD_CAST decoded_message; + return reader; +} diff --git a/lasso/xml/xml.h b/lasso/xml/xml.h index 30f80141..c4e42795 100644 --- a/lasso/xml/xml.h +++ b/lasso/xml/xml.h @@ -35,6 +35,7 @@ extern "C" { #include #include +#include #include "../export.h" #include "../errors.h" diff --git a/tests/basic_tests.c b/tests/basic_tests.c index 5ccdb413..5ac07de3 100644 --- a/tests/basic_tests.c +++ b/tests/basic_tests.c @@ -2082,6 +2082,18 @@ START_TEST(test15_ds_key_info) } END_TEST +/* test load federation */ +START_TEST(test16_test_get_issuer) +{ + char *content = NULL; + size_t len = 0; + + g_file_get_contents(TESTSDATADIR "/response-1", &content, &len, NULL); + check_str_equals(lasso_profile_get_issuer(content), "gefssstg"); + check_str_equals(lasso_profile_get_in_response_to(content), "xx"); +} +END_TEST + Suite* basic_suite() { @@ -2101,6 +2113,7 @@ basic_suite() TCase *tc_load_metadata = tcase_create("Test loading a federation metadata file"); TCase *tc_key = tcase_create("Test loading and manipulating LassoKey objects"); TCase *tc_key_info = tcase_create("Test creating and dumping ds:KeyInfo nodes"); + TCase *tc_get_issuer = tcase_create("Test get_issuer and get_request_id"); suite_add_tcase(s, tc_server_load_dump_empty_string); suite_add_tcase(s, tc_server_load_dump_random_string); @@ -2117,6 +2130,7 @@ basic_suite() suite_add_tcase(s, tc_load_metadata); suite_add_tcase(s, tc_key); suite_add_tcase(s, tc_key_info); + suite_add_tcase(s, tc_get_issuer); tcase_add_test(tc_server_load_dump_empty_string, test01_server_load_dump_empty_string); tcase_add_test(tc_server_load_dump_random_string, test02_server_load_dump_random_string); @@ -2133,6 +2147,7 @@ basic_suite() tcase_add_test(tc_load_metadata, test13_test_lasso_server_load_metadata); tcase_add_test(tc_key, test14_lasso_key); tcase_add_test(tc_key_info, test15_ds_key_info); + tcase_add_test(tc_get_issuer, test16_test_get_issuer); tcase_set_timeout(tc_load_metadata, 10); return s; } diff --git a/tests/data/response-1 b/tests/data/response-1 index c4d24214..5732e092 100644 --- a/tests/data/response-1 +++ b/tests/data/response-1 @@ -1,4 +1,4 @@ -gefssstggefssstg +gefssstggefssstg @@ -54,4 +54,4 @@ oKYPjJxpj5sK9iB5yW8= -999999500Webrooturn:oasis:names:tc:SAML:2.0:ac:classes:unspecifiedUser999999500Test999999500@apctest.ge.com \ No newline at end of file +999999500Webrooturn:oasis:names:tc:SAML:2.0:ac:classes:unspecifiedUser999999500Test999999500@apctest.ge.com