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