diff options
-rw-r--r-- | lasso/key.c | 153 | ||||
-rw-r--r-- | lasso/key.h | 10 | ||||
-rw-r--r-- | lasso/xml/private.h | 8 | ||||
-rw-r--r-- | lasso/xml/tools.c | 120 | ||||
-rw-r--r-- | lasso/xml/xml.c | 143 | ||||
-rw-r--r-- | lasso/xml/xml.h | 5 |
6 files changed, 368 insertions, 71 deletions
diff --git a/lasso/key.c b/lasso/key.c index b75352b5..2f6dcecc 100644 --- a/lasso/key.c +++ b/lasso/key.c @@ -24,6 +24,7 @@ #include "key.h" #include "keyprivate.h" #include "xml/private.h" +#include "xmlsec/xmltree.h" /*****************************************************************************/ /* private methods */ @@ -172,7 +173,7 @@ lasso_key_new_for_signature_from_file(char *filename_or_buffer, * Return value:(transfer full): a newly allocated #LassoKey object */ LassoKey* -lasso_key_new_for_signature_from_memory(void *buffer, +lasso_key_new_for_signature_from_memory(const void *buffer, size_t size, char *password, LassoSignatureMethod signature_method, @@ -221,6 +222,149 @@ lasso_key_new_for_signature_from_base64_string(char *base64_string, return key; } +static xmlNode * +find_xmlnode_with_saml2_id(xmlNode *xmlnode, const char *id) +{ + xmlNode *found = NULL; + xmlNode *t; + + if (! xmlnode) + return NULL; + + if (xmlHasProp(xmlnode, BAD_CAST "ID")) { + xmlChar *value; + + value = xmlGetProp(xmlnode, BAD_CAST "ID"); + if (lasso_strisequal((char*)value, id)) { + found = xmlnode; + } + xmlFree(value); + } + if (found) { + return found; + } + t = xmlSecGetNextElementNode(xmlnode->children); + while (t) { + found = find_xmlnode_with_saml2_id(t, id); + if (found) { + return found; + } + t = xmlSecGetNextElementNode(t->next); + } + return NULL; +} + +/** + * lasso_key_saml2_xml_verify: + * @key: a #LassoKey object + * @id: the value of the ID attribute of signed node + * @document: the document containing the signed node + * + * Verify the first signature node child of the node with the given id. It follows from the profile + * of XMLDsig used by the SAML 2.0 specification. + * + * Return value: 0 if the signature validate, an error code otherwise. + */ +lasso_error_t +lasso_key_saml2_xml_verify(LassoKey *key, char *id, xmlNode *document) +{ + xmlNode *signed_node; + LassoSignatureContext signature_context; + + + signed_node = find_xmlnode_with_saml2_id(document, id); + if (! signed_node) { + return LASSO_DS_ERROR_INVALID_REFERENCE_FOR_SAML; + } + signature_context = lasso_key_get_signature_context(key); + return lasso_verify_signature(signed_node, signed_node->doc, "ID", NULL, + signature_context.signature_key, NO_OPTION, NULL); +} + +/** + * lasso_key_saml2_xml_sign: + * @key: a #LassoKey object + * @id: the value of the ID attribute of signed node + * @document: the document containing the signed node + * + * Sign the first signature node child of the node with the given id. It no signature node is found + * a new one is added at the end of the children list of the signed node. + * + * The passed document node is modified in-place. + * + * Return value: The modified xmlNode object, or NULL if the signature failed. + */ +xmlNode* +lasso_key_saml2_xml_sign(LassoKey *key, const char *id, xmlNode *document) +{ + xmlNode *signed_node; + LassoSignatureContext signature_context; + + signed_node = find_xmlnode_with_saml2_id(document, id); + if (! signed_node) { + return NULL; + } + signature_context = lasso_key_get_signature_context(key); + lasso_xmlnode_add_saml2_signature_template(signed_node, signature_context, id); + if (lasso_sign_node(signed_node, signature_context, + "ID", id) == 0) { + return document; + } else { + return NULL; + } +} + +/** + * lasso_key_query_verify: + * key: a #LassoKey object + * query: a raw HTTP query string + * + * Check if this query string contains a proper SAML2 signature for this key. + * + * Return value: 0 if a valid signature was found, an error code otherwise. + */ +lasso_error_t +lasso_key_query_verify(LassoKey *key, const char *query) +{ + LassoSignatureContext signature_context; + lasso_bad_param(KEY, key); + + signature_context = lasso_key_get_signature_context(key); + if (! lasso_validate_signature_context(signature_context)) + return LASSO_ERROR_UNDEFINED; + return lasso_saml2_query_verify_signature(query, signature_context.signature_key); +} + +/** + * lasso_key_query_verify: + * key: a #LassoKey object + * query: a raw HTTP query string + * + * Sign the given query string using the given key. + * + * Return value: the signed query string. + */ +char* +lasso_key_query_sign(LassoKey *key, const char *query) +{ + LassoSignatureContext signature_context; + + if (! LASSO_IS_KEY(key)) + return NULL; + signature_context = lasso_key_get_signature_context(key); + if (! lasso_validate_signature_context(signature_context)) + return NULL; + return lasso_query_sign((char*)query, signature_context); +} + +/** + * lasso_key_get_signature_context: + * @key: a #LassoKey object + * + * Private method to extract the signature context embedded in a LassoKey object. + * + * Return value: a #LassoSignatureContext structure value. + */ LassoSignatureContext lasso_key_get_signature_context(LassoKey *key) { if (key->private_data && key->private_data->type == LASSO_KEY_TYPE_FOR_SIGNATURE) { @@ -228,6 +372,13 @@ lasso_key_get_signature_context(LassoKey *key) { } return LASSO_SIGNATURE_CONTEXT_NONE; } + +/** + * lasso_key_get_key_type: + * @key: a #LassoKey object + * + * Return the type of key, i.e. which operation it supports. + */ LassoKeyType lasso_key_get_key_type(LassoKey *key) { lasso_return_val_if_fail(LASSO_IS_KEY(key), diff --git a/lasso/key.h b/lasso/key.h index a225b0b4..ca6241ad 100644 --- a/lasso/key.h +++ b/lasso/key.h @@ -65,7 +65,7 @@ struct _LassoKeyClass { LASSO_EXPORT GType lasso_key_get_type(); -LASSO_EXPORT LassoKey* lasso_key_new_for_signature_from_memory(void *buffer, size_t size, +LASSO_EXPORT LassoKey* lasso_key_new_for_signature_from_memory(const void *buffer, size_t size, char *password, LassoSignatureMethod signature_method, char *certificate); LASSO_EXPORT LassoKey* lasso_key_new_for_signature_from_base64_string(char *base64_string, @@ -74,6 +74,14 @@ LASSO_EXPORT LassoKey* lasso_key_new_for_signature_from_base64_string(char *base LASSO_EXPORT LassoKey* lasso_key_new_for_signature_from_file(char *filename_or_buffer, char *password, LassoSignatureMethod signature_method, char *certificate); +LASSO_EXPORT lasso_error_t lasso_key_query_verify(LassoKey* key, const char *query); + +LASSO_EXPORT char* lasso_key_query_sign(LassoKey *key, const char *query); + +LASSO_EXPORT lasso_error_t lasso_key_saml2_xml_verify(LassoKey *key, char *id, xmlNode *document); + +LASSO_EXPORT xmlNode *lasso_key_saml2_xml_sign(LassoKey *key, const char *id, xmlNode *document); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/lasso/xml/private.h b/lasso/xml/private.h index 009596d8..629d6ca5 100644 --- a/lasso/xml/private.h +++ b/lasso/xml/private.h @@ -275,7 +275,7 @@ lasso_create_hmac_key(const xmlSecByte * buf, xmlSecSize size); lasso_error_t lasso_get_hmac_key(const xmlSecKey *key, void **buffer, size_t *size); -LassoSignatureContext lasso_make_signature_context_from_buffer(const char *buffer, size_t length, +LassoSignatureContext lasso_make_signature_context_from_buffer(const void *buffer, size_t length, const char *password, LassoSignatureMethod signature_method, const char *certificate); @@ -299,6 +299,12 @@ void set_xsi_type(xmlNode *node, const xmlChar *type_ns_prefix, const xmlChar *type_ns_href, const xmlChar *type_name); + +void lasso_xmlnode_add_saml2_signature_template(xmlNode *node, LassoSignatureContext context, + const char *id); + +gchar* lasso_xmlnode_build_deflated_query(xmlNode *xmlnode); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/lasso/xml/tools.c b/lasso/xml/tools.c index b31e7187..36c76a05 100644 --- a/lasso/xml/tools.c +++ b/lasso/xml/tools.c @@ -1012,6 +1012,17 @@ lasso_node_build_deflated_query(LassoNode *node) { /* actually deflated and b64'ed and url-escaped */ xmlNode *xmlnode; + gchar *result; + + xmlnode = lasso_node_get_xmlNode(node, FALSE); + result = lasso_xmlnode_build_deflated_query(xmlnode); + xmlFreeNode(node); + return result; +} + +gchar* +lasso_xmlnode_build_deflated_query(xmlNode *xmlnode) +{ xmlOutputBufferPtr buf; xmlCharEncodingHandlerPtr handler = NULL; xmlChar *buffer; @@ -1021,17 +1032,12 @@ lasso_node_build_deflated_query(LassoNode *node) int rc = 0; z_stream stream; - xmlnode = lasso_node_get_xmlNode(node, FALSE); - handler = xmlFindCharEncodingHandler("utf-8"); buf = xmlAllocOutputBuffer(handler); xmlNodeDumpOutput(buf, NULL, xmlnode, 0, 0, "utf-8"); xmlOutputBufferFlush(buf); buffer = buf->conv ? buf->conv->content : buf->buffer->content; - xmlFreeNode(xmlnode); - xmlnode = NULL; - in_len = strlen((char*)buffer); ret = g_malloc(in_len * 2); /* deflating should never increase the required size but we are @@ -1079,6 +1085,35 @@ lasso_node_build_deflated_query(LassoNode *node) return rret; } +void +lasso_get_query_string_param_value(const char *qs, const char *param_key, char **value, + size_t *length) +{ + size_t key_size = strlen(param_key); + + *value = NULL; + *length = 0; + while (qs) { + if (strncmp(qs, param_key, key_size) == 0 && + qs[key_size] == '=') + { + char *end; + *value = qs[key_size+1]; + end = strchr(*value, '&'); + if (! end) { + end = strchr(*value, ';'); + } + if (end) { + *length = (ptrdiff_t)(end - *value) + } else { + *length = strlen(*value); + } + return; + } + qs = strchr(qs, '&'); + } +} + gboolean lasso_node_init_from_deflated_query_part(LassoNode *node, char *deflate_string) { @@ -2367,7 +2402,7 @@ lasso_get_hmac_key(const xmlSecKey *key, void **buffer, size_t *size) * successful, LASSO_SIGNATURE_CONTEXT_NONE otherwise. The caller must free the #xmlSecKey. */ LassoSignatureContext -lasso_make_signature_context_from_buffer(const char *buffer, size_t length, const char *password, +lasso_make_signature_context_from_buffer(const void *buffer, size_t length, const char *password, LassoSignatureMethod signature_method, const char *certificate) { LassoSignatureContext context = LASSO_SIGNATURE_CONTEXT_NONE; @@ -2466,3 +2501,76 @@ set_xsi_type(xmlNode *node, type_ns_href, type_name); } + +void +lasso_xmlnode_add_saml2_signature_template(xmlNode *node, LassoSignatureContext context, + const char *id) { + xmlSecTransformId transform_id; + xmlNode *existing_signature = NULL, *signature = NULL, *reference, *key_info; + char *uri; + + if (! lasso_validate_signature_context(context) || ! node) + return; + + switch (context.signature_method) { + case LASSO_SIGNATURE_METHOD_RSA_SHA1: + transform_id = xmlSecTransformRsaSha1Id; + break; + case LASSO_SIGNATURE_METHOD_DSA_SHA1: + transform_id = xmlSecTransformDsaSha1Id; + break; + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: + transform_id = xmlSecTransformHmacSha1Id; + break; + default: + g_assert_not_reached(); + } + existing_signature = xmlSecFindChild(node, xmlSecNodeSignature, xmlSecDSigNs); + signature = xmlSecTmplSignatureCreate(NULL, + xmlSecTransformExclC14NId, + transform_id, NULL); + if (existing_signature) { + xmlSecReplaceNode(existing_signature, signature); + } else { + xmlAddChild(node, signature); + } + + /* Normally the signature is son of the signed node, which holds an Id attribute, but in + * other cases, set snippet->offset to 0 and use xmlSecTmpSignatureAddReference from another + * node get_xmlNode virtual method to add the needed reference. + */ + if (id) { + uri = g_strdup_printf("#%s", id); + reference = xmlSecTmplSignatureAddReference(signature, + xmlSecTransformSha1Id, NULL, (xmlChar*)uri, NULL); + lasso_release(uri); + } + + /* add enveloped transform */ + xmlSecTmplReferenceAddTransform(reference, xmlSecTransformEnvelopedId); + /* add exclusive C14N transform */ + xmlSecTmplReferenceAddTransform(reference, xmlSecTransformExclC14NId); + /* if the key is the public part of an asymetric key, add its certificate or the key itself */ + switch (context.signature_method) { + case LASSO_SIGNATURE_METHOD_RSA_SHA1: + case LASSO_SIGNATURE_METHOD_DSA_SHA1: + /* asymetric cryptography methods */ + key_info = xmlSecTmplSignatureEnsureKeyInfo(signature, NULL); + if (xmlSecKeyGetData(context.signature_key, xmlSecOpenSSLKeyDataX509Id)) { + /* add <dsig:KeyInfo/> */ + xmlSecTmplKeyInfoAddX509Data(key_info); + } else { + xmlSecTmplKeyInfoAddKeyValue(key_info); + } + break; + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: + if (context.signature_key->name) { + key_info = xmlSecTmplSignatureEnsureKeyInfo(signature, NULL); + xmlSecTmplKeyInfoAddKeyName(key_info, NULL); + + } + break; + default: + g_assert_not_reached(); + } +} diff --git a/lasso/xml/xml.c b/lasso/xml/xml.c index 53d733bd..dcdad0e9 100644 --- a/lasso/xml/xml.c +++ b/lasso/xml/xml.c @@ -2809,11 +2809,7 @@ lasso_node_add_signature_template(LassoNode *node, xmlNode *xmlnode, LassoNodeClass *klass = NULL; LassoNodeClassData *node_data = NULL; LassoSignatureContext context; - xmlSecTransformId transform_id; - xmlNode *signature = NULL, *reference, *key_info; - char *uri; - char *id; - + char *id = NULL; node_data = lasso_legacy_get_signature_node_data(node, &klass); if (! node_data) @@ -2827,66 +2823,11 @@ lasso_node_add_signature_template(LassoNode *node, xmlNode *xmlnode, if (lasso_legacy_extract_and_copy_signature_parameters(node, node_data)) context = lasso_node_get_signature(node); - if (! lasso_validate_signature_context(context)) - return; - - switch (context.signature_method) { - case LASSO_SIGNATURE_METHOD_RSA_SHA1: - transform_id = xmlSecTransformRsaSha1Id; - break; - case LASSO_SIGNATURE_METHOD_DSA_SHA1: - transform_id = xmlSecTransformDsaSha1Id; - break; - case LASSO_SIGNATURE_METHOD_HMAC_SHA1: - transform_id = xmlSecTransformHmacSha1Id; - break; - default: - g_assert_not_reached(); - } - signature = xmlSecTmplSignatureCreate(NULL, - xmlSecTransformExclC14NId, - transform_id, NULL); - xmlAddChild(xmlnode, signature); - - /* Normally the signature is son of the signed node, which holds an Id attribute, but in - * other cases, set snippet->offset to 0 and use xmlSecTmpSignatureAddReference from another - * node get_xmlNode virtual method to add the needed reference. - */ if (snippet_signature->offset) { id = SNIPPET_STRUCT_MEMBER(char *, node, G_TYPE_FROM_CLASS(klass), snippet_signature); - uri = g_strdup_printf("#%s", id); - reference = xmlSecTmplSignatureAddReference(signature, - xmlSecTransformSha1Id, NULL, (xmlChar*)uri, NULL); - lasso_release(uri); - } - - /* add enveloped transform */ - xmlSecTmplReferenceAddTransform(reference, xmlSecTransformEnvelopedId); - /* add exclusive C14N transform */ - xmlSecTmplReferenceAddTransform(reference, xmlSecTransformExclC14NId); - /* if the key is the public part of a symetric key, add its certificate or the key itself */ - switch (context.signature_method) { - case LASSO_SIGNATURE_METHOD_RSA_SHA1: - case LASSO_SIGNATURE_METHOD_DSA_SHA1: - /* symetric cryptography methods */ - key_info = xmlSecTmplSignatureEnsureKeyInfo(signature, NULL); - if (xmlSecKeyGetData(context.signature_key, xmlSecOpenSSLKeyDataX509Id)) { - /* add <dsig:KeyInfo/> */ - xmlSecTmplKeyInfoAddX509Data(key_info); - } else { - xmlSecTmplKeyInfoAddKeyValue(key_info); - } - break; - case LASSO_SIGNATURE_METHOD_HMAC_SHA1: - if (context.signature_key->name) { - key_info = xmlSecTmplSignatureEnsureKeyInfo(signature, NULL); - xmlSecTmplKeyInfoAddKeyName(key_info, NULL); - - } - break; - default: - g_assert_not_reached(); } + + lasso_xmlnode_add_saml2_signature_template(xmlnode, context, id); } static struct XmlSnippet* @@ -3474,3 +3415,81 @@ lasso_node_get_namespace(LassoNode *node) return (const char*)klass->node_data->ns->href; return NULL; } + + +/** + * lasso_node_export_to_saml2_query: + * @node: the #LassoNode object to pass as a query + * @param_name: the key value for the query string parameter + * @url:(allow-none): an optional URL to prepend to the query string + * @key:(allow-none): a #LassoKey object + * + * Export a node as signed query string, the node must support serialization as a query. + * + * Return value: an HTTP URL or query string if successful, NULL otherwise. + */ +char* +lasso_node_export_to_saml2_query(LassoNode *node, const char *param_name, const char *url, + LassoKey *key) +{ + char *value = NULL, *query = NULL, *signed_query = NULL, *result = NULL; + xmlChar *encoded_param = NULL; + + value = lasso_node_build_deflated_query(xmlnode); + if (! value) + goto cleanup; + encoded_param = xmlURIEscapeStr(BAD_CAST param_name, NULL); + if (! encoded_param) + goto cleanup; + query = g_strdup_printf("%s=%s", encoded_param, value); + if (! query) + goto cleanup; + if (LASSO_IS_KEY(key)) { + signed_query = lasso_key_query_sign(key, query); + } else { + lasso_transfer_string(signed_query, query); + } + if (! signed_query) + goto cleanup; + if (url) { + result = lasso_concat_url_query(url, signed_query); + } else { + lasso_transfer_string(result, signed_query); + } + +cleanup: + lasso_release_string(value); + lasso_release_xml_string(encoded_param); + lasso_release_string(query); + lasso_release_string(signed_query); + return result; +} + +/** + * lasso_node_new_from_saml2_query: + * @url_or_qs: an URL containing a query string or a query string only + * @param_name: the key value for the query string parameter to extract as a #LassoNode. + * @key:(allow-none): a #LassoKey object + * + * Verify the signature on a SAML-2 encoded query string and return the encoded node. + * + * Return value: a newly build #LassoNode if successful, NULL otherwise. + */ +LassoNode* +lasso_node_new_from_saml2_query(const char *url_or_qs, const char *param_name, LassoKey *key) +{ + char *needle = NULL; + LassoNode *result = NULL; + + if (! url_or_qs || ! param_name) + return NULL; + needle = strchr(url_or_qs, '?'); + if (needle) { + url_or_qs = (const char*)(needle+1); + } + if (key) { + goto_cleanup_if_fail(lasso_key_query_verify(key, url_or_qs) == 0); + } +cleanup: + return result; +} diff --git a/lasso/xml/xml.h b/lasso/xml/xml.h index a0630aab..22d8b9c9 100644 --- a/lasso/xml/xml.h +++ b/lasso/xml/xml.h @@ -195,6 +195,11 @@ LASSO_EXPORT gchar* lasso_get_prefix_for_idwsf2_dst_service_href(const gchar *hr LASSO_EXPORT char* lasso_node_debug(LassoNode *node, int level); +typedef struct _LassoKey LassoKey; + +LASSO_EXPORT char* lasso_node_export_to_saml2_query(LassoNode *node, const char *param_name, const + char *url, LassoKey *key); + #ifdef __cplusplus } #endif /* __cplusplus */ |