Config changes and rework for new credential APIs.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 30 Mar 2007 02:20:01 +0000 (02:20 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 30 Mar 2007 02:20:01 +0000 (02:20 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2205 cb58f699-b61c-0410-a6fe-9272a202ed29

14 files changed:
configs/shibboleth.xml.in
isapi_shib/isapi_shib.cpp
nsapi_shib/nsapi_shib.cpp
schemas/shibboleth-spconfig-2.0.xsd
shibd/shibd.cpp
shibsp/Application.h
shibsp/ServiceProvider.h
shibsp/attribute/resolver/impl/SimpleAttributeResolver.cpp
shibsp/binding/SOAPClient.h
shibsp/binding/impl/SOAPClient.cpp
shibsp/handler/impl/SAML2Consumer.cpp
shibsp/impl/XMLServiceProvider.cpp
shibsp/security/PKIXTrustEngine.cpp
util/samlquery.cpp

index 4f8bbe1..ec09c7d 100644 (file)
@@ -11,7 +11,7 @@
        </Extensions>
        -->
 
-       <!-- The OutOfProcess section pertains to components that rely on a single long-lived process. -->
+       <!-- The OutOfProcess section pertains to components that run in the shibd daemon. -->
        <OutOfProcess logger="@-PKGSYSCONFDIR-@/shibd.logger">
                
                <!--
@@ -43,7 +43,7 @@
                -->
        </OutOfProcess>
     
-       <!-- The InProcess section pertains to components that support transient process pools like most web servers. -->
+       <!-- The InProcess section pertains to components that run inside the web server. -->
        <InProcess logger="@-PKGSYSCONFDIR-@/native.logger">
                <!--
                To customize behavior, map hostnames and path components to applicationId and other settings.
                        supportContact="root@localhost"
                        logoLocation="/shibboleth-sp/logo.jpg"
                        styleSheet="/shibboleth-sp/main.css"/>
+               
+               <!-- Configure handling of outgoing messages. -->
+               <DefaultRelyingParty authType="TLS" signRequests="false" encryptRequests="true"/>
 
-               <!-- Indicates what credentials to use when communicating -->
-               <CredentialUse TLS="defcreds" Signing="defcreds" Encryption="defcreds"/>
-                       
-               <!-- When adding multiple metadata sources, uncomment the chained provider around them. -->
-               <!-- <MetadataProvider type="Chaining"> -->
+               <!-- Chains together all your metadata sources. -->
+               <MetadataProvider type="Chaining">
                        <!-- Dummy metadata for private testing, delete for production deployments. -->
                        <MetadataProvider type="XML" path="@-PKGSYSCONFDIR-@/example-metadata.xml"/>
-               <!-- </MetadataProvider> -->
+               </MetadataProvider>
 
                <!-- Chain the two built-in trust engines together. -->
                <TrustEngine type="Chaining">
                        <TrustEngine type="PKIX"/>
                </TrustEngine>
 
+               <!-- Built-in attribute resolver to extract data from SAML assertions. -->
                <AttributeResolver type="Simple" path="@-PKGSYSCONFDIR-@/resolver-simple.xml"/>
-       </Applications>
-       
-       <!-- Define all the private keys and certificates here that you reference from <CredentialUse>. -->
-       <Credentials>
-               <CredentialResolver id="defcreds">
+
+               <!-- Simple file-based resolver for key/certificate information. -->
+               <CredentialResolver type="File">
                        <Key>
                                <Path>@-PKGSYSCONFDIR-@/sp-example.key</Path>
                        </Key>
                                <Path>@-PKGSYSCONFDIR-@/sp-example.crt</Path>
                        </Certificate>
                </CredentialResolver>
-       </Credentials>
-
+       </Applications>
+       
        <!-- Each policy defines a set of rules to use to secure SAML and SOAP messages. -->
        <SecurityPolicies>
                <!-- The predefined policy handles SAML 1 and 2 protocols and permits signing and client TLS. -->
index 0e8f191..de35d5e 100644 (file)
@@ -31,6 +31,7 @@
 #include <xmltooling/unicode.h>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/util/NDC.h>
+#include <xmltooling/util/XMLConstants.h>
 #include <xmltooling/util/XMLHelper.h>
 #include <xercesc/util/XMLUniDefs.hpp>
 
index d529afe..56a5976 100644 (file)
@@ -38,6 +38,7 @@
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/util/NDC.h>
 #include <xmltooling/util/Threads.h>
+#include <xmltooling/util/XMLConstants.h>
 #include <xmltooling/util/XMLHelper.h>
 #include <xercesc/util/XMLUniDefs.hpp>
 
index 9649f1f..0d70774 100644 (file)
@@ -47,7 +47,6 @@
                                <element ref="conf:OutOfProcess"/>
                                <element ref="conf:InProcess"/>
                                <element ref="conf:Applications"/>
-                               <element ref="conf:Credentials" minOccurs="0"/>
                                <element ref="conf:SecurityPolicies"/>
                        </sequence>
                        <attribute name="logger" type="anyURI"/>
                        <sequence>\r
                                <element ref="conf:Sessions"/>\r
                                <element ref="conf:Errors"/>\r
-                               <element ref="conf:CredentialUse" minOccurs="0"/>\r
+                               <element ref="conf:DefaultRelyingParty"/>\r
                                <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>
                 <element name="MetadataProvider" type="conf:PluggableType"/>
                                <element name="TrustEngine" type="conf:PluggableType"/>\r
                                <element name="AttributeResolver" type="conf:PluggableType"/>\r
+                               <element name="CredentialResolver" type="conf:PluggableType" minOccurs="0"/>\r
                                <element ref="conf:Application" minOccurs="0" maxOccurs="unbounded"/>\r
                        </sequence>\r
                        <attribute name="id" type="conf:string" fixed="default"/>\r
                        <sequence>\r
                                <element ref="conf:Sessions" minOccurs="0"/>\r
                                <element ref="conf:Errors" minOccurs="0"/>\r
-                               <element ref="conf:CredentialUse" minOccurs="0"/>\r
+                               <element ref="conf:DefaultRelyingParty" minOccurs="0"/>\r
                                <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>\r
                                <element name="MetadataProvider" type="conf:PluggableType" minOccurs="0"/>\r
                                <element name="TrustEngine" type="conf:PluggableType" minOccurs="0"/>\r
                                <element name="AttributeResolver" type="conf:PluggableType" minOccurs="0"/>\r
+                               <element name="CredentialResolver" type="conf:PluggableType" minOccurs="0"/>\r
                        </sequence>\r
                        <attribute name="id" type="conf:string" use="required"/>\r
                        <attribute name="providerId" type="anyURI"/>\r
                </complexType>\r
        </element>\r
 \r
-       <attributeGroup name="CredentialUseGroup">\r
-               <attribute name="TLS" type="conf:string"/>\r
-               <attribute name="Signing" type="conf:string"/>\r
-               <attribute name="Encryption" type="conf:string"/>\r
-               <attribute name="signRequests" type="boolean" default="false"/>\r
-               <attribute name="signatureAlg" type="anyURI"/>\r
-               <attribute name="authType">\r
-                       <simpleType>\r
-                               <restriction base="conf:string">\r
-                                       <enumeration value="basic"/>\r
-                                       <enumeration value="digest"/>\r
-                                       <enumeration value="ntlm"/>\r
-                                       <enumeration value="gss"/>\r
-                               </restriction>\r
-                       </simpleType>\r
-               </attribute>\r
-               <attribute name="authUsername"/>\r
-               <attribute name="authPassword"/>\r
-       </attributeGroup>\r
-\r
-       <element name="CredentialUse">\r
+       <element name="DefaultRelyingParty">\r
                <annotation>\r
                        <documentation>Container for specifying security methods to use with particular peers</documentation>\r
                </annotation>\r
                        <sequence>\r
                                <element name="RelyingParty" minOccurs="0" maxOccurs="unbounded">\r
                                        <complexType>\r
-                                               <sequence>\r
-                                                       <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>\r
-                                               </sequence>\r
+                                               <sequence/>\r
                                                <attribute name="Name" type="conf:string" use="required"/>\r
-                                               <attributeGroup ref="conf:CredentialUseGroup"/>\r
+                                               <attributeGroup ref="conf:RelyingPartyGroup"/>\r
                                                <anyAttribute namespace="##other" processContents="lax"/>\r
                                        </complexType>\r
                                </element>\r
-                               <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>\r
                        </sequence>\r
-                       <attributeGroup ref="conf:CredentialUseGroup"/>\r
+                       <attributeGroup ref="conf:RelyingPartyGroup"/>\r
                <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
        </element>\r
        \r
-       <element name="Credentials">
-               <annotation>\r
-                       <documentation>Container for specifying sources of credentials</documentation>\r
-               </annotation>\r
-               <complexType>\r
-                       <sequence>\r
-                               <element name="CredentialResolver" minOccurs="1" maxOccurs="unbounded">
-                                       <annotation>\r
-                                               <documentation>References CredentialResolver plugins</documentation>\r
-                                       </annotation>\r
-                                       <complexType>
-                                               <complexContent>
-                                                       <restriction base="conf:PluggableType">
-                                                               <sequence>
-                                                                       <any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
-                                                               </sequence>
-                                                               <attribute name="id" type="conf:string" use="required"/>\r
-                                                               <anyAttribute namespace="##any" processContents="lax"/>
-                                                       </restriction>
-                                               </complexContent>
-                                       </complexType>
-                               </element>\r
-                       </sequence>
-               </complexType>
-       </element>\r
+       <attributeGroup name="RelyingPartyGroup">\r
+               <attribute name="authType" type="conf:string" default="TLS"/>\r
+               <attribute name="authUsername" type="conf:string"/>\r
+               <attribute name="authPassword" type="conf:string"/>\r
+               <attribute name="signRequests" type="boolean" default="false"/>
+               <attribute name="signatureAlg" type="anyURI"/>\r
+               <attribute name="encryptRequests" type="boolean" default="true"/>\r
+       </attributeGroup>\r
        \r
        <element name="SecurityPolicies">
                <annotation>\r
index d7083d5..e017aac 100644 (file)
@@ -49,6 +49,7 @@
 #include <shibsp/remoting/ListenerService.h>\r
 #include <xercesc/util/XMLUniDefs.hpp>\r
 #include <xmltooling/XMLToolingConfig.h>\r
+#include <xmltooling/util/XMLConstants.h>\r
 #include <xmltooling/util/XMLHelper.h>\r
 \r
 using namespace shibsp;\r
index 113d743..99797e2 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <shibsp/util/PropertySet.h>
 #include <saml/saml2/metadata/MetadataProvider.h>
+#include <xmltooling/security/CredentialResolver.h>
 #include <xmltooling/security/TrustEngine.h>
 
 namespace shibsp {
@@ -98,12 +99,19 @@ namespace shibsp {
         virtual AttributeResolver* getAttributeResolver() const=0;
 
         /**
-         * Returns configuration properties governing security interactions with a peer entity.
+         * Returns the CredentialResolver instance associated with this Application.
+         * 
+         * @return  a CredentialResolver, or NULL
+         */
+        virtual xmltooling::CredentialResolver* getCredentialResolver() const=0;
+
+        /**
+         * Returns configuration properties governing security interactions with a peer.
          * 
          * @param provider  a peer entity's metadata
          * @return  the applicable PropertySet
          */
-        virtual const PropertySet* getCredentialUse(const opensaml::saml2md::EntityDescriptor* provider) const=0;
+        virtual const PropertySet* getRelyingParty(const opensaml::saml2md::EntityDescriptor* provider) const=0;
 
         /**
          * Returns the default SessionInitiator Handler when automatically
index 45c76b3..7689a7a 100644 (file)
@@ -25,7 +25,7 @@
 
 #include <shibsp/util/PropertySet.h>
 #include <saml/binding/SecurityPolicyRule.h>
-#include <xmltooling/security/CredentialResolver.h>
+#include <xmltooling/Lockable.h>
 #include <xmltooling/util/StorageService.h>
 
 namespace shibsp {
@@ -94,14 +94,6 @@ namespace shibsp {
         virtual ListenerService* getListenerService(bool required=true) const=0;
         
         /**
-         * Returns a CredentialResolver instance mapped to a key.
-         * 
-         * @param id    a NULL-terminated key identifying the CredentialResolver to the configuration 
-         * @return  a CredentialResolver if available, or NULL
-         */
-        virtual xmltooling::CredentialResolver* getCredentialResolver(const char* id) const=0;
-
-        /**
                 * Returns the security policy settings for an identified policy.
          *
                 * @param id    identifies the policy to return
index daeb905..2802ba2 100644 (file)
@@ -455,22 +455,24 @@ void SimpleResolverImpl::resolve(
             }\r
         }\r
 \r
-        CredentialResolver* cr=NULL;\r
         const vector<saml2::EncryptedAttribute*>& encattrs = const_cast<const saml2::AttributeStatement*>(*s)->getEncryptedAttributes();\r
         if (!encattrs.empty()) {\r
-            const PropertySet* credUse = ctx.getApplication().getCredentialUse(ctx.getEntityDescriptor());\r
-            if (credUse)\r
-                cr = ctx.getApplication().getServiceProvider().getCredentialResolver(credUse->getString("Encryption").second);\r
+            const XMLCh* recipient = ctx.getApplication().getXMLString("providerId").second;\r
+            CredentialResolver* cr = ctx.getApplication().getCredentialResolver();\r
             if (!cr) {\r
                 Category::getInstance(SHIBSP_LOGCAT".AttributeResolver").warn(\r
-                    "found encrypted attributes, but no decryption credential was available"\r
+                    "found encrypted attributes, but no CredentialResolver was available"\r
                     );\r
                 return;\r
             }\r
+\r
+            // We look up credentials based on the peer who did the encrypting.\r
+            CredentialCriteria cc;\r
+            cc.setPeerName(assertingParty.get());\r
+\r
             Locker credlocker(cr);\r
-            const XMLCh* recipient = ctx.getApplication().getXMLString("providerId").second;\r
             for (vector<saml2::EncryptedAttribute*>::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea) {\r
-                auto_ptr<XMLObject> decrypted((*ea)->decrypt(cr, recipient));\r
+                auto_ptr<XMLObject> decrypted((*ea)->decrypt(*cr, recipient, &cc));\r
                 const saml2::Attribute* decattr = dynamic_cast<const saml2::Attribute*>(decrypted.get());\r
                 name = decattr->getName();\r
                 format = decattr->getNameFormat();\r
@@ -523,6 +525,7 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nam
     }\r
 \r
     SecurityPolicy policy;\r
+    MetadataCredentialCriteria mcc(*AA);\r
     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
     const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
     pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
@@ -545,7 +548,7 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nam
             query->setResource(issuer.get());\r
             request->setMinorVersion(version);\r
             SAML1SOAPClient client(soaper);\r
-            client.sendSAML(request, *AA, loc.get());\r
+            client.sendSAML(request, mcc, loc.get());\r
             response = client.receiveSAML();\r
         }\r
         catch (exception& ex) {\r
@@ -606,6 +609,7 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, con
     }\r
 \r
     SecurityPolicy policy;\r
+    MetadataCredentialCriteria mcc(*AA);\r
     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
     const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
     pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
@@ -627,7 +631,7 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, con
             query->setIssuer(iss);\r
             iss->setName(issuer.get());\r
             SAML2SOAPClient client(soaper);\r
-            client.sendSAML(query, *AA, loc.get());\r
+            client.sendSAML(query, mcc, loc.get());\r
             srt = client.receiveSAML();\r
         }\r
         catch (exception& ex) {\r
index 5c284ee..ee16ada 100644 (file)
@@ -52,10 +52,10 @@ namespace shibsp {
          * Override handles message signing for SAML payloads.
          * 
          * @param env       SOAP envelope to send
-         * @param peer      peer to send message to, expressed in TrustEngine terms
+         * @param peer      peer to send message to, expressed in metadata terms
          * @param endpoint  URL of endpoint to recieve message
          */
-        void send(const soap11::Envelope& env, const xmltooling::KeyInfoSource& peer, const char* endpoint);
+        void send(const soap11::Envelope& env, opensaml::saml2md::MetadataCredentialCriteria& peer, const char* endpoint);
 
         void reset();
 
@@ -73,8 +73,8 @@ namespace shibsp {
         /** Properties associated with the Application's security policy. */
         const PropertySet* m_settings;
 
-        /** CredentialUse properties, set after transport prep. */
-        const PropertySet* m_credUse;
+        /** RelyingParty properties, set after transport prep. */
+        const PropertySet* m_relyingParty;
 
         /** Locked CredentialResolver for transport, set after transport prep. */
         xmltooling::CredentialResolver* m_credResolver;
index 62b0a71..49a3e38 100644 (file)
@@ -38,20 +38,8 @@ using namespace xmltooling;
 using namespace log4cpp;
 using namespace std;
 
-namespace {
-    class SHIBSP_DLLLOCAL _addcert : public binary_function<X509Data*,XSECCryptoX509*,void> {
-    public:
-        void operator()(X509Data* bag, XSECCryptoX509* cert) const {
-            safeBuffer& buf=cert->getDEREncodingSB();
-            X509Certificate* x=X509CertificateBuilder::buildX509Certificate();
-            x->setValue(buf.sbStrToXMLCh());
-            bag->getX509Certificates().push_back(x);
-        }
-    };
-};
-
 SOAPClient::SOAPClient(const Application& application, opensaml::SecurityPolicy& policy)
-    : opensaml::SOAPClient(policy), m_app(application), m_settings(NULL), m_credUse(NULL), m_credResolver(NULL)
+    : opensaml::SOAPClient(policy), m_app(application), m_settings(NULL), m_relyingParty(NULL), m_credResolver(NULL)
 {
     pair<bool,const char*> policyId = m_app.getString("policyId");
     m_settings = application.getServiceProvider().getPolicySettings(policyId.second);
@@ -65,24 +53,25 @@ SOAPClient::SOAPClient(const Application& application, opensaml::SecurityPolicy&
     setValidating(validate.first && validate.second);
 }
 
-void SOAPClient::send(const soap11::Envelope& env, const KeyInfoSource& peer, const char* endpoint)
+void SOAPClient::send(const soap11::Envelope& env, MetadataCredentialCriteria& peer, const char* endpoint)
 {
-    if (!m_peer)
-        m_peer = dynamic_cast<const RoleDescriptor*>(&peer);
-    if (m_peer) {
-        const EntityDescriptor* entity = m_peer ? dynamic_cast<const EntityDescriptor*>(m_peer->getParent()) : NULL;
-        m_credUse = entity ? m_app.getCredentialUse(entity) : NULL;
-    }
-    
     // Check for message signing requirements.   
-    if (m_credUse) {
-        pair<bool,bool> flag = m_credUse->getBool("signRequests");
-        if (flag.first && flag.second) {
-            CredentialResolver* cr=NULL;
-            pair<bool,const char*> cred = m_credUse->getString("Signing");
-            if (cred.first && (cr=m_app.getServiceProvider().getCredentialResolver(cred.second))) {
-                // Looks like we're supposed to sign, so check for message.
+    m_relyingParty = m_app.getRelyingParty(dynamic_cast<const EntityDescriptor*>(peer.getRole().getParent()));
+    pair<bool,bool> flag = m_relyingParty->getBool("signRequests");
+    if (flag.first && flag.second) {
+        m_credResolver=m_app.getCredentialResolver();
+        if (m_credResolver) {
+            m_credResolver->lock();
+            // Fill in criteria to use.
+            peer.setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
+            pair<bool,const char*> algcrit = m_relyingParty->getString("sigAlgorithm");
+            if (algcrit.first)
+                peer.setKeyAlgorithm(algcrit.second);
+            const Credential* cred = m_credResolver->resolve(&peer);
+            peer.setKeyAlgorithm(NULL);
+
+            if (cred) {
+                // Check for message.
                 const vector<XMLObject*>& bodies=const_cast<const soap11::Body*>(env.getBody())->getUnknownXMLObjects();
                 if (!bodies.empty()) {
                     opensaml::SignableObject* msg = dynamic_cast<opensaml::SignableObject*>(bodies.front());
@@ -90,28 +79,22 @@ void SOAPClient::send(const soap11::Envelope& env, const KeyInfoSource& peer, co
                         // Build a Signature.
                         Signature* sig = SignatureBuilder::buildSignature();
                         msg->setSignature(sig);
-                        pair<bool,const XMLCh*> alg = m_credUse->getXMLString("sigAlgorithm");
+                        pair<bool,const XMLCh*> alg = m_relyingParty->getXMLString("sigAlgorithm");
                         if (alg.first)
                             sig->setSignatureAlgorithm(alg.second);
-                        Locker locker(cr);
-                        sig->setSigningKey(cr->getKey());
-                    
-                        // Build KeyInfo.
-                        const vector<XSECCryptoX509*>& certs = cr->getCertificates();
-                        if (!certs.empty()) {
-                            KeyInfo* keyInfo=KeyInfoBuilder::buildKeyInfo();
-                            sig->setKeyInfo(keyInfo);
-                            X509Data* x509Data=X509DataBuilder::buildX509Data();
-                            keyInfo->getX509Datas().push_back(x509Data);
-                            for_each(certs.begin(),certs.end(),bind1st(_addcert(),x509Data));
-                        }
 
                         // Sign it. The marshalling step in the base class should be a no-op.
                         vector<Signature*> sigs(1,sig);
-                        env.marshall((DOMDocument*)NULL,&sigs);
+                        env.marshall((DOMDocument*)NULL,&sigs,cred);
                     }
                 }
             }
+            else {
+                Category::getInstance(SHIBSP_LOGCAT".SOAPClient").error("no signing credential supplied, leaving unsigned.");
+            }
+        }
+        else {
+            Category::getInstance(SHIBSP_LOGCAT".SOAPClient").error("no CredentialResolver available, leaving unsigned.");
         }
     }
     
@@ -137,53 +120,52 @@ void SOAPClient::prepareTransport(SOAPTransport& transport)
 
     opensaml::SOAPClient::prepareTransport(transport);
 
-    if (!m_credUse) {
-        const EntityDescriptor* entity = m_peer ? dynamic_cast<const EntityDescriptor*>(m_peer->getParent()) : NULL;
-        m_credUse = entity ? m_app.getCredentialUse(entity) : NULL;
-    }
-    if (m_credUse) {
-        pair<bool,const char*> authType=m_credUse->getString("authType");
-        if (authType.first) {
-            SOAPTransport::transport_auth_t type=SOAPTransport::transport_auth_none;
-            pair<bool,const char*> username=m_credUse->getString("authUsername");
-            pair<bool,const char*> password=m_credUse->getString("authPassword");
-            if (!username.first || !password.first)
-                log.error("transport authType (%s) specified but authUsername or authPassword was missing", authType.second);
-            else if (!strcmp(authType.second,"basic"))
-                type = SOAPTransport::transport_auth_basic;
-            else if (!strcmp(authType.second,"digest"))
-                type = SOAPTransport::transport_auth_digest;
-            else if (!strcmp(authType.second,"ntlm"))
-                type = SOAPTransport::transport_auth_ntlm;
-            else if (!strcmp(authType.second,"gss"))
-                type = SOAPTransport::transport_auth_gss;
-            else
-                log.error("unknown authType (%s) specified in CredentialUse element", authType.second);
-            if (type > SOAPTransport::transport_auth_none) {
-                if (transport.setAuth(type,username.second,password.second))
-                    log.debug("configured for transport authentication (method=%s, username=%s)", authType.second, username.second);
-                else
-                    log.error("failed to configure transport authentication (method=%s)", authType.second);
-            }
-        }
-        
-        authType = m_credUse->getString("TLS");
-        if (authType.first) {
-            m_credResolver = m_app.getServiceProvider().getCredentialResolver(authType.second);
-            if (m_credResolver) {
+    pair<bool,const char*> authType=m_relyingParty->getString("authType");
+    if (!authType.first || !strcmp(authType.second,"TLS")) {
+        if (!m_credResolver) {
+            m_credResolver = m_app.getCredentialResolver();
+            if (m_credResolver)
                 m_credResolver->lock();
-                if (!transport.setCredentialResolver(m_credResolver)) {
-                    m_credResolver->unlock();
-                    m_credResolver = NULL;
-                    log.error("failed to load CredentialResolver into SOAPTransport");
-                }
+        }
+        if (m_credResolver) {
+            m_criteria->setUsage(CredentialCriteria::TLS_CREDENTIAL);
+            const Credential* cred = m_credResolver->resolve(m_criteria);
+            if (cred) {
+                if (!transport.setCredential(cred))
+                    log.error("failed to load Credential into SOAPTransport");
             }
             else {
-                log.error("unable to access CredentialResolver (%s)", authType.second);
+                log.error("no TLS credential supplied");
             }
         }
-    } 
-
+        else {
+            log.error("no CredentialResolver available for TLS");
+        }
+    }
+    else {
+        SOAPTransport::transport_auth_t type=SOAPTransport::transport_auth_none;
+        pair<bool,const char*> username=m_relyingParty->getString("authUsername");
+        pair<bool,const char*> password=m_relyingParty->getString("authPassword");
+        if (!username.first || !password.first)
+            log.error("transport authType (%s) specified but authUsername or authPassword was missing", authType.second);
+        else if (!strcmp(authType.second,"basic"))
+            type = SOAPTransport::transport_auth_basic;
+        else if (!strcmp(authType.second,"digest"))
+            type = SOAPTransport::transport_auth_digest;
+        else if (!strcmp(authType.second,"ntlm"))
+            type = SOAPTransport::transport_auth_ntlm;
+        else if (!strcmp(authType.second,"gss"))
+            type = SOAPTransport::transport_auth_gss;
+        else
+            log.error("unknown authType (%s) specified for RelyingParty", authType.second);
+        if (type > SOAPTransport::transport_auth_none) {
+            if (transport.setAuth(type,username.second,password.second))
+                log.debug("configured for transport authentication (method=%s, username=%s)", authType.second, username.second);
+            else
+                log.error("failed to configure transport authentication (method=%s)", authType.second);
+        }
+    }
+    
     transport.setConnectTimeout(m_settings->getUnsignedInt("connectTimeout").second);
     transport.setTimeout(m_settings->getUnsignedInt("timeout").second);
 
@@ -197,7 +179,7 @@ void SOAPClient::prepareTransport(SOAPTransport& transport)
 
 void SOAPClient::reset()
 {
-    m_credUse = NULL;
+    m_relyingParty = NULL;
     if (m_credResolver)
         m_credResolver->unlock();
     m_credResolver = NULL;
index e7ba328..0d79176 100644 (file)
@@ -184,22 +184,24 @@ string SAML2Consumer::implementProtocol(
             badtokens.push_back(*a);
         }
     }
-\r
-    CredentialResolver* cr=NULL;\r
-    const PropertySet* credUse = application.getCredentialUse(\r
-        policy.getIssuerMetadata() ? dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent()) : NULL\r
-        );\r
-    if (credUse)\r
-        cr = application.getServiceProvider().getCredentialResolver(credUse->getString("Encryption").second);
+
+    // We look up decryption credentials based on the peer who did the encrypting.
+    CredentialCriteria cc;
+    if (policy.getIssuerMetadata()) {
+        auto_ptr_char assertingParty(dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent())->getEntityID());
+        cc.setPeerName(assertingParty.get());
+    }
+    CredentialResolver* cr=application.getCredentialResolver();
+
     if (!cr && !encassertions.empty())
-        m_log.warn("found encrypted assertions, but no decryption credential was available");
+        m_log.warn("found encrypted assertions, but no CredentialResolver was available");
 
     for (vector<saml2::EncryptedAssertion*>::const_iterator ea = encassertions.begin(); cr && ea!=encassertions.end(); ++ea) {
         // Attempt to decrypt it.
         saml2::Assertion* decrypted=NULL;
         try {
             Locker credlocker(cr);
-            auto_ptr<XMLObject> wrapper((*ea)->decrypt(cr, application.getXMLString("providerId").second));
+            auto_ptr<XMLObject> wrapper((*ea)->decrypt(*cr, application.getXMLString("providerId").second, &cc));
             decrypted = dynamic_cast<saml2::Assertion*>(wrapper.get());
             if (decrypted) {
                 wrapper.release();
@@ -289,7 +291,7 @@ string SAML2Consumer::implementProtocol(
             else {
                 Locker credlocker(cr);
                 try {
-                    auto_ptr<XMLObject> decryptedID(encname->decrypt(cr,application.getXMLString("providerId").second));
+                    auto_ptr<XMLObject> decryptedID(encname->decrypt(*cr,application.getXMLString("providerId").second,&cc));
                     ssoName = dynamic_cast<NameID*>(decryptedID.get());
                     if (ssoName) {
                         ownedName = true;
index 7254acd..849fc16 100644 (file)
@@ -97,8 +97,10 @@ namespace {
         AttributeResolver* getAttributeResolver() const {\r
             return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver;\r
         }\r
-\r
-        const PropertySet* getCredentialUse(const EntityDescriptor* provider) const;\r
+        CredentialResolver* getCredentialResolver() const {\r
+            return (!m_credResolver && m_base) ? m_base->getCredentialResolver() : m_credResolver;\r
+        }\r
+        const PropertySet* getRelyingParty(const EntityDescriptor* provider) const;\r
 \r
         const Handler* getDefaultSessionInitiator() const;\r
         const Handler* getSessionInitiatorById(const char* id) const;\r
@@ -122,6 +124,7 @@ namespace {
         MetadataProvider* m_metadata;\r
         TrustEngine* m_trust;\r
         AttributeResolver* m_attrResolver;\r
+        CredentialResolver* m_credResolver;\r
         vector<const XMLCh*> m_audiences;\r
 \r
         // manage handler objects\r
@@ -150,11 +153,12 @@ namespace {
         // pointer to default session initiator\r
         const Handler* m_sessionInitDefault;\r
 \r
-        DOMPropertySet* m_credDefault;\r
+        // RelyingParty properties\r
+        DOMPropertySet* m_partyDefault;\r
 #ifdef HAVE_GOOD_STL\r
-        map<xstring,PropertySet*> m_credMap;\r
+        map<xstring,PropertySet*> m_partyMap;\r
 #else\r
-        map<const XMLCh*,PropertySet*> m_credMap;\r
+        map<const XMLCh*,PropertySet*> m_partyMap;\r
 #endif\r
     };\r
 \r
@@ -168,7 +172,6 @@ namespace {
         \r
         RequestMapper* m_requestMapper;\r
         map<string,Application*> m_appmap;\r
-        map<string,CredentialResolver*> m_credResolverMap;\r
         map< string,pair< PropertySet*,vector<const SecurityPolicyRule*> > > m_policyMap;\r
         \r
         // Provides filter to exclude special config elements.\r
@@ -254,15 +257,6 @@ namespace {
             return (i!=m_impl->m_appmap.end()) ? i->second : NULL;\r
         }\r
 \r
-        CredentialResolver* getCredentialResolver(const char* id) const {\r
-            if (id) {\r
-                map<string,CredentialResolver*>::const_iterator i=m_impl->m_credResolverMap.find(id);\r
-                if (i!=m_impl->m_credResolverMap.end())\r
-                    return i->second;\r
-            }\r
-            return NULL;\r
-        }\r
-\r
         const PropertySet* getPolicySettings(const char* id) const {\r
             map<string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::const_iterator i = m_impl->m_policyMap.find(id);\r
             if (i!=m_impl->m_policyMap.end())\r
@@ -297,8 +291,8 @@ namespace {
     static const XMLCh Applications[] =         UNICODE_LITERAL_12(A,p,p,l,i,c,a,t,i,o,n,s);\r
     static const XMLCh _ArtifactMap[] =         UNICODE_LITERAL_11(A,r,t,i,f,a,c,t,M,a,p);\r
     static const XMLCh _AttributeResolver[] =   UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);\r
-    static const XMLCh Credentials[] =          UNICODE_LITERAL_11(C,r,e,d,e,n,t,i,a,l,s);\r
-    static const XMLCh CredentialUse[] =        UNICODE_LITERAL_13(C,r,e,d,e,n,t,i,a,l,U,s,e);\r
+    static const XMLCh _CredentialResolver[] =  UNICODE_LITERAL_18(C,r,e,d,e,n,t,i,a,l,R,e,s,o,l,v,e,r);\r
+    static const XMLCh DefaultRelyingParty[] =  UNICODE_LITERAL_19(D,e,f,a,u,l,t,R,e,l,y,i,n,g,P,a,r,t,y);\r
     static const XMLCh fatal[] =                UNICODE_LITERAL_5(f,a,t,a,l);\r
     static const XMLCh _Handler[] =             UNICODE_LITERAL_7(H,a,n,d,l,e,r);\r
     static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);\r
@@ -347,8 +341,8 @@ XMLApplication::XMLApplication(
     const ServiceProvider* sp,\r
     const DOMElement* e,\r
     const XMLApplication* base\r
-    ) : m_sp(sp), m_base(base), m_metadata(NULL), m_trust(NULL), m_attrResolver(NULL),\r
-        m_credDefault(NULL), m_sessionInitDefault(NULL), m_acsDefault(NULL)\r
+    ) : m_sp(sp), m_base(base), m_metadata(NULL), m_trust(NULL), m_attrResolver(NULL), m_credResolver(NULL),\r
+        m_partyDefault(NULL), m_sessionInitDefault(NULL), m_acsDefault(NULL)\r
 {\r
 #ifdef _DEBUG\r
     xmltooling::NDC ndc("XMLApplication");\r
@@ -526,16 +520,31 @@ XMLApplication::XMLApplication(
             }\r
         }\r
 \r
-        // Finally, load credential mappings.\r
-        child = XMLHelper::getFirstChildElement(e,CredentialUse);\r
+        if (conf.isEnabled(SPConfig::Credentials)) {\r
+            child = XMLHelper::getFirstChildElement(e,_CredentialResolver);\r
+            if (child) {\r
+                auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
+                log.info("building CredentialResolver of type %s...",type.get());\r
+                try {\r
+                    m_credResolver = xmlConf.CredentialResolverManager.newPlugin(type.get(),child);\r
+                }\r
+                catch (exception& ex) {\r
+                    log.crit("error building CredentialResolver: %s", ex.what());\r
+                }\r
+            }\r
+        }\r
+\r
+\r
+        // Finally, load relying parties.\r
+        child = XMLHelper::getFirstChildElement(e,DefaultRelyingParty);\r
         if (child) {\r
-            m_credDefault=new DOMPropertySet();\r
-            m_credDefault->load(child,log,this);\r
+            m_partyDefault=new DOMPropertySet();\r
+            m_partyDefault->load(child,log,this);\r
             child = XMLHelper::getFirstChildElement(child,RelyingParty);\r
             while (child) {\r
-                DOMPropertySet* rp=new DOMPropertySet();\r
+                auto_ptr<DOMPropertySet> rp(new DOMPropertySet());\r
                 rp->load(child,log,this);\r
-                m_credMap[child->getAttributeNS(NULL,saml2::Attribute::NAME_ATTRIB_NAME)]=rp;\r
+                m_partyMap[child->getAttributeNS(NULL,saml2::Attribute::NAME_ATTRIB_NAME)]=rp.release();\r
                 child = XMLHelper::getNextSiblingElement(child,RelyingParty);\r
             }\r
         }\r
@@ -559,15 +568,14 @@ XMLApplication::XMLApplication(
 \r
 void XMLApplication::cleanup()\r
 {\r
-    for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup<Handler>());\r
-    \r
-    delete m_credDefault;\r
+    delete m_partyDefault;\r
 #ifdef HAVE_GOOD_STL\r
-    for_each(m_credMap.begin(),m_credMap.end(),cleanup_pair<xstring,PropertySet>());\r
+    for_each(m_partyMap.begin(),m_partyMap.end(),cleanup_pair<xstring,PropertySet>());\r
 #else\r
-    for_each(m_credMap.begin(),m_credMap.end(),cleanup_pair<const XMLCh*,PropertySet>());\r
+    for_each(m_partyMap.begin(),m_partyMap.end(),cleanup_pair<const XMLCh*,PropertySet>());\r
 #endif\r
-\r
+    for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup<Handler>());\r
+    delete m_credResolver;\r
     delete m_attrResolver;\r
     delete m_trust;\r
     delete m_metadata;\r
@@ -585,10 +593,11 @@ short XMLApplication::acceptNode(const DOMNode* node) const
         XMLString::equals(name,SingleLogoutService::LOCAL_NAME) ||\r
         XMLString::equals(name,ManageNameIDService::LOCAL_NAME) ||\r
         XMLString::equals(name,SessionInitiator) ||\r
-        XMLString::equals(name,CredentialUse) ||\r
+        XMLString::equals(name,DefaultRelyingParty) ||\r
         XMLString::equals(name,RelyingParty) ||\r
         XMLString::equals(name,_MetadataProvider) ||\r
         XMLString::equals(name,_TrustEngine) ||\r
+        XMLString::equals(name,_CredentialResolver) ||\r
         XMLString::equals(name,_AttributeResolver))\r
         return FILTER_REJECT;\r
 \r
@@ -643,29 +652,29 @@ const PropertySet* XMLApplication::getPropertySet(const char* name, const char*
     return m_base->getPropertySet(name,ns);\r
 }\r
 \r
-const PropertySet* XMLApplication::getCredentialUse(const EntityDescriptor* provider) const\r
+const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provider) const\r
 {\r
-    if (!m_credDefault && m_base)\r
-        return m_base->getCredentialUse(provider);\r
+    if (!m_partyDefault && m_base)\r
+        return m_base->getRelyingParty(provider);\r
     else if (!provider)\r
-        return m_credDefault;\r
+        return m_partyDefault;\r
         \r
 #ifdef HAVE_GOOD_STL\r
-    map<xstring,PropertySet*>::const_iterator i=m_credMap.find(provider->getEntityID());\r
-    if (i!=m_credMap.end())\r
+    map<xstring,PropertySet*>::const_iterator i=m_partyMap.find(provider->getEntityID());\r
+    if (i!=m_partyMap.end())\r
         return i->second;\r
     const EntitiesDescriptor* group=dynamic_cast<const EntitiesDescriptor*>(provider->getParent());\r
     while (group) {\r
         if (group->getName()) {\r
-            i=m_credMap.find(group->getName());\r
-            if (i!=m_credMap.end())\r
+            i=m_partyMap.find(group->getName());\r
+            if (i!=m_partyMap.end())\r
                 return i->second;\r
         }\r
         group=dynamic_cast<const EntitiesDescriptor*>(group->getParent());\r
     }\r
 #else\r
-    map<const XMLCh*,PropertySet*>::const_iterator i=m_credMap.begin();\r
-    for (; i!=m_credMap.end(); i++) {\r
+    map<const XMLCh*,PropertySet*>::const_iterator i=m_partyMap.begin();\r
+    for (; i!=m_partyMap.end(); i++) {\r
         if (XMLString::equals(i->first,provider->getId()))\r
             return i->second;\r
         const EntitiesDescriptor* group=dynamic_cast<const EntitiesDescriptor*>(provider->getParent());\r
@@ -676,7 +685,7 @@ const PropertySet* XMLApplication::getCredentialUse(const EntityDescriptor* prov
         }\r
     }\r
 #endif\r
-    return m_credDefault;\r
+    return m_partyDefault;\r
 }\r
 \r
 const Handler* XMLApplication::getDefaultSessionInitiator() const\r
@@ -734,7 +743,6 @@ short XMLConfigImpl::acceptNode(const DOMNode* node) const
     const XMLCh* name=node->getLocalName();\r
     if (XMLString::equals(name,Applications) ||\r
         XMLString::equals(name,_ArtifactMap) ||\r
-        XMLString::equals(name,Credentials) ||\r
         XMLString::equals(name,Extensions::LOCAL_NAME) ||\r
         XMLString::equals(name,Implementation) ||\r
         XMLString::equals(name,Listener) ||\r
@@ -969,27 +977,6 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o
             }\r
         }\r
         \r
-        // Now we load the credentials map.\r
-        if (conf.isEnabled(SPConfig::Credentials)) {\r
-            child = XMLHelper::getLastChildElement(e,Credentials);\r
-            if (child) {\r
-                // Step down and process resolvers.\r
-                child=XMLHelper::getFirstChildElement(child);\r
-                while (child) {\r
-                    auto_ptr_char id(child->getAttributeNS(NULL,_id));\r
-                    auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
-                    try {\r
-                        CredentialResolver* cr=xmlConf.CredentialResolverManager.newPlugin(type.get(),child);\r
-                        m_credResolverMap[id.get()] = cr;\r
-                    }\r
-                    catch (exception& ex) {\r
-                        log.crit("failed to instantiate CredentialResolver (%s): %s", id.get(), ex.what());\r
-                    }\r
-                    child = XMLHelper::getNextSiblingElement(child);\r
-                }\r
-            }\r
-        }\r
-\r
         // Load security policies.\r
         child = XMLHelper::getLastChildElement(e,SecurityPolicies);\r
         if (child) {\r
@@ -1053,7 +1040,6 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o
 XMLConfigImpl::~XMLConfigImpl()\r
 {\r
     for_each(m_appmap.begin(),m_appmap.end(),cleanup_pair<string,Application>());\r
-    for_each(m_credResolverMap.begin(),m_credResolverMap.end(),cleanup_pair<string,CredentialResolver>());\r
     for (map< string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::iterator i=m_policyMap.begin(); i!=m_policyMap.end(); ++i) {\r
         delete i->second.first;\r
         for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup<SecurityPolicyRule>());\r
index 2330e7c..ecbc9e9 100644 (file)
 #include "security/PKIXTrustEngine.h"
 
 #include <saml/saml2/metadata/Metadata.h>
+#include <saml/saml2/metadata/MetadataCredentialCriteria.h>
+#include <saml/saml2/metadata/ObservableMetadataProvider.h>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/security/AbstractPKIXTrustEngine.h>
+#include <xmltooling/security/KeyInfoResolver.h>
+#include <xmltooling/security/X509Credential.h>
 
 using namespace shibsp;
 using namespace opensaml::saml2md;
@@ -35,27 +39,86 @@ using namespace xmltooling;
 using namespace std;
 
 namespace shibsp {
-    /**
-     * Adapter between shibmd:KeyAuthority extension and the PKIXValidationInfoIterator interface. 
-     */
-    class SHIBSP_API MetadataPKIXIterator : public AbstractPKIXTrustEngine::PKIXValidationInfoIterator
+
+    class SHIBSP_DLLLOCAL PKIXTrustEngine : public AbstractPKIXTrustEngine, public ObservableMetadataProvider::Observer
     {
-        const XMLObject* m_obj;
-        const Extensions* m_extBlock;
-        const KeyAuthority* m_current;
-        vector<XMLObject*>::const_iterator m_iter;
-        
-        bool m_certsOwned;
-        vector<XSECCryptoX509*> m_certs;
-        vector<XSECCryptoX509CRL*> m_crls;
+    public:
+        PKIXTrustEngine(const DOMElement* e=NULL) : AbstractPKIXTrustEngine(e), m_credLock(RWLock::create()) {
+        }
+        virtual ~PKIXTrustEngine() {
+            delete m_credLock;
+        }
         
+        AbstractPKIXTrustEngine::PKIXValidationInfoIterator* getPKIXValidationInfoIterator(
+            const CredentialResolver& pkixSource, CredentialCriteria* criteria=NULL
+            ) const;
+
+        void onEvent(const MetadataProvider& metadata) const {
+            m_credLock->wrlock();
+            m_credentialMap[&metadata].clear();
+            m_credLock->unlock();
+        }
+
+        const KeyInfoResolver* getKeyInfoResolver() const {
+            return m_keyInfoResolver ? m_keyInfoResolver : XMLToolingConfig::getConfig().getKeyInfoResolver();
+        }
+
+    private:
+        friend class SHIBSP_DLLLOCAL MetadataPKIXIterator;
+        mutable RWLock* m_credLock;
+        typedef map< const KeyAuthority*,vector<X509Credential*> > credmap_t;
+        mutable map<const MetadataProvider*,credmap_t> m_credentialMap;
+    };
+    
+    SHIBSP_DLLLOCAL PluginManager<TrustEngine,const DOMElement*>::Factory PKIXTrustEngineFactory;
+
+    TrustEngine* SHIBSP_DLLLOCAL PKIXTrustEngineFactory(const DOMElement* const & e)
+    {
+        return new PKIXTrustEngine(e);
+    }
+
+    class SHIBSP_DLLLOCAL MetadataPKIXIterator : public AbstractPKIXTrustEngine::PKIXValidationInfoIterator
+    {
     public:
-        MetadataPKIXIterator(const RoleDescriptor& role, const KeyResolver& keyResolver)
-            : PKIXValidationInfoIterator(keyResolver), m_obj(role.getParent()), m_extBlock(NULL), m_current(NULL), m_certsOwned(false) {
+        MetadataPKIXIterator(const PKIXTrustEngine& engine, const MetadataProvider& pkixSource, MetadataCredentialCriteria& criteria)
+                : m_caching(false), m_engine(engine), m_obj(criteria.getRole().getParent()), m_extBlock(NULL), m_current(NULL) {
+            m_engine.m_credLock->rdlock();
+
+            const ObservableMetadataProvider* observable = dynamic_cast<const ObservableMetadataProvider*>(&pkixSource);
+
+            // While holding read lock, see if this metadata plugin has been seen before.
+            m_credCache = m_engine.m_credentialMap.find(&pkixSource);
+            if (m_credCache==m_engine.m_credentialMap.end()) {
+
+                // We need to elevate the lock and retry.
+                m_engine.m_credLock->unlock();
+                m_engine.m_credLock->wrlock();
+                m_credCache = m_engine.m_credentialMap.find(&pkixSource);
+                if (m_credCache==m_engine.m_credentialMap.end()) {
+
+                    // It's still brand new, so see if we can hook it for cache activation.
+                    if (observable)
+                        observable->addObserver(&m_engine);
+
+                    // Prime the map reference with an empty credential map.
+                    m_credCache = m_engine.m_credentialMap.insert(make_pair(&pkixSource,PKIXTrustEngine::credmap_t())).first;
+                    
+                    // Downgrade the lock.
+                    // We don't have to recheck because we never erase the master map entry entirely, even on changes.
+                    m_engine.m_credLock->unlock();
+                    m_engine.m_credLock->rdlock();
+                }
+            }
+            
+            if (observable) {
+                // We've hooked the metadata for changes, and we know we can cache against it.
+                m_caching = true;
+            }
         }
 
         virtual ~MetadataPKIXIterator() {
-            clear();
+            m_engine.m_credLock->unlock();
+            for_each(m_ownedCreds.begin(), m_ownedCreds.end(), xmltooling::cleanup<Credential>());
         }
 
         bool next();
@@ -75,33 +138,17 @@ namespace shibsp {
     
     private:
         void populate();
-
-        void clear() {
-            if (m_certsOwned)
-                for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup<XSECCryptoX509>());
-            m_certs.clear();
-            for_each(m_crls.begin(), m_crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
-            m_crls.clear();
-        }
-    };
-
-    class SHIBSP_DLLLOCAL PKIXTrustEngine : public AbstractPKIXTrustEngine
-    {
-    public:
-        PKIXTrustEngine(const DOMElement* e=NULL) : AbstractPKIXTrustEngine(e) {}
-        virtual ~PKIXTrustEngine() {}
-        
-        AbstractPKIXTrustEngine::PKIXValidationInfoIterator* getPKIXValidationInfoIterator(
-            const KeyInfoSource& pkixSource, const KeyResolver& keyResolver
-            ) const;
+        bool m_caching;
+        const PKIXTrustEngine& m_engine;
+        map<const MetadataProvider*,PKIXTrustEngine::credmap_t>::iterator m_credCache;
+        const XMLObject* m_obj;
+        const Extensions* m_extBlock;
+        const KeyAuthority* m_current;
+        vector<XMLObject*>::const_iterator m_iter;
+        vector<XSECCryptoX509*> m_certs;
+        vector<XSECCryptoX509CRL*> m_crls;
+        vector<X509Credential*> m_ownedCreds;
     };
-    
-    SHIBSP_DLLLOCAL PluginManager<TrustEngine,const DOMElement*>::Factory PKIXTrustEngineFactory;
-
-    TrustEngine* SHIBSP_DLLLOCAL PKIXTrustEngineFactory(const DOMElement* const & e)
-    {
-        return new PKIXTrustEngine(e);
-    }
 };
 
 void shibsp::registerPKIXTrustEngine()
@@ -110,10 +157,16 @@ void shibsp::registerPKIXTrustEngine()
 }
 
 AbstractPKIXTrustEngine::PKIXValidationInfoIterator* PKIXTrustEngine::getPKIXValidationInfoIterator(
-    const KeyInfoSource& pkixSource, const KeyResolver& keyResolver
+    const CredentialResolver& pkixSource, CredentialCriteria* criteria
     ) const
 {
-    return new MetadataPKIXIterator(dynamic_cast<const RoleDescriptor&>(pkixSource),keyResolver);
+    // Make sure these are metadata objects.
+    const MetadataProvider& metadata = dynamic_cast<const MetadataProvider&>(pkixSource);
+    MetadataCredentialCriteria* metacrit = dynamic_cast<MetadataCredentialCriteria*>(criteria);
+    if (!metacrit)
+        throw MetadataException("Cannot obtain PKIX information without a MetadataCredentialCriteria object.");
+
+    return new MetadataPKIXIterator(*this, metadata,*metacrit);
 }
 
 bool MetadataPKIXIterator::next()
@@ -165,31 +218,53 @@ bool MetadataPKIXIterator::next()
 void MetadataPKIXIterator::populate()
 {
     // Dump anything old.
-    clear();
+    m_certs.clear();
+    m_crls.clear();
+    for_each(m_ownedCreds.begin(), m_ownedCreds.end(), xmltooling::cleanup<Credential>());
 
-    // We have to aggregate the resolution results.
-    KeyResolver::ResolvedCertificates certs;
-    XSECCryptoX509CRL* crl;
+    if (m_caching) {
+        // We're holding a read lock. Search for "resolved" creds.
+        PKIXTrustEngine::credmap_t::iterator cached = m_credCache->second.find(m_current);
+        if (cached!=m_credCache->second.end()) {
+            // 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());
+            }
+        }
+    }
+
+    // We're either not caching or didn't find the results we need, so we have to resolve them.
     const vector<KeyInfo*>& keyInfos = m_current->getKeyInfos();
     for (vector<KeyInfo*>::const_iterator k = keyInfos.begin(); k!=keyInfos.end(); ++k) {
-        vector<XSECCryptoX509*>::size_type count = m_keyResolver.resolveCertificates(*k,certs); 
-        if (count > 0) {
-            // Transfer certificates out of wrapper. 
-            bool own = certs.release(m_certs);
-            if (!m_certs.empty() && own != m_certsOwned) {
-                // Ugh. We have a mashup of "owned" and "unowned".
-                // The ones we just added need to be removed and perhaps freed.
-                do {
-                    if (own)
-                        delete m_certs.back();
-                    m_certs.pop_back();
-                } while (--count > 0);
-            }
-            m_certsOwned = own;
+        auto_ptr<Credential> cred (m_engine.getKeyInfoResolver()->resolve(*k));
+        X509Credential* xcred = dynamic_cast<X509Credential*>(cred.get());
+        if (xcred) {
+            m_ownedCreds.push_back(xcred);
+            cred.release();
+        }
+    }
+
+    // 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());
+    }
+
+    // As a last step, if we're caching, try and elevate to a write lock for cache insertion.
+    if (m_caching) {
+        m_engine.m_credLock->unlock();
+        m_engine.m_credLock->wrlock();
+        PKIXTrustEngine::credmap_t::iterator cached = m_credCache->second.find(m_current);
+        if (m_credCache->second.count(m_current)==0) {
+            // Transfer objects into cache.
+            m_credCache->second[m_current] = m_ownedCreds;
+            m_ownedCreds.clear();
         }
+        m_engine.m_credLock->unlock();
+        m_engine.m_credLock->rdlock();
 
-        crl = m_keyResolver.resolveCRL(*k);
-        if (crl)
-            m_crls.push_back(crl);
+        // In theory we could have lost the objects but that shouldn't be possible
+        // since the metadata itself is locked and shouldn't change behind us.
     }
 }
index bc9a103..3206d66 100644 (file)
@@ -161,6 +161,7 @@ int main(int argc,char* argv[])
 \r
         SecurityPolicy policy;\r
         shibsp::SOAPClient soaper(*app,policy);\r
+        MetadataCredentialCriteria mcc(*AA);\r
 \r
         if (ver == v20) {\r
             auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);\r
@@ -183,7 +184,7 @@ int main(int argc,char* argv[])
                     nameid->setNameQualifier(domain.get());\r
                     iss->setName(issuer.get());\r
                     SAML2SOAPClient client(soaper);\r
-                    client.sendSAML(query, *AA, loc.get());\r
+                    client.sendSAML(query, mcc, loc.get());\r
                     srt = client.receiveSAML();\r
                 }\r
                 catch (exception& ex) {\r
@@ -226,7 +227,7 @@ int main(int argc,char* argv[])
                     query->setResource(issuer.get());\r
                     request->setMinorVersion(ver==v11 ? 1 : 0);\r
                     SAML1SOAPClient client(soaper);\r
-                    client.sendSAML(request, *AA, loc.get());\r
+                    client.sendSAML(request, mcc, loc.get());\r
                     response = client.receiveSAML();\r
                 }\r
                 catch (exception& ex) {\r