Convert usage bits to a mask.
[shibboleth/xmltooling.git] / xmltooling / security / impl / FilesystemCredentialResolver.cpp
index a790bd7..6fd9a48 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #include "internal.h"
+#include "logging.h"
 #include "security/BasicX509Credential.h"
 #include "security/CredentialCriteria.h"
 #include "security/CredentialResolver.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <openssl/pkcs12.h>
-#include <log4cpp/Category.hh>
 #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 log4cpp;
 using namespace std;
 
 // OpenSSL password callback...
@@ -69,16 +69,37 @@ namespace xmltooling {
     {
     public:
         FilesystemCredential(FilesystemCredentialResolver* resolver, XSECCryptoKey* key, const std::vector<XSECCryptoX509*>& xseccerts)
-                : BasicX509Credential(key, xseccerts), m_resolver(resolver) {
+                : BasicX509Credential(key, xseccerts), m_resolver(resolver), m_usage(UNSPECIFIED_CREDENTIAL) {
+            if (!m_xseccerts.empty())
+                extractNames(m_xseccerts.front(), m_keyNames);
             initKeyInfo();
         }
         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;
+            }
+        }
+
+        void addKeyNames(const DOMElement* e);
+
         void attach(SSL_CTX* ctx) const;
     
     private:
         FilesystemCredentialResolver* m_resolver;
+        unsigned int m_usage;
     };
 
 #if defined (_MSC_VER)
