Move session cookie management into session cache.
authorScott Cantor <cantor.2@osu.edu>
Mon, 26 Nov 2007 06:50:17 +0000 (06:50 +0000)
committerScott Cantor <cantor.2@osu.edu>
Mon, 26 Nov 2007 06:50:17 +0000 (06:50 +0000)
Fully remote more handlers to provide access to session cookie.

20 files changed:
adfs/adfs.cpp
shibsp/AbstractSPRequest.cpp
shibsp/SessionCache.h
shibsp/handler/AbstractHandler.h
shibsp/handler/AssertionConsumerService.h
shibsp/handler/impl/AbstractHandler.cpp
shibsp/handler/impl/AssertionConsumerService.cpp
shibsp/handler/impl/AssertionLookup.cpp
shibsp/handler/impl/LocalLogoutInitiator.cpp
shibsp/handler/impl/SAML1Consumer.cpp
shibsp/handler/impl/SAML2Consumer.cpp
shibsp/handler/impl/SAML2Logout.cpp
shibsp/handler/impl/SAML2LogoutInitiator.cpp
shibsp/handler/impl/SAML2NameIDMgmt.cpp
shibsp/handler/impl/SAML2SessionInitiator.cpp
shibsp/handler/impl/SAMLDSSessionInitiator.cpp
shibsp/handler/impl/Shib1SessionInitiator.cpp
shibsp/handler/impl/WAYFSessionInitiator.cpp
shibsp/impl/StorageServiceSessionCache.cpp
util/resolvertest.cpp

index 8a37559..98c905e 100644 (file)
@@ -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<bool,long> doRequest(
-            const Application& application, const char* requestURL, const char* entityID, HTTPResponse& httpResponse
-            ) const;
+        pair<bool,long> doRequest(const Application& application, const char* entityID, HTTPResponse& httpResponse) const;
 
         string m_appId;
         auto_ptr_XMLCh m_binding;
@@ -326,7 +325,7 @@ pair<bool,long> 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<ResolutionContext> ctx(
+    auto_ptr<ResolutionContext> 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<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent()) : NULL,
         m_protocol.get(),
         nameid.get(),
@@ -681,7 +682,7 @@ pair<bool,long> 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<bool,long> 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<bool,long> ADFSLogoutInitiator::doRequest(
-    const Application& application, const char* requestURL, const char* entityID, HTTPResponse& response
-    ) const
+pair<bool,long> ADFSLogoutInitiator::doRequest(const Application& application, const char* entityID, HTTPResponse& response) const
 {
 #ifndef SHIBSP_LITE
     try {
@@ -793,10 +791,6 @@ pair<bool,long> ADFSLogout::run(SPRequest& request, bool isHandler) const
     param = request.getParameter("wreply");
     const Application& app = request.getApplication();
 
-    // Get the session_id.
-    pair<string,const char*> 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<string,string> parammap;
@@ -808,16 +802,16 @@ pair<bool,long> 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<string> 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)
index 6c6f6f9..d76b910 100644 (file)
@@ -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<string,const char*> 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<bool,unsigned int> 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;
index bb32ae7..21a1ea6 100644 (file)
 #define __shibsp_sessioncache_h__
 
 #include <shibsp/base.h>
-
 #ifndef SHIBSP_LITE
 # include <saml/saml1/core/Assertions.h>
 # include <saml/saml2/metadata/Metadata.h>
 #endif
 #include <xmltooling/Lockable.h>
+#include <xmltooling/io/HTTPRequest.h>
+#include <xmltooling/io/HTTPResponse.h>
 
 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.
          * 
          * <p>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<std::string>* 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.
          * 
          * <p>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.
+         * 
+         * <p>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. */
index 4c0cc6f..140fa11 100644 (file)
@@ -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. */
index 22a8d6e..a7289fe 100644 (file)
@@ -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.
          * 
          * <p>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<bool,long> processMessage(
+            const Application& application, const xmltooling::HTTPRequest& httpRequest, xmltooling::HTTPResponse& httpResponse
             ) const;
-            
+        
         std::pair<bool,long> 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;
index 301ae06..d74b4dd 100644 (file)
@@ -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<string,const char*> relay_cookie = application.getCookieNameProps("_shibstate_");
-            // In process, we should be able to cast down to a full SPRequest.
-            SPRequest& request = dynamic_cast<SPRequest&>(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<bool,const char*> homeURL=application.getString("homeURL");
-            relayState=homeURL.first ? homeURL.second : "/";
+    if (relayState == "cookie") {
+        // Pull the value from the "relay state" cookie.
+        pair<string,const char*> 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<bool,const char*> homeURL=application.getString("homeURL");
+        relayState=homeURL.first ? homeURL.second : "/";
+        return;
     }
 
     if (relayState == "default")
index 2499eeb..7737f2d 100644 (file)
@@ -88,53 +88,17 @@ pair<bool,long> 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<string> 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<HTTPRequest> 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<HTTPRequest> req(getRequest(in));
+
+    // Wrap a response shim.
+    DDF ret(NULL);
+    DDFJanitor jout(ret);
+    auto_ptr<HTTPResponse> 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<bool,long> 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<XMLObject> 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<XMLObject> 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<bool,long> 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<string,const char*> 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<bool,bool> 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<bool,bool> idpHistory=sessionProps->getBool("idpHistory");
+
     if (!idpHistory.first || idpHistory.second) {
+        pair<bool,const char*> 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<bool,unsigned int> 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());
         }
     }
 }
