From 66a7b1f30513115db03cd5bbe9b28fdbe7846cc0 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Tue, 29 May 2007 02:16:28 +0000 Subject: [PATCH] Assertion export and lookup service using URI binding. --- configs/shibboleth.xml.in | 4 +- schemas/shibboleth-2.0-native-sp-config.xsd | 2 + shibsp/Makefile.am | 1 + shibsp/ServiceProvider.cpp | 25 +++- shibsp/handler/impl/AbstractHandler.cpp | 3 + shibsp/handler/impl/AssertionLookup.cpp | 189 ++++++++++++++++++++++++ shibsp/handler/impl/SAML2ArtifactResolution.cpp | 6 +- shibsp/impl/XMLServiceProvider.cpp | 26 +++- shibsp/shibsp-lite.vcproj | 4 + shibsp/shibsp.vcproj | 4 + shibsp/util/SPConstants.h | 2 +- 11 files changed, 251 insertions(+), 15 deletions(-) create mode 100644 shibsp/handler/impl/AssertionLookup.cpp diff --git a/configs/shibboleth.xml.in b/configs/shibboleth.xml.in index df65ed2..f7d7ebc 100644 --- a/configs/shibboleth.xml.in +++ b/configs/shibboleth.xml.in @@ -169,7 +169,7 @@ + + diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am index 4acf181..b35e327 100644 --- a/shibsp/Makefile.am +++ b/shibsp/Makefile.am @@ -103,6 +103,7 @@ common_sources = \ attribute/Attribute.cpp \ handler/impl/AbstractHandler.cpp \ handler/impl/AssertionConsumerService.cpp \ + handler/impl/AssertionLookup.cpp \ handler/impl/ChainingSessionInitiator.cpp \ handler/impl/RemotedHandler.cpp \ handler/impl/SAML1Consumer.cpp \ diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp index e301bdf..66f125c 100644 --- a/shibsp/ServiceProvider.cpp +++ b/shibsp/ServiceProvider.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include using namespace shibsp; @@ -91,6 +92,7 @@ namespace shibsp { request.clearHeader("Shib-AuthnContext-Class"); request.clearHeader("Shib-AuthnContext-Decl"); request.clearHeader("Shib-Attributes"); + request.clearHeader("Shib-Assertion-Count"); //request.clearHeader("Shib-Application-ID"); handle inside app method request.getApplication().clearAttributeHeaders(request); } @@ -349,9 +351,26 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio // Maybe export the assertion keys. pair exp=settings.first->getBool("exportAssertion"); if (exp.first && exp.second) { - //setHeader("Shib-Attributes", reinterpret_cast(serialized)); - // TODO: export lookup URLs to access assertions by ID - const vector& tokens = session->getAssertionIDs(); + const PropertySet* sessions=app->getPropertySet("Sessions"); + pair exportLocation = sessions ? sessions->getString("exportLocation") : pair(false,NULL); + if (!exportLocation.first) + request.log(SPRequest::SPWarn, "can't export assertions without an exportLocation Sessions property"); + else { + const URLEncoder* encoder = XMLToolingConfig::getConfig().getURLEncoder(); + string exportName = "Shib-Assertion-00"; + const char* handlerURL=request.getHandlerURL(targetURL.c_str()); + string baseURL = string(handlerURL) + exportLocation.second + "?key=" + session->getID() + "&ID="; + const vector& tokens = session->getAssertionIDs(); + vector::size_type count = 0; + for (vector::const_iterator tokenids = tokens.begin(); tokenids!=tokens.end(); ++tokenids) { + count++; + *(exportName.rbegin()) = '0' + (count%10); + *(++exportName.rbegin()) = '0' + (count/10); + string fullURL = baseURL + encoder->encode(*tokenids); + request.setHeader(exportName.c_str(), fullURL.c_str()); + } + request.setHeader("Shib-Assertion-Count", exportName.c_str() + 15); + } } // Export the attributes. diff --git a/shibsp/handler/impl/AbstractHandler.cpp b/shibsp/handler/impl/AbstractHandler.cpp index 1054f4c..c8710cd 100644 --- a/shibsp/handler/impl/AbstractHandler.cpp +++ b/shibsp/handler/impl/AbstractHandler.cpp @@ -55,6 +55,7 @@ namespace shibsp { SHIBSP_DLLLOCAL PluginManager>::Factory SAML1ConsumerFactory; SHIBSP_DLLLOCAL PluginManager>::Factory SAML2ConsumerFactory; SHIBSP_DLLLOCAL PluginManager>::Factory SAML2ArtifactResolutionFactory; + SHIBSP_DLLLOCAL PluginManager>::Factory AssertionLookupFactory; }; void SHIBSP_API shibsp::registerHandlers() @@ -68,6 +69,8 @@ void SHIBSP_API shibsp::registerHandlers() conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2ConsumerFactory); conf.ArtifactResolutionServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2ArtifactResolutionFactory); + + conf.HandlerManager.registerFactory(SAML20_BINDING_URI, AssertionLookupFactory); } AbstractHandler::AbstractHandler( diff --git a/shibsp/handler/impl/AssertionLookup.cpp b/shibsp/handler/impl/AssertionLookup.cpp new file mode 100644 index 0000000..5b8f006 --- /dev/null +++ b/shibsp/handler/impl/AssertionLookup.cpp @@ -0,0 +1,189 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * 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. + */ + +/** + * AssertionLookup.cpp + * + * Handler for looking assertions in SessionCache + */ + +#include "internal.h" +#include "Application.h" +#include "exceptions.h" +#include "ServiceProvider.h" +#include "SessionCache.h" +#include "handler/AbstractHandler.h" +#include "handler/RemotedHandler.h" +#include "util/SPConstants.h" + +using namespace shibspconstants; +using namespace shibsp; +using namespace opensaml; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +namespace shibsp { + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class SHIBSP_API AssertionLookup : public AbstractHandler, public RemotedHandler + { + public: + AssertionLookup(const DOMElement* e, const char* appId); + virtual ~AssertionLookup() {} + + pair run(SPRequest& request, bool isHandler=true) const; + void receive(DDF& in, ostream& out); + + private: + pair processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const; + + set m_acl; + }; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + + Handler* SHIBSP_DLLLOCAL AssertionLookupFactory(const pair& p) + { + return new AssertionLookup(p.first, p.second); + } + +}; + +AssertionLookup::AssertionLookup(const DOMElement* e, const char* appId) + : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".AssertionLookup")) +{ + setAddress("run::AssertionLookup"); + if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) { + pair acl = getString("exportACL"); + if (!acl.first) { + m_acl.insert("127.0.0.1"); + return; + } + string aclbuf=acl.second; + int j = 0; + for (unsigned int i=0; i < aclbuf.length(); i++) { + if (aclbuf.at(i)==' ') { + m_acl.insert(aclbuf.substr(j, i-j)); + j = i+1; + } + } + m_acl.insert(aclbuf.substr(j, aclbuf.length()-j)); + } +} + +pair AssertionLookup::run(SPRequest& request, bool isHandler) const +{ + string relayState; + SPConfig& conf = SPConfig::getConfig(); + if (conf.isEnabled(SPConfig::InProcess)) { + if (m_acl.count(request.getRemoteAddr()) == 0) { + m_log.error("request for assertion lookup blocked from invalid address (%s)", request.getRemoteAddr()); + istringstream msg("Assertion Lookup Blocked"); + return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN)); + } + } + + try { + if (conf.isEnabled(SPConfig::OutOfProcess)) { + // When out of process, we run natively and directly process the message. + return processMessage(request.getApplication(), request, request); + } + else { + // When not out of process, we remote all the message processing. + DDF out,in = wrap(request, NULL, true); + DDFJanitor jin(in), jout(out); + + in.addmember("application_id").string(request.getApplication().getId()); + out=request.getServiceProvider().getListenerService()->send(in); + return unwrap(request, out); + } + } + catch (exception& ex) { + m_log.error("error while processing request: %s", ex.what()); + istringstream msg("Assertion Lookup Failed"); + return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR)); + } +} + +void AssertionLookup::receive(DDF& in, ostream& out) +{ + // Find application. + const char* aid=in["application_id"].string(); + const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL; + if (!app) { + // Something's horribly wrong. + m_log.error("couldn't find application (%s) for assertion lookup", aid ? aid : "(missing)"); + throw ConfigurationException("Unable to locate application for assertion lookup, deleted?"); + } + + // Unpack the request. + auto_ptr req(getRequest(in)); + //m_log.debug("found %d client certificates", req->getClientCertificates().size()); + + // Wrap a response shim. + DDF ret(NULL); + DDFJanitor jout(ret); + auto_ptr resp(getResponse(ret)); + + // Since we're remoted, the result should either be a throw, a false/0 return, + // which we just return as an empty structure, or a response/redirect, + // which we capture in the facade and send back. + processMessage(*app, *req.get(), *resp.get()); + out << ret; +} + +pair AssertionLookup::processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const +{ +#ifndef SHIBSP_LITE + const char* key = httpRequest.getParameter("key"); + const char* ID = httpRequest.getParameter("ID"); + if (!key || !*key || !ID || !*ID) { + m_log.error("assertion lookup request failed, missing required parameters"); + throw FatalProfileException("Missing key or ID parameters."); + } + + m_log.debug("processing assertion lookup request (session: %s, assertion: %s)", key, ID); + + // The cache will either silently pass a session or NULL back, or throw an exception out. + Session* session = application.getServiceProvider().getSessionCache()->find(key, application); + if (!session) { + m_log.error("valid session (%s) not found for assertion lookup", key); + throw FatalProfileException("Session key not found."); + } + + Locker locker(session, false); + + const Assertion* assertion = session->getAssertion(ID); + if (!assertion) { + m_log.error("assertion (%s) not found in session (%s)", ID, key); + throw FatalProfileException("Assertion not found."); + } + + stringstream s; + s << *assertion; + httpResponse.setContentType("application/samlassertion+xml"); + return make_pair(true, httpResponse.sendResponse(s)); +#else + return make_pair(false,0); +#endif +} diff --git a/shibsp/handler/impl/SAML2ArtifactResolution.cpp b/shibsp/handler/impl/SAML2ArtifactResolution.cpp index e61ade5..c6de827 100644 --- a/shibsp/handler/impl/SAML2ArtifactResolution.cpp +++ b/shibsp/handler/impl/SAML2ArtifactResolution.cpp @@ -57,9 +57,6 @@ using namespace std; namespace shibsp { - class SHIBSP_API Attribute; - class SHIBSP_API ResolutionContext; - #if defined (_MSC_VER) #pragma warning( push ) #pragma warning( disable : 4250 ) @@ -145,7 +142,6 @@ pair SAML2ArtifactResolution::run(SPRequest& request, bool isHandler) try { if (conf.isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively and directly process the message. - string entityID; return processMessage(request.getApplication(), request, request); } else { @@ -199,7 +195,7 @@ void SAML2ArtifactResolution::receive(DDF& in, ostream& out) if (!app) { // Something's horribly wrong. m_log.error("couldn't find application (%s) for artifact resolution", aid ? aid : "(missing)"); - throw ConfigurationException("Unable to locate application for new session, deleted?"); + throw ConfigurationException("Unable to locate application for artifact resolution, deleted?"); } // Unpack the request. diff --git a/shibsp/impl/XMLServiceProvider.cpp b/shibsp/impl/XMLServiceProvider.cpp index 187394f..b886e93 100644 --- a/shibsp/impl/XMLServiceProvider.cpp +++ b/shibsp/impl/XMLServiceProvider.cpp @@ -55,7 +55,7 @@ # include # include # include -#include +# include using namespace opensaml::saml2; using namespace opensaml::saml2p; using namespace opensaml::saml2md; @@ -499,10 +499,28 @@ XMLApplication::XMLApplication( } } + Handler* handler=NULL; const PropertySet* sessions = getPropertySet("Sessions"); - // Process handlers. - Handler* handler=NULL; + // Process assertion export handler. + pair location = sessions ? sessions->getString("exportLocation") : pair(false,NULL); + if (location.first) { + try { + handler = conf.HandlerManager.newPlugin(samlconstants::SAML20_BINDING_URI, make_pair(sessions->getElement(), getId())); + m_handlers.push_back(handler); + + // Insert into location map. + if (*location.second == '/') + m_handlerMap[location.second]=handler; + else + m_handlerMap[string("/") + location.second]=handler; + } + catch (exception& ex) { + log.error("caught exception installing assertion lookup handler: %s", ex.what()); + } + } + + // Process other handlers. bool hardACS=false, hardSessionInit=false, hardArt=false; const DOMElement* child = sessions ? XMLHelper::getFirstChildElement(sessions->getElement()) : NULL; while (child) { @@ -613,7 +631,7 @@ XMLApplication::XMLApplication( m_handlers.push_back(handler); // Insert into location map. - pair location=handler->getString("Location"); + location=handler->getString("Location"); if (location.first && *location.second == '/') m_handlerMap[location.second]=handler; else if (location.first) diff --git a/shibsp/shibsp-lite.vcproj b/shibsp/shibsp-lite.vcproj index cfbfdbe..2b49d94 100644 --- a/shibsp/shibsp-lite.vcproj +++ b/shibsp/shibsp-lite.vcproj @@ -290,6 +290,10 @@ > + + diff --git a/shibsp/shibsp.vcproj b/shibsp/shibsp.vcproj index f1da7e9..ac0b337 100644 --- a/shibsp/shibsp.vcproj +++ b/shibsp/shibsp.vcproj @@ -458,6 +458,10 @@ > + + diff --git a/shibsp/util/SPConstants.h b/shibsp/util/SPConstants.h index 98c8b37..31c2097 100644 --- a/shibsp/util/SPConstants.h +++ b/shibsp/util/SPConstants.h @@ -27,7 +27,7 @@ #ifndef SHIBSP_LITE # include #else -# include +# include #endif /** -- 2.1.4