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