Allow message-only policy rules, basic SAML SOAP client.
[shibboleth/cpp-opensaml.git] / saml / binding / impl / SimpleSigningRule.cpp
index 3e7952d..7f1ea7f 100644 (file)
 
 #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>
@@ -69,12 +67,12 @@ namespace opensaml {
 };
 
 
-pair<saml2::Issuer*,const saml2md::RoleDescriptor*> SimpleSigningRule::evaluate(
-    const GenericRequest& request,
+pair<saml2::Issuer*,const RoleDescriptor*> SimpleSigningRule::evaluate(
     const XMLObject& message,
+    const GenericRequest* request,
     const MetadataProvider* metadataProvider,
     const QName* role,
-    const opensaml::TrustEngine* trustEngine
+    const TrustEngine* trustEngine
     ) const
 {
     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SimpleSigning");
@@ -82,18 +80,24 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> SimpleSigningRule::evaluate(
     
     pair<saml2::Issuer*,const RoleDescriptor*> ret = pair<saml2::Issuer*,const RoleDescriptor*>(NULL,NULL);  
     
+    const HTTPRequest* httpRequest = dynamic_cast<const HTTPRequest*>(request);
+    if (!request || !httpRequest) {
+        log.debug("ignoring message, no HTTP protocol request available");
+        return ret;
+    }
+
     if (!metadataProvider || !role || !trustEngine) {
         log.debug("ignoring message, no metadata supplied");
         return ret;
     }
     
-    const char* signature = request.getParameter("Signature");
+    const char* signature = request->getParameter("Signature");
     if (!signature) {
         log.debug("ignoring unsigned message");
         return ret;
     }
     
-    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;
@@ -118,31 +122,47 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> SimpleSigningRule::evaluate(
             return ret;
         }
 
-        log.debug("matched assertion issuer against metadata, searching for applicable role...");
+        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;
         }
 
-        // 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=");
+        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.
+            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.
+            pch = httpRequest->getParameter("SAMLRequest");
+            if (pch)
+                input = string("SAMLRequest=") + pch;
+            else {
+                pch = httpRequest->getParameter("SAMLResponse");
+                input = string("SAMLResponse=") + 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=NULL;
-        const char* k = request.getParameter("KeyInfo");
-        if (k) {
+        pch = request->getParameter("KeyInfo");
+        if (pch) {
             try {
-                istringstream kstrm(k);
+                istringstream kstrm(pch);
                 DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(kstrm);
                 XercesJanitor<DOMDocument> janitor(doc);
                 XMLObject* kxml = XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true);
@@ -185,13 +205,12 @@ pair<saml2::Issuer*,const XMLCh*> SimpleSigningRule::getIssuerAndProtocol(const
     // 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)) {
+        if (XMLString::equals(ns, samlconstants::SAML20P_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()) {
+            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();