[key] add methods to send message using SAML 2.0 redirect and post bindings

This commit is contained in:
Benjamin Dauvergne 2012-03-17 15:26:21 +01:00
parent f55c927255
commit f42bef0836
6 changed files with 367 additions and 70 deletions

View File

@ -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),

View File

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

View File

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

View File

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

View File

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

View File

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