index c43712e..11ac325 100644 (file)
@@ -123,7 +123,7 @@ pair<bool,long> 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<bool,long> 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.");
index d9383a7..c75bc87 100644 (file)
@@ -97,22 +97,17 @@ pair<bool,long> LocalLogoutInitiator::run(SPRequest& request, bool isHandler) co
     if (ret.first)
         return ret;
 
-    // Get session ID from cookie.
-    pair<string,const char*> 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<string> 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<string,const char*> 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.");
 }
index 7ad7355..893b84a 100644 (file)
@@ -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<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent()) : NULL,
         (!response->getMinorVersion().first || response->getMinorVersion().second==1) ?
             samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM,
index bde48ed..2045185 100644 (file)
@@ -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<const EntityDescriptor*>(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<saml2::Assertion>());
-        return key;
     }
     catch (exception&) {
         if (ownedName)
index 05a80a7..1c6feb3 100644 (file)
@@ -93,13 +93,9 @@ namespace shibsp {
 #endif
 
     private:
-        pair<bool,long> doRequest(
-            const Application& application, const char* session_id, const HTTPRequest& httpRequest, HTTPResponse& httpResponse
-            ) const;
+        pair<bool,long> 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<bool,long> sendResponse(
             const XMLCh* requestID,
             const XMLCh* code,
@@ -211,21 +207,16 @@ pair<bool,long> 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<string,const char*> 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<string> 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<HTTPRequest> req(getRequest(in));
+
+    // Wrap a response shim.
     DDF ret(NULL);
     DDFJanitor jout(ret);
-    auto_ptr<HTTPRequest> req(getRequest(in));
     auto_ptr<HTTPResponse> 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<bool,long> SAML2Logout::doRequest(
-    const Application& application, const char* session_id, const HTTPRequest& request, HTTPResponse& response
-    ) const
+pair<bool,long> 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<string,const char*> 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<string> 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<string,const char*> shib_cookie=application.getCookieNameProps("_shibsession_");
-            response.setCookie(shib_cookie.first.c_str(), shib_cookie.second);
         }
         else {
             worked1 = worked2 = true;
@@ -360,7 +344,7 @@ pair<bool,long> 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<bool,long> 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<bool,long> SAML2Logout::doRequest(
             // Now we actually terminate everything except for the active session,
             // if this is front-channel, for notification purposes.
             for (vector<string>::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<bool,long> 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<string,const char*> shib_cookie=application.getCookieNameProps("_shibsession_");
-            response.setCookie(shib_cookie.first.c_str(), shib_cookie.second);
         }
 
         return sendResponse(
@@ -542,41 +522,6 @@ pair<bool,long> 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<bool,long> SAML2Logout::sendResponse(
     const XMLCh* requestID,
     const XMLCh* code,
index 50f06b9..b68c44f 100644 (file)
@@ -78,7 +78,9 @@ namespace shibsp {
 #endif
 
     private:
-        pair<bool,long> doRequest(const Application& application, const char* requestURL, Session* session, HTTPResponse& httpResponse) const;
+        pair<bool,long> doRequest(
+            const Application& application, const HTTPRequest& request, HTTPResponse& httpResponse, Session* session
+            ) const;
 
         string m_appId;
 #ifndef SHIBSP_LITE
@@ -199,16 +201,14 @@ pair<bool,long> 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<string> 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<HTTPRequest> 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<string,const char*> 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<bool,long> 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<string,const char*> shib_cookie=application.getCookieNameProps("_shibsession_");
-    response.setCookie(shib_cookie.first.c_str(), shib_cookie.second);
-
     // Do back channel notification.
     vector<string> 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<bool,long> 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<bool,long> 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<bool,long> 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
 }
index a63bc6c..3d5e69d 100644 (file)
@@ -93,12 +93,9 @@ namespace shibsp {
 #endif
 
     private:
-        pair<bool,long> doRequest(
-            const Application& application, const char* session_id, const HTTPRequest& httpRequest, HTTPResponse& httpResponse
-            ) const;
+        pair<bool,long> 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<bool,long> sendResponse(
@@ -201,21 +198,16 @@ SAML2NameIDMgmt::SAML2NameIDMgmt(const DOMElement* e, const char* appId)
 
 pair<bool,long> SAML2NameIDMgmt::run(SPRequest& request, bool isHandler) const
 {
-    // Get the session_id in case this is a front-channel request.
-    pair<string,const char*> 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<string> 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<HTTPRequest> req(getRequest(in));
+
+    // Wrap a response shim.
     DDF ret(NULL);
     DDFJanitor jout(ret);
-    auto_ptr<HTTPRequest> req(getRequest(in));
     auto_ptr<HTTPResponse> 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<bool,long> 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<bool,long> 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<bool,long> SAML2NameIDMgmt::doRequest(
                 true
                 );
         }
-
+        
         bool ownedName = false;
         NameID* nameid = mgmtRequest->getNameID();
         if (!nameid) {
@@ -338,8 +333,8 @@ pair<bool,long> 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<EntityDescriptor*>(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<bool,long> 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<bool,long> SAML2NameIDMgmt::sendResponse(
     const XMLCh* requestID,
     const XMLCh* code,
index a022c91..e5ddc7d 100644 (file)
@@ -247,7 +247,7 @@ pair<bool,long> 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");
index 1aec1e2..20b1ff4 100644 (file)
@@ -99,7 +99,7 @@ pair<bool,long> 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)
index 6d2152f..598d294 100644 (file)
@@ -128,7 +128,7 @@ pair<bool,long> 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.
index fe627d1..60c6352 100644 (file)
@@ -95,7 +95,7 @@ pair<bool,long> 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.
index 155e8ae..843a37d 100644 (file)
@@ -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<string>& sessions
             );
         bool matches(
-            const char* key,
+            const xmltooling::HTTPRequest& request,
             const saml2md::EntityDescriptor* issuer,
             const saml2::NameID& nameid,
             const set<string>* 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<string,const char*> 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<string,const char*> 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<string,const char*> shib_cookie = application.getCookieNameProps("_shibsession_");
+    string k(key.get());\r
+    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<string>* 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;
 }
index d818e80..6e589f7 100644 (file)
@@ -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