From: Scott Cantor Date: Tue, 17 Apr 2012 20:06:03 +0000 (+0000) Subject: SSPCPP-437 and SSPCPP-438 X-Git-Tag: 2.5.0~130 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-sp.git;a=commitdiff_plain;h=2e203ee6612fde38424ba5806c59db8b08686c47 SSPCPP-437 and SSPCPP-438 --- diff --git a/shibsp/binding/ArtifactResolver.h b/shibsp/binding/ArtifactResolver.h index 13cd0ab..1957713 100644 --- a/shibsp/binding/ArtifactResolver.h +++ b/shibsp/binding/ArtifactResolver.h @@ -40,6 +40,8 @@ namespace shibsp { ArtifactResolver(); virtual ~ArtifactResolver(); + bool isSupported(const opensaml::saml2md::SSODescriptorType& ssoDescriptor) const; + opensaml::saml1p::Response* resolve( const std::vector& artifacts, const opensaml::saml2md::IDPSSODescriptor& idpDescriptor, diff --git a/shibsp/binding/impl/ArtifactResolver.cpp b/shibsp/binding/impl/ArtifactResolver.cpp index ebb82c9..840929e 100644 --- a/shibsp/binding/impl/ArtifactResolver.cpp +++ b/shibsp/binding/impl/ArtifactResolver.cpp @@ -29,13 +29,21 @@ #include "binding/ArtifactResolver.h" #include "binding/SOAPClient.h" #include "security/SecurityPolicy.h" +#include "util/SPConstants.h" +#include +#include +#include +#include +#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -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 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& 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 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(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& endpoints=ssoDescriptor.getArtifactResolutionServices(); - for (vector::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) { + + vector::const_iterator ep_start, ep_end; + const vector& endpoints = ssoDescriptor.getArtifactResolutionServices(); + ep_start = find_if(endpoints.begin(), endpoints.end(), + boost::bind(&pair::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::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(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(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(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(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; + try { + DOMDocument* doc = (policy.getValidating() ? XMLToolingConfig::getConfig().getValidatingParser() : XMLToolingConfig::getConfig().getParser()).parse(in); + XercesJanitor 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(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 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 } diff --git a/shibsp/util/SPConstants.cpp b/shibsp/util/SPConstants.cpp index 120fd5c..bc6c8a5 100644 --- a/shibsp/util/SPConstants.cpp +++ b/shibsp/util/SPConstants.cpp @@ -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"; diff --git a/shibsp/util/SPConstants.h b/shibsp/util/SPConstants.h index 53cbdd9..dac5693 100644 --- a/shibsp/util/SPConstants.h +++ b/shibsp/util/SPConstants.h @@ -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[];