https://bugs.internet2.edu/jira/browse/CPPXT-19
[shibboleth/xmltooling.git] / xmltooling / security / impl / FilesystemCredentialResolver.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * FilesystemCredentialResolver.cpp
19  * 
20  * Supplies credentials from local files
21  */
22
23 #include "internal.h"
24 #include "logging.h"
25 #include "security/BasicX509Credential.h"
26 #include "security/CredentialCriteria.h"
27 #include "security/CredentialResolver.h"
28 #include "security/KeyInfoResolver.h"
29 #include "security/OpenSSLCredential.h"
30 #include "security/OpenSSLCryptoX509CRL.h"
31 #include "util/NDC.h"
32 #include "util/PathResolver.h"
33 #include "util/XMLHelper.h"
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <openssl/pkcs12.h>
38 #include <xercesc/util/XMLUniDefs.hpp>
39 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
40 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
41 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
42
43 using namespace xmlsignature;
44 using namespace xmltooling::logging;
45 using namespace xmltooling;
46 using namespace std;
47
48 // OpenSSL password callback...
49 static int passwd_callback(char* buf, int len, int verify, void* passwd)
50 {
51     if(!verify)
52     {
53         if(passwd && len > strlen(reinterpret_cast<char*>(passwd)))
54         {
55             strcpy(buf,reinterpret_cast<char*>(passwd));
56             return strlen(buf);
57         }
58     }  
59     return 0;
60 }
61
62 namespace xmltooling {
63
64 #if defined (_MSC_VER)
65     #pragma warning( push )
66     #pragma warning( disable : 4250 )
67 #endif
68
69     class XMLTOOL_DLLLOCAL FilesystemCredentialResolver;
70     class XMLTOOL_DLLLOCAL FilesystemCredential : public OpenSSLCredential, public BasicX509Credential
71     {
72     public:
73         FilesystemCredential(
74             FilesystemCredentialResolver* resolver, XSECCryptoKey* key, const std::vector<XSECCryptoX509*>& xseccerts, XSECCryptoX509CRL* crl=NULL
75             ) : BasicX509Credential(key, xseccerts, crl), m_resolver(resolver), m_usage(UNSPECIFIED_CREDENTIAL) {
76             extract();
77         }
78         virtual ~FilesystemCredential() {
79         }
80
81         unsigned int getUsage() const {
82             return m_usage;
83         }
84
85         void setUsage(const XMLCh* usage) {
86             if (usage && *usage) {
87                 auto_ptr_char u(usage);
88                 if (!strcmp(u.get(), "signing"))
89                     m_usage = SIGNING_CREDENTIAL | TLS_CREDENTIAL;
90                 else if (!strcmp(u.get(), "TLS"))
91                     m_usage = TLS_CREDENTIAL;
92                 else if (!strcmp(u.get(), "encryption"))
93                     m_usage = ENCRYPTION_CREDENTIAL;
94             }
95         }
96
97         void addKeyNames(const DOMElement* e);
98         
99         void initKeyInfo() {
100             BasicX509Credential::initKeyInfo();
101         }
102
103         void attach(SSL_CTX* ctx) const;
104     
105     private:
106         FilesystemCredentialResolver* m_resolver;
107         unsigned int m_usage;
108     };
109
110 #if defined (_MSC_VER)
111     #pragma warning( pop )
112 #endif
113
114     class XMLTOOL_DLLLOCAL FilesystemCredentialResolver : public CredentialResolver
115     {
116     public:
117         FilesystemCredentialResolver(const DOMElement* e);
118         virtual ~FilesystemCredentialResolver() {
119             delete m_credential;
120             for_each(m_certs.begin(),m_certs.end(),X509_free);
121         }
122
123         Lockable* lock() { return this; }
124         void unlock() {}
125         
126         const Credential* resolve(const CredentialCriteria* criteria=NULL) const {
127             return (criteria ? (criteria->matches(*m_credential) ? m_credential : NULL) : m_credential);
128         }
129
130         virtual vector<const Credential*>::size_type resolve(
131             vector<const Credential*>& results, const CredentialCriteria* criteria=NULL
132             ) const {
133             if (!criteria || criteria->matches(*m_credential)) {
134                 results.push_back(m_credential);
135                 return 1;
136             }
137             return 0;
138         }
139
140         void attach(SSL_CTX* ctx) const;
141
142     private:
143         XSECCryptoKey* loadKey();
144         XSECCryptoX509CRL* loadCRL();
145         
146         enum format_t { PEM=SSL_FILETYPE_PEM, DER=SSL_FILETYPE_ASN1, _PKCS12, UNKNOWN };
147     
148         format_t getEncodingFormat(BIO* in) const;
149         string formatToString(format_t format) const;
150         format_t xmlFormatToFormat(const XMLCh* format_xml) const;
151     
152         format_t m_keyformat,m_crlformat;
153         string m_keypath,m_keypass,m_crlpath;
154         vector<X509*> m_certs;
155         FilesystemCredential* m_credential;
156     };
157
158     CredentialResolver* XMLTOOL_DLLLOCAL FilesystemCredentialResolverFactory(const DOMElement* const & e)
159     {
160         return new FilesystemCredentialResolver(e);
161     }
162
163     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);
164     static const XMLCh CAPath[] =           UNICODE_LITERAL_6(C,A,P,a,t,h);
165     static const XMLCh Certificate[] =      UNICODE_LITERAL_11(C,e,r,t,i,f,i,c,a,t,e);
166     static const XMLCh _certificate[] =     UNICODE_LITERAL_11(c,e,r,t,i,f,i,c,a,t,e);
167     static const XMLCh CRL[] =              UNICODE_LITERAL_3(C,R,L);
168     static const XMLCh format[] =           UNICODE_LITERAL_6(f,o,r,m,a,t);
169     static const XMLCh Key[] =              UNICODE_LITERAL_3(K,e,y);
170     static const XMLCh _key[] =             UNICODE_LITERAL_3(k,e,y);
171     static const XMLCh keyName[] =          UNICODE_LITERAL_7(k,e,y,N,a,m,e);
172     static const XMLCh Name[] =             UNICODE_LITERAL_4(N,a,m,e);
173     static const XMLCh password[] =         UNICODE_LITERAL_8(p,a,s,s,w,o,r,d);
174     static const XMLCh Path[] =             UNICODE_LITERAL_4(P,a,t,h);
175     static const XMLCh _use[] =             UNICODE_LITERAL_3(u,s,e);
176 };
177
178 FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e) : m_credential(NULL)
179 {
180 #ifdef _DEBUG
181     NDC ndc("FilesystemCredentialResolver");
182 #endif
183     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER);
184
185     if (e && (e->hasAttributeNS(NULL,_certificate) || e->hasAttributeNS(NULL,_key))) {
186         // Dummy up a simple file resolver config using these attributes.
187         DOMElement* dummy = e->getOwnerDocument()->createElementNS(NULL,_CredentialResolver);
188         DOMElement* child;
189         DOMElement* path;
190         if (e->hasAttributeNS(NULL,_key)) {
191             child = e->getOwnerDocument()->createElementNS(NULL,Key);
192             dummy->appendChild(child);
193             path = e->getOwnerDocument()->createElementNS(NULL,Path);
194             child->appendChild(path);
195             path->appendChild(e->getOwnerDocument()->createTextNode(e->getAttributeNS(NULL,_key)));
196             if (e->hasAttributeNS(NULL,password))
197                 child->setAttributeNS(NULL,password,e->getAttributeNS(NULL,password));
198             if (e->hasAttributeNS(NULL,keyName)) {
199                 path = e->getOwnerDocument()->createElementNS(NULL,Name);
200                 child->appendChild(path);
201                 path->appendChild(e->getOwnerDocument()->createTextNode(e->getAttributeNS(NULL,keyName)));
202             }
203         }
204         if (e->hasAttributeNS(NULL,_certificate)) {
205             child = e->getOwnerDocument()->createElementNS(NULL,Certificate);
206             dummy->appendChild(child);
207             path = e->getOwnerDocument()->createElementNS(NULL,Path);
208             child->appendChild(path);
209             path->appendChild(e->getOwnerDocument()->createTextNode(e->getAttributeNS(NULL,_certificate)));
210         }
211         e = dummy;  // reset "root" to the dummy config element
212     }
213     
214     const DOMElement* root=e;
215     const XMLCh* usage = root->getAttributeNS(NULL,_use);
216
217     XSECCryptoKey* key=NULL;
218     vector<XSECCryptoX509*> xseccerts;
219     XSECCryptoX509CRL* crl=NULL;
220
221     format_t fformat;
222     const XMLCh* format_xml=NULL;
223     BIO* in = NULL;
224     
225     // Move to Key
226     const DOMElement* keynode=XMLHelper::getFirstChildElement(root,Key);
227     if (keynode) {
228         // Get raw format attrib value, but defer processing til later since may need to 
229         // determine format dynamically, and we need the Path for that.
230         format_xml=keynode->getAttributeNS(NULL,format);
231             
232         const XMLCh* password_xml=keynode->getAttributeNS(NULL,password);
233         if (password_xml) {
234             auto_ptr_char kp(password_xml);
235             m_keypass=kp.get();
236         }
237         
238         e=XMLHelper::getFirstChildElement(keynode,Path);
239         if (e && e->hasChildNodes()) {
240             const XMLCh* s=e->getFirstChild()->getNodeValue();
241             auto_ptr_char kpath(s);
242             m_keypath = kpath.get();
243             XMLToolingConfig::getConfig().getPathResolver()->resolve(m_keypath, PathResolver::XMLTOOLING_CFG_FILE);
244 #ifdef WIN32
245             struct _stat stat_buf;
246             if (_stat(m_keypath.c_str(), &stat_buf) != 0)
247 #else
248             struct stat stat_buf;
249             if (stat(m_keypath.c_str(), &stat_buf) != 0)
250 #endif
251             {
252                 log.error("key file (%s) can't be opened", m_keypath.c_str());
253                 throw XMLSecurityException("FilesystemCredentialResolver can't access key file ($1)",params(1,m_keypath.c_str()));
254             }
255         }
256         else {
257             log.error("Path element missing inside Key element");
258             throw XMLSecurityException("FilesystemCredentialResolver can't access key file, no Path element specified.");
259         }
260
261         // Determine the key encoding format dynamically, if not explicitly specified
262         if (format_xml && *format_xml) {
263             fformat = xmlFormatToFormat(format_xml);
264             if (fformat != UNKNOWN) {
265                 m_keyformat = fformat;
266             }
267             else {
268                 auto_ptr_char unknown(format_xml);
269                 log.error("configuration specifies unknown key encoding format (%s)", unknown.get());
270                 throw XMLSecurityException("FilesystemCredentialResolver configuration contains unknown key encoding format ($1)",params(1,unknown.get()));
271             }
272         }
273         else {
274             in=BIO_new(BIO_s_file_internal());
275             if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
276                 m_keyformat = getEncodingFormat(in);
277                 log.debug("key encoding format for (%s) dynamically resolved as (%s)", m_keypath.c_str(), formatToString(m_keyformat).c_str());
278             }
279             else {
280                 log.error("key file (%s) can't be read to determine encoding format", m_keypath.c_str());
281                 throw XMLSecurityException("FilesystemCredentialResolver can't read key file ($1) to determine encoding format",params(1,m_keypath.c_str()));
282             }
283             if (in)
284                 BIO_free(in);
285             in = NULL;    
286         }
287         
288         // Load the key.
289         key = loadKey();
290     }
291     
292     // Check for CRL.
293     const DOMElement* crlnode=XMLHelper::getFirstChildElement(root,CRL);
294     if (crlnode) {
295         // Get raw format attrib value, but defer processing til later since may need to 
296         // determine format dynamically, and we need the Path for that.
297         format_xml=crlnode->getAttributeNS(NULL,format);
298             
299         e=XMLHelper::getFirstChildElement(crlnode,Path);
300         if (e && e->hasChildNodes()) {
301             const XMLCh* s=e->getFirstChild()->getNodeValue();
302             auto_ptr_char kpath(s);
303             m_crlpath=kpath.get();
304             XMLToolingConfig::getConfig().getPathResolver()->resolve(m_crlpath, PathResolver::XMLTOOLING_CFG_FILE);
305 #ifdef WIN32
306             struct _stat stat_buf;
307             if (_stat(m_crlpath.c_str(), &stat_buf) != 0)
308 #else
309             struct stat stat_buf;
310             if (stat(m_crlpath.c_str(), &stat_buf) != 0)
311 #endif
312             {
313                 log.error("CRL file (%s) can't be opened", m_crlpath.c_str());
314                 throw XMLSecurityException("FilesystemCredentialResolver can't access CRL file ($1)",params(1,m_crlpath.c_str()));
315             }
316         }
317         else {
318             log.error("Path element missing inside CRL element");
319             throw XMLSecurityException("FilesystemCredentialResolver can't access CRL file, no Path element specified.");
320         }
321
322         // Determine the CRL encoding format dynamically, if not explicitly specified
323         if (format_xml && *format_xml) {
324             fformat = xmlFormatToFormat(format_xml);
325             if (fformat != UNKNOWN) {
326                 m_crlformat = fformat;
327             }
328             else {
329                 auto_ptr_char unknown(format_xml);
330                 log.error("configuration specifies unknown CRL encoding format (%s)", unknown.get());
331                 throw XMLSecurityException("FilesystemCredentialResolver configuration contains unknown CRL encoding format ($1)",params(1,unknown.get()));
332             }
333         }
334         else {
335             in=BIO_new(BIO_s_file_internal());
336             if (in && BIO_read_filename(in,m_crlpath.c_str())>0) {
337                 m_crlformat = getEncodingFormat(in);
338                 log.debug("CRL encoding format for (%s) dynamically resolved as (%s)", m_crlpath.c_str(), formatToString(m_crlformat).c_str());
339             }
340             else {
341                 log.error("CRL file (%s) can't be read to determine encoding format", m_crlpath.c_str());
342                 throw XMLSecurityException("FilesystemCredentialResolver can't read CRL file ($1) to determine encoding format",params(1,m_crlpath.c_str()));
343             }
344             if (in)
345                 BIO_free(in);
346             in = NULL;
347         }
348         
349         // Load the key.
350         crl = loadCRL();
351     }
352
353     // Check for Certificate
354     e=XMLHelper::getFirstChildElement(root,Certificate);
355     if (!e) {
356         m_credential = new FilesystemCredential(this,key,xseccerts,crl);
357         m_credential->addKeyNames(keynode);
358         m_credential->setUsage(usage);
359         m_credential->initKeyInfo();
360         return;
361     }
362     auto_ptr_char certpass(e->getAttributeNS(NULL,password));
363     
364     const DOMElement* ep=XMLHelper::getFirstChildElement(e,Path);
365     if (!ep || !ep->hasChildNodes()) {
366         log.error("Path element missing inside Certificate element or is empty");
367         delete key;
368         delete crl;
369         throw XMLSecurityException("FilesystemCredentialResolver can't access certificate file, missing or empty Path element.");
370     }
371     
372     auto_ptr_char certpath2(ep->getFirstChild()->getNodeValue());
373     string certpath(certpath2.get());
374     XMLToolingConfig::getConfig().getPathResolver()->resolve(certpath, PathResolver::XMLTOOLING_CFG_FILE);
375
376     format_xml=e->getAttributeNS(NULL,format);
377     if (format_xml && *format_xml) {
378         fformat = xmlFormatToFormat(format_xml);
379         if (fformat == UNKNOWN) {
380             auto_ptr_char unknown(format_xml);
381             log.error("configuration specifies unknown certificate encoding format (%s)", unknown.get());
382             delete key;
383             delete crl;
384             throw XMLSecurityException("FilesystemCredentialResolver configuration contains unknown certificate encoding format ($1)",params(1,unknown.get()));
385         }
386     }
387     
388     try {
389         X509* x=NULL;
390         PKCS12* p12=NULL;
391         in=BIO_new(BIO_s_file_internal());
392         if (in && BIO_read_filename(in,certpath.c_str())>0) {
393             if (!format_xml || !*format_xml) {
394                 // Determine the cert encoding format dynamically, if not explicitly specified
395                 fformat = getEncodingFormat(in);
396                 log.debug("certificate encoding format for (%s) dynamically resolved as (%s)", certpath.c_str(), formatToString(fformat).c_str());
397             }
398
399             Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).info(
400                 "loading certificate from file (%s)", certpath.c_str()
401                 );
402
403             switch(fformat) {
404                 case PEM:
405                     while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get())))
406                         m_certs.push_back(x);
407                     break;
408                                 
409                 case DER:
410                     x=d2i_X509_bio(in,NULL);
411                     if (x)
412                         m_certs.push_back(x);
413                     else {
414                         log_openssl();
415                         BIO_free(in);
416                         throw XMLSecurityException("FilesystemCredentialResolver unable to load DER certificate from file ($1)",params(1,certpath.c_str()));
417                     }
418                     break;
419
420                 case _PKCS12:
421                     p12=d2i_PKCS12_bio(in,NULL);
422                     if (p12) {
423                         PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);
424                         PKCS12_free(p12);
425                     }
426                     if (x) {
427                         m_certs.push_back(x);
428                         x=NULL;
429                     } else {
430                         log_openssl();
431                         BIO_free(in);
432                         throw XMLSecurityException("FilesystemCredentialResolver unable to load PKCS12 certificate from file ($1)",params(1,certpath.c_str()));
433                     }
434                     break;
435             } // end switch
436
437         } else {
438             log_openssl();
439             if (in) {
440                 BIO_free(in);
441                 in=NULL;
442             }
443             throw XMLSecurityException("FilesystemCredentialResolver unable to load certificate(s) from file ($1)",params(1,certpath.c_str()));
444         }
445         if (in) {
446             BIO_free(in);
447             in=NULL;
448         }
449
450         if (m_certs.empty())
451             throw XMLSecurityException("FilesystemCredentialResolver unable to load any certificate(s)");
452
453         // Load any extra CA files.
454         const DOMElement* extra=XMLHelper::getFirstChildElement(e,CAPath);
455         while (extra) {
456             if (!extra->hasChildNodes()) {
457                 log.warn("skipping empty CAPath element");
458                 extra = XMLHelper::getNextSiblingElement(extra,CAPath);
459                 continue;
460             }
461             auto_ptr_char capath2(extra->getFirstChild()->getNodeValue());
462             string capath(capath2.get());
463             XMLToolingConfig::getConfig().getPathResolver()->resolve(capath, PathResolver::XMLTOOLING_CFG_FILE);
464             x=NULL;
465             p12=NULL;
466             in=BIO_new(BIO_s_file_internal());
467             if (in && BIO_read_filename(in,capath.c_str())>0) {
468                 if (!format_xml || !*format_xml) {
469                     // Determine the cert encoding format dynamically, if not explicitly specified
470                     fformat = getEncodingFormat(in);
471                     log.debug("CA certificate encoding format for (%s) dynamically resolved as (%s)", capath.c_str(), formatToString(fformat).c_str());
472                 }
473
474                 Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).info(
475                     "loading CA certificate from file (%s)", capath.c_str()
476                     );
477
478                 switch (fformat) {
479                     case PEM:
480                         while (x=PEM_read_bio_X509(in,NULL,NULL,NULL))
481                             m_certs.push_back(x);
482                         break;
483
484                     case DER:
485                         x=d2i_X509_bio(in,NULL);
486                         if (x)
487                             m_certs.push_back(x);
488                         else {
489                             log_openssl();
490                             BIO_free(in);
491                             throw XMLSecurityException("FilesystemCredentialResolver unable to load DER CA certificate from file ($1)",params(1,capath.c_str()));
492                         }
493                         break;
494
495                     case _PKCS12:
496                         p12 = d2i_PKCS12_bio(in, NULL);
497                         if (p12) {
498                             PKCS12_parse(p12, NULL, NULL, &x, NULL);
499                             PKCS12_free(p12);
500                         }
501                         if (x) {
502                             m_certs.push_back(x);
503                             x=NULL;
504                         }
505                         else {
506                             log_openssl();
507                             BIO_free(in);
508                             throw XMLSecurityException("FilesystemCredentialResolver unable to load PKCS12 CA certificate from file ($1)",params(1,capath.c_str()));
509                         }
510                         break;
511                 } //end switch
512
513                 BIO_free(in);
514             }
515             else {
516                 if (in)
517                     BIO_free(in);
518                 log_openssl();
519                 log.error("CA certificate file (%s) can't be opened", capath.c_str());
520                 throw XMLSecurityException("FilesystemCredentialResolver can't open CA certificate file ($1)",params(1,capath.c_str()));
521             }
522             
523             extra = XMLHelper::getNextSiblingElement(extra,CAPath);
524         }
525     }
526     catch (XMLToolingException&) {
527         delete key;
528         delete crl;
529         for_each(m_certs.begin(), m_certs.end(), X509_free);
530         throw;
531     }
532
533     // Reflect certs over to XSEC form and wrap with credential object.
534     for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++)
535         xseccerts.push_back(new OpenSSLCryptoX509(*j));
536     if (!key && !xseccerts.empty())
537         key = xseccerts.front()->clonePublicKey();
538     m_credential = new FilesystemCredential(this, key, xseccerts, crl);
539     m_credential->addKeyNames(keynode);
540     m_credential->setUsage(usage);
541     m_credential->initKeyInfo();
542 }
543
544 XSECCryptoKey* FilesystemCredentialResolver::loadKey()
545 {
546 #ifdef _DEBUG
547     NDC ndc("loadKey");
548 #endif
549     Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).info(
550         "loading private key from file (%s)", m_keypath.c_str()
551         );
552
553     // Get a EVP_PKEY.
554     EVP_PKEY* pkey=NULL;
555     BIO* in=BIO_new(BIO_s_file_internal());
556     if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
557         switch (m_keyformat) {
558             case PEM:
559                 pkey=PEM_read_bio_PrivateKey(in, NULL, passwd_callback, const_cast<char*>(m_keypass.c_str()));
560                 break;
561             
562             case DER:
563                 pkey=d2i_PrivateKey_bio(in, NULL);
564                 break;
565                 
566             default: {
567                 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
568                 if (p12) {
569                     PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
570                     PKCS12_free(p12);
571                 }
572             }
573         }
574     }
575     if (in)
576         BIO_free(in);
577     
578     // Now map it to an XSEC wrapper.
579     if (pkey) {
580         XSECCryptoKey* ret=NULL;
581         switch (pkey->type) {
582             case EVP_PKEY_RSA:
583                 ret=new OpenSSLCryptoKeyRSA(pkey);
584                 break;
585                 
586             case EVP_PKEY_DSA:
587                 ret=new OpenSSLCryptoKeyDSA(pkey);
588                 break;
589             
590             default:
591                 Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).error("unsupported private key type");
592         }
593         EVP_PKEY_free(pkey);
594         if (ret)
595             return ret;
596     }
597
598     log_openssl();
599     throw XMLSecurityException("FilesystemCredentialResolver unable to load private key from file."); 
600 }
601
602 XSECCryptoX509CRL* FilesystemCredentialResolver::loadCRL()
603 {
604 #ifdef _DEBUG
605     NDC ndc("loadCRL");
606 #endif
607     Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver."FILESYSTEM_CREDENTIAL_RESOLVER).info(
608         "loading CRL from file (%s)", m_crlpath.c_str()
609         );
610
611     X509_CRL* crl=NULL;
612     BIO* in=BIO_new(BIO_s_file_internal());
613     if (in && BIO_read_filename(in,m_crlpath.c_str())>0) {
614         switch (m_crlformat) {
615             case PEM:
616                 crl=PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
617                 break;
618             
619             case DER:
620                 crl=d2i_X509_CRL_bio(in, NULL);
621                 break;
622         }
623     }
624     if (in)
625         BIO_free(in);
626     
627     // Now map it to an XSEC wrapper.
628     if (crl) {
629         XSECCryptoX509CRL* ret=new OpenSSLCryptoX509CRL(crl);
630         X509_CRL_free(crl);
631         return ret;
632     }
633
634     log_openssl();
635     throw XMLSecurityException("FilesystemCredentialResolver unable to load CRL from file."); 
636 }
637
638 // Used to determine the encoding format of credentials files
639 // dynamically. Supports: PEM, DER, PKCS12.
640 FilesystemCredentialResolver::format_t FilesystemCredentialResolver::getEncodingFormat(BIO* in) const
641 {
642     PKCS12* p12 = NULL;
643     format_t format;
644
645     const int READSIZE = 1;
646     char buf[READSIZE];
647     char b1;
648     int mark;
649
650     try {
651         if ( (mark = BIO_tell(in)) < 0 ) 
652             throw XMLSecurityException("getEncodingFormat: BIO_tell() can't get the file position");
653         if ( BIO_read(in, buf, READSIZE) <= 0 ) 
654             throw XMLSecurityException("getEncodingFormat: BIO_read() can't read from the stream");
655         if ( BIO_seek(in, mark) < 0 ) 
656             throw XMLSecurityException("getEncodingFormat: BIO_seek() can't reset the file position");
657     }
658     catch (...) {
659         log_openssl();
660         throw;
661     }
662
663     b1 = buf[0];
664
665     // This is a slight variation of the Java code by Chad La Joie.
666     //
667     // Check the first byte of the file.  If it's some kind of
668     // DER-encoded structure (including PKCS12), it will begin with ASCII 048.
669     // Otherwise, assume it's PEM.
670     if (b1 !=  48) {
671         format = PEM;
672     } else {
673         // Here we know it's DER-encoded, now try to parse it as a PKCS12
674         // ASN.1 structure.  If it fails, must be another kind of DER-encoded
675         // key/cert structure.  A little inefficient...but it works.
676         if ( (p12=d2i_PKCS12_bio(in,NULL)) == NULL ) {
677             format = DER;
678         } else {
679             format = _PKCS12;
680         }
681         if (p12)
682             PKCS12_free(p12);    
683         if ( BIO_seek(in, mark) < 0 ) {
684             log_openssl();
685             throw XMLSecurityException("getEncodingFormat: BIO_seek() can't reset the file position");
686         }
687     }
688
689     return format;
690 }
691
692 // Convert key/cert format_t types to a human-meaningful string for debug output
693 string FilesystemCredentialResolver::formatToString(format_t format) const
694 {
695     switch(format) {
696         case PEM:
697             return "PEM";
698         case DER:
699             return "DER";
700         case _PKCS12:
701             return "PKCS12";
702         default:
703             return "UNKNOWN";
704     }
705 }
706
707 // Convert key/cert raw XML format attribute (XMLCh[]) to format_t type
708 FilesystemCredentialResolver::format_t FilesystemCredentialResolver::xmlFormatToFormat(const XMLCh* format_xml) const
709 {
710     static const XMLCh cPEM[] = UNICODE_LITERAL_3(P,E,M);
711     static const XMLCh cDER[] = UNICODE_LITERAL_3(D,E,R);
712     static const XMLCh cPKCS12[] = { chLatin_P, chLatin_K, chLatin_C, chLatin_S, chDigit_1, chDigit_2, chNull };
713     format_t format;
714
715     if (!XMLString::compareString(format_xml,cPEM))
716         format=PEM;
717     else if (!XMLString::compareString(format_xml,cDER))
718         format=DER;
719     else if (!XMLString::compareString(format_xml,cPKCS12))
720         format=_PKCS12;
721     else
722         format=UNKNOWN;
723
724     return format;
725 }
726
727 void FilesystemCredentialResolver::attach(SSL_CTX* ctx) const
728 {
729 #ifdef _DEBUG
730     NDC ndc("attach");
731 #endif
732
733     if (m_keypath.empty())
734         throw XMLSecurityException("No key available, unable to attach private key to SSL context.");
735
736     // Attach key.
737     SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
738     SSL_CTX_set_default_passwd_cb_userdata(ctx, const_cast<char*>(m_keypass.c_str()));
739
740     int ret=0;
741     switch (m_keyformat) {
742         case PEM:
743             ret=SSL_CTX_use_PrivateKey_file(ctx, m_keypath.c_str(), m_keyformat);
744             break;
745             
746         case DER:
747             ret=SSL_CTX_use_RSAPrivateKey_file(ctx, m_keypath.c_str(), m_keyformat);
748             break;
749             
750         default: {
751             BIO* in=BIO_new(BIO_s_file_internal());
752             if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
753                 EVP_PKEY* pkey=NULL;
754                 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
755                 if (p12) {
756                     PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);
757                     PKCS12_free(p12);
758                     if (pkey) {
759                         ret=SSL_CTX_use_PrivateKey(ctx, pkey);
760                         EVP_PKEY_free(pkey);
761                     }
762                 }
763             }
764             if (in)
765                 BIO_free(in);
766         }
767     }
768     
769     if (ret!=1) {
770         log_openssl();
771         throw XMLSecurityException("Unable to attach private key to SSL context.");
772     }
773
774     // Attach certs.
775     for (vector<X509*>::const_iterator i=m_certs.begin(); i!=m_certs.end(); i++) {
776         if (i==m_certs.begin()) {
777             if (SSL_CTX_use_certificate(ctx, *i) != 1) {
778                 log_openssl();
779                 throw XMLSecurityException("Unable to attach client certificate to SSL context.");
780             }
781         }
782         else {
783             // When we add certs, they don't get ref counted, so we need to duplicate them.
784             X509* dup = X509_dup(*i);
785             if (SSL_CTX_add_extra_chain_cert(ctx, dup) != 1) {
786                 X509_free(dup);
787                 log_openssl();
788                 throw XMLSecurityException("Unable to attach CA certificate to SSL context.");
789             }
790         }
791     }
792 }
793
794 void FilesystemCredential::addKeyNames(const DOMElement* e)
795 {
796     e = e ? XMLHelper::getFirstChildElement(e, Name) : NULL;
797     while (e) {
798         if (e->hasChildNodes()) {
799             auto_ptr_char n(e->getFirstChild()->getNodeValue());
800             if (n.get() && *n.get())
801                 m_keyNames.insert(n.get());
802         }
803         e = XMLHelper::getNextSiblingElement(e, Name);
804     }
805 }
806
807 void FilesystemCredential::attach(SSL_CTX* ctx) const
808 {
809     return m_resolver->attach(ctx);
810 }