From: Scott Cantor Date: Mon, 26 Nov 2007 06:50:17 +0000 (+0000) Subject: Move session cookie management into session cache. X-Git-Tag: 2.0-beta2~10 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-sp.git;a=commitdiff_plain;h=15734309cfc5aa285b82ff19ba214a1dcaf1cada Move session cookie management into session cache. Fully remote more handlers to provide access to session cookie. --- diff --git a/adfs/adfs.cpp b/adfs/adfs.cpp index 8a37559..98c905e 100644 --- a/adfs/adfs.cpp +++ b/adfs/adfs.cpp @@ -162,9 +162,10 @@ namespace { auto_ptr_XMLCh m_protocol; private: - string implementProtocol( + void implementProtocol( const Application& application, const HTTPRequest& httpRequest, + HTTPResponse& httpResponse, SecurityPolicy& policy, const PropertySet* settings, const XMLObject& xmlObject @@ -208,9 +209,7 @@ namespace { #endif private: - pair doRequest( - const Application& application, const char* requestURL, const char* entityID, HTTPResponse& httpResponse - ) const; + pair doRequest(const Application& application, const char* entityID, HTTPResponse& httpResponse) const; string m_appId; auto_ptr_XMLCh m_binding; @@ -326,7 +325,7 @@ pair ADFSSessionInitiator::run(SPRequest& request, const char* entity // Since we're passing the ACS by value, we need to compute the return URL, // so we'll need the target resource for real. - recoverRelayState(request.getApplication(), request, target, false); + recoverRelayState(request.getApplication(), request, request, target, false); } else { // We're running as a "virtual handler" from within the filter. @@ -528,9 +527,10 @@ XMLObject* ADFSDecoder::decode(string& relayState, const GenericRequest& generic return xmlObject.release(); } -string ADFSConsumer::implementProtocol( +void ADFSConsumer::implementProtocol( const Application& application, const HTTPRequest& httpRequest, + HTTPResponse& httpResponse, SecurityPolicy& policy, const PropertySet* settings, const XMLObject& xmlObject @@ -615,7 +615,7 @@ string ADFSConsumer::implementProtocol( } // The context will handle deleting attributes and new tokens. - auto_ptr ctx( + auto_ptr ctx( resolveAttributes( application, policy.getIssuerMetadata(), @@ -633,10 +633,11 @@ string ADFSConsumer::implementProtocol( tokens.insert(tokens.end(), ctx->getResolvedAssertions().begin(), ctx->getResolvedAssertions().end()); } - return application.getServiceProvider().getSessionCache()->insert( + application.getServiceProvider().getSessionCache()->insert( now + lifetime.second, application, - httpRequest.getRemoteAddr().c_str(), + httpRequest, + httpResponse, policy.getIssuerMetadata() ? dynamic_cast(policy.getIssuerMetadata()->getParent()) : NULL, m_protocol.get(), nameid.get(), @@ -681,7 +682,7 @@ pair ADFSLogoutInitiator::run(SPRequest& request, bool isHandler) con if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively. - return doRequest(request.getApplication(), request.getRequestURL(), entityID.c_str(), request); + return doRequest(request.getApplication(), entityID.c_str(), request); } else { // When not out of process, we remote the request. @@ -689,7 +690,6 @@ pair ADFSLogoutInitiator::run(SPRequest& request, bool isHandler) con DDF out,in(m_address.c_str()); DDFJanitor jin(in), jout(out); in.addmember("application_id").string(request.getApplication().getId()); - in.addmember("url").string(request.getRequestURL()); in.addmember("entity_id").string(entityID.c_str()); out=request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); @@ -716,7 +716,7 @@ void ADFSLogoutInitiator::receive(DDF& in, ostream& out) // Since we're remoted, the result should either be a throw, which we pass on, // 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. - doRequest(*app, in["url"].string(), in["entity_id"].string(), *resp.get()); + doRequest(*app, in["entity_id"].string(), *resp.get()); out << ret; #else @@ -724,9 +724,7 @@ void ADFSLogoutInitiator::receive(DDF& in, ostream& out) #endif } -pair ADFSLogoutInitiator::doRequest( - const Application& application, const char* requestURL, const char* entityID, HTTPResponse& response - ) const +pair ADFSLogoutInitiator::doRequest(const Application& application, const char* entityID, HTTPResponse& response) const { #ifndef SHIBSP_LITE try { @@ -793,10 +791,6 @@ pair ADFSLogout::run(SPRequest& request, bool isHandler) const param = request.getParameter("wreply"); const Application& app = request.getApplication(); - // Get the session_id. - pair shib_cookie = app.getCookieNameProps("_shibsession_"); - const char* session_id = request.getCookie(shib_cookie.first.c_str()); - if (!returning) { // Pass control to the first front channel notification point, if any. map parammap; @@ -808,16 +802,16 @@ pair ADFSLogout::run(SPRequest& request, bool isHandler) const } // Best effort on back channel and to remove the user agent's session. - if (session_id) { + string session_id = app.getServiceProvider().getSessionCache()->active(request, app); + if (!session_id.empty()) { vector sessions(1,session_id); notifyBackChannel(app, request.getRequestURL(), sessions, false); try { - app.getServiceProvider().getSessionCache()->remove(session_id, app); + app.getServiceProvider().getSessionCache()->remove(request, &request, app); } catch (exception& ex) { - m_log.error("error removing session (%s): %s", session_id, ex.what()); + m_log.error("error removing session (%s): %s", session_id.c_str(), ex.what()); } - request.setCookie(shib_cookie.first.c_str(), shib_cookie.second); } if (param) diff --git a/shibsp/AbstractSPRequest.cpp b/shibsp/AbstractSPRequest.cpp index 6c6f6f9..d76b910 100644 --- a/shibsp/AbstractSPRequest.cpp +++ b/shibsp/AbstractSPRequest.cpp @@ -81,17 +81,10 @@ Session* AbstractSPRequest::getSession(bool checkTimeout, bool ignoreAddress, bo else if (cache) m_sessionTried = true; - // Get session ID from cookie. - const Application& app = getApplication(); - pair shib_cookie = app.getCookieNameProps("_shibsession_"); - const char* session_id = getCookie(shib_cookie.first.c_str()); - if (!session_id || !*session_id) - return NULL; - // Need address checking and timeout settings. time_t timeout=0; if (checkTimeout || !ignoreAddress) { - const PropertySet* props=app.getPropertySet("Sessions"); + const PropertySet* props=getApplication().getPropertySet("Sessions"); if (props) { if (checkTimeout) { pair p=props->getUnsignedInt("timeout"); @@ -106,7 +99,7 @@ Session* AbstractSPRequest::getSession(bool checkTimeout, bool ignoreAddress, bo // The cache will either silently pass a session or NULL back, or throw an exception out. Session* session = getServiceProvider().getSessionCache()->find( - session_id, app, ignoreAddress ? NULL : getRemoteAddr().c_str(), checkTimeout ? &timeout : NULL + *this, getApplication(), ignoreAddress ? NULL : getRemoteAddr().c_str(), checkTimeout ? &timeout : NULL ); if (cache) m_session = session; diff --git a/shibsp/SessionCache.h b/shibsp/SessionCache.h index bb32ae7..21a1ea6 100644 --- a/shibsp/SessionCache.h +++ b/shibsp/SessionCache.h @@ -24,12 +24,13 @@ #define __shibsp_sessioncache_h__ #include - #ifndef SHIBSP_LITE # include # include #endif #include +#include +#include namespace shibsp { @@ -200,13 +201,15 @@ namespace shibsp { #ifndef SHIBSP_LITE /** - * Inserts a new session into the cache. + * Inserts a new session into the cache and binds the session to the outgoing + * client response. * *

The SSO tokens and Attributes remain owned by the caller and are copied by the cache. * * @param expires expiration time of session * @param application reference to Application that owns the Session - * @param client_addr network address of client + * @param httpRequest request that initiated session + * @param httpResponse current response to client * @param issuer issuing metadata of assertion issuer, if known * @param protocol protocol family used to initiate the session * @param nameid principal identifier, normalized to SAML 2, if any @@ -216,12 +219,12 @@ namespace shibsp { * @param authncontext_decl specifics of authentication event, if known * @param tokens assertions to cache with session, if any * @param attributes optional array of resolved Attributes to cache with session - * @return newly created session's key */ - virtual std::string insert( + virtual void insert( time_t expires, const Application& application, - const char* client_addr=NULL, + const xmltooling::HTTPRequest& httpRequest, + xmltooling::HTTPResponse& httpResponse, const opensaml::saml2md::EntityDescriptor* issuer=NULL, const XMLCh* protocol=NULL, const opensaml::saml2::NameID* nameid=NULL, @@ -260,18 +263,17 @@ namespace shibsp { )=0; /** - * Determines whether a given session (based on its ID) matches a set of input - * criteria. + * Determines whether the Session bound to a client request matches a set of input criteria. * - * @param key session key to check + * @param request request in which to locate Session * @param issuer required source of session(s) * @param nameid required name identifier * @param indexes session indexes * @param application reference to Application that owns the Session - * @return true iff the session matches the input criteria + * @return true iff the Session exists and matches the input criteria */ virtual bool matches( - const char* key, + const xmltooling::HTTPRequest& request, const opensaml::saml2md::EntityDescriptor* issuer, const opensaml::saml2::NameID& nameid, const std::set* indexes, @@ -285,7 +287,16 @@ namespace shibsp { #endif /** - * Locates an existing session. + * Returns the ID of the session bound to the specified client request, if possible. + * + * @param request request from client containing session, or a reference to it + * @param application reference to Application that owns the Session + * @return ID of session, if any known, or an empty string + */ + virtual std::string active(const xmltooling::HTTPRequest& request, const Application& application) const=0; + + /** + * Locates an existing session by ID. * *

If the client address is supplied, then a check will be performed against * the address recorded in the record. @@ -299,7 +310,23 @@ namespace shibsp { virtual Session* find( const char* key, const Application& application, const char* client_addr=NULL, time_t* timeout=NULL )=0; - + + /** + * Locates an existing session bound to a request. + * + *

If the client address is supplied, then a check will be performed against + * the address recorded in the record. + * + * @param request request from client containing session, or a reference to it + * @param application reference to Application that owns the Session + * @param client_addr network address of client (if known) + * @param timeout inactivity timeout to enforce (0 for none, NULL to bypass check/update of last access) + * @return pointer to locked Session, or NULL + */ + virtual Session* find( + const xmltooling::HTTPRequest& request, const Application& application, const char* client_addr=NULL, time_t* timeout=NULL + )=0; + /** * Deletes an existing session. * @@ -307,6 +334,15 @@ namespace shibsp { * @param application reference to Application that owns the Session */ virtual void remove(const char* key, const Application& application)=0; + + /** + * Deletes an existing session bound to a request. + * + * @param request request from client containing session, or a reference to it + * @param response optional response to client enabling removal of session or reference + * @param application reference to Application that owns the Session + */ + virtual void remove(const xmltooling::HTTPRequest& request, xmltooling::HTTPResponse* response, const Application& application)=0; }; /** SessionCache implementation backed by a StorageService. */ diff --git a/shibsp/handler/AbstractHandler.h b/shibsp/handler/AbstractHandler.h index 4c0cc6f..140fa11 100644 --- a/shibsp/handler/AbstractHandler.h +++ b/shibsp/handler/AbstractHandler.h @@ -130,7 +130,9 @@ namespace shibsp { * @param response outgoing HTTP response * @param relayState RelayState token to supply with message */ - virtual void preserveRelayState(const Application& application, xmltooling::HTTPResponse& response, std::string& relayState) const; + virtual void preserveRelayState( + const Application& application, xmltooling::HTTPResponse& response, std::string& relayState + ) const; /** * Implements various mechanisms to recover RelayState, @@ -141,11 +143,16 @@ namespace shibsp { * * @param application the associated Application * @param request incoming HTTP request + * @param response outgoing HTTP response * @param relayState RelayState token supplied with message * @param clear true iff the token state should be cleared */ virtual void recoverRelayState( - const Application& application, xmltooling::HTTPRequest& request, std::string& relayState, bool clear=true + const Application& application, + const xmltooling::HTTPRequest& request, + xmltooling::HTTPResponse& response, + std::string& relayState, + bool clear=true ) const; /** Logging object. */ diff --git a/shibsp/handler/AssertionConsumerService.h b/shibsp/handler/AssertionConsumerService.h index 22a8d6e..a7289fe 100644 --- a/shibsp/handler/AssertionConsumerService.h +++ b/shibsp/handler/AssertionConsumerService.h @@ -70,9 +70,7 @@ namespace shibsp { * @param httpRequest client request that initiated session * @param issuedTo address for which security assertion was issued */ - void checkAddress( - const Application& application, const xmltooling::HTTPRequest& httpRequest, const char* issuedTo - ) const; + void checkAddress(const Application& application, const xmltooling::HTTPRequest& httpRequest, const char* issuedTo) const; #ifndef SHIBSP_LITE void generateMetadata(opensaml::saml2md::SPSSODescriptor& role, const char* handlerURL) const; @@ -81,18 +79,20 @@ namespace shibsp { * Implement protocol-specific handling of the incoming decoded message. * *

The result of implementing the protocol should be an exception or - * the key to a newly created session. + * modifications to the request/response objects to reflect processing + * of the message. * * @param application reference to application receiving message * @param httpRequest client request that included message + * @param httpResponse response to client * @param policy the SecurityPolicy in effect, after having evaluated the message * @param settings policy configuration settings in effect * @param xmlObject a protocol-specific message object - * @return the key to the newly created session */ - virtual std::string implementProtocol( + virtual void implementProtocol( const Application& application, const xmltooling::HTTPRequest& httpRequest, + xmltooling::HTTPResponse& httpResponse, opensaml::SecurityPolicy& policy, const PropertySet* settings, const xmltooling::XMLObject& xmlObject @@ -143,18 +143,21 @@ namespace shibsp { #endif private: - std::string processMessage( - const Application& application, - xmltooling::HTTPRequest& httpRequest, - std::string& entityID, - std::string& relayState + std::pair processMessage( + const Application& application, const xmltooling::HTTPRequest& httpRequest, xmltooling::HTTPResponse& httpResponse ) const; - + std::pair sendRedirect( - SPRequest& request, const char* key, const char* entityID, const char* relayState + const Application& application, + const xmltooling::HTTPRequest& request, + xmltooling::HTTPResponse& response, + const char* entityID, + const char* relayState + ) const; + + void maintainHistory( + const Application& application, const xmltooling::HTTPRequest& request, xmltooling::HTTPResponse& response, const char* entityID ) const; - - void maintainHistory(SPRequest& request, const char* entityID, const char* cookieProps) const; #ifndef SHIBSP_LITE opensaml::MessageDecoder* m_decoder; diff --git a/shibsp/handler/impl/AbstractHandler.cpp b/shibsp/handler/impl/AbstractHandler.cpp index 301ae06..d74b4dd 100644 --- a/shibsp/handler/impl/AbstractHandler.cpp +++ b/shibsp/handler/impl/AbstractHandler.cpp @@ -302,7 +302,9 @@ void AbstractHandler::preserveRelayState(const Application& application, HTTPRes throw ConfigurationException("Unsupported relayState mechanism ($1).", params(1,mech.second)); } -void AbstractHandler::recoverRelayState(const Application& application, HTTPRequest& httpRequest, string& relayState, bool clear) const +void AbstractHandler::recoverRelayState( + const Application& application, const HTTPRequest& request, HTTPResponse& response, string& relayState, bool clear + ) const { SPConfig& conf = SPConfig::getConfig(); @@ -356,34 +358,30 @@ void AbstractHandler::recoverRelayState(const Application& application, HTTPRequ } } - if (conf.isEnabled(SPConfig::InProcess)) { - if (relayState == "cookie") { - // Pull the value from the "relay state" cookie. - pair relay_cookie = application.getCookieNameProps("_shibstate_"); - // In process, we should be able to cast down to a full SPRequest. - SPRequest& request = dynamic_cast(httpRequest); - const char* state = request.getCookie(relay_cookie.first.c_str()); - if (state && *state) { - // URL-decode the value. - char* rscopy=strdup(state); - XMLToolingConfig::getConfig().getURLEncoder()->decode(rscopy); - relayState = rscopy; - free(rscopy); - - if (clear) - request.setCookie(relay_cookie.first.c_str(),relay_cookie.second); - return; - } - - relayState.erase(); - } - - // Check for "default" value. - if (relayState.empty() || relayState == "default") { - pair homeURL=application.getString("homeURL"); - relayState=homeURL.first ? homeURL.second : "/"; + if (relayState == "cookie") { + // Pull the value from the "relay state" cookie. + pair relay_cookie = application.getCookieNameProps("_shibstate_"); + const char* state = request.getCookie(relay_cookie.first.c_str()); + if (state && *state) { + // URL-decode the value. + char* rscopy=strdup(state); + XMLToolingConfig::getConfig().getURLEncoder()->decode(rscopy); + relayState = rscopy; + free(rscopy); + + if (clear) + response.setCookie(relay_cookie.first.c_str(),relay_cookie.second); return; } + + relayState.erase(); + } + + // Check for "default" value. + if (relayState.empty() || relayState == "default") { + pair homeURL=application.getString("homeURL"); + relayState=homeURL.first ? homeURL.second : "/"; + return; } if (relayState == "default") diff --git a/shibsp/handler/impl/AssertionConsumerService.cpp b/shibsp/handler/impl/AssertionConsumerService.cpp index 2499eeb..7737f2d 100644 --- a/shibsp/handler/impl/AssertionConsumerService.cpp +++ b/shibsp/handler/impl/AssertionConsumerService.cpp @@ -88,53 +88,17 @@ pair AssertionConsumerService::run(SPRequest& request, bool isHandler string relayState; SPConfig& conf = SPConfig::getConfig(); - try { - if (conf.isEnabled(SPConfig::OutOfProcess)) { - // When out of process, we run natively and directly process the message. - // RelayState will be fully handled during message processing. - string entityID; - string key = processMessage(request.getApplication(), request, entityID, relayState); - return sendRedirect(request, key.c_str(), entityID.c_str(), relayState.c_str()); - } - else { - // When not out of process, we remote all the message processing. - DDF out,in = wrap(request); - DDFJanitor jin(in), jout(out); - - try { - out=request.getServiceProvider().getListenerService()->send(in); - } - catch (XMLToolingException& ex) { - // Try for RelayState recovery. - if (ex.getProperty("RelayState")) - relayState = ex.getProperty("RelayState"); - try { - recoverRelayState(request.getApplication(), request, relayState); - } - catch (exception& ex2) { - m_log.error("trapped an error during RelayState recovery while handling an error: %s", ex2.what()); - } - throw; - } - - // We invoke RelayState recovery one last time on this side of the boundary. - if (out["RelayState"].isstring()) - relayState = out["RelayState"].string(); - recoverRelayState(request.getApplication(), request, relayState); - - // If it worked, we have a session key. - if (!out["key"].isstring()) - throw FatalProfileException("Remote processing of SSO profile did not return a usable session key."); - - // Take care of cookie business and wrap it up. - return sendRedirect(request, out["key"].string(), out["entity_id"].string(), relayState.c_str()); - } + if (conf.isEnabled(SPConfig::OutOfProcess)) { + // When out of process, we run natively and directly process the message. + return processMessage(request.getApplication(), request, request); } - catch (XMLToolingException& ex) { - // Try and preserve RelayState. - if (!relayState.empty()) - ex.addProperty("RelayState", relayState.c_str()); - throw; + else { + // When not out of process, we remote all the message processing. + vector headers(1, "Cookie"); + DDF out,in = wrap(request, &headers); + DDFJanitor jin(in), jout(out); + out=request.getServiceProvider().getListenerService()->send(in); + return unwrap(request, out); } } @@ -150,33 +114,22 @@ void AssertionConsumerService::receive(DDF& in, ostream& out) } // Unpack the request. - auto_ptr http(getRequest(in)); - - // Do the work. - string relayState, entityID; - try { - string key = processMessage(*app, *http.get(), entityID, relayState); - - // Repack for return to caller. - DDF ret=DDF(NULL).structure(); - DDFJanitor jret(ret); - ret.addmember("key").string(key.c_str()); - if (!entityID.empty()) - ret.addmember("entity_id").string(entityID.c_str()); - if (!relayState.empty()) - ret.addmember("RelayState").string(relayState.c_str()); - out << ret; - } - catch (XMLToolingException& ex) { - // Try and preserve RelayState if we can. - if (!relayState.empty()) - ex.addProperty("RelayState", relayState.c_str()); - throw; - } + auto_ptr req(getRequest(in)); + + // 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; } -string AssertionConsumerService::processMessage( - const Application& application, HTTPRequest& httpRequest, string& entityID, string& relayState +pair AssertionConsumerService::processMessage( + const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse ) const { #ifndef SHIBSP_LITE @@ -195,43 +148,36 @@ string AssertionConsumerService::processMessage( // Create the policy. shibsp::SecurityPolicy policy(application, &m_role, validate.first && validate.second); - // Decode the message and process it in a protocol-specific way. - auto_ptr msg(m_decoder->decode(relayState, httpRequest, policy)); - if (!msg.get()) - throw BindingException("Failed to decode an SSO protocol response."); - recoverRelayState(application, httpRequest, relayState); - string key = implementProtocol(application, httpRequest, policy, settings, *msg.get()); - - auto_ptr_char issuer(policy.getIssuer() ? policy.getIssuer()->getName() : NULL); - if (issuer.get()) - entityID = issuer.get(); - - return key; + string relayState; + + try { + // Decode the message and process it in a protocol-specific way. + auto_ptr msg(m_decoder->decode(relayState, httpRequest, policy)); + if (!msg.get()) + throw BindingException("Failed to decode an SSO protocol response."); + recoverRelayState(application, httpRequest, httpResponse, relayState); + implementProtocol(application, httpRequest, httpResponse, policy, settings, *msg.get()); + + auto_ptr_char issuer(policy.getIssuer() ? policy.getIssuer()->getName() : NULL); + + // History cookie. + if (issuer.get() && *issuer.get()) + maintainHistory(application, httpRequest, httpResponse, issuer.get()); + + // Now redirect to the state value. By now, it should be set to *something* usable. + return make_pair(true, httpResponse.sendRedirect(relayState.c_str())); + } + catch (XMLToolingException& ex) { + if (!relayState.empty()) + ex.addProperty("RelayState", relayState.c_str()); + throw; + } #else throw ConfigurationException("Cannot process message using lite version of shibsp library."); #endif } -pair AssertionConsumerService::sendRedirect( - SPRequest& request, const char* key, const char* entityID, const char* relayState - ) const -{ - // We've got a good session, so set the session cookie. - pair shib_cookie=request.getApplication().getCookieNameProps("_shibsession_"); - string k(key); - k += shib_cookie.second; - request.setCookie(shib_cookie.first.c_str(), k.c_str()); - - // History cookie. - maintainHistory(request, entityID, shib_cookie.second); - - // Now redirect to the state value. By now, it should be set to *something* usable. - return make_pair(true, request.sendRedirect(relayState)); -} - -void AssertionConsumerService::checkAddress( - const Application& application, const HTTPRequest& httpRequest, const char* issuedTo - ) const +void AssertionConsumerService::checkAddress(const Application& application, const HTTPRequest& httpRequest, const char* issuedTo) const { const PropertySet* props=application.getPropertySet("Sessions"); pair checkAddress = props ? props->getBool("checkAddress") : make_pair(false,true); @@ -443,22 +389,28 @@ void AssertionConsumerService::extractMessageDetails(const Assertion& assertion, #endif -void AssertionConsumerService::maintainHistory(SPRequest& request, const char* entityID, const char* cookieProps) const +void AssertionConsumerService::maintainHistory( + const Application& application, const HTTPRequest& request, HTTPResponse& response, const char* entityID + ) const { - if (!entityID) - return; - - const PropertySet* sessionProps=request.getApplication().getPropertySet("Sessions"); + static const char* defProps="; path=/"; + + const PropertySet* sessionProps=application.getPropertySet("Sessions"); pair idpHistory=sessionProps->getBool("idpHistory"); + if (!idpHistory.first || idpHistory.second) { + pair cookieProps=sessionProps->getString("cookieProps"); + if (!cookieProps.first) + cookieProps.second=defProps; + // Set an IdP history cookie locally (essentially just a CDC). CommonDomainCookie cdc(request.getCookie(CommonDomainCookie::CDCName)); // Either leave in memory or set an expiration. pair days=sessionProps->getUnsignedInt("idpHistoryDays"); if (!days.first || days.second==0) { - string c = string(cdc.set(entityID)) + cookieProps; - request.setCookie(CommonDomainCookie::CDCName, c.c_str()); + string c = string(cdc.set(entityID)) + cookieProps.second; + response.setCookie(CommonDomainCookie::CDCName, c.c_str()); } else { time_t now=time(NULL) + (days.second * 24 * 60 * 60); @@ -470,8 +422,8 @@ void AssertionConsumerService::maintainHistory(SPRequest& request, const char* e #endif char timebuf[64]; strftime(timebuf,64,"%a, %d %b %Y %H:%M:%S GMT",ptime); - string c = string(cdc.set(entityID)) + cookieProps + "; expires=" + timebuf; - request.setCookie(CommonDomainCookie::CDCName, c.c_str()); + string c = string(cdc.set(entityID)) + cookieProps.second + "; expires=" + timebuf; + response.setCookie(CommonDomainCookie::CDCName, c.c_str()); } } } diff --git a/shibsp/handler/impl/AssertionLookup.cpp b/shibsp/handler/impl/AssertionLookup.cpp index c43712e..11ac325 100644 --- a/shibsp/handler/impl/AssertionLookup.cpp +++ b/shibsp/handler/impl/AssertionLookup.cpp @@ -123,7 +123,7 @@ pair AssertionLookup::run(SPRequest& request, bool isHandler) const } else { // When not out of process, we remote all the message processing. - DDF out,in = wrap(request, NULL, true); + DDF out,in = wrap(request); DDFJanitor jin(in), jout(out); out=request.getServiceProvider().getListenerService()->send(in); @@ -177,7 +177,7 @@ pair AssertionLookup::processMessage(const Application& application, 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); + Session* session = application.getServiceProvider().getSessionCache()->find(httpRequest, application); if (!session) { m_log.error("valid session (%s) not found for assertion lookup", key); throw FatalProfileException("Session key not found."); diff --git a/shibsp/handler/impl/LocalLogoutInitiator.cpp b/shibsp/handler/impl/LocalLogoutInitiator.cpp index d9383a7..c75bc87 100644 --- a/shibsp/handler/impl/LocalLogoutInitiator.cpp +++ b/shibsp/handler/impl/LocalLogoutInitiator.cpp @@ -97,22 +97,17 @@ pair LocalLogoutInitiator::run(SPRequest& request, bool isHandler) co if (ret.first) return ret; - // Get session ID from cookie. - pair shib_cookie = request.getApplication().getCookieNameProps("_shibsession_"); - const char* session_id = request.getCookie(shib_cookie.first.c_str()); - if (session_id) { + const Application& app = request.getApplication(); + string session_id = app.getServiceProvider().getSessionCache()->active(request, app); + if (!session_id.empty()) { // Do back channel notification. vector sessions(1, session_id); - if (!notifyBackChannel(request.getApplication(), request.getRequestURL(), sessions, true)) { - request.getApplication().getServiceProvider().getSessionCache()->remove(session_id, request.getApplication()); - return sendLogoutPage(request.getApplication(), request, true, "Partial logout failure."); + if (!notifyBackChannel(app, request.getRequestURL(), sessions, true)) { + app.getServiceProvider().getSessionCache()->remove(request, &request, app); + return sendLogoutPage(app, request, true, "Partial logout failure."); } - request.getServiceProvider().getSessionCache()->remove(session_id, request.getApplication()); - - // Clear the cookie. - pair shib_cookie=request.getApplication().getCookieNameProps("_shibsession_"); - request.setCookie(shib_cookie.first.c_str(), shib_cookie.second); + request.getServiceProvider().getSessionCache()->remove(request, &request, app); } - return sendLogoutPage(request.getApplication(), request, true, "Logout was successful."); + return sendLogoutPage(app, request, true, "Logout was successful."); } diff --git a/shibsp/handler/impl/SAML1Consumer.cpp b/shibsp/handler/impl/SAML1Consumer.cpp index 7ad7355..893b84a 100644 --- a/shibsp/handler/impl/SAML1Consumer.cpp +++ b/shibsp/handler/impl/SAML1Consumer.cpp @@ -75,9 +75,10 @@ namespace shibsp { } private: - string implementProtocol( + void implementProtocol( const Application& application, const HTTPRequest& httpRequest, + HTTPResponse& httpResponse, SecurityPolicy& policy, const PropertySet* settings, const XMLObject& xmlObject @@ -99,9 +100,10 @@ namespace shibsp { #ifndef SHIBSP_LITE -string SAML1Consumer::implementProtocol( +void SAML1Consumer::implementProtocol( const Application& application, const HTTPRequest& httpRequest, + HTTPResponse& httpResponse, SecurityPolicy& policy, const PropertySet* settings, const XMLObject& xmlObject @@ -270,10 +272,11 @@ string SAML1Consumer::implementProtocol( // Now merge in bad tokens for caching. tokens.insert(tokens.end(), badtokens.begin(), badtokens.end()); - return application.getServiceProvider().getSessionCache()->insert( + application.getServiceProvider().getSessionCache()->insert( now + lifetime.second, application, - httpRequest.getRemoteAddr().c_str(), + httpRequest, + httpResponse, policy.getIssuerMetadata() ? dynamic_cast(policy.getIssuerMetadata()->getParent()) : NULL, (!response->getMinorVersion().first || response->getMinorVersion().second==1) ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM, diff --git a/shibsp/handler/impl/SAML2Consumer.cpp b/shibsp/handler/impl/SAML2Consumer.cpp index bde48ed..2045185 100644 --- a/shibsp/handler/impl/SAML2Consumer.cpp +++ b/shibsp/handler/impl/SAML2Consumer.cpp @@ -68,9 +68,10 @@ namespace shibsp { } private: - string implementProtocol( + void implementProtocol( const Application& application, const HTTPRequest& httpRequest, + HTTPResponse& httpResponse, SecurityPolicy& policy, const PropertySet* settings, const XMLObject& xmlObject @@ -91,9 +92,10 @@ namespace shibsp { #ifndef SHIBSP_LITE -string SAML2Consumer::implementProtocol( +void SAML2Consumer::implementProtocol( const Application& application, const HTTPRequest& httpRequest, + HTTPResponse& httpResponse, SecurityPolicy& policy, const PropertySet* settings, const XMLObject& xmlObject @@ -378,10 +380,11 @@ string SAML2Consumer::implementProtocol( // Now merge in bad tokens for caching. tokens.insert(tokens.end(), badtokens.begin(), badtokens.end()); - string key = application.getServiceProvider().getSessionCache()->insert( + application.getServiceProvider().getSessionCache()->insert( sessionExp, application, - httpRequest.getRemoteAddr().c_str(), + httpRequest, + httpResponse, policy.getIssuerMetadata() ? dynamic_cast(policy.getIssuerMetadata()->getParent()) : NULL, samlconstants::SAML20P_NS, ssoName, @@ -396,7 +399,6 @@ string SAML2Consumer::implementProtocol( if (ownedName) delete ssoName; for_each(ownedtokens.begin(), ownedtokens.end(), xmltooling::cleanup()); - return key; } catch (exception&) { if (ownedName) diff --git a/shibsp/handler/impl/SAML2Logout.cpp b/shibsp/handler/impl/SAML2Logout.cpp index 05a80a7..1c6feb3 100644 --- a/shibsp/handler/impl/SAML2Logout.cpp +++ b/shibsp/handler/impl/SAML2Logout.cpp @@ -93,13 +93,9 @@ namespace shibsp { #endif private: - pair doRequest( - const Application& application, const char* session_id, const HTTPRequest& httpRequest, HTTPResponse& httpResponse - ) const; + pair doRequest(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const; #ifndef SHIBSP_LITE - bool stronglyMatches(const XMLCh* idp, const XMLCh* sp, const saml2::NameID& n1, const saml2::NameID& n2) const; - pair sendResponse( const XMLCh* requestID, const XMLCh* code, @@ -211,21 +207,16 @@ pair SAML2Logout::run(SPRequest& request, bool isHandler) const if (ret.first) return ret; - // Get the session_id in case this is a front-channel LogoutRequest. - pair shib_cookie = request.getApplication().getCookieNameProps("_shibsession_"); - const char* session_id = request.getCookie(shib_cookie.first.c_str()); - SPConfig& conf = SPConfig::getConfig(); if (conf.isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively and directly process the message. - return doRequest(request.getApplication(), session_id, request, request); + return doRequest(request.getApplication(), request, request); } else { // When not out of process, we remote all the message processing. - DDF out,in = wrap(request, NULL, true); + vector headers(1,"Cookie"); + DDF out,in = wrap(request, &headers, true); DDFJanitor jin(in), jout(out); - if (session_id) - in.addmember("session_id").string(session_id); out=request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } @@ -243,54 +234,47 @@ void SAML2Logout::receive(DDF& in, ostream& out) } // Unpack the request. + auto_ptr req(getRequest(in)); + + // Wrap a response shim. DDF ret(NULL); DDFJanitor jout(ret); - auto_ptr req(getRequest(in)); auto_ptr resp(getResponse(ret)); // Since we're remoted, the result should either be a throw, which we pass on, // 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. - doRequest(*app, in["session_id"].string(), *req.get(), *resp.get()); + doRequest(*app, *req.get(), *resp.get()); out << ret; } -pair SAML2Logout::doRequest( - const Application& application, const char* session_id, const HTTPRequest& request, HTTPResponse& response - ) const +pair SAML2Logout::doRequest(const Application& application, const HTTPRequest& request, HTTPResponse& response) const { #ifndef SHIBSP_LITE + // First capture the active session ID. SessionCache* cache = application.getServiceProvider().getSessionCache(); - if (!strcmp(request.getMethod(),"GET") && request.getParameter("notifying")) { + string session_id = cache->active(request, application); + if (!strcmp(request.getMethod(),"GET") && request.getParameter("notifying")) { // This is returning from a front-channel notification, so we have to do the back-channel and then // respond. To do that, we need state from the original request. if (!request.getParameter("entityID")) { - if (session_id) { - cache->remove(session_id, application); - // Clear the cookie. - pair shib_cookie=application.getCookieNameProps("_shibsession_"); - response.setCookie(shib_cookie.first.c_str(), shib_cookie.second); - } + cache->remove(request, &response, application); throw FatalProfileException("Application notification loop did not return entityID for LogoutResponse."); } // Best effort on back channel and to remove the user agent's session. bool worked1 = false,worked2 = false; - if (session_id) { + if (!session_id.empty()) { vector sessions(1,session_id); worked1 = notifyBackChannel(application, request.getRequestURL(), sessions, false); try { - cache->remove(session_id, application); + cache->remove(request, &response, application); worked2 = true; } catch (exception& ex) { m_log.error("error removing session (%s): %s", session_id, ex.what()); } - - // Clear the cookie. - pair shib_cookie=application.getCookieNameProps("_shibsession_"); - response.setCookie(shib_cookie.first.c_str(), shib_cookie.second); } else { worked1 = worked2 = true; @@ -360,7 +344,7 @@ pair SAML2Logout::doRequest( // Message from IdP to logout one or more sessions. // If this is front-channel, we have to have a session_id to use already. - if (m_decoder->isUserAgentPresent() && !session_id) { + if (m_decoder->isUserAgentPresent() && session_id.empty()) { m_log.error("no active session"); return sendResponse( logoutRequest->getID(), @@ -428,11 +412,11 @@ pair SAML2Logout::doRequest( // For a front-channel LogoutRequest, we have to match the information in the request // against the current session. - if (session_id) { - if (!cache->matches(session_id, entity, *nameid, &indexes, application)) { + if (!session_id.empty()) { + if (!cache->matches(request, entity, *nameid, &indexes, application)) { return sendResponse( logoutRequest->getID(), - StatusCode::REQUESTER, StatusCode::REQUEST_DENIED, "Active sessions did not match logout request.", + StatusCode::REQUESTER, StatusCode::REQUEST_DENIED, "Active session did not match logout request.", relayState.c_str(), policy.getIssuerMetadata(), application, @@ -452,8 +436,8 @@ pair SAML2Logout::doRequest( // Now we actually terminate everything except for the active session, // if this is front-channel, for notification purposes. for (vector::const_iterator sit = sessions.begin(); sit != sessions.end(); ++sit) - if (session_id && strcmp(sit->c_str(), session_id)) - cache->remove(sit->c_str(), application); + if (*sit != session_id) + cache->remove(sit->c_str(), application); // using the ID-based removal operation } catch (exception& ex) { m_log.error("error while logging out matching sessions: %s", ex.what()); @@ -487,19 +471,15 @@ pair SAML2Logout::doRequest( // For back-channel requests, or if no front-channel notification is needed... bool worked1 = false,worked2 = false; worked1 = notifyBackChannel(application, request.getRequestURL(), sessions, false); - if (session_id) { + if (!session_id.empty()) { // One last session to yoink... try { - cache->remove(session_id, application); + cache->remove(request, &response, application); worked2 = true; } catch (exception& ex) { - m_log.error("error removing active session (%s): %s", session_id, ex.what()); + m_log.error("error removing active session (%s): %s", session_id.c_str(), ex.what()); } - - // Clear the cookie. - pair shib_cookie=application.getCookieNameProps("_shibsession_"); - response.setCookie(shib_cookie.first.c_str(), shib_cookie.second); } return sendResponse( @@ -542,41 +522,6 @@ pair SAML2Logout::doRequest( #ifndef SHIBSP_LITE -bool SAML2Logout::stronglyMatches(const XMLCh* idp, const XMLCh* sp, const saml2::NameID& n1, const saml2::NameID& n2) const -{ - if (!XMLString::equals(n1.getName(), n2.getName())) - return false; - - const XMLCh* s1 = n1.getFormat(); - const XMLCh* s2 = n2.getFormat(); - if (!s1 || !*s1) - s1 = saml2::NameID::UNSPECIFIED; - if (!s2 || !*s2) - s2 = saml2::NameID::UNSPECIFIED; - if (!XMLString::equals(s1,s2)) - return false; - - s1 = n1.getNameQualifier(); - s2 = n2.getNameQualifier(); - if (!s1 || !*s1) - s1 = idp; - if (!s2 || !*s2) - s2 = idp; - if (!XMLString::equals(s1,s2)) - return false; - - s1 = n1.getSPNameQualifier(); - s2 = n2.getSPNameQualifier(); - if (!s1 || !*s1) - s1 = sp; - if (!s2 || !*s2) - s2 = sp; - if (!XMLString::equals(s1,s2)) - return false; - - return true; -} - pair SAML2Logout::sendResponse( const XMLCh* requestID, const XMLCh* code, diff --git a/shibsp/handler/impl/SAML2LogoutInitiator.cpp b/shibsp/handler/impl/SAML2LogoutInitiator.cpp index 50f06b9..b68c44f 100644 --- a/shibsp/handler/impl/SAML2LogoutInitiator.cpp +++ b/shibsp/handler/impl/SAML2LogoutInitiator.cpp @@ -78,7 +78,9 @@ namespace shibsp { #endif private: - pair doRequest(const Application& application, const char* requestURL, Session* session, HTTPResponse& httpResponse) const; + pair doRequest( + const Application& application, const HTTPRequest& request, HTTPResponse& httpResponse, Session* session + ) const; string m_appId; #ifndef SHIBSP_LITE @@ -199,16 +201,14 @@ pair SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) co if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively. - return doRequest(request.getApplication(), request.getRequestURL(), session, request); + return doRequest(request.getApplication(), request, request, session); } else { // When not out of process, we remote the request. - Locker locker(session, false); - DDF out,in(m_address.c_str()); + session->unlock(); + vector headers(1,"Cookie"); + DDF out,in = wrap(request,&headers); DDFJanitor jin(in), jout(out); - in.addmember("application_id").string(request.getApplication().getId()); - in.addmember("session_id").string(session->getID()); - in.addmember("url").string(request.getRequestURL()); out=request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } @@ -230,6 +230,9 @@ void SAML2LogoutInitiator::receive(DDF& in, ostream& out) throw ConfigurationException("Unable to locate application for logout, deleted?"); } + // Unpack the request. + auto_ptr req(getRequest(in)); + // Set up a response shim. DDF ret(NULL); DDFJanitor jout(ret); @@ -237,7 +240,7 @@ void SAML2LogoutInitiator::receive(DDF& in, ostream& out) Session* session = NULL; try { - session = app->getServiceProvider().getSessionCache()->find(in["session_id"].string(), *app, NULL, NULL); + session = app->getServiceProvider().getSessionCache()->find(*req.get(), *app, NULL, NULL); } catch (exception& ex) { m_log.error("error accessing current session: %s", ex.what()); @@ -249,16 +252,12 @@ void SAML2LogoutInitiator::receive(DDF& in, ostream& out) // Since we're remoted, the result should either be a throw, which we pass on, // 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. - doRequest(*app, in["url"].string(), session, *resp.get()); + doRequest(*app, *req.get(), *resp.get(), session); } else { m_log.error("no NameID or issuing entityID found in session"); session->unlock(); - app->getServiceProvider().getSessionCache()->remove(in["session_id"].string(), *app); - - // Clear the cookie. - pair shib_cookie=app->getCookieNameProps("_shibsession_"); - resp->setCookie(shib_cookie.first.c_str(), shib_cookie.second); + app->getServiceProvider().getSessionCache()->remove(*req.get(), resp.get(), *app); } } out << ret; @@ -268,19 +267,15 @@ void SAML2LogoutInitiator::receive(DDF& in, ostream& out) } pair SAML2LogoutInitiator::doRequest( - const Application& application, const char* requestURL, Session* session, HTTPResponse& response + const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse, Session* session ) const { - // Clear the cookie. - pair shib_cookie=application.getCookieNameProps("_shibsession_"); - response.setCookie(shib_cookie.first.c_str(), shib_cookie.second); - // Do back channel notification. vector sessions(1, session->getID()); - if (!notifyBackChannel(application, requestURL, sessions, false)) { + if (!notifyBackChannel(application, httpRequest.getRequestURL(), sessions, false)) { session->unlock(); - application.getServiceProvider().getSessionCache()->remove(sessions.front().c_str(), application); - return sendLogoutPage(application, response, true, "Partial logout failure."); + application.getServiceProvider().getSessionCache()->remove(httpRequest, &httpResponse, application); + return sendLogoutPage(application, httpResponse, true, "Partial logout failure."); } #ifndef SHIBSP_LITE @@ -345,22 +340,21 @@ pair SAML2LogoutInitiator::doRequest( } if (!logoutResponse) - ret = sendLogoutPage(application, response, false, "Identity provider did not respond to logout request."); + ret = sendLogoutPage(application, httpResponse, false, "Identity provider did not respond to logout request."); else if (!logoutResponse->getStatus() || !logoutResponse->getStatus()->getStatusCode() || !XMLString::equals(logoutResponse->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) { delete logoutResponse; - ret = sendLogoutPage(application, response, false, "Identity provider returned a SAML error in response to logout request."); + ret = sendLogoutPage(application, httpResponse, false, "Identity provider returned a SAML error in response to logout request."); } else { delete logoutResponse; - ret = sendLogoutPage(application, response, false, "Logout completed successfully."); + ret = sendLogoutPage(application, httpResponse, false, "Logout completed successfully."); } if (session) { - string session_id = session->getID(); session->unlock(); session = NULL; - application.getServiceProvider().getSessionCache()->remove(session_id.c_str(), application); + application.getServiceProvider().getSessionCache()->remove(httpRequest, &httpResponse, application); } return ret; } @@ -369,7 +363,7 @@ pair SAML2LogoutInitiator::doRequest( msg->setDestination(ep->getLocation()); auto_ptr_char dest(ep->getLocation()); - ret.second = sendMessage(*encoder, msg.get(), NULL, dest.get(), role, application, response); + ret.second = sendMessage(*encoder, msg.get(), NULL, dest.get(), role, application, httpResponse); ret.first = true; msg.release(); // freed by encoder } @@ -378,17 +372,15 @@ pair SAML2LogoutInitiator::doRequest( } if (session) { - string session_id = session->getID(); session->unlock(); session = NULL; - application.getServiceProvider().getSessionCache()->remove(session_id.c_str(), application); + application.getServiceProvider().getSessionCache()->remove(httpRequest, &httpResponse, application); } return ret; #else - string session_id = session->getID(); session->unlock(); - application.getServiceProvider().getSessionCache()->remove(session_id.c_str(), application); + application.getServiceProvider().getSessionCache()->remove(httpRequest, &httpResponse, application); throw ConfigurationException("Cannot perform logout using lite version of shibsp library."); #endif } diff --git a/shibsp/handler/impl/SAML2NameIDMgmt.cpp b/shibsp/handler/impl/SAML2NameIDMgmt.cpp index a63bc6c..3d5e69d 100644 --- a/shibsp/handler/impl/SAML2NameIDMgmt.cpp +++ b/shibsp/handler/impl/SAML2NameIDMgmt.cpp @@ -93,12 +93,9 @@ namespace shibsp { #endif private: - pair doRequest( - const Application& application, const char* session_id, const HTTPRequest& httpRequest, HTTPResponse& httpResponse - ) const; + pair doRequest(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const; #ifndef SHIBSP_LITE - bool stronglyMatches(const XMLCh* idp, const XMLCh* sp, const saml2::NameID& n1, const saml2::NameID& n2) const; bool notifyBackChannel(const Application& application, const char* requestURL, const NameID& nameid, const NewID* newid) const; pair sendResponse( @@ -201,21 +198,16 @@ SAML2NameIDMgmt::SAML2NameIDMgmt(const DOMElement* e, const char* appId) pair SAML2NameIDMgmt::run(SPRequest& request, bool isHandler) const { - // Get the session_id in case this is a front-channel request. - pair shib_cookie = request.getApplication().getCookieNameProps("_shibsession_"); - const char* session_id = request.getCookie(shib_cookie.first.c_str()); - SPConfig& conf = SPConfig::getConfig(); if (conf.isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively and directly process the message. - return doRequest(request.getApplication(), session_id, request, request); + return doRequest(request.getApplication(), request, request); } else { // When not out of process, we remote all the message processing. - DDF out,in = wrap(request, NULL, true); + vector headers(1,"Cookie"); + DDF out,in = wrap(request, &headers, true); DDFJanitor jin(in), jout(out); - if (session_id) - in.addmember("session_id").string(session_id); out=request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } @@ -233,20 +225,22 @@ void SAML2NameIDMgmt::receive(DDF& in, ostream& out) } // Unpack the request. + auto_ptr req(getRequest(in)); + + // Wrap a response shim. DDF ret(NULL); DDFJanitor jout(ret); - auto_ptr req(getRequest(in)); auto_ptr resp(getResponse(ret)); // Since we're remoted, the result should either be a throw, which we pass on, // 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. - doRequest(*app, in["session_id"].string(), *req.get(), *resp.get()); + doRequest(*app, *req.get(), *resp.get()); out << ret; } pair SAML2NameIDMgmt::doRequest( - const Application& application, const char* session_id, const HTTPRequest& request, HTTPResponse& response + const Application& application, const HTTPRequest& request, HTTPResponse& response ) const { #ifndef SHIBSP_LITE @@ -276,9 +270,10 @@ pair SAML2NameIDMgmt::doRequest( throw SecurityPolicyException("Security of ManageNameIDRequest not established."); // Message from IdP to change or terminate a NameID. - + // If this is front-channel, we have to have a session_id to use already. - if (m_decoder->isUserAgentPresent() && !session_id) { + string session_id = cache->active(request, application); + if (m_decoder->isUserAgentPresent() && session_id.empty()) { m_log.error("no active session"); return sendResponse( mgmtRequest->getID(), @@ -290,7 +285,7 @@ pair SAML2NameIDMgmt::doRequest( true ); } - + bool ownedName = false; NameID* nameid = mgmtRequest->getNameID(); if (!nameid) { @@ -338,8 +333,8 @@ pair SAML2NameIDMgmt::doRequest( // For a front-channel request, we have to match the information in the request // against the current session. EntityDescriptor* entity = policy.getIssuerMetadata() ? dynamic_cast(policy.getIssuerMetadata()->getParent()) : NULL; - if (session_id) { - if (!cache->matches(session_id, entity, *nameid, NULL, application)) { + if (!session_id.empty()) { + if (!cache->matches(request, entity, *nameid, NULL, application)) { return sendResponse( mgmtRequest->getID(), StatusCode::REQUESTER, StatusCode::REQUEST_DENIED, "Active session did not match NameID mgmt request.", @@ -476,41 +471,6 @@ pair SAML2NameIDMgmt::doRequest( #ifndef SHIBSP_LITE -bool SAML2NameIDMgmt::stronglyMatches(const XMLCh* idp, const XMLCh* sp, const saml2::NameID& n1, const saml2::NameID& n2) const -{ - if (!XMLString::equals(n1.getName(), n2.getName())) - return false; - - const XMLCh* s1 = n1.getFormat(); - const XMLCh* s2 = n2.getFormat(); - if (!s1 || !*s1) - s1 = saml2::NameID::UNSPECIFIED; - if (!s2 || !*s2) - s2 = saml2::NameID::UNSPECIFIED; - if (!XMLString::equals(s1,s2)) - return false; - - s1 = n1.getNameQualifier(); - s2 = n2.getNameQualifier(); - if (!s1 || !*s1) - s1 = idp; - if (!s2 || !*s2) - s2 = idp; - if (!XMLString::equals(s1,s2)) - return false; - - s1 = n1.getSPNameQualifier(); - s2 = n2.getSPNameQualifier(); - if (!s1 || !*s1) - s1 = sp; - if (!s2 || !*s2) - s2 = sp; - if (!XMLString::equals(s1,s2)) - return false; - - return true; -} - pair SAML2NameIDMgmt::sendResponse( const XMLCh* requestID, const XMLCh* code, diff --git a/shibsp/handler/impl/SAML2SessionInitiator.cpp b/shibsp/handler/impl/SAML2SessionInitiator.cpp index a022c91..e5ddc7d 100644 --- a/shibsp/handler/impl/SAML2SessionInitiator.cpp +++ b/shibsp/handler/impl/SAML2SessionInitiator.cpp @@ -247,7 +247,7 @@ pair SAML2SessionInitiator::run(SPRequest& request, const char* entit if (acsByIndex.first && !acsByIndex.second) { // Since we're passing the ACS by value, we need to compute the return URL, // so we'll need the target resource for real. - recoverRelayState(request.getApplication(), request, target, false); + recoverRelayState(request.getApplication(), request, request, target, false); } option = request.getParameter("isPassive"); diff --git a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp index 1aec1e2..20b1ff4 100644 --- a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp +++ b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp @@ -99,7 +99,7 @@ pair SAMLDSSessionInitiator::run(SPRequest& request, const char* enti option = request.getParameter("target"); if (option) target = option; - recoverRelayState(request.getApplication(), request, target, false); + recoverRelayState(request.getApplication(), request, request, target, false); option = request.getParameter("isPassive"); if (option) diff --git a/shibsp/handler/impl/Shib1SessionInitiator.cpp b/shibsp/handler/impl/Shib1SessionInitiator.cpp index 6d2152f..598d294 100644 --- a/shibsp/handler/impl/Shib1SessionInitiator.cpp +++ b/shibsp/handler/impl/Shib1SessionInitiator.cpp @@ -128,7 +128,7 @@ pair Shib1SessionInitiator::run(SPRequest& request, const char* entit // Since we're passing the ACS by value, we need to compute the return URL, // so we'll need the target resource for real. - recoverRelayState(request.getApplication(), request, target, false); + recoverRelayState(request.getApplication(), request, request, target, false); } else { // We're running as a "virtual handler" from within the filter. diff --git a/shibsp/handler/impl/WAYFSessionInitiator.cpp b/shibsp/handler/impl/WAYFSessionInitiator.cpp index fe627d1..60c6352 100644 --- a/shibsp/handler/impl/WAYFSessionInitiator.cpp +++ b/shibsp/handler/impl/WAYFSessionInitiator.cpp @@ -95,7 +95,7 @@ pair WAYFSessionInitiator::run(SPRequest& request, const char* entity option = request.getParameter("target"); if (option) target = option; - recoverRelayState(request.getApplication(), request, target, false); + recoverRelayState(request.getApplication(), request, request, target, false); } else { // We're running as a "virtual handler" from within the filter. diff --git a/shibsp/impl/StorageServiceSessionCache.cpp b/shibsp/impl/StorageServiceSessionCache.cpp index 155e8ae..843a37d 100644 --- a/shibsp/impl/StorageServiceSessionCache.cpp +++ b/shibsp/impl/StorageServiceSessionCache.cpp @@ -71,10 +71,11 @@ namespace shibsp { #ifndef SHIBSP_LITE void receive(DDF& in, ostream& out); - string insert( + void insert( time_t expires, const Application& application, - const char* client_addr=NULL, + const HTTPRequest& httpRequest, + HTTPResponse& httpResponse, const saml2md::EntityDescriptor* issuer=NULL, const XMLCh* protocol=NULL, const saml2::NameID* nameid=NULL, @@ -94,7 +95,7 @@ namespace shibsp { vector& sessions ); bool matches( - const char* key, + const xmltooling::HTTPRequest& request, const saml2md::EntityDescriptor* issuer, const saml2::NameID& nameid, const set* indexes, @@ -105,6 +106,29 @@ namespace shibsp { void remove(const char* key, const Application& application); void test(); + string active(const xmltooling::HTTPRequest& request, const Application& application) const { + pair shib_cookie = application.getCookieNameProps("_shibsession_"); + const char* session_id = request.getCookie(shib_cookie.first.c_str()); + return (session_id ? session_id : ""); + } + + Session* find(const HTTPRequest& request, const Application& application, const char* client_addr=NULL, time_t* timeout=NULL) { + string id = active(request, application); + if (!id.empty()) + return find(id.c_str(), application, client_addr, timeout); + return NULL; + } + + void remove(const HTTPRequest& request, HTTPResponse* response, const Application& application) { + pair shib_cookie = application.getCookieNameProps("_shibsession_"); + const char* session_id = request.getCookie(shib_cookie.first.c_str()); + if (session_id && *session_id) { + if (response) + response->setCookie(shib_cookie.first.c_str(), shib_cookie.second); + remove(session_id, application); + } + } + void cleanup(); Category& m_log; @@ -801,10 +825,11 @@ void SSCache::insert(const char* key, time_t expires, const char* name, const ch } } -string SSCache::insert( +void SSCache::insert( time_t expires, const Application& application, - const char* client_addr, + const HTTPRequest& httpRequest, + HTTPResponse& httpResponse, const saml2md::EntityDescriptor* issuer, const XMLCh* protocol, const saml2::NameID* nameid, @@ -874,8 +899,7 @@ string SSCache::insert( strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime); obj.addmember("expires").string(timebuf); - if (client_addr) - obj.addmember("client_addr").string(client_addr); + obj.addmember("client_addr").string(httpRequest.getRemoteAddr().c_str()); if (issuer) obj.addmember("entity_id").string(entity_id.get()); if (protocol) { @@ -953,7 +977,7 @@ string SSCache::insert( } const char* pid = obj["entity_id"].string(); - m_log.info("new session created: SessionID (%s) IdP (%s) Address (%s)", key.get(), pid ? pid : "none", client_addr); + m_log.info("new session created: SessionID (%s) IdP (%s) Address (%s)", key.get(), pid ? pid : "none", httpRequest.getRemoteAddr().c_str()); // Transaction Logging TransactionLog* xlog = application.getServiceProvider().getTransactionLog(); @@ -966,7 +990,7 @@ string SSCache::insert( ") for principal from (IdP: " << (pid ? pid : "none") << ") at (ClientAddress: " << - (client_addr ? client_addr : "none") << + httpRequest.getRemoteAddr() << ") with (NameIdentifier: " << (nameid ? name.get() : "none") << ")"; @@ -983,11 +1007,14 @@ string SSCache::insert( xlog->log.info("}"); } - return key.get(); + pair shib_cookie = application.getCookieNameProps("_shibsession_"); + string k(key.get()); + k += shib_cookie.second; + httpResponse.setCookie(shib_cookie.first.c_str(), k.c_str()); } bool SSCache::matches( - const char* key, + const xmltooling::HTTPRequest& request, const saml2md::EntityDescriptor* issuer, const saml2::NameID& nameid, const set* indexes, @@ -996,7 +1023,7 @@ bool SSCache::matches( { auto_ptr_char entityID(issuer ? issuer->getEntityID() : NULL); try { - Session* session = find(key, application); + Session* session = find(request, application); if (session) { Locker locker(session, false); if (XMLString::equals(session->getEntityID(), entityID.get()) && session->getNameID() && @@ -1006,7 +1033,7 @@ bool SSCache::matches( } } catch (exception& ex) { - m_log.error("error while matching session (%s): %s", key, ex.what()); + m_log.error("error while matching session: %s", ex.what()); } return false; } diff --git a/util/resolvertest.cpp b/util/resolvertest.cpp index d818e80..6e589f7 100644 --- a/util/resolvertest.cpp +++ b/util/resolvertest.cpp @@ -84,9 +84,10 @@ public: } private: - string implementProtocol( + void implementProtocol( const Application& application, const HTTPRequest& httpRequest, + HTTPResponse& httpResponse, SecurityPolicy& policy, const PropertySet* settings, const XMLObject& xmlObject