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