X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=saml%2Fbinding%2Fimpl%2FSimpleSigningRule.cpp;h=a7a475954c0202aaa3904d499cc4b6c7f7879087;hb=932cfaae2176c2eba1a9938dc420591a9551a7f3;hp=3e7952dcd3e5a04f2e25adb137f256f87103b930;hpb=42c774ed39a783c3ba6b90c36ba87ce1bd874c09;p=shibboleth%2Fcpp-opensaml.git diff --git a/saml/binding/impl/SimpleSigningRule.cpp b/saml/binding/impl/SimpleSigningRule.cpp index 3e7952d..a7a4759 100644 --- a/saml/binding/impl/SimpleSigningRule.cpp +++ b/saml/binding/impl/SimpleSigningRule.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2006 Internet2 + * Copyright 2001-2007 Internet2 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,20 +22,14 @@ #include "internal.h" #include "exceptions.h" -#include "RootObject.h" #include "binding/HTTPRequest.h" #include "binding/SimpleSigningRule.h" -#include "saml2/core/Protocols.h" +#include "saml2/core/Assertions.h" #include "saml2/metadata/Metadata.h" #include "saml2/metadata/MetadataProvider.h" -#include "security/TrustEngine.h" #include -#include -#include -#include -#include -#include +#include using namespace opensaml::saml2md; using namespace opensaml; @@ -69,138 +63,120 @@ namespace opensaml { }; -pair SimpleSigningRule::evaluate( - const GenericRequest& request, - const XMLObject& message, - const MetadataProvider* metadataProvider, - const QName* role, - const opensaml::TrustEngine* trustEngine - ) const +void SimpleSigningRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const { Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SimpleSigning"); log.debug("evaluating simple signing policy"); - pair ret = pair(NULL,NULL); - - 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; } - - const char* signature = request.getParameter("Signature"); + else if (!policy.getTrustEngine()) { + log.debug("ignoring message, no TrustEngine supplied"); + return; + } + + const HTTPRequest* httpRequest = dynamic_cast(request); + if (!request || !httpRequest) { + log.debug("ignoring message, no HTTP protocol request available"); + return; + } + + const char* signature = request->getParameter("Signature"); if (!signature) { log.debug("ignoring unsigned message"); - return ret; + return; } - 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; } - try { - log.debug("extracting issuer from message"); - pair issuerInfo = 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; - } - - 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; - } - + 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. - string input; - const HTTPRequest& httpRequest = dynamic_cast(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="); - - // Check for KeyInfo, but defensively (we might be able to run without it). - KeyInfo* keyInfo=NULL; - const char* k = request.getParameter("KeyInfo"); - if (k) { - try { - istringstream kstrm(k); - DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(kstrm); - XercesJanitor janitor(doc); - XMLObject* kxml = XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true); - janitor.release(); - if (!(keyInfo=dynamic_cast(kxml))) - delete kxml; - } - catch (XMLToolingException& ex) { - log.warn("Failed to load KeyInfo from message: %s", ex.what()); + + // 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. + + // 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"). + + unsigned int 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; } + input = string("SAMLRequest=") + reinterpret_cast(decoded); + XMLString::release(&decoded); } - - 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; + else { + 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; + } + input = string("SAMLResponse=") + reinterpret_cast(decoded); + XMLString::release(&decoded); } - 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"); + pch = httpRequest->getParameter("RelayState"); + if (pch) + input = input + "&RelayState=" + pch; + input = input + "&SigAlg=" + sigAlgorithm; } - return ret; -} -pair 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(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& assertions = dynamic_cast(message).getAssertions(); - if (!assertions.empty()) { - issuer = assertions.front()->getIssuer(); - if (issuer && issuer->getName()) - return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS); - } + // Check for KeyInfo, but defensively (we might be able to run without it). + KeyInfo* keyInfo=NULL; + pch = request->getParameter("KeyInfo"); + if (pch) { + try { + istringstream kstrm(pch); + DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(kstrm); + XercesJanitor janitor(doc); + XMLObject* kxml = XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true); + janitor.release(); + if (!(keyInfo=dynamic_cast(kxml))) + delete kxml; + } + catch (XMLToolingException& ex) { + log.warn("Failed to load KeyInfo from message: %s", ex.what()); } } - return pair(NULL,NULL); + + auto_ptr kjanitor(keyInfo); + auto_ptr_XMLCh alg(sigAlgorithm); + + if (!policy.getTrustEngine()->validate( + alg.get(), signature, keyInfo, input.c_str(), input.length(), + *(policy.getIssuerMetadata()), policy.getMetadataProvider()->getKeyResolver() + )) { + log.error("unable to verify message signature with supplied trust engine"); + return; + } + + log.debug("signature verified against message issuer"); + policy.setSecure(true); }