binding/SAMLArtifact.h \
binding/SecurityPolicy.h \
binding/SecurityPolicyRule.h \
+ binding/SimpleSigningRule.h \
binding/URLEncoder.h
encinclude_HEADERS = \
saml2/binding/SAML2ArtifactDecoder.h \
saml2/binding/SAML2ArtifactEncoder.h \
saml2/binding/SAML2POSTDecoder.h \
- saml2/binding/SAML2POSTEncoder.h
+ saml2/binding/SAML2POSTEncoder.h \
+ saml2/binding/SAML2Redirect.h \
+ saml2/binding/SAML2RedirectDecoder.h \
+ saml2/binding/SAML2RedirectEncoder.h
saml2mdinclude_HEADERS = \
saml2/metadata/AbstractMetadataProvider.h \
binding/impl/MessageSigningRule.cpp \
binding/impl/SAMLArtifact.cpp \
binding/impl/SecurityPolicy.cpp \
+ binding/impl/SimpleSigningRule.cpp \
binding/impl/URLEncoder.cpp \
saml1/core/impl/AssertionsImpl.cpp \
saml1/core/impl/AssertionsSchemaValidators.cpp \
saml2/binding/impl/SAML2ArtifactEncoder.cpp \
saml2/binding/impl/SAML2POSTDecoder.cpp \
saml2/binding/impl/SAML2POSTEncoder.cpp \
+ saml2/binding/impl/SAML2Redirect.cpp \
+ saml2/binding/impl/SAML2RedirectDecoder.cpp \
+ saml2/binding/impl/SAML2RedirectEncoder.cpp \
encryption/EncryptedKeyResolver.cpp \
security/impl/TrustEngine.cpp \
security/impl/AbstractPKIXTrustEngine.cpp \
auto_ptr<XSECCryptoHash> hasher(XSECPlatformUtils::g_cryptoProvider->hashSHA1());
if (hasher.get()) {
- auto_ptr<char> dup(strdup(s));
unsigned char buf[21];
- hasher->hash(reinterpret_cast<unsigned char*>(dup.get()),strlen(dup.get()));
+ hasher->hash(reinterpret_cast<unsigned char*>(const_cast<char*>(s)),strlen(s));
if (hasher->finish(buf,20)==20) {
string ret;
if (toHex) {
#define MESSAGEROUTING_POLICY_RULE "org.opensaml.binding.MessageRoutingRule"
/**
- * SecurityPolicyRule for protocol message signing.
+ * SecurityPolicyRule for protocol message XML signing.
*
- * Allows the message issuer to be authenticated using an XML or binding-specific
- * digital signature over the message. The transport layer is not considered.
+ * Allows the message issuer to be authenticated using an XML digital signature
+ * over the message. The transport layer is not considered.
*/
#define MESSAGESIGNING_POLICY_RULE "org.opensaml.binding.MessageSigningRule"
+
+ /**
+ * SecurityPolicyRule for protocol message "blob" signing.
+ *
+ * Allows the message issuer to be authenticated using a non-XML digital signature
+ * over the message body. The transport layer is not considered.
+ */
+ #define SIMPLESIGNING_POLICY_RULE "org.opensaml.binding.SimpleSigningRule"
};
#endif /* __saml_secrule_h__ */
--- /dev/null
+/*
+ * 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/SimpleSigningRule.h
+ *
+ * Blob-oriented signature checking SecurityPolicyRule
+ */
+
+#include <saml/binding/SecurityPolicyRule.h>
+
+
+namespace opensaml {
+ /**
+ * Blob-oriented signature checking SecurityPolicyRule for
+ * bindings that support non-XML signature techniques.
+ *
+ * Subclasses can provide support for additional message types
+ * by overriding the issuer derivation method.
+ */
+ class SAML_API SimpleSigningRule : public SecurityPolicyRule
+ {
+ public:
+ SimpleSigningRule(const DOMElement* e) {}
+ virtual ~SimpleSigningRule() {}
+
+ std::pair<saml2::Issuer*,const saml2md::RoleDescriptor*> evaluate(
+ const GenericRequest& request,
+ const xmltooling::XMLObject& message,
+ const saml2md::MetadataProvider* metadataProvider,
+ const xmltooling::QName* role,
+ const TrustEngine* trustEngine
+ ) const;
+
+ protected:
+ /**
+ * Examines the message and/or its contents and extracts the issuer's claimed
+ * identity along with a protocol identifier. The two together can be used to
+ * locate metadata to use in validating the signature. Conventions may be needed
+ * to properly encode non-SAML2 issuer information into a compatible form.
+ *
+ * <p>The caller is responsible for freeing the Issuer object.
+ *
+ * @param message message to examine
+ * @return a pair consisting of a SAML 2.0 Issuer object and a protocol constant.
+ */
+ virtual std::pair<saml2::Issuer*,const XMLCh*> getIssuerAndProtocol(const xmltooling::XMLObject& message) const;
+ };
+
+};
namespace saml2p {
SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML2ArtifactEncoderFactory;
SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML2POSTEncoderFactory;
+ SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML2RedirectEncoderFactory;
};
};
conf.MessageEncoderManager.registerFactory(samlconstants::SAML1_PROFILE_BROWSER_POST, saml1p::SAML1POSTEncoderFactory);
conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_ARTIFACT, saml2p::SAML2ArtifactEncoderFactory);
conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_POST, saml2p::SAML2POSTEncoderFactory);
+ conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_REDIRECT, saml2p::SAML2RedirectEncoderFactory);
}
namespace {
SAML_DLLLOCAL PluginManager<SecurityPolicyRule,const DOMElement*>::Factory MessageFlowRuleFactory;
SAML_DLLLOCAL PluginManager<SecurityPolicyRule,const DOMElement*>::Factory MessageRoutingRuleFactory;
SAML_DLLLOCAL PluginManager<SecurityPolicyRule,const DOMElement*>::Factory MessageSigningRuleFactory;
+ SAML_DLLLOCAL PluginManager<SecurityPolicyRule,const DOMElement*>::Factory SimpleSigningRuleFactory;
};
void SAML_API opensaml::registerSecurityPolicyRules()
conf.SecurityPolicyRuleManager.registerFactory(MESSAGEFLOW_POLICY_RULE, MessageFlowRuleFactory);
conf.SecurityPolicyRuleManager.registerFactory(MESSAGEROUTING_POLICY_RULE, MessageRoutingRuleFactory);
conf.SecurityPolicyRuleManager.registerFactory(MESSAGESIGNING_POLICY_RULE, MessageSigningRuleFactory);
+ conf.SecurityPolicyRuleManager.registerFactory(SIMPLESIGNING_POLICY_RULE, SimpleSigningRuleFactory);
}
SecurityPolicy::~SecurityPolicy()
--- /dev/null
+/*
+ * 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.
+ */
+
+/**
+ * SimpleSigningRule.cpp
+ *
+ * Blob-oriented signature checking SecurityPolicyRule
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "RootObject.h"
+#include "binding/HTTPRequest.h"
+#include "binding/SimpleSigningRule.h"
+#include "saml2/core/Protocols.h"
+#include "saml2/metadata/Metadata.h"
+#include "saml2/metadata/MetadataProvider.h"
+#include "security/TrustEngine.h"
+
+#include <log4cpp/Category.hh>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/ReplayCache.h>
+#include <xsec/enc/XSECCryptoException.hpp>
+#include <xsec/enc/XSECCryptoProvider.hpp>
+#include <xsec/framework/XSECException.hpp>
+
+using namespace opensaml::saml2md;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace opensaml {
+ SecurityPolicyRule* SAML_DLLLOCAL SimpleSigningRuleFactory(const DOMElement* const & e)
+ {
+ return new SimpleSigningRule(e);
+ }
+
+ // Appends a raw parameter=value pair to the string.
+ static bool appendParameter(string& s, const char* data, const char* name)
+ {
+ const char* start = strstr(data,name);
+ if (!start)
+ return false;
+ if (!s.empty())
+ s += '&';
+ const char* end = strchr(start,'&');
+ if (end)
+ s.append(start, end-start);
+ else
+ s.append(start);
+ return true;
+ }
+};
+
+
+pair<saml2::Issuer*,const saml2md::RoleDescriptor*> SimpleSigningRule::evaluate(
+ const GenericRequest& request,
+ const XMLObject& message,
+ const MetadataProvider* metadataProvider,
+ const QName* role,
+ const opensaml::TrustEngine* trustEngine
+ ) const
+{
+ Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SimpleSigning");
+ log.debug("evaluating simple signing policy");
+
+ pair<saml2::Issuer*,const RoleDescriptor*> ret = pair<saml2::Issuer*,const RoleDescriptor*>(NULL,NULL);
+
+ if (!metadataProvider || !role || !trustEngine) {
+ log.debug("ignoring message, no metadata supplied");
+ return ret;
+ }
+
+ const char* signature = request.getParameter("Signature");
+ if (!signature) {
+ log.debug("ignoring unsigned message");
+ return ret;
+ }
+
+ const char* sigAlgorithm = request.getParameter("SigAlg");
+ if (!sigAlgorithm) {
+ log.error("SigAlg parameter not found, no way to verify the signature");
+ return ret;
+ }
+
+ try {
+ log.debug("extracting issuer from message");
+ pair<saml2::Issuer*,const XMLCh*> issuerInfo = getIssuerAndProtocol(message);
+
+ auto_ptr<saml2::Issuer> issuer(issuerInfo.first);
+ if (!issuerInfo.first || !issuerInfo.second ||
+ (issuer->getFormat() && !XMLString::equals(issuer->getFormat(), saml2::NameIDType::ENTITY))) {
+ log.warn("issuer identity not estabished, or was not an entityID");
+ return ret;
+ }
+
+ log.debug("searching metadata for message issuer...");
+ const EntityDescriptor* entity = metadataProvider->getEntityDescriptor(issuer->getName());
+ if (!entity) {
+ auto_ptr_char temp(issuer->getName());
+ log.warn("no metadata found, can't establish identity of issuer (%s)", temp.get());
+ return ret;
+ }
+
+ log.debug("matched assertion issuer against metadata, searching for applicable role...");
+ const RoleDescriptor* roledesc=entity->getRoleDescriptor(*role, issuerInfo.second);
+ if (!roledesc) {
+ log.warn("unable to find compatible role (%s) in metadata", role->toString().c_str());
+ return ret;
+ }
+
+ // We have to construct a string containing the signature input by accessing the
+ // request directly. We can't use the decoded parameters because we need the raw
+ // data and URL-encoding isn't canonical.
+ string input;
+ const HTTPRequest& httpRequest = dynamic_cast<const HTTPRequest&>(request);
+ const char* raw =
+ (!strcmp(httpRequest.getMethod(), "GET")) ? httpRequest.getQueryString() : httpRequest.getRequestBody();
+ if (!appendParameter(input, raw, "SAMLRequest="))
+ appendParameter(input, raw, "SAMLResponse=");
+ appendParameter(input, raw, "RelayState=");
+ appendParameter(input, raw, "SigAlg=");
+
+ auto_ptr_XMLCh alg(sigAlgorithm);
+ if (!trustEngine->validate(alg.get(), signature, NULL, input.c_str(), input.length(), *roledesc, metadataProvider->getKeyResolver())) {
+ log.error("unable to verify signature on message with supplied trust engine");
+ return ret;
+ }
+
+ if (log.isDebugEnabled()) {
+ auto_ptr_char iname(entity->getEntityID());
+ log.debug("message from (%s), signature verified", iname.get());
+ }
+
+ ret.first = issuer.release();
+ ret.second = roledesc;
+ }
+ catch (bad_cast&) {
+ // Just trap it.
+ log.warn("caught a bad_cast while extracting issuer");
+ }
+ return ret;
+}
+
+pair<saml2::Issuer*,const XMLCh*> SimpleSigningRule::getIssuerAndProtocol(const XMLObject& message) const
+{
+ // We just let any bad casts throw here.
+
+ // Shortcuts some of the casting.
+ const XMLCh* ns = message.getElementQName().getNamespaceURI();
+ if (ns) {
+ if (XMLString::equals(ns, samlconstants::SAML20P_NS) || XMLString::equals(ns, samlconstants::SAML20_NS)) {
+ // 2.0 namespace should be castable to a specialized 2.0 root.
+ const saml2::RootObject& root = dynamic_cast<const saml2::RootObject&>(message);
+ saml2::Issuer* issuer = root.getIssuer();
+ if (issuer && issuer->getName()) {
+ return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
+ }
+
+ // No issuer in the message, so we have to try the Response approach.
+ const vector<saml2::Assertion*>& assertions = dynamic_cast<const saml2p::Response&>(message).getAssertions();
+ if (!assertions.empty()) {
+ issuer = assertions.front()->getIssuer();
+ if (issuer && issuer->getName())
+ return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
+ }
+ }
+ }
+ return pair<saml2::Issuer*,const XMLCh*>(NULL,NULL);
+}
<Tool\r
Name="VCLinkerTool"\r
AdditionalDependencies="..\..\cpp-xmltooling\Debug\xmltooling_1D.lib xerces-c_2D.lib xsec_1D.lib log4cppD.lib libeay32_0_9_8D.lib"\r
- OutputFile="$(OutDir)\$(ProjectName)_6D.dll"\r
+ OutputFile="$(OutDir)\$(ProjectName)2_0D.dll"\r
LinkIncremental="2"\r
GenerateDebugInformation="true"\r
SubSystem="2"\r
+ ImportLibrary="$(TargetDir)$(ProjectName)2D.lib"\r
TargetMachine="1"\r
/>\r
<Tool\r
<Tool\r
Name="VCLinkerTool"\r
AdditionalDependencies="..\..\cpp-xmltooling\Release\xmltooling_1.lib xerces-c_2.lib xsec_1.lib log4cpp.lib libeay32_0_9_8.lib"\r
- OutputFile="$(OutDir)\$(ProjectName)_6.dll"\r
+ OutputFile="$(OutDir)\$(ProjectName)2_0.dll"\r
LinkIncremental="1"\r
GenerateDebugInformation="true"\r
SubSystem="2"\r
OptimizeReferences="2"\r
EnableCOMDATFolding="2"\r
+ ImportLibrary="$(TargetDir)$(ProjectName)2.lib"\r
TargetMachine="1"\r
/>\r
<Tool\r
RelativePath=".\saml2\binding\impl\SAML2RedirectDecoder.cpp"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\saml2\binding\impl\SAML2RedirectEncoder.cpp"\r
+ >\r
+ </File>\r
</Filter>\r
</Filter>\r
</Filter>\r
>\r
</File>\r
<File\r
+ RelativePath=".\binding\impl\SimpleSigningRule.cpp"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath=".\binding\impl\URLEncoder.cpp"\r
>\r
</File>\r
>\r
</File>\r
<File\r
+ RelativePath=".\zlib\compress.c"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath=".\zlib\crc32.c"\r
>\r
</File>\r
<File\r
+ RelativePath=".\zlib\deflate.c"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath=".\zlib\inffast.c"\r
>\r
</File>\r
>\r
</File>\r
<File\r
+ RelativePath=".\zlib\trees.c"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath=".\zlib\zutil.c"\r
>\r
</File>\r
RelativePath=".\saml2\binding\SAML2RedirectDecoder.h"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\saml2\binding\SAML2RedirectEncoder.h"\r
+ >\r
+ </File>\r
</Filter>\r
</Filter>\r
<Filter\r
>\r
</File>\r
<File\r
+ RelativePath=".\binding\SimpleSigningRule.h"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath=".\binding\URLEncoder.h"\r
>\r
</File>\r
>\r
</File>\r
<File\r
+ RelativePath=".\zlib\deflate.h"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath=".\zlib\inffast.h"\r
>\r
</File>\r
>\r
</File>\r
<File\r
+ RelativePath=".\zlib\trees.h"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath=".\zlib\zconf.h"\r
>\r
</File>\r
namespace opensaml {
namespace saml2p {
+ /**
+ * Deflates data in accordance with RFC1951. The caller must free the
+ * resulting buffer using delete[]
+ *
+ * @param in the data to compress
+ * @param in_len length of input data
+ * @param out_len will contain the length of the resulting data
+ * @return allocated buffer of out_len bytes containing deflated data
+ */
+ char* deflate(char* in, unsigned int in_len, unsigned int* out_len);
- unsigned int inflate(char* in, unsigned int inlen, std::ostream& out);
-
+ /**
+ * Inflates data compressed in accordance with RFC1951 and sends the
+ * results to an output stream.
+ *
+ * @param in the data to inflate
+ * @param in_len length of input data
+ * @param out reference to output stream to receive data
+ * @return number of bytes written to stream
+ */
+ unsigned int inflate(char* in, unsigned int in_len, std::ostream& out);
};
};
--- /dev/null
+/*
+ * 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/saml2/binding/SAML2RedirectEncoder.h
+ *
+ * SAML 2.0 HTTP-Redirect binding message encoder
+ */
+
+#include <saml/binding/MessageEncoder.h>
+
+
+namespace opensaml {
+ namespace saml2p {
+
+ /**
+ * SAML 2.0 HTTP-Redirect binding message encoder
+ */
+ class SAML_API SAML2RedirectEncoder : public MessageEncoder
+ {
+ public:
+ SAML2RedirectEncoder(const DOMElement* e) {}
+ virtual ~SAML2RedirectEncoder() {}
+
+ long encode(
+ GenericResponse& genericResponse,
+ xmltooling::XMLObject* xmlObject,
+ const char* destination,
+ const char* recipientID=NULL,
+ const char* relayState=NULL,
+ const xmlsignature::CredentialResolver* credResolver=NULL,
+ const XMLCh* sigAlgorithm=NULL
+ ) const;
+ };
+
+ };
+};
};
};
+char* opensaml::saml2p::deflate(char* in, unsigned int in_len, unsigned int* out_len)
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("deflate");
+#endif
+ Category& log = Category::getInstance(SAML_LOGCAT".MessageDecoder.SAML2Redirect.zlib");
+
+ z_stream z;
+ memset(&z, 0, sizeof(z_stream));
+
+ z.zalloc = saml_zalloc;
+ z.zfree = saml_zfree;
+ z.opaque = NULL;
+ z.next_in = (Bytef*)in;
+ z.avail_in = in_len;
+ *out_len = 0;
+
+ int ret = deflateInit2(&z, 9, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
+ if (ret != Z_OK) {
+ log.error("zlib deflateInit2 failed with error code (%d)", ret);
+ return NULL;
+ }
+
+ int dlen = in_len + (in_len >> 8) + 12; /* orig_size * 1.001 + 12 */
+ char* out = new char[dlen];
+ z.next_out = (Bytef*)out;
+ z.avail_out = dlen;
+
+ ret = deflate(&z, Z_FINISH);
+ if (ret != Z_STREAM_END) {
+ deflateEnd(&z);
+ log.error("zlib deflateInit2 failed with error code (%d)", ret);
+ delete[] out;
+ }
+
+ *out_len = z.total_out;
+ deflateEnd(&z);
+ return out;
+}
+
unsigned int opensaml::saml2p::inflate(char* in, unsigned int in_len, ostream& out)
{
#ifdef _DEBUG
int ret = inflateInit2(&z, -15);
if (ret != Z_OK) {
- log.error("zlib inflateInit failed with error code (%d)", ret);
+ log.error("zlib inflateInit2 failed with error code (%d)", ret);
delete[] buf;
return 0;
}
--- /dev/null
+/*
+ * 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.
+ */
+
+/**
+ * SAML2RedirectEncoder.cpp
+ *
+ * SAML 2.0 HTTP-POST binding message encoder
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "binding/HTTPResponse.h"
+#include "binding/URLEncoder.h"
+#include "saml2/binding/SAML2Redirect.h"
+#include "saml2/binding/SAML2RedirectEncoder.h"
+#include "saml2/core/Protocols.h"
+
+#include <fstream>
+#include <sstream>
+#include <log4cpp/Category.hh>
+#include <xercesc/util/Base64.hpp>
+#include <xsec/enc/XSECCryptoException.hpp>
+#include <xsec/enc/XSECCryptoProvider.hpp>
+#include <xsec/framework/XSECException.hpp>
+#include <xmltooling/util/NDC.h>
+
+using namespace opensaml::saml2p;
+using namespace opensaml;
+using namespace xmlsignature;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace opensaml {
+ namespace saml2p {
+ MessageEncoder* SAML_DLLLOCAL SAML2RedirectEncoderFactory(const DOMElement* const & e)
+ {
+ return new SAML2RedirectEncoder(e);
+ }
+ };
+};
+
+long SAML2RedirectEncoder::encode(
+ GenericResponse& genericResponse,
+ XMLObject* xmlObject,
+ const char* destination,
+ 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.SAML2POST");
+
+ log.debug("validating input");
+ HTTPResponse* httpResponse=dynamic_cast<HTTPResponse*>(&genericResponse);
+ if (!httpResponse)
+ throw BindingException("Unable to cast response interface to HTTPResponse type.");
+ if (xmlObject->getParent())
+ throw BindingException("Cannot encode XML content with parent.");
+
+ StatusResponseType* response = NULL;
+ RequestAbstractType* request = dynamic_cast<RequestAbstractType*>(xmlObject);
+ if (!request) {
+ response = dynamic_cast<StatusResponseType*>(xmlObject);
+ if (!response)
+ throw BindingException("XML content for SAML 2.0 HTTP-POST Encoder must be a SAML 2.0 protocol message.");
+ }
+
+ // Check for XML signature.
+ if (request ? request->getSignature() : response->getSignature()) {
+ log.debug("message already signed, removing native signature due to size considerations");
+ request ? request->setSignature(NULL) : response->setSignature(NULL);
+ }
+
+ log.debug("marshalling, deflating, base64-encoding the message");
+ DOMElement* rootElement = xmlObject->marshall();
+ string xmlbuf;
+ XMLHelper::serialize(rootElement, xmlbuf);
+
+ unsigned int len;
+ char* deflated = deflate(const_cast<char*>(xmlbuf.c_str()), xmlbuf.length(), &len);
+ if (!deflated)
+ throw BindingException("Failed to deflate message.");
+
+ XMLByte* encoded=Base64::encode(reinterpret_cast<XMLByte*>(deflated),len,&len);
+ delete[] deflated;
+ if (!encoded)
+ throw BindingException("Base64 encoding of XML failed.");
+
+ // Create beginnings of redirect query string.
+ URLEncoder* escaper = SAMLConfig::getConfig().getURLEncoder();
+ xmlbuf.erase();
+ xmlbuf.append(reinterpret_cast<char*>(encoded),len);
+ xmlbuf = (request ? "SAMLRequest=" : "SAMLResponse=") + escaper->encode(xmlbuf.c_str());
+ if (relayState)
+ xmlbuf = xmlbuf + "&RelayState=" + escaper->encode(relayState);
+
+ if (credResolver) {
+ // Sign the query string after adding the algorithm.
+ if (!sigAlgorithm)
+ sigAlgorithm = DSIGConstants::s_unicodeStrURIRSA_SHA1;
+ auto_ptr_char alg(sigAlgorithm);
+ xmlbuf = xmlbuf + "&SigAlg=" + escaper->encode(alg.get());
+
+ try {
+ char sigbuf[1024];
+ memset(sigbuf,0,sizeof(sigbuf));
+ auto_ptr<XSECCryptoKey> key(credResolver->getKey());
+ Signature::createRawSignature(key.get(), sigAlgorithm, xmlbuf.c_str(), xmlbuf.length(), sigbuf, sizeof(sigbuf)-1);
+ xmlbuf = xmlbuf + "&Signature=" + escaper->encode(sigbuf);
+ }
+ catch(XSECException& e) {
+ auto_ptr_char temp(e.getMsg());
+ throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());
+ }
+ catch(XSECCryptoException& e) {
+ throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());
+ }
+ }
+
+ // Generate redirect.
+ log.debug("message encoded, sending redirect to client");
+ xmlbuf.insert(0,1,(strchr(destination,'?') ? '&' : '?'));
+ xmlbuf.insert(0,destination);
+ long ret = httpResponse->sendRedirect(xmlbuf.c_str());
+
+ // Cleanup by destroying XML.
+ delete xmlObject;
+
+ return ret;
+}
) const;
virtual bool validate(
+ const XMLCh* sigAlgorithm,
+ const char* sig,
+ xmlsignature::KeyInfo* keyInfo,
+ const char* in,
+ unsigned int in_len,
+ const saml2md::RoleDescriptor& role,
+ const xmlsignature::KeyResolver* keyResolver=NULL
+ ) const;
+
+ virtual bool validate(
XSECCryptoX509* certEE,
const std::vector<XSECCryptoX509*>& certChain,
const saml2md::RoleDescriptor& role,
const xmlsignature::KeyResolver* keyResolver=NULL
) const;
virtual bool validate(
+ const XMLCh* sigAlgorithm,
+ const char* sig,
+ xmlsignature::KeyInfo* keyInfo,
+ const char* in,
+ unsigned int in_len,
+ const saml2md::RoleDescriptor& role,
+ const xmlsignature::KeyResolver* keyResolver=NULL
+ ) const;
+ virtual bool validate(
XSECCryptoX509* certEE,
const std::vector<XSECCryptoX509*>& certChain,
const saml2md::RoleDescriptor& role,
* @param sig reference to a signature object to validate
* @param role metadata role supplying key information
* @param keyResolver optional externally supplied KeyResolver, or NULL
+ * @return true iff the signature validates
*/
virtual bool validate(
xmlsignature::Signature& sig,
const saml2md::RoleDescriptor& role,
const xmlsignature::KeyResolver* keyResolver=NULL
) const=0;
+
+ /**
+ * Determines whether a raw signature is correct and valid with respect to
+ * the information known about the signer.
+ *
+ * <p>A custom KeyResolver can be supplied from outside the TrustEngine.
+ * Alternatively, one may be specified to the plugin constructor.
+ * A non-caching, inline resolver will be used as a fallback.
+ *
+ * @param sigAlgorithm XML Signature identifier for the algorithm used
+ * @param sig null-terminated base64-encoded signature value
+ * @param keyInfo KeyInfo object accompanying the signature, if any
+ * @param in the input data over which the signature was created
+ * @param in_len size of input data in bytes
+ * @param role metadata role supplying key information
+ * @param keyResolver optional externally supplied KeyResolver, or NULL
+ * @return true iff the signature validates
+ */
+ virtual bool validate(
+ const XMLCh* sigAlgorithm,
+ const char* sig,
+ xmlsignature::KeyInfo* keyInfo,
+ const char* in,
+ unsigned int in_len,
+ const saml2md::RoleDescriptor& role,
+ const xmlsignature::KeyResolver* keyResolver=NULL
+ ) const=0;
};
log.error("failed to verify signature with embedded certificates");
return false;
}
+
+bool AbstractPKIXTrustEngine::validate(
+ const XMLCh* sigAlgorithm,
+ const char* sig,
+ KeyInfo* keyInfo,
+ const char* in,
+ unsigned int in_len,
+ const RoleDescriptor& role,
+ const KeyResolver* keyResolver
+ ) const
+{
+#ifdef _DEBUG
+ NDC ndc("validate");
+#endif
+ Category& log=Category::getInstance(SAML_LOGCAT".TrustEngine");
+
+ // Pull the certificate chain out of the KeyInfo using an inline KeyResolver.
+ KeyResolver::ResolvedCertificates certs;
+ if (!keyInfo || 0==m_inlineResolver->resolveCertificates(keyInfo, certs)) {
+ log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
+ return false;
+ }
+
+ log.debug("validating signature using certificate from within KeyInfo");
+
+ // Find and save off a pointer to the certificate that unlocks the object.
+ // Most of the time, this will be the first one anyway.
+ XSECCryptoX509* certEE=NULL;
+ SignatureValidator keyValidator;
+ for (vector<XSECCryptoX509*>::const_iterator i=certs.v().begin(); !certEE && i!=certs.v().end(); ++i) {
+ try {
+ auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
+ if (Signature::verifyRawSignature(key.get(), sigAlgorithm, sig, in, in_len)) {
+ log.info("signature verified with key inside signature, attempting certificate validation...");
+ certEE=(*i);
+ }
+ }
+ catch (SignatureException&) {
+ // trap failures
+ }
+ }
+
+ if (certEE)
+ return validate(certEE,certs.v(),role,true,keyResolver);
+
+ log.error("failed to verify signature with embedded certificates");
+ return false;
+}
}
bool ChainingTrustEngine::validate(
+ const XMLCh* sigAlgorithm,
+ const char* sig,
+ KeyInfo* keyInfo,
+ const char* in,
+ unsigned int in_len,
+ const RoleDescriptor& role,
+ const KeyResolver* keyResolver
+ ) const
+{
+ for (vector<X509TrustEngine*>::const_iterator i=m_engines.begin(); i!=m_engines.end(); ++i) {
+ if (static_cast<TrustEngine*>(*i)->validate(sigAlgorithm, sig, keyInfo, in, in_len, role, keyResolver))
+ return true;
+ }
+ return false;
+}
+
+bool ChainingTrustEngine::validate(
XSECCryptoX509* certEE,
const vector<XSECCryptoX509*>& certChain,
const RoleDescriptor& role,
const KeyResolver* keyResolver=NULL
) const;
virtual bool validate(
+ const XMLCh* sigAlgorithm,
+ const char* sig,
+ KeyInfo* keyInfo,
+ const char* in,
+ unsigned int in_len,
+ const RoleDescriptor& role,
+ const KeyResolver* keyResolver=NULL
+ ) const;
+ virtual bool validate(
XSECCryptoX509* certEE,
const vector<XSECCryptoX509*>& certChain,
const RoleDescriptor& role,
}
bool ExplicitKeyTrustEngine::validate(
+ const XMLCh* sigAlgorithm,
+ const char* sig,
+ KeyInfo* keyInfo,
+ const char* in,
+ unsigned int in_len,
+ const RoleDescriptor& role,
+ const KeyResolver* keyResolver
+ ) const
+{
+ MetadataKeyInfoIterator keys(role);
+ return static_cast<xmltooling::TrustEngine*>(m_engine)->validate(sigAlgorithm,sig,keyInfo,in,in_len,keys,keyResolver);
+}
+
+bool ExplicitKeyTrustEngine::validate(
XSECCryptoX509* certEE,
const vector<XSECCryptoX509*>& certChain,
const RoleDescriptor& role,
saml2/core/impl/Terminate20Test.h \
saml2/binding/SAML2ArtifactTest.h \
saml2/binding/SAML2POSTTest.h \
+ saml2/binding/SAML2RedirectTest.h \
saml2/metadata/FilesystemMetadataProviderTest.h
noinst_HEADERS = \
opensaml::TrustEngine* m_trust;\r
map<string,string> m_fields;\r
map<string,string> m_headers;\r
- string m_method,m_url;\r
+ string m_method,m_url,m_query;\r
vector<XSECCryptoX509*> m_clientCerts;\r
vector<const SecurityPolicyRule*> m_rules;\r
\r
m_headers.clear();\r
m_method.erase();\r
m_url.erase();\r
+ m_query.erase();\r
\r
try {\r
string config = data_path + "binding/ExampleMetadataProvider.xml";\r
m_trust = SAMLConfig::getConfig().TrustEngineManager.newPlugin(EXPLICIT_KEY_SAMLTRUSTENGINE, NULL);\r
\r
m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(MESSAGEFLOW_POLICY_RULE,NULL));\r
+ m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(MESSAGEROUTING_POLICY_RULE,NULL));\r
m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(MESSAGESIGNING_POLICY_RULE,NULL));\r
+ m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(SIMPLESIGNING_POLICY_RULE,NULL));\r
}\r
catch (XMLToolingException& ex) {\r
TS_TRACE(ex.what());\r
m_headers.clear();\r
m_method.erase();\r
m_url.erase();\r
+ m_query.erase();\r
}\r
\r
// HTTPRequest methods\r
}\r
\r
const char* getQueryString() const {\r
- return NULL;\r
+ return m_query.c_str();\r
}\r
\r
string getRemoteUser() const {\r
char* pch = strchr(dup,'?');\r
if (pch) {\r
*pch++=0;\r
+ m_query = pch;\r
char* name=pch;\r
while (name && *name) {\r
pch=strchr(pch,'=');\r
--- /dev/null
+/*
+ * 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 "binding.h"
+
+#include <saml/saml2/core/Protocols.h>
+
+using namespace opensaml::saml2p;
+using namespace opensaml::saml2;
+
+class SAML2RedirectTest : public CxxTest::TestSuite, public SAMLBindingBaseTestCase {
+public:
+ void setUp() {
+ SAMLBindingBaseTestCase::setUp();
+ }
+
+ void tearDown() {
+ SAMLBindingBaseTestCase::tearDown();
+ }
+
+ void testSAML2Redirect() {
+ try {
+ QName idprole(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);
+ SecurityPolicy policy(m_rules, m_metadata, &idprole, m_trust);
+
+ // Read message to use from file.
+ string path = data_path + "saml2/binding/SAML2Response.xml";
+ ifstream in(path.c_str());
+ DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in);
+ XercesJanitor<DOMDocument> janitor(doc);
+ auto_ptr<Response> toSend(
+ dynamic_cast<Response*>(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(),true))
+ );
+ janitor.release();
+
+ // Freshen timestamp and ID.
+ toSend->setIssueInstant(time(NULL));
+ toSend->setID(NULL);
+
+ // Encode message.
+ auto_ptr<MessageEncoder> encoder(
+ SAMLConfig::getConfig().MessageEncoderManager.newPlugin(samlconstants::SAML20_BINDING_HTTP_REDIRECT, NULL)
+ );
+ encoder->encode(*this,toSend.get(),"https://sp.example.org/SAML/SSO","https://sp.example.org/","state",m_creds);
+ toSend.release();
+
+ // Decode message.
+ string relayState;
+ auto_ptr<MessageDecoder> decoder(
+ SAMLConfig::getConfig().MessageDecoderManager.newPlugin(samlconstants::SAML20_BINDING_HTTP_REDIRECT, NULL)
+ );
+ Locker locker(m_metadata);
+ auto_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState,*this,policy)));
+
+ // Test the results.
+ TSM_ASSERT_EQUALS("RelayState was not the expected result.", relayState, "state");
+ TSM_ASSERT("SAML Response not decoded successfully.", response.get());
+ TSM_ASSERT("Message was not verified.", policy.getIssuer()!=NULL);
+ auto_ptr_char entityID(policy.getIssuer()->getName());
+ TSM_ASSERT("Issuer was not expected.", !strcmp(entityID.get(),"https://idp.example.org/"));
+ TSM_ASSERT_EQUALS("Assertion count was not correct.", response->getAssertions().size(), 1);
+
+ // Trigger a replay.
+ TSM_ASSERT_THROWS("Did not catch the replay.", decoder->decode(relayState,*this,policy), BindingException);
+ }
+ catch (XMLToolingException& ex) {
+ TS_TRACE(ex.what());
+ throw;
+ }
+ }
+};
RelativePath=".\saml2\binding\SAML2POSTTest.cpp"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\saml2\binding\SAML2RedirectTest.cpp"\r
+ >\r
+ </File>\r
</Filter>\r
</Filter>\r
<Filter\r
/>\r
</FileConfiguration>\r
</File>\r
+ <File\r
+ RelativePath=".\saml2\binding\SAML2RedirectTest.h"\r
+ >\r
+ <FileConfiguration\r
+ Name="Debug|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o "$(InputDir)$(InputName)".cpp "$(InputPath)""\r
+ Outputs=""$(InputDir)$(InputName)".cpp"\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o "$(InputDir)$(InputName)".cpp "$(InputPath)""\r
+ Outputs=""$(InputDir)$(InputName)".cpp"\r
+ />\r
+ </FileConfiguration>\r
+ </File>\r
</Filter>\r
</Filter>\r
<Filter\r