The new OASIS "SAML V2.0 Enhanced Client or Proxy Profile Version 2.0"
specification added new options that can appear in the PAOS HTTP header.
Section 2.3.1 enumerates the following URN options which can appear
in the PAOS HEADER:
urn:oasis:names:tc:SAML:protocol:ext:channel-binding
urn:oasis:names:tc:SAML:2.0:cm:holder-of-key
urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp:2.0:WantAuthnRequestsSigned
urn:oasis:names:tc:SAML:2.0:conditions:delegation
Of these only the holder-of-key was previously defined in Lasso,
this patch adds the other 3 constants.
License: MIT
Signed-off-by: John Dennis <jdennis@redhat.com>
Note: the rest of this message is formatted as reStructuredText (rst).
Test Failure
============
The unit tests run by "make check" fail with the following error:
::
tests.c:61:F:Lasso keys:test08_lasso_key:0: No logging output expected: message «ID _E3F8E9116EE08F0E2607CF9789649BB4 already defined
» was emitted for domain «Lasso» at the level «128»
This is not a regression in Lasso, rather the failure is caused by one
of the components Lasso is dependent upon. It was first observed when
the identical Lasso package was built in Fedora 22, no problems were
observed in Fedora 21. This implies one or more updated components in
Fedora 22 is the cause.
This was a particularity difficult error to track down, first one had
to identify who was emitting the message and on what file descriptor
(stream) and who was triggering on the message emission and causing a
check failure. The obvious assumption the check library was
responsible for detecting the message emission and failing the test is
wrong.
Who is emitting the message and why?
------------------------------------
The message is emitted by libxml2 in the function `xmlAddID()`
(valid.c:2578). It occurs at the end of xmlAddID() when it detects the
ID (which is supposed to be unique to the document is already defined,
which for valid XML is illegal (violates uniquenesss constraint). The
message emission occurs because of the code fragment
::
if (xmlHashAddEntry(table, value, ret) < 0) {
#ifdef LIBXML_VALID_ENABLED
/*
* The id is already defined in this DTD.
*/
xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
"ID %s already defined\n", value, NULL, NULL);
#endif /* LIBXML_VALID_ENABLED */
xmlFreeID(ret);
return(NULL);
}
Why is the message emission different between libxml2 versions?
---------------------------------------------------------------
The change occured between libxml2 version 2.9.1 and 2.9.2 in commit
a16eb968075a82ec33b2c1e77db8909a35b44620
::
commit a16eb968075a82ec33b2c1e77db8909a35b44620
Author: Daniel Veillard <veillard@redhat.com>
Date: Tue Jun 10 16:06:14 2014 +0800
erroneously ignores a validation error if no error callback set
Reported by Stefan Behnel
https://bugzilla.gnome.org/show_bug.cgi?id=724903
diff --git a/valid.c b/valid.c
index aedd9d7..1e03a7c 100644
--- a/valid.c
+++ b/valid.c
@@ -2633,11 +2633,8 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
/*
* The id is already defined in this DTD.
*/
- if ((ctxt != NULL) && (ctxt->error != NULL)) {
- xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
- "ID %s already defined\n",
- value, NULL, NULL);
- }
+ xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
+ "ID %s already defined\n", value, NULL, NULL);
#endif /* LIBXML_VALID_ENABLED */
xmlFreeID(ret);
return(NULL);
In both versions of libxml2 the conditional complilation
LIBXML_VALID_ENABLED is enabled by default via the configure
script. What is different is the the requirement ctxt be
non-NULL. Lasso invokes xmlAddID with a NULL ctxt parameter. Because
the NULL test for ctxt is absent in libxlm2 2.9.2 the message is now
emitted where previously it was not.
Who triggers on messge emission and fails the test?
---------------------------------------------------
This is a Lasso feature, it is not part of libcheck. In tests/tests.c
is the following function
::
void error_logger(const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, G_GNUC_UNUSED gpointer user_data)
{
fail("No logging output expected: message «%s» was emitted for domain «%s» at the level"
" «%d»", message, log_domain, log_level);
}
Before the test are run the error_logger function is installed as a
glib handler
::
g_log_set_default_handler(error_logger, NULL);
When the message is emitted the error_logger traps it and invokes the
libcheck (deprecated) function fail() which aborts the test case.
Why does `test08_lasso_key` cause an XML validation failure?
------------------------------------------------------------
`test08_lasso_key` invokes `lasso_key_saml2_xml_verify()` twice on the
same XML document. Any time `lasso_key_saml2_xml_verify()` is called
more than once the XML validation will fail on the second and
subsequent invocations. This occurs because
`lasso_key_saml2_xml_verify()` invokes `lasso_verify_signature()`
passing it the node id in the `id_attr_name` parameter. Inside
`lasso_verify_signature()` is this code fragment:
::
/* Find ID */
if (id_attr_name) {
id = xmlGetProp(signed_node, (xmlChar*)id_attr_name);
if (id) {
xmlAddID(NULL, doc, id, xmlHasProp(signed_node, (xmlChar*)id_attr_name));
}
}
Note that it unconditionally invokes `xmlAddID()`, which adds the ID
to the set of unique element ID's in the document. But if you invoke
`xmlAddID()` more than once with the same ID in the same document you
violate the uniqueness constraint.
The ID needs to be registered in the document because the <Reference>
element of the <SignedInfo> may utilize an XPointer reference to the
signed data. In it's simplest form the XPointer reference is an ID
attribute on a node. Thus to locate the signed data referenced by the
ID it should (must?) be in a table of ID's for the document.
Simple Solution (patch)
-----------------------
The solution is simple now that the problem is understood. The ID
should not be unconditionally added to the document, instead it should
only be added if it's not already registered. Prior to calling
`xmlAddID()` one should call `xmlGetID()` and test for a NULL result
indicating the ID has not be registered previously.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
lasso_get_hmac_key() did not check return value. Now check the return
code, emit a critical message and return early with cleanup.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
ECP needs a place to store the messageID and idp_list. Normally values
like this would located in a "context" passed to the relevant
routines. But currently there is no such context, the closest thing to
a context we have is the profile so we add them here in the profile
private data using accessors. They are currently not relevant outside
of ECP.
Adds functions:
lasso_profile_get_message_id()
lasso_profile_set_message_id()
lasso_profile_get_idp_list()
lasso_profile_set_idp_list()
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
Re-implement lasso_profile_saml20_build_paos_request_msg() and
lasso_saml20_login_process_paos_response_msg() to use the
functionality introduced by earlier patches and to assure they are
functionally complete.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
ECP needs a place to store the messageID and idp_list. Normally values
like this would located in a "context" passed to the relevant
routines. But currently there is no such context, the closest thing to
a context we have is the profile so we add them here in the profile
private data using accessors. They are currently not relevant outside
of ECP.
Adds functions:
lasso_profile_get_message_id()
lasso_profile_set_message_id()
lasso_profile_get_idp_list()
lasso_profile_set_idp_list()
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
ECP does not require an SP to know the remote IdP provider. Existing
code made the assumption the remote provider always was
necessary. Determination and setting of the remote consumer URL is
different in the presence of ECP. Rework the logic to reflect
differing requirements.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
Lasso uses an internal private variable bound to the provider to
indicate which protocol the provider is servicing. It is vital this
value be correctly set because many Lasso routines used it to dispatch
to the appropriate protocol handlers.
Normally the provider's protocol conformance is set as a side-effect
of parsing the XML metadata that describes the provider (e.g. an SP or
IdP). However there are some providers (e.g. an ECP client) which do
not have metadata. For providers lacking metadata it is essential
there be a mechanism to set the protocol conformance otherwise the
library will malfunction.
The function comes with documentation that includes a clear warning
this is to be used only in limited circumstances.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
Implement everything needed to support a SAMLv2 ECP client.
Re-implement lasso_ecp_process_authn_request_msg() and
lasso_ecp_process_response_msg() to use the Lasso XML serialization
subsystem with the ECP and PASO LassoNode's introduced earlier. This
replaces one-off explicit direct use of the libxml API with Lasso
common code. In the process provide support for 100% of the ECP and
PAOS SAMLv2 parameters, not just a subset. Include support for
receiving an IDPList from the SP in conjuction with selecting an IdP
known to the ECP client. Add extensive documentation.
Modify LassoSamlp2AuthnRequest to preserve it's original XML (enable
keep_xmlnode flag) so that when serializing the SOAP request the
LassoSamlp2AuthnRequest received from the SP is exactly duplicated.
Add the following internal static utility functions:
is_provider_in_sp_idplist()
is_idp_entry_in_entity_id_list()
intersect_sp_idplist_with_entity_id_list()
Add the following exported utility functions:
lasso_ecp_is_provider_in_sp_idplist()
lasso_ecp_is_idp_entry_known_idp_supporting_ecp()
lasso_ecp_set_known_sp_provided_idp_entries_supporting_ecp()
lasso_ecp_has_sp_idplist()
lasso_ecp_get_endpoint_url_by_entity_id()
lasso_ecp_process_sp_idp_list()
Add the following members to the ECP class:
message_id
response_consumer_url
relaystate
issuer
provider_name
is_passive
sp_idp_list
known_sp_provided_idp_entries_supporting_ecp
known_idp_entity_ids_supporting_ecp
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
Re-implement lasso_node_export_to_ecp_soap_response() and
lasso_node_export_to_paos_request(). Add new function
lasso_node_export_to_paos_request_full() with full functionality which
deprecates lasso_node_export_to_paos_request().
The existing code had two significant deficiencies, it performed
explicit direct xml manipulation using the libxml API rather than
calling into Lasso's extensive XML utilities, this was in stark
contrast the rest of the Lasso library. It also failed to handle a
number of ECP parameters leaving a functionality gap in the API.
The new code makes use of the Lasso XML serialization
subsystem. Rather than hand crafted xml manipulation we use the ECP
and PAOS LassoNode objects introduced in an earlier patch. This is
consistent with the rest of Lasso and because those LassoNodes are
used elsewhere we have a better guarantee of robustness because the
same common code is being called from multiple places. Other Lasso
common utilities (some introduced in previous patches) are invoked
instead of handcrafted xml manipulation, once again common code is
preferred.
Finally lasso_node_export_to_paos_request_full() was introduced to
expose in the Lasso API all ECP
parameters. lasso_node_export_to_paos_request() now trivially calls
into lasso_node_export_to_paos_request_full().
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
Add lasso_server_get_filtered_provider_list() utility.
Iterate over the server providers and build a list of provider EntityID's who
have the specified role and at least one endpoint matching the
protocol_type and http_method. Return a GList list of EntityID's
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
Locate the provider in the server's list of providers, then select an
endpoint given the @endpoint_description and return that endpoint's URL.
If the provider cannot be found or if the provider does not have a
matching endpoint NULL will be returned.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
prefix_from_href_and_nodename() did not know about the ECP and PAOS
XML prefixes so add them.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
Add function lasso_node_export_to_soap_with_headers()
Utility function to build a full SOAP envelope message with arbitrary
headers. The LassoNode becomes the body of the SOAP envelope. The
headers are passed as a GList of LassoNode's and are added as header
elements to the SOAP envelope header. This is a flexible way to build
a SOAP envelope that contains headers without constraints on the
headers.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
LassoSamlp2IDPList is supposed to handle a list of LassoSamlp2IDPEntry
but in fact it had no list support. Change the snippet flag
SNIPPET_NODE to SNIPPET_LIST_NODES and add the special list comment on
the struct member so that the binding generator knows what type of
GList it is.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
The SAMLv2 protocol defines 5 XML types which we need to map to
LassoNode objectes so thay can be serialized from XML and back into
XML.
ecp:RelayState
ecp:Request
ecp:Response
paos:Request
paso:Response
This patch addes these 5 new LassoNode's and updates the build
configuration to include them.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
The existing lasso_saml20_profile_process_soap_response() assumed
there were no SOAP headers (prior to ECP none of the SOAP messages
contained headers). A new function
lasso_saml20_profile_process_soap_response_with_headers() was
implemented that serializes from the XML SOAP headers into a
LassoSoapHeader node and optionally will return the LassoSoapHeader
node.
The functionality in lasso_saml20_profile_process_soap_response() was
moved into the new
lasso_saml20_profile_process_soap_response_with_headers() and now
lasso_saml20_profile_process_soap_response() simply calls
lasso_saml20_profile_process_soap_response_with_headers() passing NULL
for the header return.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
The existing LassoSoapEnvelope constructors did not populate the node
with it's constituent members, namely a SOAP header (LassoSoapHeader)
and a SOAP body (LassoSoapBody). lasso_soap_envelope_new_full() allows
one to create a SOAP envelope and immediately begin to add header and
body elements.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
The existing Lasso code never made use of SOAP headers because up
until now nothing used them. LassoSoapHeader was unable to serialize
from XML into a GList of LassoNode objects because it was missing one
of the necessary snippet flags. This corrects this omission and now
parsing a SOAP header will yield a sequence of LassoNode's.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
The public utils.h header includes the private xml/private.h file
which is not installed. Therefore anyone trying to build against lasso
and include utils.h will fail because xml/private.h cannot be
found. There doesn't seem to be any need to include this file.
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
Because all warnings are treated as errors and this warning is emitted:
warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE"
the build fails.
The fix is to define _DEFAULT_SOURCE in lasso/xml/tools.c
The effect of defining the _DEFAULT_SOURCE macro is equivalent to
the effect of explicitly defining three macros in earlier glibc
versions: -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809C
Signed-off-by: John Dennis <jdennis@redhat.com>
License: MIT
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.