gcc const fix, converted linefeeds
[shibboleth/cpp-xmltooling.git] / xmltooling / signature / impl / InlineKeyResolver.cpp
1 /*
2  *  Copyright 2001-2005 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  * InlineKeyResolver.cpp
19  * 
20  * Resolves key information directly from recognized KeyInfo structures.
21  */
22
23 #include "internal.h"
24 #include "signature/CachingKeyResolver.h"
25 #include "signature/KeyInfo.h"
26 #include "util/NDC.h"
27 #include "util/Threads.h"
28 #include "util/XMLConstants.h"
29 #include "validation/ValidatorSuite.h"
30
31 #include <log4cpp/Category.hh>
32 #include <xercesc/util/XMLUniDefs.hpp>
33 #include <xsec/dsig/DSIGKeyInfoX509.hpp>
34 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
35 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
36 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
37 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
38 #include <xsec/enc/XSECCryptoException.hpp>
39 #include <xsec/framework/XSECException.hpp>
40
41 using namespace xmlsignature;
42 using namespace xmltooling;
43 using namespace log4cpp;
44 using namespace std;
45
46 namespace xmlsignature {
47     class XMLTOOL_DLLLOCAL InlineKeyResolver : public CachingKeyResolver
48     {
49     public:
50         InlineKeyResolver(const DOMElement* e);
51         virtual ~InlineKeyResolver();
52
53         XSECCryptoKey* resolveKey(const KeyInfo* keyInfo) const;
54         XSECCryptoKey* resolveKey(DSIGKeyInfoList* keyInfo) const;
55         vector<XSECCryptoX509*>::size_type resolveCertificates(const KeyInfo* keyInfo, ResolvedCertificates& certs) const;
56         vector<XSECCryptoX509*>::size_type resolveCertificates(DSIGKeyInfoList* keyInfo, ResolvedCertificates& certs) const;
57         XSECCryptoX509CRL* resolveCRL(const KeyInfo* keyInfo) const;
58         XSECCryptoX509CRL* resolveCRL(DSIGKeyInfoList* keyInfo) const;
59         
60         void clearCache() {
61             if (m_lock)
62                 m_lock->wrlock();
63             m_cache.clear();
64             if (m_lock)
65                 m_lock->unlock();
66         }
67         
68     private:
69         struct XMLTOOL_DLLLOCAL CacheEntry {
70             CacheEntry() : m_key(NULL), m_crl(NULL) {}
71             ~CacheEntry() {
72                 delete m_key;
73                 for_each(m_certs.begin(),m_certs.end(),xmltooling::cleanup<XSECCryptoX509>());
74                 delete m_crl;
75             }
76             XSECCryptoKey* m_key;
77             vector<XSECCryptoX509*> m_certs;
78             XSECCryptoX509CRL* m_crl;
79         };
80
81         void _resolve(const KeyInfo* keyInfo, CacheEntry& entry) const;
82         XSECCryptoKey* _resolveKey(const KeyInfo* keyInfo) const;
83         vector<XSECCryptoX509*>::size_type _resolveCertificates(const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs) const;
84         XSECCryptoX509CRL* _resolveCRL(const KeyInfo* keyInfo) const;
85
86         RWLock* m_lock;
87         mutable map<const KeyInfo*,CacheEntry> m_cache;
88     };
89
90     KeyResolver* XMLTOOL_DLLLOCAL InlineKeyResolverFactory(const DOMElement* const & e)
91     {
92         return new InlineKeyResolver(e);
93     }
94 };
95
96 static const XMLCh cache[] = UNICODE_LITERAL_5(c,a,c,h,e);
97
98 InlineKeyResolver::InlineKeyResolver(const DOMElement* e) : m_lock(NULL)
99 {
100     const XMLCh* flag = e ? e->getAttributeNS(NULL,cache) : NULL;
101     if (flag && XMLString::equals(flag,XMLConstants::XML_TRUE) || XMLString::equals(flag,XMLConstants::XML_ONE))
102         m_lock=RWLock::create();
103 }
104
105 InlineKeyResolver::~InlineKeyResolver()
106 {
107     clearCache();
108     delete m_lock;
109 }
110
111 void InlineKeyResolver::_resolve(const KeyInfo* keyInfo, CacheEntry& entry) const
112 {
113     if (_resolveCertificates(keyInfo, entry.m_certs)>0)
114         entry.m_key = entry.m_certs.front()->clonePublicKey();
115     else
116         entry.m_key = _resolveKey(keyInfo);
117     entry.m_crl = _resolveCRL(keyInfo);
118 }
119
120 XSECCryptoKey* InlineKeyResolver::_resolveKey(const KeyInfo* keyInfo) const
121 {
122 #ifdef _DEBUG
123     NDC ndc("_resolveKey");
124 #endif
125     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver");
126
127     if (!keyInfo)
128         return NULL;
129
130     // Check for ds:X509Data
131     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();
132     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); j!=x509Datas.end(); ++j) {
133         try {
134             const vector<X509Certificate*> x509Certs=const_cast<const X509Data*>(*j)->getX509Certificates();
135             if (!x509Certs.empty()) {
136                 auto_ptr_char x(x509Certs.front()->getValue());
137                 if (!x.get()) {
138                     log.warn("skipping empty ds:X509Certificate");
139                 }
140                 else {
141                     log.debug("resolving ds:X509Certificate");
142                     auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
143                     x509->loadX509Base64Bin(x.get(), strlen(x.get()));
144                     return x509->clonePublicKey();
145                 }
146             }
147         }
148         catch(XSECException& e) {
149             auto_ptr_char temp(e.getMsg());
150             log.error("caught XML-Security exception loading certificate: %s", temp.get());
151         }
152         catch(XSECCryptoException& e) {
153             log.error("caught XML-Security exception loading certificate: %s", e.getMsg());
154         }
155     }
156
157     // Check for ds:KeyValue
158     const vector<KeyValue*>& keyValues = keyInfo->getKeyValues();
159     for (vector<KeyValue*>::const_iterator i=keyValues.begin(); i!=keyValues.end(); ++i) {
160         try {
161             SchemaValidators.validate(*i);    // see if it's a "valid" key
162             RSAKeyValue* rsakv = (*i)->getRSAKeyValue();
163             if (rsakv) {
164                 log.debug("resolving ds:RSAKeyValue");
165                 auto_ptr_char mod(rsakv->getModulus()->getValue());
166                 auto_ptr_char exp(rsakv->getExponent()->getValue());
167                 auto_ptr<XSECCryptoKeyRSA> rsa(XSECPlatformUtils::g_cryptoProvider->keyRSA());
168                 rsa->loadPublicModulusBase64BigNums(mod.get(), strlen(mod.get()));
169                 rsa->loadPublicExponentBase64BigNums(exp.get(), strlen(exp.get()));
170                 return rsa.release();
171             }
172             DSAKeyValue* dsakv = (*i)->getDSAKeyValue();
173             if (dsakv) {
174                 log.debug("resolving ds:DSAKeyValue");
175                 auto_ptr<XSECCryptoKeyDSA> dsa(XSECPlatformUtils::g_cryptoProvider->keyDSA());
176                 auto_ptr_char y(dsakv->getY()->getValue());
177                 dsa->loadYBase64BigNums(y.get(), strlen(y.get()));
178                 if (dsakv->getP()) {
179                     auto_ptr_char p(dsakv->getP()->getValue());
180                     dsa->loadPBase64BigNums(p.get(), strlen(p.get()));
181                 }
182                 if (dsakv->getQ()) {
183                     auto_ptr_char q(dsakv->getQ()->getValue());
184                     dsa->loadQBase64BigNums(q.get(), strlen(q.get()));
185                 }
186                 if (dsakv->getG()) {
187                     auto_ptr_char g(dsakv->getG()->getValue());
188                     dsa->loadGBase64BigNums(g.get(), strlen(g.get()));
189                 }
190                 return dsa.release();
191             }
192         }
193         catch (ValidationException& ex) {
194             log.warn("skipping invalid ds:KeyValue (%s)", ex.what());
195         }
196         catch(XSECException& e) {
197             auto_ptr_char temp(e.getMsg());
198             log.error("caught XML-Security exception loading key: %s", temp.get());
199         }
200         catch(XSECCryptoException& e) {
201             log.error("caught XML-Security exception loading key: %s", e.getMsg());
202         }
203     }
204
205     log.warn("unable to resolve key");
206     return NULL;
207 }
208
209 vector<XSECCryptoX509*>::size_type InlineKeyResolver::_resolveCertificates(
210     const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs
211     ) const
212 {
213 #ifdef _DEBUG
214     NDC ndc("_resolveCertificates");
215 #endif
216     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver");
217
218     if (!keyInfo)
219         return 0;
220
221     // Check for ds:X509Data
222     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();
223     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); certs.empty() && j!=x509Datas.end(); ++j) {
224         const vector<X509Certificate*> x509Certs=const_cast<const X509Data*>(*j)->getX509Certificates();
225         for (vector<X509Certificate*>::const_iterator k=x509Certs.begin(); k!=x509Certs.end(); ++k) {
226             try {
227                 auto_ptr_char x((*k)->getValue());
228                 if (!x.get()) {
229                     log.warn("skipping empty ds:X509Certificate");
230                 }
231                 else {
232                     log.debug("resolving ds:X509Certificate");
233                     auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
234                     x509->loadX509Base64Bin(x.get(), strlen(x.get()));
235                     certs.push_back(x509.release());
236                 }
237             }
238             catch(XSECException& e) {
239                 auto_ptr_char temp(e.getMsg());
240                 log.error("caught XML-Security exception loading certificate: %s", temp.get());
241             }
242             catch(XSECCryptoException& e) {
243                 log.error("caught XML-Security exception loading certificate: %s", e.getMsg());
244             }
245         }
246     }
247     if (log.isDebugEnabled()) {
248         log.debug("resolved %d certificate%s", certs.size(), certs.size()==1 ? "" : "s");
249     }
250     return certs.size();
251 }
252
253 XSECCryptoX509CRL* InlineKeyResolver::_resolveCRL(const KeyInfo* keyInfo) const
254 {
255 #ifdef _DEBUG
256     NDC ndc("_resolveCRL");
257 #endif
258     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver");
259
260     if (!keyInfo)
261         return NULL;
262
263     // Check for ds:X509Data
264     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();
265     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); j!=x509Datas.end(); ++j) {
266         const vector<X509CRL*> x509CRLs=const_cast<const X509Data*>(*j)->getX509CRLs();
267         for (vector<X509CRL*>::const_iterator k=x509CRLs.begin(); k!=x509CRLs.end(); ++k) {
268             try {
269                 auto_ptr_char x((*k)->getValue());
270                 if (!x.get()) {
271                     log.warn("skipping empty ds:X509CRL");
272                 }
273                 else {
274                     log.debug("resolving ds:X509CRL");
275                     auto_ptr<XSECCryptoX509CRL> crl(XMLToolingConfig::getConfig().X509CRL());
276                     crl->loadX509CRLBase64Bin(x.get(), strlen(x.get()));
277                     return crl.release();
278                 }
279             }
280             catch(XSECException& e) {
281                 auto_ptr_char temp(e.getMsg());
282                 log.error("caught XML-Security exception loading certificate: %s", temp.get());
283             }
284             catch(XSECCryptoException& e) {
285                 log.error("caught XML-Security exception loading certificate: %s", e.getMsg());
286             }
287         }
288     }
289     return NULL;
290 }
291
292 XSECCryptoKey* InlineKeyResolver::resolveKey(const KeyInfo* keyInfo) const
293 {
294     // Caching?
295     if (m_lock) {
296         // Get read lock.
297         m_lock->rdlock();
298         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);
299         if (i != m_cache.end()) {
300             // Found in cache, so just return the results.
301             SharedLock locker(m_lock,false);
302             return i->second.m_key ? i->second.m_key->clone() : NULL;
303         }
304         else {
305             // Elevate lock.
306             m_lock->unlock();
307             m_lock->wrlock();
308             SharedLock locker(m_lock,false);
309             // Recheck cache.
310             i=m_cache.find(keyInfo);
311             if (i == m_cache.end()) {
312                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;
313                 _resolve(i->first, i->second);
314             }
315             return i->second.m_key ? i->second.m_key->clone() : NULL;
316         }
317     }
318     return _resolveKey(keyInfo);
319 }
320
321 XSECCryptoX509CRL* InlineKeyResolver::resolveCRL(const KeyInfo* keyInfo) const
322 {
323     // Caching?
324     if (m_lock) {
325         // Get read lock.
326         m_lock->rdlock();
327         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);
328         if (i != m_cache.end()) {
329             // Found in cache, so just return the results.
330             SharedLock locker(m_lock,false);
331             return i->second.m_crl ? i->second.m_crl->clone() : NULL;
332         }
333         else {
334             // Elevate lock.
335             m_lock->unlock();
336             m_lock->wrlock();
337             SharedLock locker(m_lock,false);
338             // Recheck cache.
339             i=m_cache.find(keyInfo);
340             if (i == m_cache.end()) {
341                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;
342                 _resolve(i->first, i->second);
343             }
344             return i->second.m_crl ? i->second.m_crl->clone() : NULL;
345         }
346     }
347     return _resolveCRL(keyInfo);
348 }
349
350 vector<XSECCryptoX509*>::size_type InlineKeyResolver::resolveCertificates(
351     const KeyInfo* keyInfo, ResolvedCertificates& certs
352     ) const
353 {
354     // Caching?
355     if (m_lock) {
356         // Get read lock.
357         m_lock->rdlock();
358         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);
359         if (i != m_cache.end()) {
360             // Found in cache, so just return the results.
361             SharedLock locker(m_lock,false);
362             accessCertificates(certs).assign(i->second.m_certs.begin(), i->second.m_certs.end());
363             accessOwned(certs) = false;
364             return accessCertificates(certs).size();
365         }
366         else {
367             // Elevate lock.
368             m_lock->unlock();
369             m_lock->wrlock();
370             SharedLock locker(m_lock,false);
371             // Recheck cache.
372             i=m_cache.find(keyInfo);
373             if (i == m_cache.end()) {
374                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;
375                 _resolve(i->first, i->second);
376             }
377             accessCertificates(certs).assign(i->second.m_certs.begin(), i->second.m_certs.end());
378             accessOwned(certs) = false;
379             return accessCertificates(certs).size();
380         }
381     }
382     accessOwned(certs) = true;
383     return _resolveCertificates(keyInfo, accessCertificates(certs));
384 }
385
386 XSECCryptoKey* InlineKeyResolver::resolveKey(DSIGKeyInfoList* keyInfo) const
387 {
388 #ifdef _DEBUG
389     NDC ndc("resolveKey");
390 #endif
391
392     if (!keyInfo)
393         return NULL;
394
395     // Default resolver handles RSA/DSAKeyValue and X509Certificate elements.
396     try {
397         XSECKeyInfoResolverDefault def;
398         return def.resolveKey(keyInfo);
399     }
400     catch(XSECException& e) {
401         auto_ptr_char temp(e.getMsg());
402         Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading certificate: %s", temp.get());
403     }
404     catch(XSECCryptoException& e) {
405         Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading certificate: %s", e.getMsg());
406     }
407     return NULL;
408 }
409
410 vector<XSECCryptoX509*>::size_type InlineKeyResolver::resolveCertificates(
411     DSIGKeyInfoList* keyInfo, ResolvedCertificates& certs
412     ) const
413 {
414     accessCertificates(certs).clear();
415     accessOwned(certs) = false;
416
417     if (!keyInfo)
418         return 0;
419
420         DSIGKeyInfoList::size_type sz = keyInfo->getSize();
421     for (DSIGKeyInfoList::size_type i=0; accessCertificates(certs).empty() && i<sz; ++i) {
422         if (keyInfo->item(i)->getKeyInfoType()==DSIGKeyInfo::KEYINFO_X509) {
423             DSIGKeyInfoX509* x509 = static_cast<DSIGKeyInfoX509*>(keyInfo->item(i));
424             int count = x509->getCertificateListSize();
425             for (int j=0; j<count; ++j) {
426                 accessCertificates(certs).push_back(x509->getCertificateCryptoItem(j));
427             }
428         }
429     }
430     return accessCertificates(certs).size();
431 }
432
433 XSECCryptoX509CRL* InlineKeyResolver::resolveCRL(DSIGKeyInfoList* keyInfo) const
434 {
435 #ifdef _DEBUG
436     NDC ndc("resolveCRL");
437 #endif
438
439     if (!keyInfo)
440         return NULL;
441
442     DSIGKeyInfoList::size_type sz = keyInfo->getSize();
443     for (DSIGKeyInfoList::size_type i=0; i<sz; ++i) {
444         if (keyInfo->item(i)->getKeyInfoType()==DSIGKeyInfo::KEYINFO_X509) {
445             auto_ptr_char buf(static_cast<DSIGKeyInfoX509*>(keyInfo->item(i))->getX509CRL());
446             if (buf.get()) {
447                 try {
448                     auto_ptr<XSECCryptoX509CRL> crlobj(XMLToolingConfig::getConfig().X509CRL());
449                     crlobj->loadX509CRLBase64Bin(buf.get(), strlen(buf.get()));
450                     return crlobj.release();
451                 }
452                 catch(XSECException& e) {
453                     auto_ptr_char temp(e.getMsg());
454                     Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading CRL: %s", temp.get());
455                 }
456                 catch(XSECCryptoException& e) {
457                     Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading CRL: %s", e.getMsg());
458                 }
459             }
460         }
461     }
462     return NULL;
463 }