Drafts of remoted Shib and SAML2 SessionInitiators.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Sun, 15 Apr 2007 21:10:45 +0000 (21:10 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Sun, 15 Apr 2007 21:10:45 +0000 (21:10 +0000)
Start to rework metadata for SAML 2.
Enable relay state preservation on shibd side.
Server modules should no longer require loading of metadata.

git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2223 cb58f699-b61c-0410-a6fe-9272a202ed29

20 files changed:
apache/mod_apache.cpp
configs/Makefile.am
configs/bindingTemplate.html [new file with mode: 0644]
configs/example-metadata.xml.in
configs/shibboleth.xml.in
isapi_shib/isapi_shib.cpp
nsapi_shib/nsapi_shib.cpp
schemas/shibboleth-spconfig-2.0.xsd
shibsp/Makefile.am
shibsp/handler/AbstractHandler.h
shibsp/handler/impl/AbstractHandler.cpp
shibsp/handler/impl/AssertionConsumerService.cpp
shibsp/handler/impl/SAML2SessionInitiator.cpp [new file with mode: 0644]
shibsp/handler/impl/SAMLDSSessionInitiator.cpp
shibsp/handler/impl/SessionInitiator.cpp
shibsp/handler/impl/Shib1SessionInitiator.cpp
shibsp/handler/impl/WAYFSessionInitiator.cpp
shibsp/impl/RemotedSessionCache.cpp
shibsp/security/PKIXTrustEngine.cpp
shibsp/shibsp.vcproj

index eb0fc79..21c3a8f 100644 (file)
@@ -1020,7 +1020,6 @@ extern "C" void shib_child_init(apr_pool_t* p, server_rec* s)
     g_Config->setFeatures(
         SPConfig::Listener |
         SPConfig::Caching |
-        SPConfig::Metadata |
         SPConfig::RequestMapping |
         SPConfig::InProcess |
         SPConfig::Logging
index f403a34..f2b4010 100644 (file)
@@ -33,6 +33,7 @@ CONFIGFILES = \
        rmError.html \
        sessionError.html \
        metadataError.html \
+       bindingTemplate.html \
        sslError.html \
        sp-example.key \
        sp-example.crt
diff --git a/configs/bindingTemplate.html b/configs/bindingTemplate.html
new file mode 100644 (file)
index 0000000..7d983a6
--- /dev/null
@@ -0,0 +1,55 @@
+<html>
+       <head>
+               <title>Shibboleth Authentication Request</title>
+       </head>
+       <body onload="document.forms[0].submit()">
+
+               <h1>Shibboleth Authentication Request</h1>
+               
+               <script type="text/javascript">
+               <!--    
+               document.write("<p>You are automatically being redirected to the authentication service. ");
+               document.write("If the browser appears to be hung up after 15-20 seconds, try reloading ");
+               document.write("the page before contacting the technical support staff in charge of the ");
+               document.write("authentication service you are trying to access.</p>");
+               document.write("<h2>Redirecting...</h2>");
+               // -->
+               </script>
+               
+               <noscript>
+               <p>
+               <strong>Note:</strong> Since your browser does not support JavaScript, you must press the
+               Continue button once to proceed to the authentication service.
+               </p>
+               </noscript>
+       
+               <form method="POST" action="<mlp action/>">
+               <mlpif TARGET>
+                       <input type="hidden" name="TARGET" value="<mlp TARGET/>"/>
+               </mlpif>
+               <mlpif RelayState>
+                       <input type="hidden" name="RelayState" value="<mlp RelayState/>"/>
+               </mlpif>
+               <mlpif SAMLRequest>
+                       <input type="hidden" name="SAMLRequest" value="<mlp SAMLRequest/>"/>
+               </mlpif>
+               <mlpif SAMLResponse>
+                       <input type="hidden" name="SAMLResponse" value="<mlp SAMLResponse/>"/>
+               </mlpif>
+               <mlpif SAMLart>
+                       <input type="hidden" name="SAMLart" value="<mlp SAMLart/>"/>
+               </mlpif>
+               <mlpif SigAlg>
+                       <input type="hidden" name="SigAlg" value="<mlp SigAlg/>"/>
+               </mlpif>
+               <mlpif Signature>
+                       <input type="hidden" name="Signature" value="<mlp Signature/>"/>
+               </mlpif>
+               <noscript>
+               <div>
+               <input type="submit" value="Continue"/>
+               </div>
+               </noscript>
+               </form>
+       </body>
+</html>
index f772a43..93d1992 100644 (file)
@@ -90,33 +90,6 @@ w14fpgtAk2x8xD7cpHsZ073JHxEcjEetD8PTtrFdNu6GwIrv6Sk=
                            </ds:KeyInfo>
                        </KeyDescriptor>
 
-                       <!-- This key is used by Internet2's test site. -->
-                       <KeyDescriptor use="signing">
-                           <ds:KeyInfo>
-                               <ds:X509Data>
-                                       <ds:X509Certificate>
-MIIDADCCAmmgAwIBAgICBPIwDQYJKoZIhvcNAQEEBQAwgakxCzAJBgNVBAYTAlVT
-MRIwEAYDVQQIEwlXaXNjb25zaW4xEDAOBgNVBAcTB01hZGlzb24xIDAeBgNVBAoT
-F1VuaXZlcnNpdHkgb2YgV2lzY29uc2luMSswKQYDVQQLEyJEaXZpc2lvbiBvZiBJ
-bmZvcm1hdGlvbiBUZWNobm9sb2d5MSUwIwYDVQQDExxIRVBLSSBTZXJ2ZXIgQ0Eg
-LS0gMjAwMjA3MDFBMB4XDTA1MDUyNjAxMDE1MloXDTA5MDcwNTAxMDE1MlowPjEL
-MAkGA1UEBhMCVVMxEjAQBgNVBAoTCUludGVybmV0MjEbMBkGA1UEAxMSd2F5Zi5p
-bnRlcm5ldDIuZWR1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxpUs
-kDqIN54O/AbF9rVqe8FJ1q/Ep7edGGOQUjlnt2c2AyVuvveSfW/Hh82DjdF0HMaW
-C5kv/ZInBLi4kO6Xx2EjPijZmK11WxHx+WbhgCziY4KzetL3XT63QdCSSQVnaEJV
-oM9yWsOOHpeWaFiX2alAfkYbCVt9kQiB2amyCuwcOwPWh0Saf7UTEyXoE9IMNWUz
-oaydiwm6TH2zJ7ZNMogeL14o5Fv7I6znKwVGvqrz6iIGWTI7v/ZmnF/jwyW4GOdS
-fX7s/G+M6uSndSM5si+s7iE+MdtP0qZ2M3xd4zWSpYTWRnq3uVMc9w04mF5LZM5q
-B8ktgtaTLS5X2sWv6QIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIF
-oDANBgkqhkiG9w0BAQQFAAOBgQBDiDqvFbuhMMxAQ89CNBFLiXkcMLrX2Ht96Zux
-JfS8fAx/Obbz5im1jK7peLhFr/9KgLtAkoz4aWtBL+qWcL3a1VYTu9H3Q2w9QbV2
-rxmbK0h8tw6qTA+F4FrErGufQv+kEmm1WRXXeyqEcsadZpsXauRD8iraq9f5WrLX
-AtThLg==
-                                       </ds:X509Certificate>
-                               </ds:X509Data>
-                           </ds:KeyInfo>
-                       </KeyDescriptor>
-                       
                        <!-- This tells SPs where/how to resolve SAML 1.x artifacts into SAML assertions. -->
                        <ArtifactResolutionService index="1"
                                Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding"
@@ -172,33 +145,6 @@ w14fpgtAk2x8xD7cpHsZ073JHxEcjEetD8PTtrFdNu6GwIrv6Sk=
                            </ds:KeyInfo>
                        </KeyDescriptor>
 
-                       <!-- This key is used by Internet2's test site. -->
-                       <KeyDescriptor use="signing">
-                           <ds:KeyInfo>
-                               <ds:X509Data>
-                                       <ds:X509Certificate>
-MIIDADCCAmmgAwIBAgICBPIwDQYJKoZIhvcNAQEEBQAwgakxCzAJBgNVBAYTAlVT
-MRIwEAYDVQQIEwlXaXNjb25zaW4xEDAOBgNVBAcTB01hZGlzb24xIDAeBgNVBAoT
-F1VuaXZlcnNpdHkgb2YgV2lzY29uc2luMSswKQYDVQQLEyJEaXZpc2lvbiBvZiBJ
-bmZvcm1hdGlvbiBUZWNobm9sb2d5MSUwIwYDVQQDExxIRVBLSSBTZXJ2ZXIgQ0Eg
-LS0gMjAwMjA3MDFBMB4XDTA1MDUyNjAxMDE1MloXDTA5MDcwNTAxMDE1MlowPjEL
-MAkGA1UEBhMCVVMxEjAQBgNVBAoTCUludGVybmV0MjEbMBkGA1UEAxMSd2F5Zi5p
-bnRlcm5ldDIuZWR1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxpUs
-kDqIN54O/AbF9rVqe8FJ1q/Ep7edGGOQUjlnt2c2AyVuvveSfW/Hh82DjdF0HMaW
-C5kv/ZInBLi4kO6Xx2EjPijZmK11WxHx+WbhgCziY4KzetL3XT63QdCSSQVnaEJV
-oM9yWsOOHpeWaFiX2alAfkYbCVt9kQiB2amyCuwcOwPWh0Saf7UTEyXoE9IMNWUz
-oaydiwm6TH2zJ7ZNMogeL14o5Fv7I6znKwVGvqrz6iIGWTI7v/ZmnF/jwyW4GOdS
-fX7s/G+M6uSndSM5si+s7iE+MdtP0qZ2M3xd4zWSpYTWRnq3uVMc9w04mF5LZM5q
-B8ktgtaTLS5X2sWv6QIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIF
-oDANBgkqhkiG9w0BAQQFAAOBgQBDiDqvFbuhMMxAQ89CNBFLiXkcMLrX2Ht96Zux
-JfS8fAx/Obbz5im1jK7peLhFr/9KgLtAkoz4aWtBL+qWcL3a1VYTu9H3Q2w9QbV2
-rxmbK0h8tw6qTA+F4FrErGufQv+kEmm1WRXXeyqEcsadZpsXauRD8iraq9f5WrLX
-AtThLg==
-                                       </ds:X509Certificate>
-                               </ds:X509Data>
-                           </ds:KeyInfo>
-                       </KeyDescriptor>
-
                        <!-- This tells SPs how and where to send queries. -->
                        <AttributeService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding"
                            Location="https://idp.example.org:8443/shibboleth-idp/AA"/>
@@ -227,8 +173,8 @@ AtThLg==
        <!-- See the comment earlier about how an entityID is chosen/created. -->
        <EntityDescriptor entityID="https://sp.example.org/shibboleth">
        
-               <!-- A Shib SP contains this element with protocol support as shown. -->
-               <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol">
+               <!-- An SP supporting SAML 1 and 2 contains this element with protocol support as shown. -->
+               <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol">
                
                        <!--
                        One or more KeyDescriptors tell IdPs how the SP will authenticate itself. A single
@@ -271,35 +217,28 @@ gmYsTmak+kxO93JprrOd9xp8aZPMEprL7VCdrhbZEfyYER0=
                            </ds:KeyInfo>
                        </KeyDescriptor>
                        
-                       <!-- This tells IdPs that you support only the Shib handle format. -->
+                       <!-- This tells IdPs that you support only transient identifiers. -->
+                       <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
                        <NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
                    
                        <!--
                        This tells IdPs where and how to send authentication assertions. Mostly
                        the SP will tell the IdP what location to use in its request, but this
                        is how the IdP validates the location and also figures out which
-                       SAML profile to use. There are six listed to accomodate common testing
-                       scenarios used by C++ and Java SP installations. At deployment time,
-                       only the actual endpoints to be used are needed. 
+                       SAML version/binding to use.
                        -->
                        <AssertionConsumerService index="1" isDefault="true"
-                               Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
-                               Location="https://sp.example.org/Shibboleth.sso/SAML/POST"/>
+                               Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+                               Location="https://sp.example.org/Shibboleth.sso/SAML2/POST"/>
                        <AssertionConsumerService index="2"
-                               Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
-                               Location="https://sp.example.org/Shibboleth.sso/SAML/Artifact"/>
+                               Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                               Location="https://sp.example.org/Shibboleth.sso/SAML2/Artifact"/>
                        <AssertionConsumerService index="3"
-                       Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
-                       Location="https://sp.example.org/shibboleth-sp/Shibboleth.sso/SAML/POST"/>
-                   <AssertionConsumerService index="4"
-                       Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
-                       Location="https://sp.example.org/shibboleth-sp/Shibboleth.sso/SAML/Artifact"/>
-                       <AssertionConsumerService index="5"
                                Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
-                               Location="https://sp.example.org:9443/shibboleth-sp/Shibboleth.sso/SAML/POST"/>
-                       <AssertionConsumerService index="6"
+                               Location="https://sp.example.org/Shibboleth.sso/SAML/POST"/>
+                       <AssertionConsumerService index="4"
                                Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
-                               Location="https://sp.example.org:9443/shibboleth-sp/Shibboleth.sso/SAML/Artifact"/>
+                               Location="https://sp.example.org/Shibboleth.sso/SAML/Artifact"/>
 
                </SPSSODescriptor>
 
index eb59317..ec8050f 100644 (file)
                        <!-- Default example directs to a specific IdP's SSO service (favoring SAML 2 over Shib 1). -->
                        <SessionInitiator type="Chaining" Location="/Login" isDefault="true" id="example.org"
                                        relayState="cookie" entityID="https://idp.example.org/shibboleth">
-                               <SessionInitiator type="SAML2"/>
+                               <SessionInitiator type="SAML2" template="@-PKGSYSCONFDIR-@/bindingTemplate.html"/>
                                <SessionInitiator type="Shibboleth"/>
                        </SessionInitiator>
                        
                        <!-- An example using an old-style WAYF, which means Shib 1 only unless an entityID is provided. -->
                        <SessionInitiator type="Chaining" Location="/WAYF" id="WAYF" relayState="cookie">
-                               <SessionInitiator type="SAML2"/>
+                               <SessionInitiator type="SAML2" template="@-PKGSYSCONFDIR-@/bindingTemplate.html"/>
                                <SessionInitiator type="Shibboleth"/>
                                <SessionInitiator type="WAYF" URL="https://wayf.example.org/WAYF"/>
                        </SessionInitiator>
 
                        <!-- An example supporting the new-style of discovery service. -->
                        <SessionInitiator type="Chaining" Location="/DS" id="DS" relayState="cookie">
-                               <SessionInitiator type="SAML2"/>
+                               <SessionInitiator type="SAML2" template="@-PKGSYSCONFDIR-@/bindingTemplate.html"/>
                                <SessionInitiator type="Shibboleth"/>
                                <SessionInitiator type="SAMLDS" URL="https://ds.example.org/DS"/>
                        </SessionInitiator>
                        are used when sessions are initiated to determine how to tell the IdP where and
                        how to return the response.
                        -->
-                       <md:AssertionConsumerService Location="/SAML/POST" isDefault="true" index="1"
+                       <md:AssertionConsumerService Location="/SAML2/POST" index="1" isDefault="true"
+                               Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"/>
+                       <md:AssertionConsumerService Location="/SAML2/Artifact" index="2"
+                               Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"/>
+                       <md:AssertionConsumerService Location="/SAML/POST" index="3"
                                Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"/>
-                       <md:AssertionConsumerService Location="/SAML/Artifact" index="2"
+                       <md:AssertionConsumerService Location="/SAML/Artifact" index="4"
                                Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"/>
                        
                        <!--
index de35d5e..49f62b4 100644 (file)
@@ -155,7 +155,6 @@ extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
     g_Config->setFeatures(
         SPConfig::Listener |
         SPConfig::Caching |
-        SPConfig::Metadata |
         SPConfig::RequestMapping |
         SPConfig::InProcess |
         SPConfig::Logging
index d9717d2..c07fd44 100644 (file)
@@ -129,7 +129,6 @@ extern "C" NSAPI_PUBLIC int nsapi_shib_init(pblock* pb, ::Session* sn, Request*
     g_Config->setFeatures(
         SPConfig::Listener |
         SPConfig::Caching |
-        SPConfig::Metadata |
         SPConfig::RequestMapping |
         SPConfig::InProcess |
         SPConfig::Logging
index ac09d3d..53cfd87 100644 (file)
        <simpleType name="listOfStrings">\r
                <list itemType="conf:string"/>\r
        </simpleType>\r
+\r
+       <simpleType name="listOfURIs">\r
+               <list itemType="anyURI"/>\r
+       </simpleType>\r
        \r
        <complexType name="PluggableType">\r
                <sequence>\r
                                        <attribute name="entityIDParam" type="conf:string"/>\r
                                        <attribute name="entityID" type="anyURI"/>\r
                                        <attribute name="URL" type="anyURI"/>\r
+                                       <attribute name="outgoingBindings" type="conf:listOfURIs"/>\r
+                                       <attribute name="template" type="anyURI"/>\r
+                                       <attribute name="acsByIndex" type="boolean"/>\r
                                        <anyAttribute namespace="##any" processContents="lax"/>\r
                                </restriction>\r
                        </complexContent>\r
index f800d54..d8450ba 100644 (file)
@@ -101,6 +101,7 @@ libshibsp_la_SOURCES = \
        handler/impl/RemotedHandler.cpp \
        handler/impl/SAML1Consumer.cpp \
        handler/impl/SAML2Consumer.cpp \
+       handler/impl/SAML2SessionInitiator.cpp \
        handler/impl/SAMLDSSessionInitiator.cpp \
        handler/impl/SessionInitiator.cpp \
        handler/impl/Shib1SessionInitiator.cpp \
index b9de142..00cbf57 100644 (file)
 
 #include <log4cpp/Category.hh>
 #include <saml/binding/HTTPRequest.h>
+#include <saml/binding/HTTPResponse.h>
 #include <xmltooling/XMLObject.h>
 
 namespace shibsp {
 
+    class SHIBSP_API Application;
     class SHIBSP_API SPRequest;
 
 #if defined (_MSC_VER)
@@ -76,10 +78,11 @@ namespace shibsp {
          * <p>If a supported mechanism can be identified, the input parameter will be
          * replaced with a suitable state key.
          * 
-         * @param request       the active SPRequest
+         * @param application   the associated Application
+         * @param response      outgoing HTTP response
          * @param relayState    RelayState token to supply with message
          */
-        virtual void preserveRelayState(SPRequest& request, std::string& relayState) const;
+        virtual void preserveRelayState(const Application& application, opensaml::HTTPResponse& response, std::string& relayState) const;
 
         /**
          * Implements various mechanisms to recover RelayState,
@@ -88,11 +91,14 @@ namespace shibsp {
          * <p>If a supported mechanism can be identified, the input parameter will be
          * replaced with the recovered state information.
          * 
-         * @param httpRequest   incoming HTTP request
+         * @param application   the associated Application
+         * @param request       incoming HTTP request
          * @param relayState    RelayState token supplied with message
          * @param clear         true iff the token state should be cleared
          */
-        virtual void recoverRelayState(opensaml::HTTPRequest& httpRequest, std::string& relayState, bool clear=true) const;
+        virtual void recoverRelayState(
+            const Application& application, opensaml::HTTPRequest& request, std::string& relayState, bool clear=true
+            ) const;
         
         /** Logging object. */
         log4cpp::Category& m_log;
index 1f3195c..75c5432 100644 (file)
@@ -28,7 +28,8 @@
 #include "handler/AbstractHandler.h"
 #include "remoting/ListenerService.h"
 
-#include <log4cpp/Category.hh>
+#include <saml/SAMLConfig.h>
+#include <saml/binding/SAMLArtifact.h>
 #include <saml/saml1/core/Protocols.h>
 #include <saml/saml2/core/Protocols.h>
 #include <saml/util/SAMLConstants.h>
@@ -114,7 +115,7 @@ void AbstractHandler::checkError(const XMLObject* response) const
     }
 }
 
-void AbstractHandler::preserveRelayState(SPRequest& request, string& relayState) const
+void AbstractHandler::preserveRelayState(const Application& application, HTTPResponse& response, string& relayState) const
 {
     if (relayState.empty())
         return;
@@ -129,9 +130,9 @@ void AbstractHandler::preserveRelayState(SPRequest& request, string& relayState)
         // value so we can recognize it on the way back.
         if (relayState != "cookie") {
             const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
-            pair<string,const char*> shib_cookie=request.getApplication().getCookieNameProps("_shibstate_");
+            pair<string,const char*> shib_cookie=application.getCookieNameProps("_shibstate_");
             string stateval = urlenc->encode(relayState.c_str()) + shib_cookie.second;
-            request.setCookie(shib_cookie.first.c_str(),stateval.c_str());
+            response.setCookie(shib_cookie.first.c_str(),stateval.c_str());
             relayState = "cookie";
         }
     }
@@ -139,14 +140,30 @@ void AbstractHandler::preserveRelayState(SPRequest& request, string& relayState)
         if (relayState.find("ss:")!=0) {
             mech.second+=3;
             if (*mech.second) {
-                DDF out,in = DDF("set::RelayState").structure();
-                in.addmember("id").string(mech.second);
-                in.addmember("value").string(relayState.c_str());
-                DDFJanitor jin(in),jout(out);
-                out = request.getServiceProvider().getListenerService()->send(in);
-                if (!out.isstring())
-                    throw IOException("StorageService-backed RelayState mechanism did not return a state key.");
-                relayState = string(mech.second-3) + ':' + out.string();
+                if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+                    StorageService* storage = application.getServiceProvider().getStorageService(mech.second);
+                    if (storage) {
+                        string rsKey;
+                        SAMLConfig::getConfig().generateRandomBytes(rsKey,20);
+                        rsKey = SAMLArtifact::toHex(rsKey);
+                        storage->createString("RelayState", rsKey.c_str(), relayState.c_str(), time(NULL) + 600);
+                        relayState = string(mech.second-3) + ':' + rsKey;
+                    }
+                    else {
+                        m_log.error("Storage-backed RelayState with invalid StorageService ID (%s)", mech.second);
+                        relayState.erase();
+                    }
+                }
+                else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+                    DDF out,in = DDF("set::RelayState").structure();
+                    in.addmember("id").string(mech.second);
+                    in.addmember("value").string(relayState.c_str());
+                    DDFJanitor jin(in),jout(out);
+                    out = application.getServiceProvider().getListenerService()->send(in);
+                    if (!out.isstring())
+                        throw IOException("StorageService-backed RelayState mechanism did not return a state key.");
+                    relayState = string(mech.second-3) + ':' + out.string();
+                }
             }
         }
     }
@@ -154,7 +171,7 @@ void AbstractHandler::preserveRelayState(SPRequest& request, string& relayState)
         throw ConfigurationException("Unsupported relayState mechanism ($1).", params(1,mech.second));
 }
 
-void AbstractHandler::recoverRelayState(HTTPRequest& httpRequest, string& relayState, bool clear) const
+void AbstractHandler::recoverRelayState(const Application& application, HTTPRequest& httpRequest, string& relayState, bool clear) const
 {
     SPConfig& conf = SPConfig::getConfig();
 
@@ -186,18 +203,14 @@ void AbstractHandler::recoverRelayState(HTTPRequest& httpRequest, string& relayS
                     }
                 }
                 else if (conf.isEnabled(SPConfig::InProcess)) {
-                    // In process, we should be able to cast down to a full SPRequest.
-                    SPRequest& request = dynamic_cast<SPRequest&>(httpRequest);
                     DDF out,in = DDF("get::RelayState").structure();
                     in.addmember("id").string(ssid.c_str());
                     in.addmember("key").string(key);
                     in.addmember("clear").integer(clear ? 1 : 0);
                     DDFJanitor jin(in),jout(out);
-                    out = request.getServiceProvider().getListenerService()->send(in);
+                    out = application.getServiceProvider().getListenerService()->send(in);
                     if (!out.isstring()) {
-                        Category::getInstance(SHIBSP_LOGCAT".Handler").error(
-                            "StorageService-backed RelayState mechanism did not return a state value."
-                            );
+                        m_log.error("StorageService-backed RelayState mechanism did not return a state value.");
                         relayState.erase();
                     }
                     else {
@@ -210,11 +223,11 @@ void AbstractHandler::recoverRelayState(HTTPRequest& httpRequest, string& relayS
     }
     
     if (conf.isEnabled(SPConfig::InProcess)) {
-        // In process, we should be able to cast down to a full SPRequest.
-        SPRequest& request = dynamic_cast<SPRequest&>(httpRequest);
         if (relayState == "cookie") {
             // Pull the value from the "relay state" cookie.
-            pair<string,const char*> relay_cookie = request.getApplication().getCookieNameProps("_shibstate_");
+            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.
@@ -233,7 +246,7 @@ void AbstractHandler::recoverRelayState(HTTPRequest& httpRequest, string& relayS
 
         // Check for "default" value.
         if (relayState.empty() || relayState == "default") {
-            pair<bool,const char*> homeURL=request.getApplication().getString("homeURL");
+            pair<bool,const char*> homeURL=application.getString("homeURL");
             relayState=homeURL.first ? homeURL.second : "/";
             return;
         }
index c5e9080..75b38e8 100644 (file)
@@ -87,7 +87,7 @@ pair<bool,long> AssertionConsumerService::run(SPRequest& request, bool isHandler
                 if (ex.getProperty("RelayState"))
                     relayState = ex.getProperty("RelayState");
                 try {
-                    recoverRelayState(request, relayState);
+                    recoverRelayState(request.getApplication(), request, relayState);
                 }
                 catch (exception& ex2) {
                     m_log.error("trapped an error during RelayState recovery while handling an error: %s", ex2.what());
@@ -98,7 +98,7 @@ pair<bool,long> AssertionConsumerService::run(SPRequest& request, bool isHandler
             // We invoke RelayState recovery one last time on this side of the boundary.
             if (out["RelayState"].isstring())
                 relayState = out["RelayState"].string(); 
-            recoverRelayState(request, relayState);
+            recoverRelayState(request.getApplication(), request, relayState);
     
             // If it worked, we have a session key.
             if (!out["key"].isstring())
@@ -182,7 +182,7 @@ string AssertionConsumerService::processMessage(
     auto_ptr<XMLObject> msg(m_decoder->decode(relayState, httpRequest, policy));
     if (!msg.get())
         throw BindingException("Failed to decode an SSO protocol response.");
-    recoverRelayState(httpRequest, relayState);
+    recoverRelayState(application, httpRequest, relayState);
     string key = implementProtocol(application, httpRequest, policy, settings, *msg.get());
 
     auto_ptr_char issuer(policy.getIssuer() ? policy.getIssuer()->getName() : NULL);
diff --git a/shibsp/handler/impl/SAML2SessionInitiator.cpp b/shibsp/handler/impl/SAML2SessionInitiator.cpp
new file mode 100644 (file)
index 0000000..600f45e
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ *  Copyright 2001-2007 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SAML2SessionInitiator.cpp
+ * 
+ * SAML 2.0 AuthnRequest support.
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "exceptions.h"
+#include "ServiceProvider.h"
+#include "SPRequest.h"
+#include "handler/AbstractHandler.h"
+#include "handler/RemotedHandler.h"
+#include "handler/SessionInitiator.h"
+#include "util/SPConstants.h"
+
+#include <saml/SAMLConfig.h>
+#include <saml/binding/MessageEncoder.h>
+#include <saml/saml2/core/Protocols.h>
+#include <saml/saml2/metadata/EndpointManager.h>
+#include <saml/saml2/metadata/Metadata.h>
+#include <saml/saml2/metadata/MetadataCredentialCriteria.h>
+
+using namespace shibsp;
+using namespace opensaml::saml2;
+using namespace opensaml::saml2p;
+using namespace opensaml::saml2md;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
+    class SHIBSP_DLLLOCAL SAML2SessionInitiator : public SessionInitiator, public AbstractHandler, public RemotedHandler
+    {
+    public:
+        SAML2SessionInitiator(const DOMElement* e, const char* appId);
+        virtual ~SAML2SessionInitiator() {
+            if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+                XMLString::release(&m_outgoing);
+                for_each(m_encoders.begin(), m_encoders.end(), cleanup_pair<const XMLCh*,MessageEncoder>());
+            }
+        }
+        
+        void setParent(const PropertySet* parent);
+        void receive(DDF& in, ostream& out);
+        pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const;
+
+    private:
+        pair<bool,long> doRequest(
+            const Application& application,
+            HTTPResponse& httpResponse,
+            const char* entityID,
+            const XMLCh* acsIndex,
+            const XMLCh* acsLocation,
+            const XMLCh* acsBinding,
+            string& relayState
+            ) const;
+
+        string m_appId;
+        XMLCh* m_outgoing;
+        vector<const XMLCh*> m_bindings;
+        map<const XMLCh*,MessageEncoder*> m_encoders;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    SessionInitiator* SHIBSP_DLLLOCAL SAML2SessionInitiatorFactory(const pair<const DOMElement*,const char*>& p)
+    {
+        return new SAML2SessionInitiator(p.first, p.second);
+    }
+
+};
+
+SAML2SessionInitiator::SAML2SessionInitiator(const DOMElement* e, const char* appId)
+    : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator")), m_appId(appId), m_outgoing(NULL)
+{
+    // If Location isn't set, defer address registration until the setParent call.
+    pair<bool,const char*> loc = getString("Location");
+    if (loc.first) {
+        string address = m_appId + loc.second + "::run::SAML2SI";
+        setAddress(address.c_str());
+    }
+
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+        pair<bool,const XMLCh*> outgoing = getXMLString("outgoingBindings");
+        if (outgoing.first) {
+            m_outgoing = XMLString::replicate(outgoing.second);
+        }
+        else {
+            // No override, so we'll install a default binding precedence.
+            string prec = string(samlconstants::SAML20_BINDING_HTTP_REDIRECT) + ' ' + samlconstants::SAML20_BINDING_HTTP_POST + ' ' +
+                samlconstants::SAML20_BINDING_HTTP_POST_SIMPLESIGN + ' ' + samlconstants::SAML20_BINDING_HTTP_ARTIFACT;
+            m_outgoing = XMLString::transcode(prec.c_str());
+            XMLString::trim(m_outgoing);
+        }
+
+        int pos;
+        XMLCh* start = m_outgoing;
+        while (start && *start) {
+            pos = XMLString::indexOf(start,chSpace);
+            if (pos != -1)
+                *(start + pos)=chNull;
+            m_bindings.push_back(start);
+            try {
+                auto_ptr_char b(start);
+                MessageEncoder * encoder = SAMLConfig::getConfig().MessageEncoderManager.newPlugin(b.get(),e);
+                m_encoders[start] = encoder;
+                m_log.info("supporting outgoing binding (%s)", b.get());
+            }
+            catch (exception& ex) {
+                m_log.error("error building MessageEncoder: %s", ex.what());
+            }
+            if (pos != -1)
+                start = start + pos + 1;
+            else
+                break;
+        }
+    }
+}
+
+void SAML2SessionInitiator::setParent(const PropertySet* parent)
+{
+    DOMPropertySet::setParent(parent);
+    pair<bool,const char*> loc = getString("Location");
+    if (loc.first) {
+        string address = m_appId + loc.second + "::run::SAML2SI";
+        setAddress(address.c_str());
+    }
+    else {
+        m_log.warn("no Location property in SAML2 SessionInitiator (or parent), can't register as remoted handler");
+    }
+}
+
+pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const
+{
+    // We have to know the IdP to function.
+    if (!entityID || !*entityID)
+        return make_pair(false,0);
+
+    string target;
+    const Handler* ACS=NULL;
+    const char* option;
+    const Application& app=request.getApplication();
+    pair<bool,bool> acsByIndex = getBool("acsByIndex");
+
+    if (isHandler) {
+        option=request.getParameter("acsIndex");
+        if (option)
+            ACS = app.getAssertionConsumerServiceByIndex(atoi(option));
+
+        option = request.getParameter("target");
+        if (option)
+            target = option;
+        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);
+        }
+    }
+    else {
+        // We're running as a "virtual handler" from within the filter.
+        // The target resource is the current one and everything else is defaulted.
+        target=request.getRequestURL();
+    }
+
+    m_log.debug("attempting to initiate session using SAML 2.0 with provider (%s)", entityID);
+
+    // To invoke the request builder, the key requirement is to figure out how and whether
+    // to express the ACS, by index or value, and if by value, where.
+
+    SPConfig& conf = SPConfig::getConfig();
+    if (conf.isEnabled(SPConfig::OutOfProcess)) {
+        if (acsByIndex.first && acsByIndex.second) {
+            // Pass by Index. This also allows for defaulting it entirely and sending nothing.
+            if (isHandler) {
+                // We may already have RelayState set if we looped back here,
+                // but just in case target is a resource, we reset it back.
+                target.erase();
+                option = request.getParameter("target");
+                if (option)
+                    target = option;
+            }
+            return doRequest(app, request, entityID, ACS ? ACS->getXMLString("index").second : NULL, NULL, NULL, target);
+        }
+
+        // Since we're not passing by index, we need to fully compute the return URL and binding.
+        if (!ACS)
+            ACS = app.getDefaultAssertionConsumerService();
+
+        // Compute the ACS URL. We add the ACS location to the base handlerURL.
+        string ACSloc=request.getHandlerURL(target.c_str());
+        pair<bool,const char*> loc=ACS ? ACS->getString("Location") : pair<bool,const char*>(false,NULL);
+        if (loc.first) ACSloc+=loc.second;
+
+        if (isHandler) {
+            // We may already have RelayState set if we looped back here,
+            // but just in case target is a resource, we reset it back.
+            target.erase();
+            option = request.getParameter("target");
+            if (option)
+                target = option;
+        }
+
+        auto_ptr_XMLCh wideloc(ACSloc.c_str());
+        return doRequest(app, request, entityID, NULL, wideloc.get(), ACS ? ACS->getXMLString("Binding").second : NULL, target);
+    }
+
+    // Remote the call.
+    DDF out,in = DDF(m_address.c_str()).structure();
+    DDFJanitor jin(in), jout(out);
+    in.addmember("application_id").string(app.getId());
+    in.addmember("entity_id").string(entityID);
+    if (acsByIndex.first && acsByIndex.second) {
+        if (ACS)
+            in.addmember("acsIndex").string(ACS->getString("index").second);
+    }
+    else {
+        // Since we're not passing by index, we need to fully compute the return URL and binding.
+        if (!ACS)
+            ACS = app.getDefaultAssertionConsumerService();
+
+        // Compute the ACS URL. We add the ACS location to the base handlerURL.
+        string ACSloc=request.getHandlerURL(target.c_str());
+        pair<bool,const char*> loc=ACS ? ACS->getString("Location") : pair<bool,const char*>(false,NULL);
+        if (loc.first) ACSloc+=loc.second;
+        in.addmember("acsLocation").string(ACSloc.c_str());
+        if (ACS)
+            in.addmember("acsBinding").string(ACS->getString("Binding").second);
+    }
+
+    if (isHandler) {
+        // We may already have RelayState set if we looped back here,
+        // but just in case target is a resource, we reset it back.
+        target.erase();
+        option = request.getParameter("target");
+        if (option)
+            target = option;
+    }
+    if (!target.empty())
+        in.addmember("RelayState").string(target.c_str());
+
+    // Remote the processing.
+    out = request.getServiceProvider().getListenerService()->send(in);
+    return unwrap(request, out);
+}
+
+void SAML2SessionInitiator::receive(DDF& in, ostream& out)
+{
+    // Find application.
+    const char* aid=in["application_id"].string();
+    const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
+    if (!app) {
+        // Something's horribly wrong.
+        m_log.error("couldn't find application (%s) to generate AuthnRequest", aid ? aid : "(missing)");
+        throw ConfigurationException("Unable to locate application for new session, deleted?");
+    }
+
+    const char* entityID = in["entity_id"].string();
+    if (!entityID)
+        throw ConfigurationException("No entityID parameter supplied to remoted SessionInitiator.");
+
+    DDF ret(NULL);
+    DDFJanitor jout(ret);
+
+    // Wrap the outgoing object with a Response facade.
+    auto_ptr<HTTPResponse> http(getResponse(ret));
+
+    auto_ptr_XMLCh index(in["acsIndex"].string());
+    auto_ptr_XMLCh loc(in["acsLocation"].string());
+    auto_ptr_XMLCh bind(in["acsBinding"].string());
+
+    string relayState(in["RelayState"].string() ? in["RelayState"].string() : "");
+
+    // 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, *http.get(), entityID, index.get(), loc.get(), bind.get(), relayState);
+    out << ret;
+}
+
+pair<bool,long> SAML2SessionInitiator::doRequest(
+    const Application& app,
+    HTTPResponse& httpResponse,
+    const char* entityID,
+    const XMLCh* acsIndex,
+    const XMLCh* acsLocation,
+    const XMLCh* acsBinding,
+    string& relayState
+    ) const
+{
+    // Use metadata to locate the IdP's SSO service.
+    MetadataProvider* m=app.getMetadataProvider();
+    Locker locker(m);
+    const EntityDescriptor* entity=m->getEntityDescriptor(entityID);
+    if (!entity) {
+        m_log.error("unable to locate metadata for provider (%s)", entityID);
+        return make_pair(false,0);
+    }
+    const IDPSSODescriptor* role=entity->getIDPSSODescriptor(samlconstants::SAML20P_NS);
+    if (!role) {
+        m_log.error("unable to locate SAML 2.0 identity provider role for provider (%s)", entityID);
+        return make_pair(false,0);
+    }
+
+    // Loop over the supportable outgoing bindings.
+    const EndpointType* ep=NULL;
+    const MessageEncoder* encoder=NULL;
+    vector<const XMLCh*>::const_iterator b;
+    for (b = m_bindings.begin(); b!=m_bindings.end(); ++b) {
+        if (ep=EndpointManager<SingleSignOnService>(role->getSingleSignOnServices()).getByBinding(*b)) {
+            map<const XMLCh*,MessageEncoder*>::const_iterator enc = m_encoders.find(*b);
+            if (enc!=m_encoders.end())
+                encoder = enc->second;
+            break;
+        }
+    }
+    if (!ep || !encoder) {
+        m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
+        return make_pair(false,0);
+    }
+
+    preserveRelayState(app, httpResponse, relayState);
+
+    // For now just build a dummy AuthnRequest.
+    auto_ptr<AuthnRequest> req(AuthnRequestBuilder::buildAuthnRequest());
+    req->setDestination(ep->getLocation());
+    if (acsIndex)
+        req->setAssertionConsumerServiceIndex(acsIndex);
+    if (acsLocation)
+        req->setAssertionConsumerServiceURL(acsLocation);
+    if (acsBinding)
+        req->setProtocolBinding(acsBinding);
+    Issuer* issuer = IssuerBuilder::buildIssuer();
+    req->setIssuer(issuer);
+    issuer->setName(app.getXMLString("providerId").second);
+
+    auto_ptr_char dest(ep->getLocation());
+
+    // Check for signing.
+    const PropertySet* relyingParty = app.getRelyingParty(entity);
+    pair<bool,bool> flag = relyingParty->getBool("signRequests");
+    if ((flag.first && flag.second) || role->WantAuthnRequestsSigned()) {
+        CredentialResolver* credResolver=app.getCredentialResolver();
+        if (credResolver) {
+            Locker credLocker(credResolver);
+            // Fill in criteria to use.
+            MetadataCredentialCriteria mcc(*role);
+            mcc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
+            pair<bool,const XMLCh*> sigalg = relyingParty->getXMLString("signatureAlg");
+            if (sigalg.first)
+                mcc.setXMLAlgorithm(sigalg.second);
+            const Credential* cred = credResolver->resolve(&mcc);
+            if (cred) {
+                // Signed request.
+                long ret = encoder->encode(
+                    httpResponse,
+                    req.get(),
+                    dest.get(),
+                    entityID,
+                    relayState.c_str(),
+                    cred,
+                    sigalg.second,
+                    relyingParty->getXMLString("digestAlg").second
+                    );
+                req.release();  // freed by encoder
+                return make_pair(true,ret);
+            }
+            else {
+                m_log.warn("no signing credential resolved, leaving AuthnRequest unsigned");
+            }
+        }
+    }
+
+    // Unsigned request.
+    long ret = encoder->encode(httpResponse, req.get(), dest.get(), entityID, relayState.c_str());
+    req.release();  // freed by encoder
+    return make_pair(true,ret);
+}
index 4bf2e3f..b8ba663 100644 (file)
@@ -92,7 +92,7 @@ pair<bool,long> SAMLDSSessionInitiator::run(SPRequest& request, const char* enti
         option = request.getParameter("target");
         if (option)
             target = option;
-        recoverRelayState(request, target, false);
+        recoverRelayState(request.getApplication(), request, target, false);
 
         option = request.getParameter("isPassive");
         if (option)
@@ -121,7 +121,7 @@ pair<bool,long> SAMLDSSessionInitiator::run(SPRequest& request, const char* enti
         if (option)
             target = option;
     }
-    preserveRelayState(request, target);
+    preserveRelayState(request.getApplication(), request, target);
 
     const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
     if (isHandler) {
index 6c4182c..bb4e840 100644 (file)
@@ -31,6 +31,7 @@ using namespace std;
 namespace shibsp {
     SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory ChainingSessionInitiatorFactory;
     SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory Shib1SessionInitiatorFactory;
+    SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory SAML2SessionInitiatorFactory;
     SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory WAYFSessionInitiatorFactory;
     SHIBSP_DLLLOCAL PluginManager<SessionInitiator,pair<const DOMElement*,const char*>>::Factory SAMLDSSessionInitiatorFactory;
 };
@@ -40,6 +41,7 @@ void SHIBSP_API shibsp::registerSessionInitiators()
     SPConfig& conf=SPConfig::getConfig();
     conf.SessionInitiatorManager.registerFactory(CHAINING_SESSION_INITIATOR, ChainingSessionInitiatorFactory);
     conf.SessionInitiatorManager.registerFactory(SHIB1_SESSION_INITIATOR, Shib1SessionInitiatorFactory);
+    conf.SessionInitiatorManager.registerFactory(SAML2_SESSION_INITIATOR, SAML2SessionInitiatorFactory);
     conf.SessionInitiatorManager.registerFactory(WAYF_SESSION_INITIATOR, WAYFSessionInitiatorFactory);
     conf.SessionInitiatorManager.registerFactory(SAMLDS_SESSION_INITIATOR, SAMLDSSessionInitiatorFactory);
 }
index ad441e9..af6e5d3 100644 (file)
 
 #include "internal.h"
 #include "Application.h"
+#include "exceptions.h"
+#include "ServiceProvider.h"
 #include "SPRequest.h"
 #include "handler/AbstractHandler.h"
+#include "handler/RemotedHandler.h"
 #include "handler/SessionInitiator.h"
 #include "util/SPConstants.h"
 
@@ -46,14 +49,34 @@ namespace shibsp {
     #pragma warning( disable : 4250 )
 #endif
 
-    class SHIBSP_DLLLOCAL Shib1SessionInitiator : public SessionInitiator, public AbstractHandler
+    class SHIBSP_DLLLOCAL Shib1SessionInitiator : public SessionInitiator, public AbstractHandler, public RemotedHandler
     {
     public:
         Shib1SessionInitiator(const DOMElement* e, const char* appId)
-            : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator")) {}
+                : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator")), m_appId(appId) {
+            // If Location isn't set, defer address registration until the setParent call.
+            pair<bool,const char*> loc = getString("Location");
+            if (loc.first) {
+                string address = m_appId + loc.second + "::run::Shib1SI";
+                setAddress(address.c_str());
+            }
+        }
         virtual ~Shib1SessionInitiator() {}
         
+        void setParent(const PropertySet* parent);
+        void receive(DDF& in, ostream& out);
         pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const;
+
+    private:
+        pair<bool,long> doRequest(
+            const Application& application,
+            HTTPResponse& httpResponse,
+            const char* entityID,
+            const char* acsLocation,
+            string& relayState
+            ) const;
+
+        string m_appId;
     };
 
 #if defined (_MSC_VER)
@@ -67,6 +90,19 @@ namespace shibsp {
 
 };
 
+void Shib1SessionInitiator::setParent(const PropertySet* parent)
+{
+    DOMPropertySet::setParent(parent);
+    pair<bool,const char*> loc = getString("Location");
+    if (loc.first) {
+        string address = m_appId + loc.second + "::run::Shib1SI";
+        setAddress(address.c_str());
+    }
+    else {
+        m_log.warn("no Location property in Shib1 SessionInitiator (or parent), can't register as remoted handler");
+    }
+}
+
 pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const
 {
     // We have to know the IdP to function.
@@ -74,28 +110,105 @@ pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, const char* entit
         return make_pair(false,0);
 
     string target;
-    const char* option;
     const Handler* ACS=NULL;
+    const char* option;
     const Application& app=request.getApplication();
 
     if (isHandler) {
         option=request.getParameter("acsIndex");
         if (option)
-            ACS=app.getAssertionConsumerServiceByIndex(atoi(option));
+            ACS = app.getAssertionConsumerServiceByIndex(atoi(option));
 
         option = request.getParameter("target");
         if (option)
             target = option;
-        recoverRelayState(request, target, false);
+
+        // 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);
     }
     else {
         // We're running as a "virtual handler" from within the filter.
         // The target resource is the current one and everything else is defaulted.
         target=request.getRequestURL();
     }
-        
-    m_log.debug("attempting to initiate session using SAML 1.x with provider (%s)", entityID);
 
+    // Since we're not passing by index, we need to fully compute the return URL and binding.
+    if (!ACS)
+        ACS = app.getDefaultAssertionConsumerService();
+
+    // Compute the ACS URL. We add the ACS location to the base handlerURL.
+    string ACSloc=request.getHandlerURL(target.c_str());
+    pair<bool,const char*> loc=ACS ? ACS->getString("Location") : pair<bool,const char*>(false,NULL);
+    if (loc.first) ACSloc+=loc.second;
+
+    if (isHandler) {
+        // We may already have RelayState set if we looped back here,
+        // but just in case target is a resource, we reset it back.
+        target.erase();
+        option = request.getParameter("target");
+        if (option)
+            target = option;
+    }
+
+    m_log.debug("attempting to initiate session using Shibboleth with provider (%s)", entityID);
+
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
+        return doRequest(app, request, entityID, ACSloc.c_str(), target);
+
+    // Remote the call.
+    DDF out,in = DDF(m_address.c_str()).structure();
+    DDFJanitor jin(in), jout(out);
+    in.addmember("application_id").string(app.getId());
+    in.addmember("entity_id").string(entityID);
+    in.addmember("acsLocation").string(ACSloc.c_str());
+    if (!target.empty())
+        in.addmember("RelayState").string(target.c_str());
+
+    // Remote the processing.
+    out = request.getServiceProvider().getListenerService()->send(in);
+    return unwrap(request, out);
+}
+
+void Shib1SessionInitiator::receive(DDF& in, ostream& out)
+{
+    // Find application.
+    const char* aid=in["application_id"].string();
+    const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
+    if (!app) {
+        // Something's horribly wrong.
+        m_log.error("couldn't find application (%s) to generate AuthnRequest", aid ? aid : "(missing)");
+        throw ConfigurationException("Unable to locate application for new session, deleted?");
+    }
+
+    const char* entityID = in["entity_id"].string();
+    const char* acsLocation = in["acsLocation"].string();
+    if (!entityID || !acsLocation)
+        throw ConfigurationException("No entityID or acsLocation parameter supplied to remoted SessionInitiator.");
+
+    DDF ret(NULL);
+    DDFJanitor jout(ret);
+
+    // Wrap the outgoing object with a Response facade.
+    auto_ptr<HTTPResponse> http(getResponse(ret));
+
+    string relayState(in["RelayState"].string() ? in["RelayState"].string() : "");
+
+    // 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, *http.get(), entityID, acsLocation, relayState);
+    out << ret;
+}
+
+pair<bool,long> Shib1SessionInitiator::doRequest(
+    const Application& app,
+    HTTPResponse& httpResponse,
+    const char* entityID,
+    const char* acsLocation,
+    string& relayState
+    ) const
+{
     // Use metadata to invoke the SSO service directly.
     MetadataProvider* m=app.getMetadataProvider();
     Locker locker(m);
@@ -116,35 +229,20 @@ pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, const char* entit
         m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
         return make_pair(false,0);
     }
-    auto_ptr_char dest(ep->getLocation());
-
-    if (!ACS)
-        ACS = app.getDefaultAssertionConsumerService();
 
-    // Compute the ACS URL. We add the ACS location to the base handlerURL.
-    string ACSloc=request.getHandlerURL(target.c_str());
-    pair<bool,const char*> loc=ACS ? ACS->getString("Location") : pair<bool,const char*>(false,NULL);
-    if (loc.first) ACSloc+=loc.second;
-
-    if (isHandler) {
-        // We may already have RelayState set if we looped back here,
-        // but just in case target is a resource, we reset it back.
-        option = request.getParameter("target");
-        if (option)
-            target = option;
-    }
-    preserveRelayState(request, target);
+    preserveRelayState(app, httpResponse, relayState);
 
     // Shib 1.x requires a target value.
-    if (target.empty())
-        target = "default";
+    if (relayState.empty())
+        relayState = "default";
 
     char timebuf[16];
     sprintf(timebuf,"%u",time(NULL));
     const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
-    string req=string(dest.get()) + (strchr(dest.get(),'?') ? '&' : '?') + "shire=" + urlenc->encode(ACSloc.c_str()) +
-        "&time=" + timebuf + "&target=" + urlenc->encode(target.c_str()) +
+    auto_ptr_char dest(ep->getLocation());
+    string req=string(dest.get()) + (strchr(dest.get(),'?') ? '&' : '?') + "shire=" + urlenc->encode(acsLocation) +
+        "&time=" + timebuf + "&target=" + urlenc->encode(relayState.c_str()) +
         "&providerId=" + urlenc->encode(app.getString("entityID").second);
 
-    return make_pair(true, request.sendRedirect(req.c_str()));
+    return make_pair(true, httpResponse.sendRedirect(req.c_str()));
 }
index e53c3ee..148573c 100644 (file)
@@ -92,7 +92,7 @@ pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, const char* entity
         option = request.getParameter("target");
         if (option)
             target = option;
-        recoverRelayState(request, target, false);
+        recoverRelayState(request.getApplication(), request, target, false);
     }
     else {
         // We're running as a "virtual handler" from within the filter.
@@ -117,7 +117,7 @@ pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, const char* entity
         if (option)
             target = option;
     }
-    preserveRelayState(request, target);
+    preserveRelayState(request.getApplication(), request, target);
 
     // WAYF requires a target value.
     if (target.empty())
index fbe8b27..92fc12d 100644 (file)
@@ -260,12 +260,7 @@ void RemotedSession::validate(const Application& application, const char* client
     time_t now = time(NULL);\r
     if (now > m_expires) {\r
         m_cache->m_log.info("session expired (ID: %s)", m_obj.name());\r
-        RetryableProfileException ex("Your session has expired, and you must re-authenticate.");\r
-        if (!getEntityID())\r
-            throw ex;\r
-        MetadataProvider* m=application.getMetadataProvider();\r
-        Locker locker(m);\r
-        annotateException(&ex,m->getEntityDescriptor(getEntityID(),false)); // throws it\r
+        throw RetryableProfileException("Your session has expired, and you must re-authenticate.");\r
     }\r
 \r
     // Address check?\r
@@ -274,15 +269,10 @@ void RemotedSession::validate(const Application& application, const char* client
             m_cache->m_log.debug("comparing client address %s against %s", client_addr, getClientAddress());\r
         if (strcmp(getClientAddress(),client_addr)) {\r
             m_cache->m_log.warn("client address mismatch");\r
-            RetryableProfileException ex(\r
+            throw RetryableProfileException(\r
                 "Your IP address ($1) does not match the address recorded at the time the session was established.",\r
                 params(1,client_addr)\r
                 );\r
-            if (!getEntityID())\r
-                throw ex;\r
-            MetadataProvider* m=application.getMetadataProvider();\r
-            Locker locker(m);\r
-            annotateException(&ex,m->getEntityDescriptor(getEntityID(),false)); // throws it\r
         }\r
     }\r
 \r
index 169de81..bc9f27c 100644 (file)
@@ -243,7 +243,8 @@ void MetadataPKIXIterator::populate()
             // Copy over the information.
             for (vector<X509Credential*>::const_iterator c=cached->second.begin(); c!=cached->second.end(); ++c) {
                 m_certs.insert(m_certs.end(), (*c)->getEntityCertificateChain().begin(), (*c)->getEntityCertificateChain().end());
-                m_crls.push_back((*c)->getCRL());
+                if ((*c)->getCRL())
+                    m_crls.push_back((*c)->getCRL());
             }
             return;
         }
@@ -263,7 +264,8 @@ void MetadataPKIXIterator::populate()
     // Copy over the new information.
     for (vector<X509Credential*>::const_iterator c=m_ownedCreds.begin(); c!=m_ownedCreds.end(); ++c) {
         m_certs.insert(m_certs.end(), (*c)->getEntityCertificateChain().begin(), (*c)->getEntityCertificateChain().end());
-        m_crls.push_back((*c)->getCRL());
+        if ((*c)->getCRL())
+            m_crls.push_back((*c)->getCRL());
     }
 
     // As a last step, if we're caching, try and elevate to a write lock for cache insertion.
index b7f3f32..f8c15af 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\SAML2SessionInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\SAMLDSSessionInitiator.cpp"\r
                                                >\r
                                        </File>\r