@@ -98,13 +119,13 @@ namespace xmltooling {
         void unlock() {}
         
         const Credential* resolve(const CredentialCriteria* criteria=NULL) const {
-            return matches(criteria) ? m_credential : NULL;
+            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 (matches(criteria)) {
+            if (!criteria || criteria->matches(*m_credential)) {
                 results.push_back(m_credential);
                 return 1;
             }
@@ -115,28 +136,6 @@ namespace xmltooling {
 
     private:
         XSECCryptoKey* loadKey();
-        bool matches(const CredentialCriteria* criteria) const {
-            bool match = true;
-            if (criteria) {
-                const char* alg = criteria->getKeyAlgorithm();
-                if (alg && *alg) {
-                    const char* alg2 = m_credential->getAlgorithm();
-                    if (alg2 && *alg2)
-                        match = XMLString::equals(alg,alg2);
-                }
-                if (match && criteria->getKeySize()>0 && m_credential->getKeySize()>0)
-                    match = (criteria->getKeySize() == m_credential->getKeySize());
-                if (match && m_credential->getPublicKey()) {
-                    // See if we have to match a specific key.
-                    auto_ptr<Credential> cred(
-                        XMLToolingConfig::getConfig().getKeyInfoResolver()->resolve(*criteria,Credential::RESOLVE_KEYS)
-                        );
-                    if (cred.get())
-                        match = cred->isEqual(*(m_credential->getPublicKey()));
-                }
-            }
-            return match;
-        }
         
         enum format_t { PEM=SSL_FILETYPE_PEM, DER=SSL_FILETYPE_ASN1, _PKCS12, UNKNOWN };
     
@@ -154,23 +153,53 @@ namespace xmltooling {
     {
         return new FilesystemCredentialResolver(e);
     }
-};
 
-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 format[] =           UNICODE_LITERAL_6(f,o,r,m,a,t);
-static const XMLCh Key[] =              UNICODE_LITERAL_3(K,e,y);
-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 _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 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 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 _use[] =             UNICODE_LITERAL_3(u,s,e);
+};
 
 FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e) : m_credential(NULL)
 {
 #ifdef _DEBUG
     NDC ndc("FilesystemCredentialResolver");
 #endif
-    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver");
-
+    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER);
+
+    if (e && (e->hasAttributeNS(NULL,_certificate) || e->hasAttributeNS(NULL,_key))) {
+        // Dummy up a simple file resolver config using these attributes.
+        DOMElement* dummy = e->getOwnerDocument()->createElementNS(NULL,_CredentialResolver);
+        DOMElement* child;
+        DOMElement* path;
+        if (e->hasAttributeNS(NULL,_key)) {
+            child = e->getOwnerDocument()->createElementNS(NULL,Key);
+            dummy->appendChild(child);
+            path = e->getOwnerDocument()->createElementNS(NULL,Path);
+            child->appendChild(path);
+            path->appendChild(e->getOwnerDocument()->createTextNode(e->getAttributeNS(NULL,_key)));
+            if (e->hasAttributeNS(NULL,password))
+                child->setAttributeNS(NULL,password,e->getAttributeNS(NULL,password));
+        }
+        if (e->hasAttributeNS(NULL,_certificate)) {
+            child = e->getOwnerDocument()->createElementNS(NULL,Certificate);
+            dummy->appendChild(child);
+            path = e->getOwnerDocument()->createElementNS(NULL,Path);
+            child->appendChild(path);
+            path->appendChild(e->getOwnerDocument()->createTextNode(e->getAttributeNS(NULL,_certificate)));
+        }
+        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;
@@ -180,20 +209,20 @@ FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e)
     BIO* in = NULL;
     
     // Move to Key
-    e=XMLHelper::getFirstChildElement(root,Key);
-    if (e) {
+    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=e->getAttributeNS(NULL,format);
+        format_xml=keynode->getAttributeNS(NULL,format);
             
-        const XMLCh* password_xml=e->getAttributeNS(NULL,password);
+        const XMLCh* password_xml=keynode->getAttributeNS(NULL,password);
         if (password_xml) {
             auto_ptr_char kp(password_xml);
             m_keypass=kp.get();
         }
         
-        e=XMLHelper::getFirstChildElement(e,Path);
+        e=XMLHelper::getFirstChildElement(keynode,Path);
         if (e && e->hasChildNodes()) {
             const XMLCh* s=e->getFirstChild()->getNodeValue();
             auto_ptr_char kpath(s);
@@ -250,11 +279,13 @@ FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e)
     e=XMLHelper::getFirstChildElement(root,Certificate);
     if (!e) {
         m_credential = new FilesystemCredential(this,key,xseccerts);
+        m_credential->addKeyNames(keynode);
+        m_credential->setUsage(usage);
         return;
     }
     auto_ptr_char certpass(e->getAttributeNS(NULL,password));
     
-    DOMElement* ep=XMLHelper::getFirstChildElement(e,Path);
+    const DOMElement* ep=XMLHelper::getFirstChildElement(e,Path);
     if (!ep || !ep->hasChildNodes()) {
         log.error("Path element missing inside Certificate element or is empty");
         delete key;
@@ -335,7 +366,7 @@ FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e)
             throw XMLSecurityException("FilesystemCredentialResolver unable to load any certificate(s)");
 
         // Load any extra CA files.
-        DOMElement* extra=XMLHelper::getFirstChildElement(e,CAPath);
+        const DOMElement* extra=XMLHelper::getFirstChildElement(e,CAPath);
         while (extra) {
             if (!extra->hasChildNodes()) {
                 log.warn("skipping empty CAPath element");
@@ -413,6 +444,8 @@ FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e)
     if (!key && !xseccerts.empty())
         key = xseccerts.front()->clonePublicKey();
     m_credential = new FilesystemCredential(this, key, xseccerts);
+    m_credential->addKeyNames(keynode);
+    m_credential->setUsage(usage);
 }
 
 XSECCryptoKey* FilesystemCredentialResolver::loadKey()
@@ -459,7 +492,7 @@ XSECCryptoKey* FilesystemCredentialResolver::loadKey()
                 break;
             
             default:
-                Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver").error("unsupported private key type");
+                Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).error("unsupported private key type");
         }
         EVP_PKEY_free(pkey);
         if (ret)
@@ -564,7 +597,10 @@ void FilesystemCredentialResolver::attach(SSL_CTX* ctx) const
 #ifdef _DEBUG
     NDC ndc("attach");
 #endif
-    
+
+    if (m_keypath.empty())
+        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()));
@@ -623,6 +659,19 @@ 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);