From 54bc3fd9396935d92c53bbb69d003e8d121720c2 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Tue, 26 Sep 2006 19:32:50 +0000 Subject: [PATCH] MessageEncoder, ArtifactMap, and SAML 1.x encoder classes. --- .project | 18 +- saml/Makefile.am | 7 + saml/SAMLConfig.cpp | 12 +- saml/SAMLConfig.h | 51 ++++- saml/binding/ArtifactMap.h | 88 +++++++++ saml/binding/MessageEncoder.h | 186 +++++++++++++++++ saml/binding/impl/ArtifactMap.cpp | 220 +++++++++++++++++++++ saml/binding/impl/MessageEncoder.cpp | 42 ++++ saml/exceptions.h | 2 +- saml/saml.vcproj | 32 +++ saml/saml1/binding/SAML1ArtifactEncoder.h | 49 +++++ saml/saml1/binding/SAML1POSTEncoder.h | 49 +++++ saml/saml1/binding/impl/SAML1ArtifactEncoder.cpp | 98 +++++++++ saml/saml1/binding/impl/SAML1POSTEncoder.cpp | 140 +++++++++++++ .../metadata/impl/SignatureMetadataFilter.cpp | 4 +- samltest/ArtifactMapTest.h | 55 ++++++ samltest/Makefile.am | 1 + samltest/samltest.vcproj | 26 +++ 18 files changed, 1059 insertions(+), 21 deletions(-) create mode 100644 saml/binding/ArtifactMap.h create mode 100644 saml/binding/MessageEncoder.h create mode 100644 saml/binding/impl/ArtifactMap.cpp create mode 100644 saml/binding/impl/MessageEncoder.cpp create mode 100644 saml/saml1/binding/SAML1ArtifactEncoder.h create mode 100644 saml/saml1/binding/SAML1POSTEncoder.h create mode 100644 saml/saml1/binding/impl/SAML1ArtifactEncoder.cpp create mode 100644 saml/saml1/binding/impl/SAML1POSTEncoder.cpp create mode 100644 samltest/ArtifactMapTest.h diff --git a/.project b/.project index 22f4aa1..5a45fbb 100644 --- a/.project +++ b/.project @@ -19,34 +19,34 @@ org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser; - org.eclipse.cdt.make.core.enableAutoBuild - false - - org.eclipse.cdt.make.core.environment - org.eclipse.cdt.make.core.enableFullBuild - true + org.eclipse.cdt.make.core.enableAutoBuild + false org.eclipse.cdt.make.core.build.target.inc all - org.eclipse.cdt.make.core.enabledIncrementalBuild + org.eclipse.cdt.make.core.enableFullBuild true - org.eclipse.cdt.make.core.build.target.clean - clean + org.eclipse.cdt.make.core.enabledIncrementalBuild + true org.eclipse.cdt.make.core.build.command make + org.eclipse.cdt.make.core.build.target.clean + clean + + org.eclipse.cdt.make.core.enableCleanBuild true diff --git a/saml/Makefile.am b/saml/Makefile.am index f9e6af3..40d2744 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -31,6 +31,8 @@ libsamlinclude_HEADERS = \ SAMLConfig.h samlbindinclude_HEADERS = \ + binding/ArtifactMap.h \ + binding/MessageEncoder.h \ binding/SAMLArtifact.h encinclude_HEADERS = \ @@ -56,6 +58,8 @@ saml1coreinclude_HEADERS = \ saml1/core/Protocols.h saml1bindinclude_HEADERS = \ + saml1/binding/SAML1ArtifactEncoder.h \ + saml1/binding/SAML1POSTEncoder.h \ saml1/binding/SAMLArtifactType0001.h \ saml1/binding/SAMLArtifactType0002.h @@ -80,11 +84,14 @@ noinst_HEADERS = \ libsaml_la_SOURCES = \ SAMLConfig.cpp \ + binding/impl/ArtifactMap.cpp \ binding/impl/SAMLArtifact.cpp \ saml1/core/impl/AssertionsImpl.cpp \ saml1/core/impl/AssertionsSchemaValidators.cpp \ saml1/core/impl/ProtocolsImpl.cpp \ saml1/core/impl/ProtocolsSchemaValidators.cpp \ + saml1/binding/impl/SAML1ArtifactEncoder.cpp \ + saml1/binding/impl/SAML1POSTEncoder.cpp \ saml1/binding/impl/SAMLArtifactType0001.cpp \ saml1/binding/impl/SAMLArtifactType0002.cpp \ saml2/core/impl/Assertions20Impl.cpp \ diff --git a/saml/SAMLConfig.cpp b/saml/SAMLConfig.cpp index 80a49e1..3d40148 100644 --- a/saml/SAMLConfig.cpp +++ b/saml/SAMLConfig.cpp @@ -24,6 +24,7 @@ #include "internal.h" #include "exceptions.h" #include "SAMLConfig.h" +#include "binding/MessageEncoder.h" #include "binding/SAMLArtifact.h" #include "saml1/core/Assertions.h" #include "saml1/core/Protocols.h" @@ -65,6 +66,7 @@ extern "C" void SAML_API xmltooling_extension_term() DECL_EXCEPTION_FACTORY(ArtifactException,opensaml); DECL_EXCEPTION_FACTORY(MetadataFilterException,opensaml::saml2md); +DECL_EXCEPTION_FACTORY(BindingException,opensaml); namespace opensaml { SAMLInternalConfig g_config; @@ -95,7 +97,9 @@ bool SAMLInternalConfig::init(bool initXMLTooling) REGISTER_EXCEPTION_FACTORY(ArtifactException,opensaml); REGISTER_EXCEPTION_FACTORY(MetadataFilterException,opensaml::saml2md); + REGISTER_EXCEPTION_FACTORY(BindingException,opensaml); + registerMessageEncoders(); registerSAMLArtifacts(); saml1::registerAssertionClasses(); saml1p::registerProtocolClasses(); @@ -122,10 +126,14 @@ void SAMLInternalConfig::term(bool termXMLTooling) saml2::AssertionSchemaValidators.destroyValidators(); saml2md::MetadataSchemaValidators.destroyValidators(); - SAMLArtifactManager.deregisterFactories(); + TrustEngineManager.deregisterFactories(); MetadataFilterManager.deregisterFactories(); MetadataProviderManager.deregisterFactories(); - TrustEngineManager.deregisterFactories(); + SAMLArtifactManager.deregisterFactories(); + MessageEncoderManager.deregisterFactories(); + + delete m_artifactMap; + m_artifactMap = NULL; if (termXMLTooling) { XMLToolingConfig::getConfig().term(); diff --git a/saml/SAMLConfig.h b/saml/SAMLConfig.h index 0474707..90e00ad 100644 --- a/saml/SAMLConfig.h +++ b/saml/SAMLConfig.h @@ -24,6 +24,7 @@ #define __saml_config_h__ #include +#include #include #include @@ -36,6 +37,8 @@ */ namespace opensaml { + class SAML_API MessageEncoder; + class SAML_API MessageDecoder; class SAML_API SAMLArtifact; class SAML_API TrustEngine; @@ -91,6 +94,27 @@ namespace opensaml { virtual void term(bool termXMLTooling=true)=0; /** + * Sets the global ArtifactMap instance. + * This method must be externally synchronized with any code that uses the object. + * Any previously set object is destroyed. + * + * @param artifactMap new ArtifactMap instance to store + */ + void setArtifactMap(ArtifactMap* artifactMap) { + delete m_artifactMap; + m_artifactMap = artifactMap; + } + + /** + * Returns the global ArtifactMap instance. + * + * @return global ArtifactMap + */ + ArtifactMap* getArtifactMap() const { + return m_artifactMap; + } + + /** * Generate random information using the underlying security library * * @param buf buffer for the information @@ -123,16 +147,16 @@ namespace opensaml { * @return SHA-1 hash of the data */ virtual std::string hashSHA1(const char* s, bool toHex=false)=0; - + /** - * Manages factories for MetadataProvider plugins. + * Manages factories for MessageDecoder plugins. */ - xmltooling::PluginManager MetadataProviderManager; - + xmltooling::PluginManager MessageDecoderManager; + /** - * Manages factories for MetadataFilter plugins. + * Manages factories for MessageEncoder plugins. */ - xmltooling::PluginManager MetadataFilterManager; + xmltooling::PluginManager MessageEncoderManager; /** * Manages factories for SAMLArtifact plugins. @@ -144,8 +168,21 @@ namespace opensaml { */ xmltooling::PluginManager TrustEngineManager; + /** + * Manages factories for MetadataProvider plugins. + */ + xmltooling::PluginManager MetadataProviderManager; + + /** + * Manages factories for MetadataFilter plugins. + */ + xmltooling::PluginManager MetadataFilterManager; + protected: - SAMLConfig() {} + SAMLConfig() : m_artifactMap(NULL) {} + + /** Global ArtifactMap instance for use by artifact-related functions. */ + ArtifactMap* m_artifactMap; }; #if defined (_MSC_VER) diff --git a/saml/binding/ArtifactMap.h b/saml/binding/ArtifactMap.h new file mode 100644 index 0000000..7f57f88 --- /dev/null +++ b/saml/binding/ArtifactMap.h @@ -0,0 +1,88 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file saml/binding/ArtifactMap.h + * + * Helper class for SAMLArtifact mapping and retrieval. + */ + +#ifndef __saml_artmap_h__ +#define __saml_artmap_h__ + +#include +#include +#include +#include + +namespace opensaml { + + class SAML_API SAMLArtifact; + class SAML_DLLLOCAL ArtifactMappings; + + /** + * Helper class for SAMLArtifact mapping and retrieval. + */ + class SAML_API ArtifactMap + { + MAKE_NONCOPYABLE(ArtifactMap); + public: + + /** + * Creates a map on top of a particular storage service context, or in-memory. + * + * @param storage pointer to a StorageService, or NULL to keep map in memory + * @param context optional label for storage context + * @param artifactTTL time to live value, determines how long artifact remains valid + */ + ArtifactMap(xmltooling::StorageService* storage=NULL, const char* context=NULL, int artifactTTL=180); + + virtual ~ArtifactMap(); + + /** + * Associates XML content with an artifact and optionally a specific relying party. + * Specifying no relying party means that the first attempt to resolve the artifact + * will succeed. The XML content cannot have a parent object, and any existing references + * to the content will be invalidated. + * + * @param content the XML content to map to an artifact + * @param artifact the artifact representing the XML content + * @param relyingParty entityID of the party authorized to resolve the artifact + * @return the generated artifact + */ + virtual void storeContent(xmltooling::XMLObject* content, const SAMLArtifact* artifact, const char* relyingParty=NULL); + + /** + * Retrieves the XML content represented by the artifact. The identity of the + * relying party can be supplied, if known. If the wrong party tries to resolve + * an artifact, an exception will be thrown and the mapping will be removed. + * The caller is responsible for freeing the XML content returned. + * + * @param artifact the artifact representing the XML content + * @param relyingParty entityID of the party trying to resolve the artifact + * @return the XML content + */ + virtual xmltooling::XMLObject* retrieveContent(const SAMLArtifact* artifact, const char* relyingParty=NULL); + + private: + xmltooling::StorageService* m_storage; + std::string m_context; + ArtifactMappings* m_mappings; + int m_artifactTTL; + }; +}; + +#endif /* __saml_artmap_h__ */ diff --git a/saml/binding/MessageEncoder.h b/saml/binding/MessageEncoder.h new file mode 100644 index 0000000..4213bf3 --- /dev/null +++ b/saml/binding/MessageEncoder.h @@ -0,0 +1,186 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file saml/binding/MessageEncoder.h + * + * Interface to SAML protocol binding message encoders. + */ + +#ifndef __saml_encoder_h__ +#define __saml_encoder_h__ + +#include + +#include +#include +#include +#include +#include + +namespace opensaml { + + class SAML_API SAMLArtifact; + namespace saml2p { + class SAML_API SAML2Artifact; + }; + + /** + * Interface to SAML protocol binding message encoders. + */ + class SAML_API MessageEncoder + { + MAKE_NONCOPYABLE(MessageEncoder); + public: + virtual ~MessageEncoder() {} + + /** + * Interface to caller-supplied URL-encoding mechanism. + * + * Since URL-encoding is not canonical, it's important that the same + * encoder is used during some binding-specific signature operations. + */ + class SAML_API URLEncoder { + MAKE_NONCOPYABLE(URLEncoder); + protected: + URLEncoder() {} + public: + virtual ~URLEncoder() {} + + /** + * Produce a URL-safe but equivalent version of the input string. + * + * @param s input string to encode + * @return a string object containing the result of encoding the input + */ + virtual std::string encode(const char* s) const=0; + }; + + /** + * Provides a URLEncoder implementation for the MessageEncoder to use. + * The implementation's lifetime must be longer than the lifetime of this object. + * This method must be externally synchronized. + * + * @param urlEncoder a URLEncoder implementation to use + */ + void setURLEncoder(const URLEncoder* urlEncoder) { + m_urlEncoder = urlEncoder; + } + + /** + * Interface to caller-supplied artifact generation mechanism. + * + * Generating an artifact for storage and retrieval requires knowledge of + * the sender's SourceID (or sometimes SourceLocation), and the relying party's + * preferred artifact type. This information can be supplied using whatever + * configuration or defaults are appropriate for the SAML application. + * An ArtifactMap implementation will invoke the supplied generator interface + * when it requires an artifact be created. + */ + class SAML_API ArtifactGenerator { + MAKE_NONCOPYABLE(ArtifactGenerator); + protected: + ArtifactGenerator() {} + public: + virtual ~ArtifactGenerator() {} + + /** + * Generate a SAML 1.x artifact suitable for consumption by the relying party. + * + * @param relyingParty the party that will recieve the artifact + * @return a SAML 1.x artifact with a random assertion handle + */ + virtual SAMLArtifact* generateSAML1Artifact(const char* relyingParty) const=0; + + /** + * Generate a SAML 2.0 artifact suitable for consumption by the relying party. + * + * @param relyingParty the party that will recieve the artifact + * @return a SAML 2.0 artifact with a random message handle + */ + virtual saml2p::SAML2Artifact* generateSAML2Artifact(const char* relyingParty) const=0; + }; + + /** + * Provides an ArtifactGenerator implementation for the MessageEncoder to use. + * The implementation's lifetime must be longer than the lifetime of this object. + * This method must be externally synchronized. + * + * @param artifactGenerator an ArtifactGenerator implementation to use + */ + void setArtifactGenerator(ArtifactGenerator* artifactGenerator) { + m_artifactGenerator = artifactGenerator; + } + + /** + * Encodes an XML object/message into a set of binding-specific data "fields". + * The XML content cannot have a parent object, and any existing references to + * the content will be invalidated if the encode method returns successfully. + * + * If a CredentialResolver is supplied, the message is also signed in a + * binding-specific manner. The CredentialResolver MUST + * be locked by the caller. + * + *

