https://bugs.internet2.edu/jira/browse/CPPXT-24
authorcantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Sat, 8 Nov 2008 02:31:13 +0000 (02:31 +0000)
committercantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Sat, 8 Nov 2008 02:31:13 +0000 (02:31 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/branches/REL_1@540 de75baf8-a10c-0410-a50a-987c0e22f00f

xmltooling/security/SecurityHelper.h
xmltooling/security/impl/FilesystemCredentialResolver.cpp
xmltooling/security/impl/SecurityHelper.cpp
xmltooling/xmltooling-lite.vcproj

index 4c0bbf9..f2a8839 100644 (file)
@@ -38,6 +38,14 @@ namespace xmltooling {
     {
     public:
         /**
+         * Access a file to try and guess the encoding format used.
+         *
+         * @param pathname  path to file
+         * @return  constant identifying encoding format
+         */
+        static const char* guessEncodingFormat(const char* pathname);
+
+        /**
          * Loads a private key from a local file.
          *
          * @param pathname  path to file containing key
index a887863..d9bcd86 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2007 Internet2
+ *  Copyright 2001-2008 Internet2
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
 #include "security/CredentialResolver.h"
 #include "security/KeyInfoResolver.h"
 #include "security/OpenSSLCredential.h"
-#include "security/OpenSSLCryptoX509CRL.h"
+#include "security/SecurityHelper.h"
 #include "util/NDC.h"
 #include "util/PathResolver.h"
 #include "util/XMLHelper.h"
 #include <openssl/pkcs12.h>
 #include <xercesc/util/XMLUniDefs.hpp>
 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
-#include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
-#include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
 
 using namespace xmlsignature;
 using namespace xmltooling::logging;
 using namespace xmltooling;
 using namespace std;
 
-// OpenSSL password callback...
-static int passwd_callback(char* buf, int len, int verify, void* passwd)
-{
-    if(!verify)
+namespace xmltooling {
+
+    // The ManagedResource classes handle memory management, loading of the files
+    // and staleness detection. A copy of the active objects is always stored in
+    // these instances.
+
+    class XMLTOOL_DLLLOCAL ManagedResource {
+    protected:
+        ManagedResource() : local(true), reloadChanges(true), filestamp(0), reloadInterval(0) {}
+        ~ManagedResource() {}
+
+        SOAPTransport* getTransport() {
+            SOAPTransport::Address addr("FilesystemCredentialResolver", source.c_str(), source.c_str());
+            string scheme(addr.m_endpoint, strchr(addr.m_endpoint,':') - addr.m_endpoint);
+            return XMLToolingConfig::getConfig().SOAPTransportManager.newPlugin(scheme.c_str(), addr);
+        }
+
+    public:
+        bool stale(Category& log, RWLock* lock=NULL) {
+            if (local) {
+#ifdef WIN32
+                struct _stat stat_buf;
+                if (_stat(source.c_str(), &stat_buf) != 0)
+                    return false;
+#else
+                struct stat stat_buf;
+                if (stat(source.c_str(), &stat_buf) != 0)
+                    return false;
+#endif
+                if (filestamp >= stat_buf.st_mtime)
+                    return false;
+
+                // If necessary, elevate lock and recheck.
+                if (lock) {
+                    log.debug("timestamp of local resource changed, elevating to a write lock");
+                    lock->unlock();
+                    lock->wrlock();
+                    if (filestamp >= stat_buf.st_mtime) {
+                        // Somebody else handled it, just downgrade.
+                        log.debug("update of local resource handled by another thread, downgrading lock");
+                        lock->unlock();
+                        lock->rdlock();
+                        return false;
+                    }
+                }
+
+                // Update the timestamp regardless. No point in repeatedly trying.
+                filestamp = stat_buf.st_mtime;
+                log.info("change detected, reloading local resource...");
+            }
+            else {
+                time_t now = time(NULL);
+
+                // Time to reload?
+                if (now - filestamp < reloadInterval)
+                    return false;
+
+                // If necessary, elevate lock and recheck.
+                if (lock) {
+                    log.debug("reload interval for remote resource elapsed, elevating to a write lock");
+                    lock->unlock();
+                    lock->wrlock();
+                    if (now - filestamp < reloadInterval) {
+                        // Somebody else handled it, just downgrade.
+                        log.debug("update of remote resource handled by another thread, downgrading lock");
+                        lock->unlock();
+                        lock->rdlock();
+                        return false;
+                    }
+                }
+
+                filestamp = now;
+                log.info("reloading remote resource...");
+            }
+            return true;
+        }
+
+        bool local,reloadChanges;
+        string format,source,backing;
+        time_t filestamp,reloadInterval;
+    };
+
+    class XMLTOOL_DLLLOCAL ManagedKey : public ManagedResource {
+    public:
+        ManagedKey() : key(NULL) {}
+        ~ManagedKey() { delete key; }
+        void load(Category& log, const char* password) {
+            if (source.empty())
+                return;
+            XSECCryptoKey* nkey=NULL;
+            if (local) {
+                nkey = SecurityHelper::loadKeyFromFile(source.c_str(), format.c_str(), password);
+            }
+            else {
+                auto_ptr<SOAPTransport> t(getTransport());
+                log.info("loading private key from URL (%s)", source.c_str());
+                nkey = SecurityHelper::loadKeyFromURL(*t.get(), backing.c_str(), format.c_str(), password);
+            }
+            delete key;
+            key = nkey;
+            if (format.empty())
+                format = SecurityHelper::guessEncodingFormat(local ? source.c_str() : backing.c_str());
+        }
+
+        XSECCryptoKey* key;
+    };
+
+    class XMLTOOL_DLLLOCAL ManagedCert : public ManagedResource {
+    public:
+        ManagedCert() {}
+        ~ManagedCert() { for_each(certs.begin(), certs.end(), xmltooling::cleanup<XSECCryptoX509>()); }
+        void load(Category& log, const char* password) {
+            if (source.empty())
+                return;
+            vector<XSECCryptoX509*> ncerts;
+            if (local) {
+                SecurityHelper::loadCertificatesFromFile(ncerts, source.c_str(), format.c_str(), password);
+            }
+            else {
+                auto_ptr<SOAPTransport> t(getTransport());
+                log.info("loading certificate(s) from URL (%s)", source.c_str());
+                SecurityHelper::loadCertificatesFromURL(ncerts, *t.get(), backing.c_str(), format.c_str(), password);
+            }
+            for_each(certs.begin(), certs.end(), xmltooling::cleanup<XSECCryptoX509>());
+            certs = ncerts;
+            if (format.empty())
+                format = SecurityHelper::guessEncodingFormat(local ? source.c_str() : backing.c_str());
+        }
+        vector<XSECCryptoX509*> certs;
+    };
+
+    class XMLTOOL_DLLLOCAL ManagedCRL : public ManagedResource {
+    public:
+        ManagedCRL() {}
+        ~ManagedCRL() { for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>()); }
+        void load(Category& log) {
+            if (source.empty())
+                return;
+            vector<XSECCryptoX509CRL*> ncrls;
+            if (local) {
+                SecurityHelper::loadCRLsFromFile(ncrls, source.c_str(), format.c_str());
+            }
+            else {
+                auto_ptr<SOAPTransport> t(getTransport());
+                log.info("loading CRL(s) from URL (%s)", source.c_str());
+                SecurityHelper::loadCRLsFromURL(ncrls, *t.get(), backing.c_str(), format.c_str());
+            }
+            for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
+            crls = ncrls;
+            if (format.empty())
+                format = SecurityHelper::guessEncodingFormat(local ? source.c_str() : backing.c_str());
+        }
+        vector<XSECCryptoX509CRL*> crls;
+    };
+
+    class XMLTOOL_DLLLOCAL FilesystemCredential;
+    class XMLTOOL_DLLLOCAL FilesystemCredentialResolver : public CredentialResolver
     {
-        if(passwd && len > strlen(reinterpret_cast<char*>(passwd)))
-        {
-            strcpy(buf,reinterpret_cast<char*>(passwd));
-            return strlen(buf);
+        friend class XMLTOOL_DLLLOCAL FilesystemCredential;
+    public:
+        FilesystemCredentialResolver(const DOMElement* e);
+        virtual ~FilesystemCredentialResolver();
+
+        Lockable* lock();
+        void unlock() {
+            m_lock->unlock();
         }
-    }
-    return 0;
-}
 
-namespace xmltooling {
+        const Credential* resolve(const CredentialCriteria* criteria=NULL) const;
+
+        virtual vector<const Credential*>::size_type resolve(
+            vector<const Credential*>& results, const CredentialCriteria* criteria=NULL
+            ) const;
+
+    private:
+        Credential* getCredential();
+
+        RWLock* m_lock;
+        Credential* m_credential;
+        string m_keypass,m_certpass;
+        unsigned int m_keyinfomask,m_usage;
+        vector<string> m_keynames;
+
+        ManagedKey m_key;
+        vector<ManagedCert> m_certs;
+        vector<ManagedCRL> m_crls;
+    };
 
 #if defined (_MSC_VER)
     #pragma warning( push )
     #pragma warning( disable : 4250 )
 #endif
 
-    class XMLTOOL_DLLLOCAL FilesystemCredentialResolver;
     class XMLTOOL_DLLLOCAL FilesystemCredential : public OpenSSLCredential, public BasicX509Credential
     {
     public:
         FilesystemCredential(
             FilesystemCredentialResolver* resolver, XSECCryptoKey* key, const vector<XSECCryptoX509*>& xseccerts, const vector<XSECCryptoX509CRL*>& crls
-            ) : BasicX509Credential(key, xseccerts, crls), m_resolver(resolver), m_usage(UNSPECIFIED_CREDENTIAL) {
+            ) : BasicX509Credential(key ? key : (xseccerts.empty() ? NULL : xseccerts.front()->clonePublicKey()), xseccerts, crls), m_resolver(resolver) {
             extract();
+            m_keyNames.insert(m_resolver->m_keynames.begin(), m_resolver->m_keynames.end());
         }
+
         virtual ~FilesystemCredential() {
         }
 
         unsigned int getUsage() const {
-            return m_usage;
-        }
-
-        void setUsage(const XMLCh* usage) {
-            if (usage && *usage) {
-                auto_ptr_char u(usage);
-                if (!strcmp(u.get(), "signing"))
-                    m_usage = SIGNING_CREDENTIAL | TLS_CREDENTIAL;
-                else if (!strcmp(u.get(), "TLS"))
-                    m_usage = TLS_CREDENTIAL;
-                else if (!strcmp(u.get(), "encryption"))
-                    m_usage = ENCRYPTION_CREDENTIAL;
-            }
+            return m_resolver->m_usage;
         }
 
-        void addKeyNames(const DOMElement* e);
-
         void initKeyInfo(unsigned int types=0) {
             BasicX509Credential::initKeyInfo(types);
         }
@@ -104,68 +261,24 @@ namespace xmltooling {
 
     private:
         FilesystemCredentialResolver* m_resolver;
-        unsigned int m_usage;
     };
 
 #if defined (_MSC_VER)
     #pragma warning( pop )
 #endif
 
-    class XMLTOOL_DLLLOCAL FilesystemCredentialResolver : public CredentialResolver
-    {
-    public:
-        FilesystemCredentialResolver(const DOMElement* e);
-        virtual ~FilesystemCredentialResolver() {
-            delete m_credential;
-            for_each(m_certs.begin(),m_certs.end(),X509_free);
-        }
-
-        Lockable* lock() { return this; }
-        void unlock() {}
-
-        const Credential* resolve(const CredentialCriteria* criteria=NULL) const {
-            return (criteria ? (criteria->matches(*m_credential) ? m_credential : NULL) : m_credential);
-        }
-
-        virtual vector<const Credential*>::size_type resolve(
-            vector<const Credential*>& results, const CredentialCriteria* criteria=NULL
-            ) const {
-            if (!criteria || criteria->matches(*m_credential)) {
-                results.push_back(m_credential);
-                return 1;
-            }
-            return 0;
-        }
-
-        void attach(SSL_CTX* ctx) const;
-
-    private:
-        XSECCryptoKey* loadKey();
-        void loadCRLs(vector<XSECCryptoX509CRL*>& crls);
-
-        enum format_t { PEM=SSL_FILETYPE_PEM, DER=SSL_FILETYPE_ASN1, _PKCS12, UNKNOWN };
-
-        format_t getEncodingFormat(BIO* in) const;
-        string formatToString(format_t format) const;
-        format_t xmlFormatToFormat(const XMLCh* format_xml) const;
-
-        format_t m_keyformat,m_crlformat;
-        string m_keypath,m_keypass,m_crlpath;
-        vector<X509*> m_certs;
-        FilesystemCredential* m_credential;
-    };
-
     CredentialResolver* XMLTOOL_DLLLOCAL FilesystemCredentialResolverFactory(const DOMElement* const & e)
     {
         return new FilesystemCredentialResolver(e);
     }
 
-    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);
+    static const XMLCh backingFilePath[] =  UNICODE_LITERAL_15(b,a,c,k,i,n,g,F,i,l,e,P,a,t,h);
+    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);
     static const XMLCh CAPath[] =           UNICODE_LITERAL_6(C,A,P,a,t,h);
     static const XMLCh Certificate[] =      UNICODE_LITERAL_11(C,e,r,t,i,f,i,c,a,t,e);
     static const XMLCh _certificate[] =     UNICODE_LITERAL_11(c,e,r,t,i,f,i,c,a,t,e);
     static const XMLCh CRL[] =              UNICODE_LITERAL_3(C,R,L);
-    static const XMLCh format[] =           UNICODE_LITERAL_6(f,o,r,m,a,t);
+    static const XMLCh _format[] =          UNICODE_LITERAL_6(f,o,r,m,a,t);
     static const XMLCh Key[] =              UNICODE_LITERAL_3(K,e,y);
     static const XMLCh _key[] =             UNICODE_LITERAL_3(k,e,y);
     static const XMLCh keyInfoMask[] =      UNICODE_LITERAL_11(k,e,y,I,n,f,o,M,a,s,k);
@@ -173,10 +286,14 @@ namespace xmltooling {
     static const XMLCh Name[] =             UNICODE_LITERAL_4(N,a,m,e);
     static const XMLCh password[] =         UNICODE_LITERAL_8(p,a,s,s,w,o,r,d);
     static const XMLCh Path[] =             UNICODE_LITERAL_4(P,a,t,h);
+    static const XMLCh _reloadChanges[] =   UNICODE_LITERAL_13(r,e,l,o,a,d,C,h,a,n,g,e,s);
+    static const XMLCh _reloadInterval[] =  UNICODE_LITERAL_14(r,e,l,o,a,d,I,n,t,e,r,v,a,l);
+    static const XMLCh _URL[] =             UNICODE_LITERAL_3(U,R,L);
     static const XMLCh _use[] =             UNICODE_LITERAL_3(u,s,e);
 };
 
-FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e) : m_credential(NULL)
+FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e)
+    : m_lock(NULL), m_credential(NULL), m_usage(Credential::UNSPECIFIED_CREDENTIAL)
 {
 #ifdef _DEBUG
     NDC ndc("FilesystemCredentialResolver");
@@ -184,13 +301,13 @@ FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e)
     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER);
 
     // Default to disable X509IssuerSerial due to schema validation issues.
-    unsigned int mask =
+    m_keyinfomask =
         Credential::KEYINFO_KEY_NAME |
         Credential::KEYINFO_KEY_VALUE |
         X509Credential::KEYINFO_X509_CERTIFICATE |
         X509Credential::KEYINFO_X509_SUBJECTNAME;
     if (e && e->hasAttributeNS(NULL,keyInfoMask))
-        mask = XMLString::parseInt(e->getAttributeNS(NULL,keyInfoMask));
+        m_keyinfomask = XMLString::parseInt(e->getAttributeNS(NULL,keyInfoMask));
 
     if (e && (e->hasAttributeNS(NULL,_certificate) || e->hasAttributeNS(NULL,_key))) {
         // Dummy up a simple file resolver config using these attributes.
@@ -221,561 +338,398 @@ FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e)
         e = dummy;  // reset "root" to the dummy config element
     }
 
-    const DOMElement* root=e;
-    const XMLCh* usage = root->getAttributeNS(NULL,_use);
-
-    XSECCryptoKey* key=NULL;
-    vector<XSECCryptoX509*> xseccerts;
-    vector<XSECCryptoX509CRL*> crls;
+    const XMLCh* prop;
+    const DOMElement* root = e;
 
-    format_t fformat;
-    const XMLCh* format_xml=NULL;
-    BIO* in = NULL;
+    // Save off usage flags.
+    const XMLCh* usage = root->getAttributeNS(NULL,_use);
+    if (usage && *usage) {
+        auto_ptr_char u(usage);
+        if (!strcmp(u.get(), "signing"))
+            m_usage = Credential::SIGNING_CREDENTIAL | Credential::TLS_CREDENTIAL;
+        else if (!strcmp(u.get(), "TLS"))
+            m_usage = Credential::TLS_CREDENTIAL;
+        else if (!strcmp(u.get(), "encryption"))
+            m_usage = Credential::ENCRYPTION_CREDENTIAL;
+    }
 
-    // Move to Key
-    const DOMElement* keynode=XMLHelper::getFirstChildElement(root,Key);
+    // Move to Key.
+    const DOMElement* keynode = XMLHelper::getFirstChildElement(root,Key);
     if (keynode) {
-        // Get raw format attrib value, but defer processing til later since may need to
-        // determine format dynamically, and we need the Path for that.
-        format_xml=keynode->getAttributeNS(NULL,format);
-
-        const XMLCh* password_xml=keynode->getAttributeNS(NULL,password);
-        if (password_xml) {
-            auto_ptr_char kp(password_xml);
-            m_keypass=kp.get();
-        }
-
-        e=XMLHelper::getFirstChildElement(keynode,Path);
-        if (e && e->hasChildNodes()) {
-            const XMLCh* s=e->getFirstChild()->getNodeValue();
-            auto_ptr_char kpath(s);
-            m_keypath = kpath.get();
-            XMLToolingConfig::getConfig().getPathResolver()->resolve(m_keypath, PathResolver::XMLTOOLING_CFG_FILE);
-#ifdef WIN32
-            struct _stat stat_buf;
-            if (_stat(m_keypath.c_str(), &stat_buf) != 0)
-#else
-            struct stat stat_buf;
-            if (stat(m_keypath.c_str(), &stat_buf) != 0)
-#endif
-            {
-                log.error("key file (%s) can't be opened", m_keypath.c_str());
-                throw XMLSecurityException("FilesystemCredentialResolver can't access key file ($1)",params(1,m_keypath.c_str()));
-            }
+        prop = keynode->getAttributeNS(NULL,_format);
+        if (prop && *prop) {
+            auto_ptr_char f(prop);
+            m_key.format = f.get();
+        }
+
+        prop = keynode->getAttributeNS(NULL,password);
+        if (prop && *prop) {
+            auto_ptr_char kp(prop);
+            m_keypass = kp.get();
+        }
+
+        if ((e=XMLHelper::getFirstChildElement(keynode,Path)) && e->hasChildNodes()) {
+            prop = e->getFirstChild()->getNodeValue();
+            auto_ptr_char kpath(prop);
+            m_key.source = kpath.get();
+            XMLToolingConfig::getConfig().getPathResolver()->resolve(m_key.source, PathResolver::XMLTOOLING_CFG_FILE);
+            m_key.local = true;
+            prop = e->getAttributeNS(NULL,_reloadChanges);
+            if (prop && (*prop==chLatin_f) || (*prop==chDigit_0))
+                m_key.reloadChanges = false;
+        }
+        else if ((e=XMLHelper::getFirstChildElement(keynode,_URL)) && e->hasChildNodes()) {
+            prop = e->getFirstChild()->getNodeValue();
+            auto_ptr_char kpath(prop);
+            m_key.source = kpath.get();
+            m_key.local = false;
+            prop = e->getAttributeNS(NULL,backingFilePath);
+            if (!prop || !*prop)
+                throw XMLSecurityException("FilesystemCredentialResolver can't access key, backingFilePath missing from URL element.");
+            auto_ptr_char b(prop);
+            m_key.backing = b.get();
+            XMLToolingConfig::getConfig().getPathResolver()->resolve(m_key.backing, PathResolver::XMLTOOLING_RUN_FILE);
+            prop = e->getAttributeNS(NULL,_reloadInterval);
+            if (prop && *prop)
+                m_key.reloadInterval = XMLString::parseInt(prop);
         }
         else {
-            log.error("Path element missing inside Key element");
-            throw XMLSecurityException("FilesystemCredentialResolver can't access key file, no Path element specified.");
+            log.error("Path/URL element missing inside Key element");
+            throw XMLSecurityException("FilesystemCredentialResolver can't access key, no Path or URL element specified.");
         }
 
-        // Determine the key encoding format dynamically, if not explicitly specified
-        if (format_xml && *format_xml) {
-            fformat = xmlFormatToFormat(format_xml);
-            if (fformat != UNKNOWN) {
-                m_keyformat = fformat;
-            }
-            else {
-                auto_ptr_char unknown(format_xml);
-                log.error("configuration specifies unknown key encoding format (%s)", unknown.get());
-                throw XMLSecurityException("FilesystemCredentialResolver configuration contains unknown key encoding format ($1)",params(1,unknown.get()));
+        e = XMLHelper::getFirstChildElement(keynode, Name);
+        while (e) {
+            if (e->hasChildNodes()) {
+                auto_ptr_char n(e->getFirstChild()->getNodeValue());
+                if (n.get() && *n.get())
+                    m_keynames.push_back(n.get());
             }
+            e = XMLHelper::getNextSiblingElement(e, Name);
         }
-        else {
-            in=BIO_new(BIO_s_file_internal());
-            if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
-                m_keyformat = getEncodingFormat(in);
-                log.debug("key encoding format for (%s) dynamically resolved as (%s)", m_keypath.c_str(), formatToString(m_keyformat).c_str());
-            }
-            else {
-                log.error("key file (%s) can't be read to determine encoding format", m_keypath.c_str());
-                throw XMLSecurityException("FilesystemCredentialResolver can't read key file ($1) to determine encoding format",params(1,m_keypath.c_str()));
-            }
-            if (in)
-                BIO_free(in);
-            in = NULL;
-        }
-
-        // Load the key.
-        key = loadKey();
     }
 
     // Check for CRL.
-    const DOMElement* crlnode=XMLHelper::getFirstChildElement(root,CRL);
+    const DOMElement* crlnode = XMLHelper::getFirstChildElement(root,CRL);
     if (crlnode) {
-        // Get raw format attrib value, but defer processing til later since may need to
-        // determine format dynamically, and we need the Path for that.
-        format_xml=crlnode->getAttributeNS(NULL,format);
-
-        e=XMLHelper::getFirstChildElement(crlnode,Path);
-        if (e && e->hasChildNodes()) {
-            const XMLCh* s=e->getFirstChild()->getNodeValue();
-            auto_ptr_char kpath(s);
-            m_crlpath=kpath.get();
-            XMLToolingConfig::getConfig().getPathResolver()->resolve(m_crlpath, PathResolver::XMLTOOLING_CFG_FILE);
-#ifdef WIN32
-            struct _stat stat_buf;
-            if (_stat(m_crlpath.c_str(), &stat_buf) != 0)
-#else
-            struct stat stat_buf;
-            if (stat(m_crlpath.c_str(), &stat_buf) != 0)
-#endif
-            {
-                log.error("CRL file (%s) can't be opened", m_crlpath.c_str());
-                throw XMLSecurityException("FilesystemCredentialResolver can't access CRL file ($1)",params(1,m_crlpath.c_str()));
+        const XMLCh* crlformat = crlnode->getAttributeNS(NULL,_format);
+        e = XMLHelper::getFirstChildElement(crlnode,Path);
+        while (e) {
+            if (e->hasChildNodes()) {
+                m_crls.push_back(ManagedCRL());
+                ManagedResource& crl = m_crls.back();
+                if (crlformat && *crlformat) {
+                    auto_ptr_char f(crlformat);
+                    crl.format = f.get();
+                }
+                prop = e->getFirstChild()->getNodeValue();
+                auto_ptr_char crlpath(prop);
+                crl.source = crlpath.get();
+                XMLToolingConfig::getConfig().getPathResolver()->resolve(crl.source, PathResolver::XMLTOOLING_CFG_FILE);
+                crl.local = true;
+                prop = e->getAttributeNS(NULL,_reloadChanges);
+                if (prop && (*prop==chLatin_f) || (*prop==chDigit_0))
+                    crl.reloadChanges = false;
             }
-        }
-        else {
-            log.error("Path element missing inside CRL element");
-            throw XMLSecurityException("FilesystemCredentialResolver can't access CRL file, no Path element specified.");
+            e = XMLHelper::getNextSiblingElement(e,Path);
         }
 
-        // Determine the CRL encoding format dynamically, if not explicitly specified
-        if (format_xml && *format_xml) {
-            fformat = xmlFormatToFormat(format_xml);
-            if (fformat != UNKNOWN) {
-                m_crlformat = fformat;
-            }
-            else {
-                auto_ptr_char unknown(format_xml);
-                log.error("configuration specifies unknown CRL encoding format (%s)", unknown.get());
-                throw XMLSecurityException("FilesystemCredentialResolver configuration contains unknown CRL encoding format ($1)",params(1,unknown.get()));
+        e=XMLHelper::getFirstChildElement(crlnode,_URL);
+        while (e) {
+            if (e->hasChildNodes()) {
+                m_crls.push_back(ManagedCRL());
+                ManagedResource& crl = m_crls.back();
+                if (crlformat && *crlformat) {
+                    auto_ptr_char f(crlformat);
+                    crl.format = f.get();
+                }
+                prop = e->getFirstChild()->getNodeValue();
+                auto_ptr_char crlpath(prop);
+                crl.source = crlpath.get();
+                crl.local = false;
+                prop = e->getAttributeNS(NULL,backingFilePath);
+                if (!prop || !*prop)
+                    throw XMLSecurityException("FilesystemCredentialResolver can't access CRL, backingFilePath missing from URL element.");
+                auto_ptr_char b(prop);
+                crl.backing = b.get();
+                XMLToolingConfig::getConfig().getPathResolver()->resolve(crl.backing, PathResolver::XMLTOOLING_RUN_FILE);
+                prop = e->getAttributeNS(NULL,_reloadInterval);
+                if (prop && *prop)
+                    crl.reloadInterval = XMLString::parseInt(prop);
             }
+            e = XMLHelper::getNextSiblingElement(e,_URL);
         }
-        else {
-            in=BIO_new(BIO_s_file_internal());
-            if (in && BIO_read_filename(in,m_crlpath.c_str())>0) {
-                m_crlformat = getEncodingFormat(in);
-                log.debug("CRL encoding format for (%s) dynamically resolved as (%s)", m_crlpath.c_str(), formatToString(m_crlformat).c_str());
-            }
-            else {
-                log.error("CRL file (%s) can't be read to determine encoding format", m_crlpath.c_str());
-                throw XMLSecurityException("FilesystemCredentialResolver can't read CRL file ($1) to determine encoding format",params(1,m_crlpath.c_str()));
-            }
-            if (in)
-                BIO_free(in);
-            in = NULL;
+        if (m_crls.empty()) {
+            log.error("Path/URL element missing inside CRL element");
+            throw XMLSecurityException("FilesystemCredentialResolver can't access CRL, no Path or URL element specified.");
         }
-
-        // Load the CRLs.
-        loadCRLs(crls);
     }
 
     // Check for Certificate
-    e=XMLHelper::getFirstChildElement(root,Certificate);
-    if (!e) {
-        m_credential = new FilesystemCredential(this,key,xseccerts,crls);
-        m_credential->addKeyNames(keynode);
-        m_credential->setUsage(usage);
-        m_credential->initKeyInfo(mask);
-        return;
-    }
-    auto_ptr_char certpass(e->getAttributeNS(NULL,password));
-
-    const DOMElement* ep=XMLHelper::getFirstChildElement(e,Path);
-    if (!ep || !ep->hasChildNodes()) {
-        log.error("Path element missing inside Certificate element or is empty");
-        delete key;
-        for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
-        throw XMLSecurityException("FilesystemCredentialResolver can't access certificate file, missing or empty Path element.");
-    }
-
-    auto_ptr_char certpath2(ep->getFirstChild()->getNodeValue());
-    string certpath(certpath2.get());
-    XMLToolingConfig::getConfig().getPathResolver()->resolve(certpath, PathResolver::XMLTOOLING_CFG_FILE);
-
-    format_xml=e->getAttributeNS(NULL,format);
-    if (format_xml && *format_xml) {
-        fformat = xmlFormatToFormat(format_xml);
-        if (fformat == UNKNOWN) {
-            auto_ptr_char unknown(format_xml);
-            log.error("configuration specifies unknown certificate encoding format (%s)", unknown.get());
-            delete key;
-            for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
-            throw XMLSecurityException("FilesystemCredentialResolver configuration contains unknown certificate encoding format ($1)",params(1,unknown.get()));
-        }
-    }
-
-    try {
-        X509* x=NULL;
-        PKCS12* p12=NULL;
-        in=BIO_new(BIO_s_file_internal());
-        if (in && BIO_read_filename(in,certpath.c_str())>0) {
-            if (!format_xml || !*format_xml) {
-                // Determine the cert encoding format dynamically, if not explicitly specified
-                fformat = getEncodingFormat(in);
-                log.debug("certificate encoding format for (%s) dynamically resolved as (%s)", certpath.c_str(), formatToString(fformat).c_str());
+    DOMElement* certnode = XMLHelper::getFirstChildElement(root,Certificate);
+    if (certnode) {
+        prop = certnode->getAttributeNS(NULL,password);
+        if (prop && *prop) {
+            auto_ptr_char certpass(prop);
+            m_certpass = certpass.get();
+        }
+
+        const XMLCh* certformat = certnode->getAttributeNS(NULL,_format);
+
+        e = XMLHelper::getFirstChildElement(certnode);
+        while (e) {
+            if (e->hasChildNodes() && (XMLString::equals(e->getLocalName(), Path) || XMLString::equals(e->getLocalName(), CAPath))) {
+                m_certs.push_back(ManagedCert());
+                ManagedResource& cert = m_certs.back();
+                if (certformat && *certformat) {
+                    auto_ptr_char f(certformat);
+                    cert.format = f.get();
+                }
+                prop = e->getFirstChild()->getNodeValue();
+                auto_ptr_char certpath(prop);
+                cert.source = certpath.get();
+                XMLToolingConfig::getConfig().getPathResolver()->resolve(cert.source, PathResolver::XMLTOOLING_CFG_FILE);
+                cert.local = true;
+                prop = e->getAttributeNS(NULL,_reloadChanges);
+                if (prop && (*prop==chLatin_f) || (*prop==chDigit_0))
+                    cert.reloadChanges = false;
             }
-
-            Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).info(
-                "loading certificate from file (%s)", certpath.c_str()
-                );
-
-            switch(fformat) {
-                case PEM:
-                    while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get())))
-                        m_certs.push_back(x);
-                    break;
-
-                case DER:
-                    x=d2i_X509_bio(in,NULL);
-                    if (x)
-                        m_certs.push_back(x);
-                    else {
-                        log_openssl();
-                        BIO_free(in);
-                        throw XMLSecurityException("FilesystemCredentialResolver unable to load DER certificate from file ($1)",params(1,certpath.c_str()));
-                    }
-                    break;
-
-                case _PKCS12:
-                    p12=d2i_PKCS12_bio(in,NULL);
-                    if (p12) {
-                        PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);
-                        PKCS12_free(p12);
-                    }
-                    if (x) {
-                        m_certs.push_back(x);
-                        x=NULL;
-                    } else {
-                        log_openssl();
-                        BIO_free(in);
-                        throw XMLSecurityException("FilesystemCredentialResolver unable to load PKCS12 certificate from file ($1)",params(1,certpath.c_str()));
-                    }
-                    break;
-            } // end switch
-
-        } else {
-            log_openssl();
-            if (in) {
-                BIO_free(in);
-                in=NULL;
+            else if (e->hasChildNodes() && XMLString::equals(e->getLocalName(), _URL)) {
+                m_certs.push_back(ManagedCert());
+                ManagedResource& cert = m_certs.back();
+                if (certformat && *certformat) {
+                    auto_ptr_char f(certformat);
+                    cert.format = f.get();
+                }
+                prop = e->getFirstChild()->getNodeValue();
+                auto_ptr_char certpath(prop);
+                cert.source = certpath.get();
+                cert.local = false;
+                prop = e->getAttributeNS(NULL,backingFilePath);
+                if (!prop || !*prop)
+                    throw XMLSecurityException("FilesystemCredentialResolver can't access certificate, backingFilePath missing from URL element.");
+                auto_ptr_char b(prop);
+                cert.backing = b.get();
+                XMLToolingConfig::getConfig().getPathResolver()->resolve(cert.backing, PathResolver::XMLTOOLING_RUN_FILE);
+                prop = e->getAttributeNS(NULL,_reloadInterval);
+                if (prop && *prop)
+                    cert.reloadInterval = XMLString::parseInt(prop);
             }
-            throw XMLSecurityException("FilesystemCredentialResolver unable to load certificate(s) from file ($1)",params(1,certpath.c_str()));
+            e = XMLHelper::getNextSiblingElement(e);
         }
-        if (in) {
-            BIO_free(in);
-            in=NULL;
+        if (m_certs.empty()) {
+            log.error("Path/URL element missing inside Certificate element");
+            throw XMLSecurityException("FilesystemCredentialResolver can't access certificate, no Path or URL element specified.");
         }
+    }
 
-        if (m_certs.empty())
-            throw XMLSecurityException("FilesystemCredentialResolver unable to load any certificate(s)");
+    // Do an initial load of all the objects. If anything blows up here, whatever's
+    // been loaded should be freed during teardown of the embedded objects.
+    time_t now = time(NULL);
+    m_key.filestamp = now;
+    m_key.load(log, m_keypass.c_str());
+    for (vector<ManagedCert>::iterator i = m_certs.begin(); i != m_certs.end(); ++i) {
+        i->load(log, (i==m_certs.begin()) ? m_certpass.c_str() : NULL);
+        i->filestamp = now;
+    }
+    for (vector<ManagedCRL>::iterator j = m_crls.begin(); j != m_crls.end(); ++j) {
+        j->load(log);
+        j->filestamp = now;
+    }
 
-        // Load any extra CA files.
-        const DOMElement* extra=XMLHelper::getFirstChildElement(e,CAPath);
-        while (extra) {
-            if (!extra->hasChildNodes()) {
-                log.warn("skipping empty CAPath element");
-                extra = XMLHelper::getNextSiblingElement(extra,CAPath);
-                continue;
-            }
-            auto_ptr_char capath2(extra->getFirstChild()->getNodeValue());
-            string capath(capath2.get());
-            XMLToolingConfig::getConfig().getPathResolver()->resolve(capath, PathResolver::XMLTOOLING_CFG_FILE);
-            x=NULL;
-            p12=NULL;
-            in=BIO_new(BIO_s_file_internal());
-            if (in && BIO_read_filename(in,capath.c_str())>0) {
-                if (!format_xml || !*format_xml) {
-                    // Determine the cert encoding format dynamically, if not explicitly specified
-                    fformat = getEncodingFormat(in);
-                    log.debug("CA certificate encoding format for (%s) dynamically resolved as (%s)", capath.c_str(), formatToString(fformat).c_str());
-                }
+    // Load it all into a credential object and then create the lock.
+    auto_ptr<Credential> credential(getCredential());
+    m_lock = RWLock::create();
+    m_credential = credential.release();
+}
 
-                Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).info(
-                    "loading CA certificate from file (%s)", capath.c_str()
-                    );
-
-                switch (fformat) {
-                    case PEM:
-                        while (x=PEM_read_bio_X509(in,NULL,NULL,NULL))
-                            m_certs.push_back(x);
-                        break;
-
-                    case DER:
-                        x=d2i_X509_bio(in,NULL);
-                        if (x)
-                            m_certs.push_back(x);
-                        else {
-                            log_openssl();
-                            BIO_free(in);
-                            throw XMLSecurityException("FilesystemCredentialResolver unable to load DER CA certificate from file ($1)",params(1,capath.c_str()));
-                        }
-                        break;
-
-                    case _PKCS12:
-                        p12 = d2i_PKCS12_bio(in, NULL);
-                        if (p12) {
-                            PKCS12_parse(p12, NULL, NULL, &x, NULL);
-                            PKCS12_free(p12);
-                        }
-                        if (x) {
-                            m_certs.push_back(x);
-                            x=NULL;
-                        }
-                        else {
-                            log_openssl();
-                            BIO_free(in);
-                            throw XMLSecurityException("FilesystemCredentialResolver unable to load PKCS12 CA certificate from file ($1)",params(1,capath.c_str()));
-                        }
-                        break;
-                } //end switch
-
-                BIO_free(in);
-            }
-            else {
-                if (in)
-                    BIO_free(in);
-                log_openssl();
-                log.error("CA certificate file (%s) can't be opened", capath.c_str());
-                throw XMLSecurityException("FilesystemCredentialResolver can't open CA certificate file ($1)",params(1,capath.c_str()));
-            }
+FilesystemCredentialResolver::~FilesystemCredentialResolver()
+{
+    delete m_credential;
+    delete m_lock;
+}
 
-            extra = XMLHelper::getNextSiblingElement(extra,CAPath);
+Credential* FilesystemCredentialResolver::getCredential()
+{
+    // We (unfortunately) need to duplicate all the objects and put them in one set of arrays
+    // in order to create the credential wrapper.
+    FilesystemCredential* credential=NULL;
+    auto_ptr<XSECCryptoKey> xseckey(m_key.key ? m_key.key->clone() : NULL);
+    vector<XSECCryptoX509*> xseccerts;
+    vector<XSECCryptoX509CRL*> xseccrls;
+    try {
+        for (vector<ManagedCert>::iterator i = m_certs.begin(); i != m_certs.end(); ++i) {
+            for (vector<XSECCryptoX509*>::const_iterator y = i->certs.begin(); y != i->certs.end(); ++y)
+                xseccerts.push_back(new OpenSSLCryptoX509(static_cast<OpenSSLCryptoX509*>(*y)->getOpenSSLX509()));
         }
+        for (vector<ManagedCRL>::iterator j = m_crls.begin(); j != m_crls.end(); ++j) {
+            for (vector<XSECCryptoX509CRL*>::const_iterator z = j->crls.begin(); z != j->crls.end(); ++z)
+                xseccrls.push_back((*z)->clone());
+        }
+        credential = new FilesystemCredential(this, xseckey.get(), xseccerts, xseccrls);
+        xseckey.release();
     }
-    catch (XMLToolingException&) {
-        delete key;
-        for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
-        for_each(m_certs.begin(), m_certs.end(), X509_free);
+    catch (exception&) {
+        for_each(xseccerts.begin(), xseccerts.end(), xmltooling::cleanup<XSECCryptoX509>());
+        for_each(xseccrls.begin(), xseccrls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
         throw;
     }
 
-    // Reflect certs over to XSEC form and wrap with credential object.
-    for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++)
-        xseccerts.push_back(new OpenSSLCryptoX509(*j));
-    if (!key && !xseccerts.empty())
-        key = xseccerts.front()->clonePublicKey();
-    m_credential = new FilesystemCredential(this, key, xseccerts, crls);
-    m_credential->addKeyNames(keynode);
-    m_credential->setUsage(usage);
-    m_credential->initKeyInfo(mask);
-}
-
-XSECCryptoKey* FilesystemCredentialResolver::loadKey()
-{
-#ifdef _DEBUG
-    NDC ndc("loadKey");
-#endif
-    Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).info(
-        "loading private key from file (%s)", m_keypath.c_str()
-        );
-
-    // Get a EVP_PKEY.
-    EVP_PKEY* pkey=NULL;
-    BIO* in=BIO_new(BIO_s_file_internal());
-    if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
-        switch (m_keyformat) {
-            case PEM:
-                pkey=PEM_read_bio_PrivateKey(in, NULL, passwd_callback, const_cast<char*>(m_keypass.c_str()));
-                break;
-
-            case DER:
-                pkey=d2i_PrivateKey_bio(in, NULL);
-                break;
-
-            default: {
-                PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
-                if (p12) {
-                    PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
-                    PKCS12_free(p12);
-                }
-            }
-        }
+    // At this point the copies are owned by the credential.
+    try {
+        credential->initKeyInfo(m_keyinfomask);
     }
-    if (in)
-        BIO_free(in);
-
-    // Now map it to an XSEC wrapper.
-    if (pkey) {
-        XSECCryptoKey* ret=NULL;
-        switch (pkey->type) {
-            case EVP_PKEY_RSA:
-                ret=new OpenSSLCryptoKeyRSA(pkey);
-                break;
-
-            case EVP_PKEY_DSA:
-                ret=new OpenSSLCryptoKeyDSA(pkey);
-                break;
-
-            default:
-                Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).error("unsupported private key type");
-        }
-        EVP_PKEY_free(pkey);
-        if (ret)
-            return ret;
+    catch (exception&) {
+        delete credential;
+        throw;
     }
 
-    log_openssl();
-    throw XMLSecurityException("FilesystemCredentialResolver unable to load private key from file.");
+    return credential;
 }
 
-void FilesystemCredentialResolver::loadCRLs(vector<XSECCryptoX509CRL*>& crls)
+Lockable* FilesystemCredentialResolver::lock()
 {
 #ifdef _DEBUG
-    NDC ndc("loadCRL");
+    NDC ndc("lock");
 #endif
-    Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).info(
-        "loading CRL from file (%s)", m_crlpath.c_str()
-        );
-
-    X509_CRL* crl=NULL;
-    BIO* in=BIO_new(BIO_s_file_internal());
-    if (in && BIO_read_filename(in,m_crlpath.c_str())>0) {
-        switch (m_crlformat) {
-            case PEM:
-                while (crl=PEM_read_bio_X509_CRL(in, NULL, NULL, NULL)) {
-                    crls.push_back(new OpenSSLCryptoX509CRL(crl));
-                    X509_CRL_free(crl);
-                }
-                break;
+    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER);
 
-            case DER:
-                crl=d2i_X509_CRL_bio(in, NULL);
-                if (crl) {
-                    crls.push_back(new OpenSSLCryptoX509CRL(crl));
-                    X509_CRL_free(crl);
-                }
-                break;
-        }
-    }
-    if (in)
-        BIO_free(in);
+    m_lock->rdlock();
 
-    if (crls.empty()) {
-        log_openssl();
-        throw XMLSecurityException("FilesystemCredentialResolver unable to load CRL from file.");
-    }
-}
+    // Check each managed resource while holding a read lock for staleness.
+    // If it comes back false, the lock is left as is, and the resource was stable.
+    // If it comes back true, the lock was elevated to a write lock, and the resource
+    // needs to be reloaded, and the credential replaced.
+    // Once a stale check comes back true, further checks leave the lock alone.
 
-// Used to determine the encoding format of credentials files
-// dynamically. Supports: PEM, DER, PKCS12.
-FilesystemCredentialResolver::format_t FilesystemCredentialResolver::getEncodingFormat(BIO* in) const
-{
-    PKCS12* p12 = NULL;
-    format_t format;
+    bool writelock = false, updated = false;
 
-    const int READSIZE = 1;
-    char buf[READSIZE];
-    char b1;
-    int mark;
+    if (m_key.stale(log, m_lock)) {
+        writelock = true;
+        try {
+            m_key.load(log, m_keypass.c_str());
+            updated = true;
+        }
+        catch (exception& ex) {
+            log.crit("maintaining existing key: %s", ex.what());
+        }
+    }
 
-    try {
-        if ( (mark = BIO_tell(in)) < 0 )
-            throw XMLSecurityException("getEncodingFormat: BIO_tell() can't get the file position");
-        if ( BIO_read(in, buf, READSIZE) <= 0 )
-            throw XMLSecurityException("getEncodingFormat: BIO_read() can't read from the stream");
-        if ( BIO_seek(in, mark) < 0 )
-            throw XMLSecurityException("getEncodingFormat: BIO_seek() can't reset the file position");
+    for (vector<ManagedCert>::iterator i = m_certs.begin(); i != m_certs.end(); ++i) {
+        if (i->stale(log, writelock ? NULL : m_lock)) {
+            writelock = true;
+            try {
+                i->load(log, (i==m_certs.begin()) ? m_certpass.c_str() : NULL);
+                updated = true;
+            }
+            catch (exception& ex) {
+                log.crit("maintaining existing certificate(s): %s", ex.what());
+            }
+        }
     }
-    catch (...) {
-        log_openssl();
-        throw;
+
+    for (vector<ManagedCRL>::iterator j = m_crls.begin(); j != m_crls.end(); ++j) {
+        if (j->stale(log, writelock ? NULL : m_lock)) {
+            writelock = true;
+            try {
+                j->load(log);
+                updated = true;
+            }
+            catch (exception& ex) {
+                log.crit("maintaining existing CRL(s): %s", ex.what());
+            }
+        }
     }
 
-    b1 = buf[0];
-
-    // This is a slight variation of the Java code by Chad La Joie.
-    //
-    // Check the first byte of the file.  If it's some kind of
-    // DER-encoded structure (including PKCS12), it will begin with ASCII 048.
-    // Otherwise, assume it's PEM.
-    if (b1 !=  48) {
-        format = PEM;
-    } else {
-        // Here we know it's DER-encoded, now try to parse it as a PKCS12
-        // ASN.1 structure.  If it fails, must be another kind of DER-encoded
-        // key/cert structure.  A little inefficient...but it works.
-        if ( (p12=d2i_PKCS12_bio(in,NULL)) == NULL ) {
-            format = DER;
-        } else {
-            format = _PKCS12;
-        }
-        if (p12)
-            PKCS12_free(p12);
-        if ( BIO_seek(in, mark) < 0 ) {
-            log_openssl();
-            throw XMLSecurityException("getEncodingFormat: BIO_seek() can't reset the file position");
+    if (updated) {
+        try {
+            auto_ptr<Credential> credential(getCredential());
+            delete m_credential;
+            m_credential = credential.release();
+        }
+        catch (exception& ex) {
+            log.crit("maintaining existing credentials, error reloading: %s", ex.what());
         }
     }
 
-    return format;
+    if (writelock) {
+        m_lock->unlock();
+        m_lock->rdlock();
+    }
+    return this;
+}
+
+const Credential* FilesystemCredentialResolver::resolve(const CredentialCriteria* criteria) const
+{
+    return (criteria ? (criteria->matches(*m_credential) ? m_credential : NULL) : m_credential);
 }
 
-// Convert key/cert format_t types to a human-meaningful string for debug output
-string FilesystemCredentialResolver::formatToString(format_t format) const
+vector<const Credential*>::size_type FilesystemCredentialResolver::resolve(
+    vector<const Credential*>& results, const CredentialCriteria* criteria
+    ) const
 {
-    switch(format) {
-        case PEM:
-            return "PEM";
-        case DER:
-            return "DER";
-        case _PKCS12:
-            return "PKCS12";
-        default:
-            return "UNKNOWN";
+    if (!criteria || criteria->matches(*m_credential)) {
+        results.push_back(m_credential);
+        return 1;
     }
+    return 0;
 }
 
-// Convert key/cert raw XML format attribute (XMLCh[]) to format_t type
-FilesystemCredentialResolver::format_t FilesystemCredentialResolver::xmlFormatToFormat(const XMLCh* format_xml) const
+// OpenSSL password callback...
+static int passwd_callback(char* buf, int len, int verify, void* passwd)
 {
-    static const XMLCh cPEM[] = UNICODE_LITERAL_3(P,E,M);
-    static const XMLCh cDER[] = UNICODE_LITERAL_3(D,E,R);
-    static const XMLCh cPKCS12[] = { chLatin_P, chLatin_K, chLatin_C, chLatin_S, chDigit_1, chDigit_2, chNull };
-    format_t format;
-
-    if (!XMLString::compareString(format_xml,cPEM))
-        format=PEM;
-    else if (!XMLString::compareString(format_xml,cDER))
-        format=DER;
-    else if (!XMLString::compareString(format_xml,cPKCS12))
-        format=_PKCS12;
-    else
-        format=UNKNOWN;
-
-    return format;
+    if(!verify)
+    {
+        if(passwd && len > strlen(reinterpret_cast<char*>(passwd)))
+        {
+            strcpy(buf,reinterpret_cast<char*>(passwd));
+            return strlen(buf);
+        }
+    }
+    return 0;
 }
 
-void FilesystemCredentialResolver::attach(SSL_CTX* ctx) const
+void FilesystemCredential::attach(SSL_CTX* ctx) const
 {
 #ifdef _DEBUG
     NDC ndc("attach");
 #endif
 
-    if (m_keypath.empty())
+    int ret=0;
+    const char* path = m_resolver->m_key.local ? m_resolver->m_key.source.c_str() : m_resolver->m_key.backing.c_str();
+    if (!path || !*path)
         throw XMLSecurityException("No key available, unable to attach private key to SSL context.");
 
-    // Attach key.
-    SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
-    SSL_CTX_set_default_passwd_cb_userdata(ctx, const_cast<char*>(m_keypass.c_str()));
+    if (!m_resolver->m_keypass.empty()) {
+        SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
+        SSL_CTX_set_default_passwd_cb_userdata(ctx, const_cast<char*>(m_resolver->m_keypass.c_str()));
+    }
 
-    int ret=0;
-    switch (m_keyformat) {
-        case PEM:
-            ret=SSL_CTX_use_PrivateKey_file(ctx, m_keypath.c_str(), m_keyformat);
-            break;
-
-        case DER:
-            ret=SSL_CTX_use_RSAPrivateKey_file(ctx, m_keypath.c_str(), m_keyformat);
-            break;
-
-        default: {
-            BIO* in=BIO_new(BIO_s_file_internal());
-            if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
+    if (m_resolver->m_key.format == "PEM") {
+        ret=SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM);
+    }
+    else if (m_resolver->m_key.format == "DER") {
+        ret=SSL_CTX_use_RSAPrivateKey_file(ctx, path, SSL_FILETYPE_ASN1);
+    }
+    else if (m_resolver->m_key.format == "PKCS12") {
+        BIO* in=BIO_new(BIO_s_file_internal());
+        if (in && BIO_read_filename(in,path)>0) {
+            PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
+            if (p12) {
                 EVP_PKEY* pkey=NULL;
-                PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
-                if (p12) {
-                    PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
-                    PKCS12_free(p12);
-                    if (pkey) {
-                        ret=SSL_CTX_use_PrivateKey(ctx, pkey);
-                        EVP_PKEY_free(pkey);
-                    }
+                X509* x=NULL;
+                PKCS12_parse(p12, const_cast<char*>(m_resolver->m_keypass.c_str()), &pkey, &x, NULL);
+                PKCS12_free(p12);
+                if (x)
+                    X509_free(x);
+                if (pkey) {
+                    ret=SSL_CTX_use_PrivateKey(ctx, pkey);
+                    EVP_PKEY_free(pkey);
                 }
             }
-            if (in)
-                BIO_free(in);
         }
+        if (in)
+            BIO_free(in);
     }
 
     if (ret!=1) {
@@ -784,16 +738,16 @@ void FilesystemCredentialResolver::attach(SSL_CTX* ctx) const
     }
 
     // Attach certs.
-    for (vector<X509*>::const_iterator i=m_certs.begin(); i!=m_certs.end(); i++) {
-        if (i==m_certs.begin()) {
-            if (SSL_CTX_use_certificate(ctx, *i) != 1) {
+    for (vector<XSECCryptoX509*>::const_iterator i=m_xseccerts.begin(); i!=m_xseccerts.end(); i++) {
+        if (i==m_xseccerts.begin()) {
+            if (SSL_CTX_use_certificate(ctx, static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509()) != 1) {
                 log_openssl();
                 throw XMLSecurityException("Unable to attach client certificate to SSL context.");
             }
         }
         else {
             // When we add certs, they don't get ref counted, so we need to duplicate them.
-            X509* dup = X509_dup(*i);
+            X509* dup = X509_dup(static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
             if (SSL_CTX_add_extra_chain_cert(ctx, dup) != 1) {
                 X509_free(dup);
                 log_openssl();
@@ -802,21 +756,3 @@ void FilesystemCredentialResolver::attach(SSL_CTX* ctx) const
         }
     }
 }
-
-void FilesystemCredential::addKeyNames(const DOMElement* e)
-{
-    e = e ? XMLHelper::getFirstChildElement(e, Name) : NULL;
-    while (e) {
-        if (e->hasChildNodes()) {
-            auto_ptr_char n(e->getFirstChild()->getNodeValue());
-            if (n.get() && *n.get())
-                m_keyNames.insert(n.get());
-        }
-        e = XMLHelper::getNextSiblingElement(e, Name);
-    }
-}
-
-void FilesystemCredential::attach(SSL_CTX* ctx) const
-{
-    return m_resolver->attach(ctx);
-}
index a562bb0..5de29c7 100644 (file)
@@ -51,6 +51,55 @@ static int passwd_callback(char* buf, int len, int verify, void* passwd)
     return 0;
 }
 
+const char* SecurityHelper::guessEncodingFormat(const char* pathname)
+{
+    const char* format=NULL;
+    BIO* in=BIO_new(BIO_s_file_internal());
+    if (in && BIO_read_filename(in, pathname)>0) {
+        const int READSIZE = 1;
+        char buf[READSIZE];
+        int mark;
+
+        // Examine the first byte.
+        try {
+            if ((mark = BIO_tell(in)) < 0)
+                throw XMLSecurityException("Error loading file: BIO_tell() can't get the file position.");
+            if (BIO_read(in, buf, READSIZE) <= 0)
+                throw XMLSecurityException("Error loading file: BIO_read() can't read from the stream.");
+            if (BIO_seek(in, mark) < 0)
+                throw XMLSecurityException("Error loading file: BIO_seek() can't reset the file position.");
+        }
+        catch (exception&) {
+            log_openssl();
+            BIO_free(in);
+            throw;
+        }
+
+        // Check the first byte of the file.  If it's some kind of DER-encoded structure
+        // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
+        if (buf[0] != 48) {
+            format = "PEM";
+        }
+        else {
+            // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
+            // If it fails, must be another kind of DER-encoded structure.
+            PKCS12* p12;
+            if ((p12=d2i_PKCS12_bio(in, NULL)) == NULL) {
+                format = "DER";
+            }
+            else {
+                format = "PKCS12";
+                PKCS12_free(p12);
+            }
+        }
+    }
+    if (in)
+        BIO_free(in);
+    if (format)
+        return format;
+    throw XMLSecurityException("Unable to determine encoding for file ($1).", params(1,pathname));
+}
+
 XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char* format, const char* password)
 {
 #ifdef _DEBUG
@@ -66,7 +115,7 @@ XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char*
     BIO* in=BIO_new(BIO_s_file_internal());
     if (in && BIO_read_filename(in, pathname)>0) {
         // If the format isn't set, try and guess it.
-        if (!format) {
+        if (!format || !*format) {
             const int READSIZE = 1;
             char buf[READSIZE];
             int mark;
@@ -176,7 +225,7 @@ vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromFile(
     BIO* in=BIO_new(BIO_s_file_internal());
     if (in && BIO_read_filename(in, pathname)>0) {
         // If the format isn't set, try and guess it.
-        if (!format) {
+        if (!format || !*format) {
             const int READSIZE = 1;
             char buf[READSIZE];
             int mark;
@@ -281,7 +330,7 @@ vector<XSECCryptoX509CRL*>::size_type SecurityHelper::loadCRLsFromFile(
     BIO* in=BIO_new(BIO_s_file_internal());
     if (in && BIO_read_filename(in, pathname)>0) {
         // If the format isn't set, try and guess it.
-        if (!format) {
+        if (!format || !*format) {
             const int READSIZE = 1;
             char buf[READSIZE];
             int mark;
index 0377596..572ee95 100644 (file)
                        />\r
                </Configuration>\r
                <Configuration\r
-                       Name="Release|Win32"\r
-                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
-                       IntermediateDirectory="$(ProjectName)-$(ConfigurationName)"\r
+                       Name="Debug|x64"\r
+                       OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"\r
+                       IntermediateDirectory="$(PlatformName)\$(ProjectName)-$(ConfigurationName)"\r
                        ConfigurationType="2"\r
                        CharacterSet="2"\r
-                       WholeProgramOptimization="1"\r
                        >\r
                        <Tool\r
                                Name="VCPreBuildEventTool"\r
                        />\r
                        <Tool\r
                                Name="VCMIDLTool"\r
+                               TargetEnvironment="3"\r
                        />\r
                        <Tool\r
                                Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
                                AdditionalIncludeDirectories="&quot;$(SolutionDir)&quot;;&quot;$(ProjectDir)&quot;"\r
-                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;XMLTOOLING_LITE"\r
-                               RuntimeLibrary="2"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;XMLTOOLING_LITE"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
                                PrecompiledHeaderThrough=""\r
+                               BrowseInformation="1"\r
                                WarningLevel="3"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
                        />\r
                        <Tool\r
                                Name="VCResourceCompilerTool"\r
-                               PreprocessorDefinitions="XMLTOOLING_LITE"\r
+                               PreprocessorDefinitions="_DEBUG;XMLTOOLING_LITE"\r
                        />\r
                        <Tool\r
                                Name="VCPreLinkEventTool"\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="wsock32.lib log4shib1.lib xerces-c_2.lib"\r
-                               OutputFile="$(OutDir)\$(ProjectName)1_2.dll"\r
+                               AdditionalDependencies="wsock32.lib log4shib1D.lib xerces-c_2D.lib"\r
+                               OutputFile="$(OutDir)\$(ProjectName)1_2D.dll"\r
+                               LinkIncremental="2"\r
                                GenerateDebugInformation="true"\r
                                SubSystem="2"\r
-                               OptimizeReferences="2"\r
-                               EnableCOMDATFolding="2"\r
-                               ImportLibrary="$(TargetDir)$(ProjectName)1.lib"\r
-                               TargetMachine="1"\r
+                               ImportLibrary="$(TargetDir)$(ProjectName)1D.lib"\r
+                               TargetMachine="17"\r
                        />\r
                        <Tool\r
                                Name="VCALinkTool"\r
                        />\r
                </Configuration>\r
                <Configuration\r
-                       Name="Debug|x64"\r
-                       OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"\r
-                       IntermediateDirectory="$(PlatformName)\$(ProjectName)-$(ConfigurationName)"\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ProjectName)-$(ConfigurationName)"\r
                        ConfigurationType="2"\r
                        CharacterSet="2"\r
+                       WholeProgramOptimization="1"\r
                        >\r
                        <Tool\r
                                Name="VCPreBuildEventTool"\r
                        />\r
                        <Tool\r
                                Name="VCMIDLTool"\r
-                               TargetEnvironment="3"\r
                        />\r
                        <Tool\r
                                Name="VCCLCompilerTool"\r
-                               Optimization="0"\r
                                AdditionalIncludeDirectories="&quot;$(SolutionDir)&quot;;&quot;$(ProjectDir)&quot;"\r
-                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;XMLTOOLING_LITE"\r
-                               MinimalRebuild="true"\r
-                               BasicRuntimeChecks="3"\r
-                               RuntimeLibrary="3"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;XMLTOOLING_LITE"\r
+                               RuntimeLibrary="2"\r
                                PrecompiledHeaderThrough=""\r
-                               BrowseInformation="1"\r
                                WarningLevel="3"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
                        />\r
                        <Tool\r
                                Name="VCResourceCompilerTool"\r
-                               PreprocessorDefinitions="_DEBUG;XMLTOOLING_LITE"\r
+                               PreprocessorDefinitions="XMLTOOLING_LITE"\r
                        />\r
                        <Tool\r
                                Name="VCPreLinkEventTool"\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="wsock32.lib log4shib1D.lib xerces-c_2D.lib"\r
-                               OutputFile="$(OutDir)\$(ProjectName)1_2D.dll"\r
-                               LinkIncremental="2"\r
+                               AdditionalDependencies="wsock32.lib log4shib1.lib xerces-c_2.lib"\r
+                               OutputFile="$(OutDir)\$(ProjectName)1_2.dll"\r
                                GenerateDebugInformation="true"\r
                                SubSystem="2"\r
-                               ImportLibrary="$(TargetDir)$(ProjectName)1D.lib"\r
-                               TargetMachine="17"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               ImportLibrary="$(TargetDir)$(ProjectName)1.lib"\r
+                               TargetMachine="1"\r
                        />\r
                        <Tool\r
                                Name="VCALinkTool"\r