profile: add two new class methods, lasso_profile_get_issuer and lasso_profile_get_in_response_to (#4378)

The goal of those two methods is to allow IdP and SP to load metadata
dynamically without processing completely the incoming. Currently it's
impossible as message parsing and signature checking is done in the same
function.
This commit is contained in:
Benjamin Dauvergne 2014-03-31 10:54:27 +02:00
parent 67d0a0349d
commit 65bc705235
7 changed files with 255 additions and 35 deletions

View File

@ -54,6 +54,8 @@
#endif
#include "../lasso_config.h"
#include <stdio.h>
/*****************************************************************************/
/* 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 */
/*****************************************************************************/

View File

@ -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
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -35,6 +35,7 @@ extern "C" {
#include <libxml/uri.h>
#include <libxml/tree.h>
#include <libxml/xmlreader.h>
#include "../export.h"
#include "../errors.h"

View File

@ -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;
}

View File

@ -1,4 +1,4 @@
<samlp:Response IssueInstant="2009-04-20T14:31:31.917Z" ID="dzzfpt4zXBljkK2pummN8Ro2zo-" Version="2.0" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">gefssstg</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion Version="2.0" IssueInstant="2009-04-20T14:31:31.972Z" ID="Zm9SHCgSkcP34HQTbn5ZOp6dQeB" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"><saml:Issuer>gefssstg</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<samlp:Response InResponseTo="xx" IssueInstant="2009-04-20T14:31:31.917Z" ID="dzzfpt4zXBljkK2pummN8Ro2zo-" Version="2.0" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">gefssstg</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion Version="2.0" IssueInstant="2009-04-20T14:31:31.972Z" ID="Zm9SHCgSkcP34HQTbn5ZOp6dQeB" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"><saml:Issuer>gefssstg</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
@ -54,4 +54,4 @@ oKYPjJxpj5sK9iB5yW8=
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
</ds:Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">999999500</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2009-04-20T14:36:31.973Z" Recipient="https://wg.us.wrproxy.com/idpsso.php"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotOnOrAfter="2009-04-20T14:36:31.972Z" NotBefore="2009-04-20T14:26:31.972Z"><saml:AudienceRestriction><saml:Audience>Webroot</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2009-04-20T14:31:31.971Z" SessionIndex="Zm9SHCgSkcP34HQTbn5ZOp6dQeB"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="lastname"><saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">User</saml:AttributeValue></saml:Attribute><saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="uid"><saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">999999500</saml:AttributeValue></saml:Attribute><saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="firstname"><saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Test</saml:AttributeValue></saml:Attribute><saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="mail"><saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">999999500@apctest.ge.com</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>
</ds:Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">999999500</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2009-04-20T14:36:31.973Z" Recipient="https://wg.us.wrproxy.com/idpsso.php"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotOnOrAfter="2009-04-20T14:36:31.972Z" NotBefore="2009-04-20T14:26:31.972Z"><saml:AudienceRestriction><saml:Audience>Webroot</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2009-04-20T14:31:31.971Z" SessionIndex="Zm9SHCgSkcP34HQTbn5ZOp6dQeB"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="lastname"><saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">User</saml:AttributeValue></saml:Attribute><saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="uid"><saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">999999500</saml:AttributeValue></saml:Attribute><saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="firstname"><saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Test</saml:AttributeValue></saml:Attribute><saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="mail"><saml:AttributeValue xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">999999500@apctest.ge.com</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>