An embedded URLEncoder instance may be required by some bindings + * in order to produce predictable signature input. + * + *

Artifact-based bindings require an ArtifactGenerator be set to + * produce an artifact suitable for the intended recipient. + * + *

Note that the name/value pairs resulting from the encoding operation are + * NOT URL-encoded or otherwise transformed. It is the caller's + * responsibility to apply any necessary encoding when preparing the data for + * transport. + * + * @param outputFields name/value pairs containing the results of encoding the message + * @param xmlObject XML object/message to encode + * @param recipientID optional entityID of message recipient + * @param relayState optional RelayState value to accompany message + * @param credResolver optional CredentialResolver instance to supply signing material + * @param sigAlgorithm optional signature algorithm identifier + */ + virtual void encode( + std::map& outputFields, + xmltooling::XMLObject* xmlObject, + const char* recipientID=NULL, + const char* relayState=NULL, + const xmlsignature::CredentialResolver* credResolver=NULL, + const XMLCh* sigAlgorithm=NULL + ) const=0; + + protected: + MessageEncoder() : m_urlEncoder(NULL), m_artifactGenerator(NULL) {} + + /** Pointer to a URLEncoder implementation. */ + const URLEncoder* m_urlEncoder; + + /** Pointer to an ArtifactGenerator implementation. */ + const ArtifactGenerator* m_artifactGenerator; + }; + + /** + * Registers MessageEncoder plugins into the runtime. + */ + void SAML_API registerMessageEncoders(); + + /** MessageEncoder for SAML 1.x Browser/Artifact "binding" (really part of profile) */ + #define SAML1_ARTIFACT_ENCODER "org.opensaml.saml1.binding.SAML1ArtifactEncoder" + + /** MessageEncoder for SAML 1.x Browser/POST "binding" (really part of profile) */ + #define SAML1_POST_ENCODER "org.opensaml.saml1.binding.SAML1POSTEncoder" +}; + +#endif /* __saml_encoder_h__ */ diff --git a/saml/binding/impl/ArtifactMap.cpp b/saml/binding/impl/ArtifactMap.cpp new file mode 100644 index 0000000..6866c6b --- /dev/null +++ b/saml/binding/impl/ArtifactMap.cpp @@ -0,0 +1,220 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ArtifactMap.cpp + * + * Helper class for SAMLArtifact mapping and retrieval. + */ + +#include "internal.h" +#include "exceptions.h" +#include "binding/ArtifactMap.h" +#include "binding/SAMLArtifact.h" + +#include +#include +#include +#include +#include + +using namespace opensaml; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +namespace opensaml { + // In-memory storage of mappings instead of using storage API. + class SAML_DLLLOCAL ArtifactMappings + { + public: + ArtifactMappings() : m_lock(Mutex::create()) {} + ~ArtifactMappings() { + delete m_lock; + for (map::iterator i=m_artMap.begin(); i!=m_artMap.end(); ++i) + delete i->second.m_xml; + } + void storeContent(XMLObject* content, const SAMLArtifact* artifact, const char* relyingParty, int TTL); + XMLObject* retrieveContent(const SAMLArtifact* artifact, const char* relyingParty); + + private: + struct SAML_DLLLOCAL Mapping { + Mapping() : m_xml(NULL), m_expires(0) {} + XMLObject* m_xml; + string m_relying; + time_t m_expires; + }; + + void removeMapping(const map::iterator& i); + + Mutex* m_lock; + map m_artMap; + multimap m_expMap; + }; +}; + +void ArtifactMappings::removeMapping(const map::iterator& i) +{ + // Update secondary map. + pair::iterator,multimap::iterator> range = + m_expMap.equal_range(i->second.m_expires); + for (; range.first != range.second; ++range.first) { + if (range.first->second == i->first) { + m_expMap.erase(range.first); + break; + } + } + delete i->second.m_xml; + m_artMap.erase(i); +} + +void ArtifactMappings::storeContent(XMLObject* content, const SAMLArtifact* artifact, const char* relyingParty, int TTL) +{ + Lock wrapper(m_lock); + + // Garbage collect any expired artifacts. + time_t now=time(NULL); + multimap::iterator stop=m_expMap.upper_bound(now); + for (multimap::iterator i=m_expMap.begin(); i!=stop; m_expMap.erase(i++)) { + delete m_artMap[i->second].m_xml; + m_artMap.erase(i->second); + } + + // Key is the hexed handle. + string hexed = SAMLArtifact::toHex(artifact->getMessageHandle()); + Mapping& m = m_artMap[hexed]; + m.m_xml = content; + if (relyingParty) + m.m_relying = relyingParty; + m.m_expires = now + TTL; + m_expMap.insert(make_pair(m.m_expires,hexed)); +} + +XMLObject* ArtifactMappings::retrieveContent(const SAMLArtifact* artifact, const char* relyingParty) +{ + Category& log=Category::getInstance(SAML_LOGCAT".ArtifactMap"); + Lock wrapper(m_lock); + + map::iterator i=m_artMap.find(SAMLArtifact::toHex(artifact->getMessageHandle())); + if (i==m_artMap.end()) + throw BindingException("Requested artifact not in map or may have expired."); + + if (!(i->second.m_relying.empty())) { + if (!relyingParty || i->second.m_relying != relyingParty) { + log.warn( + "request from (%s) for artifact issued to (%s)", + relyingParty ? relyingParty : "unknown", i->second.m_relying.c_str() + ); + removeMapping(i); + throw BindingException("Unauthorized artifact mapping request."); + } + } + + if (time(NULL) >= i->second.m_expires) { + removeMapping(i); + throw BindingException("Requested artifact has expired."); + } + + log.debug("resolved artifact for (%s)", relyingParty ? relyingParty : "unknown"); + XMLObject* ret = i->second.m_xml; + i->second.m_xml = NULL; // clear member so it doesn't get deleted + removeMapping(i); + return ret; +} + +ArtifactMap::ArtifactMap(xmltooling::StorageService* storage, const char* context, int artifactTTL) + : m_storage(storage), m_context(context ? context : "opensaml::ArtifactMap"), m_mappings(NULL), m_artifactTTL(artifactTTL) +{ + if (!m_storage) + m_mappings = new ArtifactMappings(); +} + +ArtifactMap::~ArtifactMap() +{ + delete m_mappings; +} + +static const XMLCh M[] = UNICODE_LITERAL_7(M,a,p,p,i,n,g); +static const XMLCh RP[] = UNICODE_LITERAL_12(r,e,l,y,i,n,g,P,a,r,t,y); + +void ArtifactMap::storeContent(XMLObject* content, const SAMLArtifact* artifact, const char* relyingParty) +{ + if (content->getParent()) + throw BindingException("Cannot store artifact mapping for XML content with parent."); + else if (!m_storage) + return m_mappings->storeContent(content, artifact, relyingParty, m_artifactTTL); + + // Marshall with defaulted document, to reuse existing DOM and/or create a bound Document. + DOMElement* root = content->marshall(); + + // Build a DOM with the same document to store the relyingParty mapping. + if (relyingParty) { + auto_ptr_XMLCh temp(relyingParty); + root = root->getOwnerDocument()->createElementNS(NULL,M); + root->setAttributeNS(NULL,RP,temp.get()); + root->appendChild(content->getDOM()); + } + + // Serialize the root element, whatever it is, for storage. + string xmlbuf; + XMLHelper::serialize(root, xmlbuf); + m_storage->createText( + m_context.c_str(), SAMLArtifact::toHex(artifact->getMessageHandle()).c_str(), xmlbuf.c_str(), time(NULL) + m_artifactTTL + ); + + // Cleanup by destroying XML. + delete content; +} + +XMLObject* ArtifactMap::retrieveContent(const SAMLArtifact* artifact, const char* relyingParty) +{ +#ifdef _DEBUG + xmltooling::NDC ndc("retrieveContent"); +#endif + + if (!m_storage) + return m_mappings->retrieveContent(artifact, relyingParty); + + string xmlbuf; + string key = SAMLArtifact::toHex(artifact->getMessageHandle()); + if (!m_storage->readText(m_context.c_str(), key.c_str(), &xmlbuf)) + throw BindingException("Artifact not found in mapping database."); + + istringstream is(xmlbuf); + DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(is); + XercesJanitor janitor(doc); + + Category& log=Category::getInstance(SAML_LOGCAT".ArtifactMap"); + m_storage->deleteText(m_context.c_str(), key.c_str()); + + // Check the root element. + DOMElement* messageRoot = doc->getDocumentElement(); + if (XMLHelper::isNodeNamed(messageRoot, NULL, M)) { + auto_ptr_char temp(messageRoot->getAttributeNS(NULL,RP)); + if (!relyingParty || strcmp(temp.get(),relyingParty)) { + log.warn("request from (%s) for artifact issued to (%s)", relyingParty ? relyingParty : "unknown", temp.get()); + throw BindingException("Unauthorized artifact mapping request."); + } + messageRoot = XMLHelper::getFirstChildElement(messageRoot); + } + + // Unmarshall... + XMLObject* xmlObject = XMLObjectBuilder::buildOneFromElement(messageRoot, true); // bind document + janitor.release(); + + log.debug("resolved artifact for (%s)", relyingParty ? relyingParty : "unknown"); + return xmlObject; +} diff --git a/saml/binding/impl/MessageEncoder.cpp b/saml/binding/impl/MessageEncoder.cpp new file mode 100644 index 0000000..7510254 --- /dev/null +++ b/saml/binding/impl/MessageEncoder.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * MessageEncoder.cpp + * + * Interface to SAML protocol binding message encoders. + */ + +#include "internal.h" +#include "binding/MessageEncoder.h" + +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +namespace opensaml { + namespace saml1p { + SAML_DLLLOCAL PluginManager::Factory SAML1ArtifactEncoderFactory; + SAML_DLLLOCAL PluginManager::Factory SAML1POSTEncoderFactory; + }; +}; + +void SAML_API opensaml::registerMessageEncoders() +{ + SAMLConfig& conf=SAMLConfig::getConfig(); + conf.MessageEncoderManager.registerFactory(SAML1_ARTIFACT_ENCODER, saml1p::SAML1ArtifactEncoderFactory); + conf.MessageEncoderManager.registerFactory(SAML1_POST_ENCODER, saml1p::SAML1POSTEncoderFactory); +} diff --git a/saml/exceptions.h b/saml/exceptions.h index 700991a..186aa4e 100644 --- a/saml/exceptions.h +++ b/saml/exceptions.h @@ -27,7 +27,7 @@ #include namespace opensaml { - + DECL_XMLTOOLING_EXCEPTION(BindingException,SAML_EXCEPTIONAPI(SAML_API),opensaml,xmltooling::XMLToolingException,Exceptions in SAML binding processing); }; #endif /* __saml_exceptions_h__ */ diff --git a/saml/saml.vcproj b/saml/saml.vcproj index 7db28ff..1c47f4e 100644 --- a/saml/saml.vcproj +++ b/saml/saml.vcproj @@ -235,6 +235,14 @@ Name="impl" > + + + + @@ -424,6 +432,14 @@ Name="impl" > + + + + @@ -486,6 +502,14 @@ Name="binding" > + + + + @@ -603,6 +627,14 @@ Name="binding" > + + + + diff --git a/saml/saml1/binding/SAML1ArtifactEncoder.h b/saml/saml1/binding/SAML1ArtifactEncoder.h new file mode 100644 index 0000000..3bd1cdd --- /dev/null +++ b/saml/saml1/binding/SAML1ArtifactEncoder.h @@ -0,0 +1,49 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file saml/saml1/binding/SAML1ArtifactEncoder.h + * + * SAML 1.x Artifact binding/profile message encoder + */ + +#include + + +namespace opensaml { + namespace saml1p { + + /** + * SAML 1.x Artifact binding/profile message encoder + */ + class SAML_API SAML1ArtifactEncoder : public MessageEncoder + { + public: + SAML1ArtifactEncoder(const DOMElement* e); + virtual ~SAML1ArtifactEncoder(); + + void encode( + std::map& outputFields, + xmltooling::XMLObject* xmlObject, + const char* recipientID=NULL, + const char* relayState=NULL, + const xmlsignature::CredentialResolver* credResolver=NULL, + const XMLCh* sigAlgorithm=NULL + ) const; + }; + + }; +}; diff --git a/saml/saml1/binding/SAML1POSTEncoder.h b/saml/saml1/binding/SAML1POSTEncoder.h new file mode 100644 index 0000000..c99c38d --- /dev/null +++ b/saml/saml1/binding/SAML1POSTEncoder.h @@ -0,0 +1,49 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file saml/saml1/binding/SAML1POSTEncoder.h + * + * SAML 1.x POST binding/profile message encoder + */ + +#include + + +namespace opensaml { + namespace saml1p { + + /** + * SAML 1.x POST binding/profile message encoder + */ + class SAML_API SAML1POSTEncoder : public MessageEncoder + { + public: + SAML1POSTEncoder(const DOMElement* e); + virtual ~SAML1POSTEncoder(); + + void encode( + std::map& outputFields, + xmltooling::XMLObject* xmlObject, + const char* recipientID=NULL, + const char* relayState=NULL, + const xmlsignature::CredentialResolver* credResolver=NULL, + const XMLCh* sigAlgorithm=NULL + ) const; + }; + + }; +}; diff --git a/saml/saml1/binding/impl/SAML1ArtifactEncoder.cpp b/saml/saml1/binding/impl/SAML1ArtifactEncoder.cpp new file mode 100644 index 0000000..197ecb4 --- /dev/null +++ b/saml/saml1/binding/impl/SAML1ArtifactEncoder.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * SAML1ArtifactEncoder.cpp + * + * SAML 1.x Artifact binding/profile message encoder + */ + +#include "internal.h" +#include "exceptions.h" +#include "saml/binding/SAMLArtifact.h" +#include "saml1/binding/SAML1ArtifactEncoder.h" +#include "saml1/core/Assertions.h" + +#include +#include + +using namespace opensaml::saml1; +using namespace opensaml::saml1p; +using namespace opensaml; +using namespace xmlsignature; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +namespace opensaml { + namespace saml1p { + MessageEncoder* SAML_DLLLOCAL SAML1ArtifactEncoderFactory(const DOMElement* const & e) + { + return new SAML1ArtifactEncoder(e); + } + }; +}; + +SAML1ArtifactEncoder::SAML1ArtifactEncoder(const DOMElement* e) {} + +SAML1ArtifactEncoder::~SAML1ArtifactEncoder() {} + +void SAML1ArtifactEncoder::encode( + map& outputFields, + XMLObject* xmlObject, + const char* recipientID, + const char* relayState, + const CredentialResolver* credResolver, + const XMLCh* sigAlgorithm + ) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("encode"); +#endif + Category& log = Category::getInstance(SAML_LOGCAT".MessageEncoder.SAML1Artifact"); + log.debug("validating input"); + + outputFields.clear(); + if (xmlObject->getParent()) + throw BindingException("Cannot encode XML content with parent."); + Assertion* assertion = dynamic_cast(xmlObject); + if (!assertion) + throw BindingException("XML content for SAML 1.x Artifact Encoder must be a SAML 1.x ."); + if (!relayState) + throw BindingException("SAML 1.x Artifact Encoder requires relay state (TARGET) value."); + + // Signing is a protocol level issue, so no signing here... + + ArtifactMap* mapper = SAMLConfig::getConfig().getArtifactMap(); + if (!mapper) + throw BindingException("SAML 1.x Artifact Encoder requires ArtifactMap be set in configuration."); + + // Obtain a fresh artifact. + if (!m_artifactGenerator) + throw BindingException("SAML 1.x Artifact Encoder requires an ArtifactGenerator instance."); + log.debug("obtaining new artifact for relying party (%s)", recipientID ? recipientID : "unknown"); + auto_ptr artifact(m_artifactGenerator->generateSAML1Artifact(recipientID)); + + // Pass back output fields. + outputFields["SAMLart"] = artifact->encode(); + outputFields["TARGET"] = relayState; + + // Store the assertion. Last step in storage will be to delete the XML. + log.debug("storing artifact and content in map"); + mapper->storeContent(xmlObject, artifact.get(), recipientID); + + log.debug("message encoded"); +} diff --git a/saml/saml1/binding/impl/SAML1POSTEncoder.cpp b/saml/saml1/binding/impl/SAML1POSTEncoder.cpp new file mode 100644 index 0000000..f62bd62 --- /dev/null +++ b/saml/saml1/binding/impl/SAML1POSTEncoder.cpp @@ -0,0 +1,140 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * SAML1POSTEncoder.cpp + * + * SAML 1.x Artifact binding/profile message encoder + */ + +#include "internal.h" +#include "exceptions.h" +#include "saml1/binding/SAML1POSTEncoder.h" +#include "saml1/core/Protocols.h" + +#include +#include +#include + +using namespace opensaml::saml1p; +using namespace opensaml; +using namespace xmlsignature; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +namespace opensaml { + namespace saml1p { + MessageEncoder* SAML_DLLLOCAL SAML1POSTEncoderFactory(const DOMElement* const & e) + { + return new SAML1POSTEncoder(e); + } + + class SAML_DLLLOCAL _addcert : public binary_function { + public: + void operator()(X509Data* bag, XSECCryptoX509* cert) const { + safeBuffer& buf=cert->getDEREncodingSB(); + X509Certificate* x=X509CertificateBuilder::buildX509Certificate(); + x->setValue(buf.sbStrToXMLCh()); + bag->getX509Certificates().push_back(x); + } + }; + }; +}; + +SAML1POSTEncoder::SAML1POSTEncoder(const DOMElement* e) {} + +SAML1POSTEncoder::~SAML1POSTEncoder() {} + +void SAML1POSTEncoder::encode( + map& outputFields, + XMLObject* xmlObject, + const char* recipientID, + const char* relayState, + const CredentialResolver* credResolver, + const XMLCh* sigAlgorithm + ) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("encode"); +#endif + Category& log = Category::getInstance(SAML_LOGCAT".MessageEncoder.SAML1POST"); + log.debug("validating input"); + + outputFields.clear(); + if (xmlObject->getParent()) + throw BindingException("Cannot encode XML content with parent."); + Response* response = dynamic_cast(xmlObject); + if (!response) + throw BindingException("XML content for SAML 1.x POST Encoder must be a SAML 1.x ."); + if (!relayState) + throw BindingException("SAML 1.x POST Encoder requires relay state (TARGET) value."); + + DOMElement* rootElement = NULL; + if (credResolver) { + // Signature based on native XML signing. + if (response->getSignature()) { + log.debug("response already signed, skipping signature operation"); + } + else { + log.debug("signing and marshalling the response"); + + // Append a Signature. + response->setSignature(SignatureBuilder::buildSignature()); + response->getSignature()->setSigningKey(credResolver->getKey()); + + // Build KeyInfo. + const vector& certs = credResolver->getCertificates(); + if (!certs.empty()) { + KeyInfo* keyInfo=KeyInfoBuilder::buildKeyInfo(); + X509Data* x509Data=X509DataBuilder::buildX509Data(); + keyInfo->getX509Datas().push_back(x509Data); + for_each(certs.begin(),certs.end(),bind1st(_addcert(),x509Data)); + response->getSignature()->setKeyInfo(keyInfo); + } + + // Sign response while marshalling. + vector sigs(1,response->getSignature()); + rootElement = response->marshall((DOMDocument*)NULL,&sigs); + } + } + else { + log.debug("marshalling the response"); + rootElement = response->marshall(); + } + + string xmlbuf; + XMLHelper::serialize(rootElement, xmlbuf); + unsigned int len=0; + XMLByte* out=Base64::encode(reinterpret_cast(xmlbuf.data()),xmlbuf.size(),&len); + if (out) { + xmlbuf.erase(); + xmlbuf.append(reinterpret_cast(out),len); + XMLString::release(&out); + } + else { + throw BindingException("Base64 encoding of XML failed."); + } + + // Pass back output fields. + outputFields["SAMLResponse"] = xmlbuf; + outputFields["TARGET"] = relayState; + + // Cleanup by destroying XML. + delete xmlObject; + + log.debug("message encoded"); +} diff --git a/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp b/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp index a008992..465fd04 100644 --- a/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp +++ b/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp @@ -15,9 +15,9 @@ */ /** - * BlacklistMetadataFilter.cpp + * SignatureMetadataFilter.cpp * - * Removes blacklisted entities from a metadata instance + * Filters out unsigned or mis-signed elements. */ #include "internal.h" diff --git a/samltest/ArtifactMapTest.h b/samltest/ArtifactMapTest.h new file mode 100644 index 0000000..72a8b2d --- /dev/null +++ b/samltest/ArtifactMapTest.h @@ -0,0 +1,55 @@ +/* + * Copyright 2001-2005 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include +#include +#include + +using namespace opensaml::saml2p; +using namespace opensaml; +using namespace std; + +class ArtifactMapTest : public CxxTest::TestSuite +{ +public: + string providerIdStr; + string handle; + ArtifactMap* artifactMap; + void setUp() { + if (handle.empty()) { + providerIdStr = "https://idp.org/SAML"; + SAMLConfig::getConfig().generateRandomBytes(handle,SAML2ArtifactType0004::HANDLE_LENGTH); + } + artifactMap = new ArtifactMap(); + } + void tearDown() { + delete artifactMap; + artifactMap=NULL; + } + void testArtifactMap(void) { + auto_ptr response(ResponseBuilder::buildResponse()); + + SAML2ArtifactType0004 artifact(SAMLConfig::getConfig().hashSHA1(providerIdStr.c_str()),666,handle); + + artifactMap->storeContent(response.get(), &artifact, providerIdStr.c_str()); + response.release(); + + auto_ptr xmlObject(artifactMap->retrieveContent(&artifact, providerIdStr.c_str())); + TSM_ASSERT_THROWS("Artifact resolution improperly succeeded.", artifactMap->retrieveContent(&artifact), BindingException); + TSM_ASSERT("Mapped content was not a Response.", dynamic_cast(xmlObject.get())!=NULL); + } +}; diff --git a/samltest/Makefile.am b/samltest/Makefile.am index 8aab1f1..d81f080 100644 --- a/samltest/Makefile.am +++ b/samltest/Makefile.am @@ -13,6 +13,7 @@ samltest_h = \ SAMLArtifactType0001Test.h \ SAMLArtifactType0002Test.h \ SAMLArtifactType0004Test.h \ + ArtifactMapTest.h \ signature/SAML1AssertionTest.h \ signature/SAML1RequestTest.h \ signature/SAML1ResponseTest.h \ diff --git a/samltest/samltest.vcproj b/samltest/samltest.vcproj index 7264b35..cf9102d 100644 --- a/samltest/samltest.vcproj +++ b/samltest/samltest.vcproj @@ -179,6 +179,10 @@ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > + + @@ -541,6 +545,28 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + + + + + + + -- 2.1.4