X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-opensaml.git;a=blobdiff_plain;f=saml%2Fbinding%2Fimpl%2FSimpleSigningRule.cpp;h=ee46c1104927c2145b3a4149bedd139ef136489c;hp=6ba58f893671bfd9a3ccc360309c578b5e4813df;hb=1462057b3b9ae7e165d34d988e30b14c213672ca;hpb=b951e528ad7d0764ddc4ced037a8bd53bd3c9890 diff --git a/saml/binding/impl/SimpleSigningRule.cpp b/saml/binding/impl/SimpleSigningRule.cpp index 6ba58f8..ee46c11 100644 --- a/saml/binding/impl/SimpleSigningRule.cpp +++ b/saml/binding/impl/SimpleSigningRule.cpp @@ -1,164 +1,201 @@ -/* - * 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 +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * UCAID licenses this file to you 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 + * 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. + * 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 + * Blob-oriented signature checking SecurityPolicyRule. */ #include "internal.h" #include "exceptions.h" -#include "binding/HTTPRequest.h" -#include "binding/SimpleSigningRule.h" +#include "binding/SecurityPolicy.h" +#include "binding/SecurityPolicyRule.h" +#include "saml2/core/Assertions.h" #include "saml2/metadata/Metadata.h" +#include "saml2/metadata/MetadataCredentialCriteria.h" #include "saml2/metadata/MetadataProvider.h" -#include "security/TrustEngine.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace opensaml::saml2md; using namespace opensaml; +using namespace xmltooling::logging; using namespace xmltooling; -using namespace log4cpp; using namespace std; using xmlsignature::KeyInfo; +using xmlsignature::SignatureException; namespace opensaml { + class SAML_DLLLOCAL SimpleSigningRule : public SecurityPolicyRule + { + public: + SimpleSigningRule(const DOMElement* e); + virtual ~SimpleSigningRule() {} + + const char* getType() const { + return SIMPLESIGNING_POLICY_RULE; + } + bool evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; + + private: + // Appends a raw parameter=value pair to the string. + static bool appendParameter(string& s, const char* data, const char* name); + + bool m_errorFatal; + }; + 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; - } + static const XMLCh errorFatal[] = UNICODE_LITERAL_10(e,r,r,o,r,F,a,t,a,l); }; +bool SimpleSigningRule::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 SimpleSigningRule::evaluate( - const GenericRequest& request, - const XMLObject& message, - const MetadataProvider* metadataProvider, - const QName* role, - const opensaml::TrustEngine* trustEngine, - const MessageExtractor& extractor - ) const +SimpleSigningRule::SimpleSigningRule(const DOMElement* e) : m_errorFatal(XMLHelper::getAttrBool(e, false, errorFatal)) { - Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SimpleSigning"); - log.debug("evaluating simple signing policy"); - - pair ret = pair(NULL,NULL); +} + +bool SimpleSigningRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const +{ + Category& log=Category::getInstance(SAML_LOGCAT ".SecurityPolicyRule.SimpleSigning"); - if (!metadataProvider || !role || !trustEngine) { - log.debug("ignoring message, no metadata supplied"); - return ret; + if (!policy.getIssuerMetadata()) { + log.debug("ignoring message, no issuer metadata supplied"); + return false; } - - const char* signature = request.getParameter("Signature"); - if (!signature) { - log.debug("ignoring unsigned message"); - return ret; + + const SignatureTrustEngine* sigtrust; + if (!(sigtrust=dynamic_cast(policy.getTrustEngine()))) { + log.debug("ignoring message, no SignatureTrustEngine supplied"); + return false; } + + const HTTPRequest* httpRequest = dynamic_cast(request); + if (!request || !httpRequest) + return false; + + const char* signature = request->getParameter("Signature"); + if (!signature) + return false; - const char* sigAlgorithm = request.getParameter("SigAlg"); + const char* sigAlgorithm = request->getParameter("SigAlg"); if (!sigAlgorithm) { log.error("SigAlg parameter not found, no way to verify the signature"); - return ret; + return false; } - try { - log.debug("extracting issuer from message"); - pair issuerInfo = extractor.getIssuerAndProtocol(message); - - auto_ptr 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; - } + string input; + const char* pch; + if (!strcmp(httpRequest->getMethod(), "GET")) { + // 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. - log.debug("matched message 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; - } + // NOTE: SimpleSign for GET means Redirect binding, which means we verify over the + // base64-encoded message directly. + + pch = httpRequest->getQueryString(); + if (!appendParameter(input, pch, "SAMLRequest=")) + appendParameter(input, pch, "SAMLResponse="); + appendParameter(input, pch, "RelayState="); + appendParameter(input, pch, "SigAlg="); + } + else { + // With POST, the input string is concatenated from the decoded form controls. + // GET should be this way too, but I messed up the spec, sorry. - string input; - const char* pch; - const HTTPRequest& httpRequest = dynamic_cast(request); - if (!strcmp(httpRequest.getMethod(), "GET")) { - // 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. - pch = httpRequest.getQueryString(); - if (!appendParameter(input, pch, "SAMLRequest=")) - appendParameter(input, pch, "SAMLResponse="); - appendParameter(input, pch, "RelayState="); - appendParameter(input, pch, "SigAlg="); + // NOTE: SimpleSign for POST means POST binding, which means we verify over the + // base64-decoded XML. This sucks, because we have to decode the base64 directly. + // Serializing the XMLObject doesn't guarantee the signature will verify (this is + // why XMLSignature exists, and why this isn't really "simpler"). + + xsecsize_t x; + pch = httpRequest->getParameter("SAMLRequest"); + if (pch) { + XMLByte* decoded=Base64::decode(reinterpret_cast(pch),&x); + if (!decoded) { + log.warn("unable to decode base64 in POST binding message"); + return false; + } + input = string("SAMLRequest=") + reinterpret_cast(decoded); +#ifdef OPENSAML_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&decoded); +#else + XMLString::release((char**)&decoded); +#endif } else { - // With POST, the input string is concatenated from the decoded form controls. - // GET should be this way too, but I messed up the spec, sorry. - pch = httpRequest.getParameter("SAMLRequest"); - if (pch) - input = string("SAMLRequest=") + pch; - else { - pch = httpRequest.getParameter("SAMLResponse"); - input = string("SAMLResponse=") + pch; + pch = httpRequest->getParameter("SAMLResponse"); + XMLByte* decoded=Base64::decode(reinterpret_cast(pch),&x); + if (!decoded) { + log.warn("unable to decode base64 in POST binding message"); + return false; } - pch = httpRequest.getParameter("RelayState"); - if (pch) - input = input + "&RelayState=" + pch; - input = input + "&SigAlg=" + sigAlgorithm; + input = string("SAMLResponse=") + reinterpret_cast(decoded); +#ifdef OPENSAML_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&decoded); +#else + XMLString::release((char**)&decoded); +#endif } - // Check for KeyInfo, but defensively (we might be able to run without it). - KeyInfo* keyInfo=NULL; - pch = request.getParameter("KeyInfo"); - if (pch) { + pch = httpRequest->getParameter("RelayState"); + if (pch) + input = input + "&RelayState=" + pch; + input = input + "&SigAlg=" + sigAlgorithm; + } + + // Check for KeyInfo, but defensively (we might be able to run without it). + KeyInfo* keyInfo=nullptr; + pch = request->getParameter("KeyInfo"); + if (pch) { + xsecsize_t x; + XMLByte* decoded=Base64::decode(reinterpret_cast(pch),&x); + if (decoded) { try { - istringstream kstrm(pch); + istringstream kstrm((char*)decoded); DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(kstrm); XercesJanitor janitor(doc); XMLObject* kxml = XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true); @@ -169,27 +206,32 @@ pair SimpleSigningRule::evaluate( catch (XMLToolingException& ex) { log.warn("Failed to load KeyInfo from message: %s", ex.what()); } +#ifdef OPENSAML_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&decoded); +#else + XMLString::release((char**)&decoded); +#endif } - - auto_ptr kjanitor(keyInfo); - auto_ptr_XMLCh alg(sigAlgorithm); - - if (!trustEngine->validate(alg.get(), signature, keyInfo, 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()); + else { + log.warn("Failed to load KeyInfo from message: Unable to decode base64-encoded KeyInfo."); } - - ret.first = issuer.release(); - ret.second = roledesc; } - catch (bad_cast&) { - // Just trap it. - log.warn("caught a bad_cast while extracting issuer"); + + auto_ptr kjanitor(keyInfo); + auto_ptr_XMLCh alg(sigAlgorithm); + + // Set up criteria object. + MetadataCredentialCriteria cc(*(policy.getIssuerMetadata())); + cc.setXMLAlgorithm(alg.get()); + + if (!sigtrust->validate(alg.get(), signature, keyInfo, input.c_str(), input.length(), *(policy.getMetadataProvider()), &cc)) { + log.error("unable to verify message signature with supplied trust engine"); + if (m_errorFatal) + throw SecurityPolicyException("Message was signed, but signature could not be verified."); + return false; } - return ret; + + log.debug("signature verified against message issuer"); + policy.setAuthenticated(true); + return true; }