SSPCPP-437 and SSPCPP-438
authorScott Cantor <cantor.2@osu.edu>
Tue, 17 Apr 2012 20:06:03 +0000 (20:06 +0000)
committerScott Cantor <cantor.2@osu.edu>
Tue, 17 Apr 2012 20:06:03 +0000 (20:06 +0000)
shibsp/binding/ArtifactResolver.h
shibsp/binding/impl/ArtifactResolver.cpp
shibsp/util/SPConstants.cpp
shibsp/util/SPConstants.h

index 13cd0ab..1957713 100644 (file)
@@ -40,6 +40,8 @@ namespace shibsp {
         ArtifactResolver();
         virtual ~ArtifactResolver();
 
+        bool isSupported(const opensaml::saml2md::SSODescriptorType& ssoDescriptor) const;
+
         opensaml::saml1p::Response* resolve(
             const std::vector<opensaml::SAMLArtifact*>& artifacts,
             const opensaml::saml2md::IDPSSODescriptor& idpDescriptor,
index ebb82c9..840929e 100644 (file)
 #include "binding/ArtifactResolver.h"
 #include "binding/SOAPClient.h"
 #include "security/SecurityPolicy.h"
+#include "util/SPConstants.h"
 
+#include <fstream>
+#include <boost/bind.hpp>
+#include <boost/algorithm/string.hpp>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/ParserPool.h>
+#include <xmltooling/util/PathResolver.h>
 #include <saml/exceptions.h>
 #include <saml/saml1/core/Protocols.h>
 #include <saml/saml1/binding/SAML1SOAPClient.h>
 #include <saml/saml2/core/Protocols.h>
 #include <saml/saml2/binding/SAML2Artifact.h>
 #include <saml/saml2/binding/SAML2SOAPClient.h>
+#include <saml/saml2/metadata/EndpointManager.h>
 #include <saml/saml2/metadata/Metadata.h>
 #include <saml/saml2/metadata/MetadataCredentialCriteria.h>
 #include <saml/util/SAMLConstants.h>
@@ -47,6 +55,7 @@ using namespace opensaml::saml2p;
 using namespace opensaml::saml2md;
 using namespace opensaml;
 using namespace xmltooling;
+using namespace boost;
 using namespace std;
 
 ArtifactResolver::ArtifactResolver()
@@ -57,6 +66,19 @@ ArtifactResolver::~ArtifactResolver()
 {
 }
 
+bool ArtifactResolver::isSupported(const SSODescriptorType& ssoDescriptor) const
+{
+    if (MessageDecoder::ArtifactResolver::isSupported(ssoDescriptor))
+        return true;
+
+    EndpointManager<ArtifactResolutionService> mgr(ssoDescriptor.getArtifactResolutionServices());
+    if (ssoDescriptor.hasSupport(samlconstants::SAML20P_NS)) {
+        return (mgr.getByBinding(shibspconstants::SHIB2_BINDING_FILE) != nullptr);
+    }
+
+    return false;
+}
+
 saml1p::Response* ArtifactResolver::resolve(
     const vector<SAMLArtifact*>& artifacts,
     const IDPSSODescriptor& idpDescriptor,
@@ -90,7 +112,7 @@ saml1p::Response* ArtifactResolver::resolve(
             client.sendSAML(request, sppolicy.getApplication().getId(), mcc, loc.get());
             response = client.receiveSAML();
         }
-        catch (exception& ex) {
+        catch (std::exception& ex) {
             Category::getInstance(SHIBSP_LOGCAT".ArtifactResolver").error("exception resolving SAML 1.x artifact(s): %s", ex.what());
             soaper.reset();
         }
@@ -103,7 +125,7 @@ saml1p::Response* ArtifactResolver::resolve(
     const xmltooling::QName* code = (response->getStatus() && response->getStatus()->getStatusCode()) ? response->getStatus()->getStatusCode()->getValue() : nullptr;
     if (!code || *code != saml1p::StatusCode::SUCCESS) {
         auto_ptr<saml1p::Response> wrapper(response);
-        BindingException ex("Identity provider returned a SAML error in response to artifact.");
+        BindingException ex("Identity provider returned a SAML error during artifact resolution.");
         annotateException(&ex, &idpDescriptor, response->getStatus());  // rethrow
     }
 
@@ -118,6 +140,8 @@ ArtifactResponse* ArtifactResolver::resolve(
     opensaml::SecurityPolicy& policy
     ) const
 {
+    Category& log = Category::getInstance(SHIBSP_LOGCAT".ArtifactResolver");
+
     MetadataCredentialCriteria mcc(ssoDescriptor);
     shibsp::SecurityPolicy& sppolicy = dynamic_cast<shibsp::SecurityPolicy&>(policy);
     shibsp::SOAPClient soaper(sppolicy);
@@ -125,32 +149,96 @@ ArtifactResponse* ArtifactResolver::resolve(
     bool foundEndpoint = false;
     auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);
     ArtifactResponse* response=nullptr;
-    const vector<ArtifactResolutionService*>& endpoints=ssoDescriptor.getArtifactResolutionServices();
-    for (vector<ArtifactResolutionService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
+
+    vector<ArtifactResolutionService*>::const_iterator ep_start, ep_end;
+    const vector<ArtifactResolutionService*>& endpoints = ssoDescriptor.getArtifactResolutionServices();
+    ep_start = find_if(endpoints.begin(), endpoints.end(),
+        boost::bind(&pair<bool,int>::second, boost::bind(&IndexedEndpointType::getIndex, _1)) == artifact.getEndpointIndex());
+    if (ep_start == endpoints.end()) {
+        ep_start = endpoints.begin();
+        ep_end = endpoints.end();
+    }
+    else {
+        ep_end = ep_start + 1;
+    }
+
+    for (vector<ArtifactResolutionService*>::const_iterator ep = ep_start; !response && ep != ep_end; ++ep) {
         try {
-            if (!XMLString::equals((*ep)->getBinding(),binding.get()))
-                continue;
-            foundEndpoint = true;
-            auto_ptr_char loc((*ep)->getLocation());
-            ArtifactResolve* request = ArtifactResolveBuilder::buildArtifactResolve();
-            Issuer* iss = IssuerBuilder::buildIssuer();
-            request->setIssuer(iss);
-            iss->setName(sppolicy.getApplication().getRelyingParty(dynamic_cast<EntityDescriptor*>(ssoDescriptor.getParent()))->getXMLString("entityID").second);
-            auto_ptr_XMLCh artbuf(artifact.encode().c_str());
-            Artifact* a = ArtifactBuilder::buildArtifact();
-            a->setArtifact(artbuf.get());
-            request->setArtifact(a);
-
-            SAML2SOAPClient client(soaper, false);
-            client.sendSAML(request, sppolicy.getApplication().getId(), mcc, loc.get());
-            StatusResponseType* srt = client.receiveSAML();
-            if (!(response = dynamic_cast<ArtifactResponse*>(srt))) {
-                delete srt;
-                break;
+            if (XMLString::equals((*ep)->getBinding(), binding.get())) {
+                foundEndpoint = true;
+                auto_ptr_char loc((*ep)->getLocation());
+                ArtifactResolve* request = ArtifactResolveBuilder::buildArtifactResolve();
+                Issuer* iss = IssuerBuilder::buildIssuer();
+                request->setIssuer(iss);
+                iss->setName(sppolicy.getApplication().getRelyingParty(dynamic_cast<EntityDescriptor*>(ssoDescriptor.getParent()))->getXMLString("entityID").second);
+                auto_ptr_XMLCh artbuf(artifact.encode().c_str());
+                Artifact* a = ArtifactBuilder::buildArtifact();
+                a->setArtifact(artbuf.get());
+                request->setArtifact(a);
+
+                SAML2SOAPClient client(soaper, false);
+                client.sendSAML(request, sppolicy.getApplication().getId(), mcc, loc.get());
+                StatusResponseType* srt = client.receiveSAML();
+                if (!(response = dynamic_cast<ArtifactResponse*>(srt))) {
+                    delete srt;
+                    break;
+                }
+            }
+            else if (XMLString::equals((*ep)->getBinding(), shibspconstants::SHIB2_BINDING_FILE)) {
+                // This implements a resolution process against the local file system for custom integration needs.
+                // The local filesystem is presumed to be "secure" so that unsigned, unencrypted responses are acceptable.
+                // The binding here is not SOAP, but rather REST-like, with the base location used to construct a filename
+                // containing the artifact message handle.
+                foundEndpoint = true;
+                auto_ptr_char temp((*ep)->getLocation());
+                if (temp.get()) {
+                    string loc(temp.get());
+                    if (starts_with(loc, "file://"))
+                        loc = loc.substr(7);
+                    XMLToolingConfig::getConfig().getPathResolver()->resolve(loc, PathResolver::XMLTOOLING_RUN_FILE);
+                    loc += '/' + SAMLArtifact::toHex(artifact.getMessageHandle());
+                    ifstream in(loc);
+                    if (in) {
+                        auto_ptr<XMLObject> xmlObject;
+                        try {
+                            DOMDocument* doc = (policy.getValidating() ? XMLToolingConfig::getConfig().getValidatingParser() : XMLToolingConfig::getConfig().getParser()).parse(in);
+                            XercesJanitor<DOMDocument> docjanitor(doc);
+
+                            if (log.isDebugEnabled()) {
+#ifdef XMLTOOLING_LOG4SHIB
+                                log.debugStream() << "received XML:\n" << *(doc->getDocumentElement()) << logging::eol;
+#else
+                                string buf;
+                                XMLHelper::serialize(doc->getDocumentElement(), buf);
+                                log.debugStream() << "received XML:\n" << buf << logging::eol;
+#endif
+    }
+                            xmlObject.reset(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true));
+                            docjanitor.release();
+                        }
+                        catch (std::exception&) {
+                            in.close();
+                            remove(loc.c_str());
+                            throw;
+                        }
+                        in.close();
+                        remove(loc.c_str());
+                        if (response = dynamic_cast<ArtifactResponse*>(xmlObject.get())) {
+                            xmlObject.release();
+                            policy.setAuthenticated(true);
+                        }
+                        else {
+                            break;
+                        }
+                    }
+                    else {
+                        throw BindingException("Unable to open artifact response file ($1)", params(1, loc.c_str()));
+                    }
+                }
             }
         }
-        catch (exception& ex) {
-            Category::getInstance(SHIBSP_LOGCAT".ArtifactResolver").error("exception resolving SAML 2.0 artifact: %s", ex.what());
+        catch (std::exception& ex) {
+            log.error("exception resolving SAML 2.0 artifact: %s", ex.what());
             soaper.reset();
         }
     }
@@ -162,7 +250,7 @@ ArtifactResponse* ArtifactResolver::resolve(
     else if (!response->getStatus() || !response->getStatus()->getStatusCode() ||
            !XMLString::equals(response->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) {
         auto_ptr<ArtifactResponse> wrapper(response);
-        BindingException ex("Identity provider returned a SAML error in response to artifact.");
+        BindingException ex("Identity provider returned a SAML error during artifact resolution.");
         annotateException(&ex, &ssoDescriptor, response->getStatus());  // rethrow
     }
 
index 120fd5c..bc6c8a5 100644 (file)
@@ -120,6 +120,14 @@ const XMLCh shibspconstants::SHIB1_AUTHNREQUEST_PROFILE_URI[] = // urn:mace:shib
   chLatin_R, chLatin_e, chLatin_q, chLatin_u, chLatin_e, chLatin_s, chLatin_t, chNull
 };
 
+const XMLCh shibspconstants::SHIB2_BINDING_FILE[] = // urn:mace:shibboleth:2.0:bindings:File
+{ chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,
+  chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,
+  chDigit_2, chPeriod, chDigit_0, chColon,
+  chLatin_b, chLatin_i, chLatin_n, chLatin_d, chLatin_i, chLatin_n, chLatin_g, chLatin_s, chColon,
+  chLatin_F, chLatin_i, chLatin_l, chLatin_e, chNull
+};
+
 const char shibspconstants::SHIB1_SESSIONINIT_PROFILE_URI[] = "urn:mace:shibboleth:sp:1.3:SessionInit";
 
 const char shibspconstants::SHIB1_LOGOUT_PROFILE_URI[] = "urn:mace:shibboleth:sp:1.3:Logout";
index 53cbdd9..dac5693 100644 (file)
@@ -74,6 +74,9 @@ namespace shibspconstants {
     /** Shibboleth 1.x AuthnRequest binding/profile ("urn:mace:shibboleth:1.0:profiles:AuthnRequest") */
     extern SHIBSP_API const XMLCh SHIB1_AUTHNREQUEST_PROFILE_URI[];
 
+    /** Shibboleth 2 filesystem-based SAML binding ("urn:mace:shibboleth:2.0:bindings:File") */
+    extern SHIBSP_API const XMLCh SHIB2_BINDING_FILE[];
+
     /** Shibboleth 1.3 SessionInit binding/profile ("urn:mace:shibboleth:sp:1.3:SessionInit") */
     extern SHIBSP_API const char SHIB1_SESSIONINIT_PROFILE_URI[];