Merge branch 'release-2.4.0'

This commit is contained in:
Benjamin Dauvergne 2012-05-05 22:32:48 +02:00
commit c7cf89b387
17 changed files with 5086 additions and 93 deletions

75
NEWS
View File

@ -1,6 +1,81 @@
NEWS
====
2.4.0 - June 6th 2011
---------------------
93 files changed, 32160 insertions(+), 607 deletions(-)
Minor version number increase since ABI was extended (new methods).
- Improvements to autoconf and automake files to compile under Darwin (Mac Os
X).
- Key rollover support:
Lasso is now able to accept messages signed by any key declared as a signing
key in a metadata and not just the last one. You can also decrypt encrypted
nodes using any of a list of private keys, allowing roll-over of encryption
certificates. Signing key roll-over is automatic, your provider just have to
provide the new signing key in their metadata. For multiple-encryption key
you can load another private key than the one loaded in the LassoServer
constuctor with code like that:
>>> import lasso
>>> server = lasso.Server(our_metadata, first_private_key_path)
>>> server.setEncryptionPrivateKey(second_private_key_path)
See the FAQ file for the workflow of a proper key roll-over.
- Partial logout reponse now produces a specific error code when parsed by
lasso_logout_process_response_msg()
- Bugs in lasso_assertion_query_build_request_msg() were fixed
- Processing of assertions is not stopped when checking that first level
status code is not success, so that later code can check the second level
status code.
- A new generic error for denied request was added,
LASSO_PROFILE_ERROR_REQUEST_DENIED
- A new API lasso_server_load_metadata() was added to load federation files
(XML files containing metadata from multiple providers) and to check
signatures on them.
- Better warning and errors are reported in logs when failing to load a
metadata file.
- Bugs around missing namespace declaration for dump file were fixed, it
prevented reloading dumped object (like LassoLogin).
- lasso_node_get_xml_node_for_any_type() must be able to copy the content of
an XML node to another (namespace, attribute and children). It did not, now
it is fixed. It can be used for example to add specific attribute like «
xsi:type="string" » to a Saml2AttributeValue. Here is a python snippet to do that:
>>> import lasso
>>> a = lasso.Saml2AttributeValue()
>>> a.setOriginalXmlnode('<Dummy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="string">Value</Dummy>')
>>> print a.debug(0)
<saml:AttributeValue xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="string">Value</saml:AttributeValue>
- support for symetric keys signatures: for a long time XMLDisg standard has
supported HMAC signature, or signature based on a shared secret key an hash
algorithm. Lasso now does support to share a key with another Lasso using
service or identity provider and to verify and sign SAML exchange using this
key. Performance can be 100 times more than with assymetric cryptography,
i.e. RSA.
- nodes able to hold any XML attribyte (like saml:AttributeValue) contains a
hashtable to for holding those attributes, those hashtable have a new syntax
for attributes of another namespace than the current node namespace,
inspired by the Python ElementTree library:
{the_namespace}the_attribute_name
ex:
{http://www.w3.org/2001/XMLSchema-instance}type
for the classic xsi:type attribute.
- The perfs benchmarking tools now allows to select a different metadata set
(for example to test with different public key sizes).
- Perl minimal version for the binding was downgraded to 5
- pseudo-XSchema validation: the new XML deserializer does more to enforce
constraints of the schema defining SAML messages. It means Lasso is less
forgiving with non-conform implementation of SAML.
- a FAQ file was started.
2.3.6 - November 29th 2011
--------------------------

1114
abi/abi-2.3.3 Normal file

File diff suppressed because it is too large Load Diff

1114
abi/abi-2.3.4 Normal file

File diff suppressed because it is too large Load Diff

1115
abi/abi-2.3.5 Normal file

File diff suppressed because it is too large Load Diff

1125
abi/abi-2.4.0 Normal file

File diff suppressed because it is too large Load Diff

View File

@ -136,6 +136,7 @@ function lassoRegisterIdWsf2DstService($prefix, $href) {
def generate_constructors(self, klass):
method_prefix = format_as_underscored(klass.name) + '_'
for m in self.binding_data.functions:
name = m.rename or m.name
if m.name == method_prefix + 'new':
php_args = []
c_args = []
@ -163,18 +164,36 @@ function lassoRegisterIdWsf2DstService($prefix, $href) {
print >> self.fd, ' }'
print >> self.fd, ''
if m.name == method_prefix + 'new_from_dump':
if len(m.args) == 1:
print >> self.fd, ' public static function newFromDump($dump) {'
print >> self.fd, ' return cptrToPhp(%s($dump));' % m.name
elif name.startswith(method_prefix) and m.args \
and clean_type(unconstify(m.args[0][0])) != klass.name:
if m.rename:
php_name = m.rename
else:
print >> self.fd, ' public static function newFromDump($server, $dump) {'
print >> self.fd, ' return cptrToPhp(%s($server->_cptr, $dump));' % m.name
# XXX: Else throw an exception
print >> self.fd, ' }'
print >> self.fd, ''
elif m.name == method_prefix + 'new_full':
pass
mname = m.name
mname = mname[len(method_prefix):]
if 'new' in mname and not mname.startswith('new'):
continue
php_name = format_underscore_as_camelcase(mname)
php_args = []
c_args = []
for arg in m.args:
arg_type, arg_name, arg_options = arg
if arg_options.get('optional'):
php_args.append('$%s = null' % arg_name)
else:
php_args.append('$%s' % arg_name)
if self.is_object(arg_type):
c_args.append('$%s->_cptr' % arg_name)
else:
c_args.append('$%s' % arg_name)
php_args = ', '.join(php_args)
c_args = ', '.join(c_args)
print >>self.fd, ' public static function %s(%s) {' % (php_name, php_args)
print >>self.fd, ' return cptrToPhp(%s(%s));' % (m.name, c_args)
print >>self.fd, ' }'
print >>self.fd, ''
def generate_getter(self, c, m):
@ -312,12 +331,14 @@ function lassoRegisterIdWsf2DstService($prefix, $href) {
php_args.append('%s = null' % arg_name)
else:
php_args.append(arg_name)
if arg_type in ('char*', 'const char*', 'gchar*', 'const gchar*') or \
arg_type in ['int', 'gint', 'gboolean', 'const gboolean'] or \
arg_type in self.binding_data.enums:
if is_xml_node(arg) or is_boolean(arg) or is_cstring(arg) or \
is_int(arg, self.binding_data) or is_glist(arg) or \
is_hashtable(arg) or is_time_t_pointer(arg):
c_args.append(arg_name)
elif is_object(arg):
c_args.append('%s->_cptr' % arg_name)
else:
c_args.append('%s._cptr' % arg_name)
raise Exception('Does not handle argument of type: %s' % ((m, arg),))
if is_out(arg):
php_args.pop()
php_args.append(arg_name)

View File

@ -15,7 +15,7 @@ dnl - Second number is the number of supported API versions where API version >
dnl first number.
dnl - Third number is the current API version implementation version number.
dnl See libtool explanations about current, age and release, later in this file.
AC_INIT([lasso], 2.3.6, lasso-devel@lists.labs.libre-entreprise.org)
AC_INIT([lasso], 2.4.0, lasso-devel@lists.labs.libre-entreprise.org)
dnl Check if autoconf ver > 2.53
AC_PREREQ(2.53)
AC_CONFIG_MACRO_DIR([m4])
@ -184,7 +184,7 @@ dnl - interfaces removed -> AGE = 0
# m = a
# r = r
current=`expr $VERSION_MAJOR + $VERSION_MINOR`
LASSO_VERSION_INFO="12:4:9"
LASSO_VERSION_INFO="13:0:10"
AC_SUBST(LASSO_VERSION_INFO)
dnl Compute the minimal supported ABI version for Win32 scripts and resources files.

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

@ -534,7 +534,24 @@ lasso_saml20_provider_load_metadata(LassoProvider *provider, xmlNode *root_node)
return TRUE;
}
static gboolean has_synchronous_methods(LassoProvider *provider, LassoMdProtocolType protocol_type)
enum {
FOR_RESPONSE = 1
};
/**
* has_synchronous_methods:
* @provider: a #LassoProvider object
* @protocol_type: a #LassoMdProtocolType value
* @for_response: a boolean stating whether we need the answer for receiving a response.
*
* Return whether the given @provider support a certain protocol with a synchronous binding.
* If we need to receive a response for this protocol, @for_response must be set to True.
*
* Return result: TRUE if @provider supports @protocol_type with a synchronous binding, eventually
* for receiving responses, FALSE otherwise.
*/
static gboolean has_synchronous_methods(LassoProvider *provider, LassoMdProtocolType protocol_type,
gboolean for_response)
{
GList *t = NULL;
const char *kind = NULL;
@ -547,6 +564,11 @@ static gboolean has_synchronous_methods(LassoProvider *provider, LassoMdProtocol
return LASSO_HTTP_METHOD_NONE;
}
if (for_response && protocol_type == LASSO_MD_PROTOCOL_TYPE_SINGLE_SIGN_ON)
{
kind = LASSO_SAML2_METADATA_ELEMENT_ASSERTION_CONSUMER_SERVICE;
}
lasso_foreach(t, provider->private_data->endpoints) {
EndpointType *endpoint_type = (EndpointType*)t->data;
if (endpoint_type && lasso_strisequal(endpoint_type->kind, kind)) {
@ -581,7 +603,7 @@ lasso_saml20_provider_get_first_http_method(LassoProvider *provider,
/* a synchronous method needs another synchronous method for receiving the
* response on the local side */
if (http_method_kind(result) == SYNCHRONOUS
&& ! has_synchronous_methods(provider, protocol_type))
&& ! has_synchronous_methods(provider, protocol_type, FOR_RESPONSE))
continue;
if (result != LASSO_HTTP_METHOD_NONE)
break;

View File

@ -721,10 +721,18 @@ lasso_saml2_assertion_add_attribute_with_node(LassoSaml2Assertion *assertion, co
lasso_assign_string(attribute->NameFormat, LASSO_SAML2_ATTRIBUTE_NAME_FORMAT_URI);
lasso_list_add_new_gobject(attribute->AttributeValue, attribute_value);
attribute_statement = LASSO_SAML2_ATTRIBUTE_STATEMENT(lasso_saml2_attribute_statement_new());
if (assertion->AttributeStatement
&& LASSO_IS_SAML2_ATTRIBUTE_STATEMENT(
assertion->AttributeStatement->data)) {
attribute_statement =
(LassoSaml2AttributeStatement*)
assertion->AttributeStatement->data;
} else {
attribute_statement = LASSO_SAML2_ATTRIBUTE_STATEMENT(lasso_saml2_attribute_statement_new());
lasso_list_add_new_gobject(assertion->AttributeStatement, attribute_statement);
}
lasso_list_add_new_gobject(attribute_statement->Attribute, attribute);
lasso_list_add_new_gobject(assertion->AttributeStatement, attribute_statement);
cleanup:
return rc;
}

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

@ -2810,11 +2810,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)
@ -2828,66 +2824,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*
@ -3475,3 +3416,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

@ -194,6 +194,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 */

View File

@ -2057,13 +2057,11 @@ START_TEST(test15_ds_key_info)
list = (GList){ .data = ds_key_info, .next = NULL, .prev = NULL };
lasso_saml2_key_info_confirmation_data_type_set_key_info(kicdt, &list);
dump = lasso_node_debug((LassoNode*)sc, 10);
printf("1 %s\n", dump);
lasso_release_gobject(sc);
lasso_release_gobject(ds_key_info);
node = lasso_node_new_from_dump(dump);
lasso_release_string(dump);
dump = lasso_node_debug(node, 10);
printf("2 %s\n", dump);
lasso_release_string(dump);
}
END_TEST

View File

@ -33,6 +33,7 @@
#include "../lasso/xml/saml_name_identifier.h"
#include "../lasso/xml/samlp_response.h"
#include "../lasso/utils.h"
#include "../lasso/key.h"
Suite* random_suite();
@ -328,6 +329,105 @@ LlTxKnCrWAXftSm1rNtewTsF\n\
}
END_TEST
START_TEST(test08_lasso_key)
{
/* normal query as produces by Lasso */
const char query1[] = "SAMLRequest=fZHNasMwEIRfxeieWrYTtQjb4DgJBNqSNqWHXopw1kQgS6523Z%2B3r%2BxQSKDkOppvd2aVo%2BpML6uBjvYZPgZAir47Y1FODwUbvJVOoUZpVQcoqZH76uFepjdc9t6Ra5xhZ8h1QiGCJ%2B0si7argr0vxTLJ1guRilpU8%2FWtyKpNnaXrukoF32SCRa%2FgMfgLFvAAIQ6wtUjKUpB4wmc8nSX8hXOZ3Ml0%2FsaijfMNTIUK1iqDMGK7sFl%2Fwp9S5mNWOY3z5ZGol3GM%2FSLugNRBkcrjc0N%2ButJj6LNd7ZzRzc%2B4plN0ve6o6MOsnayyH6sggSUW7XfjsKdBGd1q8AX7JwOLKmPcV%2B1BUUhOfgAWl6dkl19W%2FgI%3D&RelayState=fake%5B%5D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=wDxMSEPKhK%2FuU06cmL50oVx%2B7eP5%2FQirShQE%2BLv9pT3CrVwb6WBV1Tp9XS2VVJ2odLHogdA%2FE1XDW7BIRKYgkN8bXVlC2GybSYBhyn8bwAuyHs%2BnMW48LF%2FE5vFiZxbw8tMWUAktdvDuaXoZLhubX7UgV%2B%2BdRyjhckolpXTC9xuJdoHJUDF0vzzNm8xZs6LR7tjWUoz5CcjMJA3LVfWmpE5UjCyRmGbi9knGWHdY75CFtArD%2BNSkGeNx9xySrUlik6e57Zlodv4V9WBdeopAWskO58BA27GqTmnSLooeo%2FrtLxc1NZeuau11YxNzwl%2FvN8%2FQ5IsR3Xic8X1TaCCtwg%3D%3D";
/* SAMLRequest field was moved in the middle, Signature to the beginning and all & were
* changed to ; */
const char query2[] = "Signature=wDxMSEPKhK%2FuU06cmL50oVx%2B7eP5%2FQirShQE%2BLv9pT3CrVwb6WBV1Tp9XS2VVJ2odLHogdA%2FE1XDW7BIRKYgkN8bXVlC2GybSYBhyn8bwAuyHs%2BnMW48LF%2FE5vFiZxbw8tMWUAktdvDuaXoZLhubX7UgV%2B%2BdRyjhckolpXTC9xuJdoHJUDF0vzzNm8xZs6LR7tjWUoz5CcjMJA3LVfWmpE5UjCyRmGbi9knGWHdY75CFtArD%2BNSkGeNx9xySrUlik6e57Zlodv4V9WBdeopAWskO58BA27GqTmnSLooeo%2FrtLxc1NZeuau11YxNzwl%2FvN8%2FQ5IsR3Xic8X1TaCCtwg%3D%3D;RelayState=fake%5B%5D;SAMLRequest=fZHNasMwEIRfxeieWrYTtQjb4DgJBNqSNqWHXopw1kQgS6523Z%2B3r%2BxQSKDkOppvd2aVo%2BpML6uBjvYZPgZAir47Y1FODwUbvJVOoUZpVQcoqZH76uFepjdc9t6Ra5xhZ8h1QiGCJ%2B0si7argr0vxTLJ1guRilpU8%2FWtyKpNnaXrukoF32SCRa%2FgMfgLFvAAIQ6wtUjKUpB4wmc8nSX8hXOZ3Ml0%2FsaijfMNTIUK1iqDMGK7sFl%2Fwp9S5mNWOY3z5ZGol3GM%2FSLugNRBkcrjc0N%2ButJj6LNd7ZzRzc%2B4plN0ve6o6MOsnayyH6sggSUW7XfjsKdBGd1q8AX7JwOLKmPcV%2B1BUUhOfgAWl6dkl19W%2FgI%3D;SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1";
const char query3[] = "RelayState=fake%5B%5D&SAMLRequest=fZHNasMwEIRfxeieWrYTtQjb4DgJBNqSNqWHXopw1kQgS6523Z%2B3r%2BxQSKDkOppvd2aVo%2BpML6uBjvYZPgZAir47Y1FODwUbvJVOoUZpVQcoqZH76uFepjdc9t6Ra5xhZ8h1QiGCJ%2B0si7argr0vxTLJ1guRilpU8%2FWtyKpNnaXrukoF32SCRa%2FgMfgLFvAAIQ6wtUjKUpB4wmc8nSX8hXOZ3Ml0%2FsaijfMNTIUK1iqDMGK7sFl%2Fwp9S5mNWOY3z5ZGol3GM%2FSLugNRBkcrjc0N%2ButJj6LNd7ZzRzc%2B4plN0ve6o6MOsnayyH6sggSUW7XfjsKdBGd1q8AX7JwOLKmPcV%2B1BUUhOfgAWl6dkl19W%2FgI%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=wDxMSEPKhK%2FuU06cmL50oVx%2B7eP5%2FQirShQE%2BLv9pT3CrVwb6WBV1Tp9XS2VVJ2odLHogdA%2FE1XDW7BIRKYgkN8bXVlC2GybSYBhyn8bwAuyHs%2BnMW48LF%2FE5vFiZxbw8tMWUAktdvDuaXoZLhubX7UgV%2B%2BdRyjhckolpXTC9xuJdoHJUDF0vzzNm8xZs6LR7tjWUoz5CcjMJA3LVfWmpE5UjCyRmGbi9knGWHdY75CFtArD%2BNSkGeNx9xySrUlik6e57Zlodv4V9WBdeopAWskO58BA27GqTmnSLooeo%2FrtLxc1NZeuau11YxNzwl%2FvN8%2FQ5IsR3Xic8X1TacCtwg%3D%3D";
/* sp5-saml2 key */
const char pkey[] = "-----BEGIN CERTIFICATE-----\n\
MIIDnjCCAoagAwIBAgIBATANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJGUjEP\n\
MA0GA1UECBMGRnJhbmNlMQ4wDAYDVQQHEwVQYXJpczETMBEGA1UEChMKRW50cm91\n\
dmVydDEPMA0GA1UEAxMGRGFtaWVuMB4XDTA2MTAyNzA5MDc1NFoXDTExMTAyNjA5\n\
MDc1NFowVDELMAkGA1UEBhMCRlIxDzANBgNVBAgTBkZyYW5jZTEOMAwGA1UEBxMF\n\
UGFyaXMxEzARBgNVBAoTCkVudHJvdXZlcnQxDzANBgNVBAMTBkRhbWllbjCCASIw\n\
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM06Hx6VgHYR9wUf/tZVVTRkVWNq\n\
h9x+PvHA2qH4OYMuqGs4Af6lU2YsZvnrmRdcFWv0+UkdAgXhReCWAZgtB1pd/W9m\n\
6qDRldCCyysow6xPPKRz/pOTwRXm/fM0QGPeXzwzj34BXOIOuFu+n764vKn18d+u\n\
uVAEzk1576pxTp4pQPzJfdNLrLeQ8vyCshoFU+MYJtp1UA+h2JoO0Y8oGvywbUxH\n\
ioHN5PvnzObfAM4XaDQohmfxM9Uc7Wp4xKAc1nUq5hwBrHpjFMRSz6UCfMoJSGIi\n\
+3xJMkNCjL0XEw5NKVc5jRKkzSkN5j8KTM/k1jPPsDHPRYzbWWhnNtd6JlkCAwEA\n\
AaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0\n\
ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFP2WWMDShux3iF74+SoO1xf6qhqaMB8G\n\
A1UdIwQYMBaAFGjl6TRXbQDHzSlZu+e8VeBaZMB5MA0GCSqGSIb3DQEBBQUAA4IB\n\
AQAZ/imK7UMognXbs5RfSB8cMW6iNAI+JZqe9XWjvtmLfIIPbHM96o953SiFvrvQ\n\
BZjGmmPMK3UH29cjzDx1R/RQaYTyMrHyTePLh3BMd5mpJ/9eeJCSxPzE2ECqWRUa\n\
pkjukecFXqmRItwgTxSIUE9QkpzvuQRb268PwmgroE0mwtiREADnvTFkLkdiEMew\n\
fiYxZfJJLPBqwlkw/7f1SyzXoPXnz5QbNwDmrHelga6rKSprYKb3pueqaIe8j/AP\n\
NC1/bzp8cGOcJ88BD5+Ny6qgPVCrMLE5twQumJ12V3SvjGNtzFBvg2c/9S5OmVqR\n\
LlTxKnCrWAXftSm1rNtewTsF\n\
-----END CERTIFICATE-----";
LassoKey *key = lasso_key_new_for_signature_from_memory(pkey, strlen(pkey), NULL,
LASSO_SIGNATURE_METHOD_RSA_SHA1, NULL);
LassoKey *key2 = lasso_key_new_for_signature_from_file(
TESTSDATADIR "/sp5-saml2/private-key.pem", NULL,
LASSO_SIGNATURE_METHOD_RSA_SHA1, NULL);
char *message = "<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_E3F8E9116EE08F0E2607CF9789649BB4\" Version=\"2.0\" IssueInstant=\"2012-03-09T11:34:48Z\" ForceAuthn=\"false\" IsPassive=\"false\"><saml:Issuer>http://sp5/metadata</saml:Issuer><Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">\n\
<SignedInfo>\n\
<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\n\
<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/>\n\
<Reference URI=\"#_E3F8E9116EE08F0E2607CF9789649BB4\">\n\
<Transforms>\n\
<Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\n\
<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\n\
</Transforms>\n\
<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>\n\
<DigestValue>tMncKjklMJaJLbmB7bARmX14Fdg=</DigestValue>\n\
</Reference>\n\
</SignedInfo>\n\
<SignatureValue>VjAHErXE8rz5yQ/t9Ubws11E59PsU/tXPtL6eCMAVLQxV4Bv0dwyYkeHtge1DXDT\n\
usTy1c17+iuYCVqD3Db51+LMVsHchj0j44fhu/PXNQTmgiT2AuVfH97YhiBWykAs\n\
LwT8MiE9vNGiHQwsWVjhdzooVmU0M80m0Ij2DFMcYiKzmuMhE4M65qUO4tygQLiL\n\
YB5oPe0VYKEBJLfaTvuijLBTi4ecx6aU+HptAvuEOcCbcJZtGyv7jr2yuEDSq72S\n\
0hwOV0CIsQoSf/vL7R9RzTs2bpgYVGqgerhpWsz6dqo7YX0NSj9pMbXZiOyX/YzS\n\
uP3QSjow05NiPhy8ywKW8A==</SignatureValue>\n\
<KeyInfo>\n\
<KeyValue>\n\
<RSAKeyValue>\n\
<Modulus>\n\
zTofHpWAdhH3BR/+1lVVNGRVY2qH3H4+8cDaofg5gy6oazgB/qVTZixm+euZF1wV\n\
a/T5SR0CBeFF4JYBmC0HWl39b2bqoNGV0ILLKyjDrE88pHP+k5PBFeb98zRAY95f\n\
PDOPfgFc4g64W76fvri8qfXx3665UATOTXnvqnFOnilA/Ml900ust5Dy/IKyGgVT\n\
4xgm2nVQD6HYmg7Rjyga/LBtTEeKgc3k++fM5t8AzhdoNCiGZ/Ez1RztanjEoBzW\n\
dSrmHAGsemMUxFLPpQJ8yglIYiL7fEkyQ0KMvRcTDk0pVzmNEqTNKQ3mPwpMz+TW\n\
M8+wMc9FjNtZaGc213omWQ==\n\
</Modulus>\n\
<Exponent>\n\
AQAB\n\
</Exponent>\n\
</RSAKeyValue>\n\
</KeyValue>\n\
</KeyInfo>\n\
</Signature><samlp:NameIDPolicy Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\" AllowCreate=\"true\"/></samlp:AuthnRequest>";
xmlDoc *doc;
doc = xmlParseDoc(BAD_CAST message);
fail_unless(key != NULL, "Cannot load public key");
fail_unless(lasso_key_query_verify(key, query1) == 0, "Signature was not validated");
/* test reordering and semi-colon separator support */
fail_unless(lasso_key_query_verify(key, query2) == 0, "Disordered signature was not validated");
fail_unless(lasso_key_query_verify(key, query3) != 0, "Altered signature was validated");
fail_unless(lasso_key_saml2_xml_verify(key,
"_E3F8E9116EE08F0E2607CF9789649BB4", xmlDocGetRootElement(doc)) == 0,
"XML Signature is not validated");
g_object_unref(key);
fail_unless(key2 != NULL, "Cannot load public key2");
fail_unless(lasso_key_query_verify(key2, query1) == 0, "Signature was not validated");
/* test reordering and semi-colon separator support */
fail_unless(lasso_key_query_verify(key2, query2) == 0, "Disordered signature was not validated");
fail_unless(lasso_key_query_verify(key2, query3) != 0, "Altered signature was validated");
fail_unless(lasso_key_saml2_xml_verify(key2,
"_E3F8E9116EE08F0E2607CF9789649BB4", xmlDocGetRootElement(doc)) == 0,
"XML Signature is not validated");
g_object_unref(key2);
lasso_release_doc(doc);
}
END_TEST
Suite*
random_suite()
{
@ -335,6 +435,7 @@ random_suite()
TCase *tc_providers = tcase_create("Provider stuffs");
TCase *tc_servers = tcase_create("Server stuffs");
TCase *tc_node = tcase_create("Node stuff");
TCase *tc_keys = tcase_create("Lasso keys");
suite_add_tcase(s, tc_providers);
tcase_add_test(tc_providers, test01_provider_new);
@ -351,6 +452,9 @@ random_suite()
tcase_add_test(tc_node, test06_lib_statuscode);
tcase_add_test(tc_node, test07_saml2_query_verify_signature);
suite_add_tcase(s, tc_keys);
tcase_add_test(tc_keys, test08_lasso_key);
return s;
}