5fb2d612591b2b7ba1835781abe631cea897a1f5
[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     if (!keyInfo)\r
126         return NULL;\r
127 \r
128     // Check for ds:X509Data\r
129     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();\r
130     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); j!=x509Datas.end(); ++j) {\r
131         try {\r
132             const vector<X509Certificate*> x509Certs=const_cast<const X509Data*>(*j)->getX509Certificates();\r
133             if (!x509Certs.empty()) {\r
134                 auto_ptr_char x(x509Certs.front()->getValue());\r
135                 if (!x.get()) {\r
136                     log.warn("skipping empty ds:X509Certificate");\r
137                 }\r
138                 else {\r
139                     log.debug("resolving ds:X509Certificate");\r
140                     auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());\r
141                     x509->loadX509Base64Bin(x.get(), strlen(x.get()));\r
142                     return x509->clonePublicKey();\r
143                 }\r
144             }\r
145         }\r
146         catch(XSECException& e) {\r
147             auto_ptr_char temp(e.getMsg());\r
148             log.error("caught XML-Security exception loading certificate: %s", temp.get());\r
149         }\r
150         catch(XSECCryptoException& e) {\r
151             log.error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
152         }\r
153     }\r
154 \r
155     // Check for ds:KeyValue\r
156     const vector<KeyValue*>& keyValues = keyInfo->getKeyValues();\r
157     for (vector<KeyValue*>::const_iterator i=keyValues.begin(); i!=keyValues.end(); ++i) {\r
158         try {\r
159             KeyInfoSchemaValidators.validate(*i);    // see if it's a "valid" key\r
160             RSAKeyValue* rsakv = (*i)->getRSAKeyValue();\r
161             if (rsakv) {\r
162                 log.debug("resolving ds:RSAKeyValue");\r
163                 auto_ptr_char mod(rsakv->getModulus()->getValue());\r
164                 auto_ptr_char exp(rsakv->getExponent()->getValue());\r
165                 auto_ptr<XSECCryptoKeyRSA> rsa(XSECPlatformUtils::g_cryptoProvider->keyRSA());\r
166                 rsa->loadPublicModulusBase64BigNums(mod.get(), strlen(mod.get()));\r
167                 rsa->loadPublicExponentBase64BigNums(exp.get(), strlen(exp.get()));\r
168                 return rsa.release();\r
169             }\r
170             DSAKeyValue* dsakv = (*i)->getDSAKeyValue();\r
171             if (dsakv) {\r
172                 log.debug("resolving ds:DSAKeyValue");\r
173                 auto_ptr<XSECCryptoKeyDSA> dsa(XSECPlatformUtils::g_cryptoProvider->keyDSA());\r
174                 auto_ptr_char y(dsakv->getY()->getValue());\r
175                 dsa->loadYBase64BigNums(y.get(), strlen(y.get()));\r
176                 if (dsakv->getP()) {\r
177                     auto_ptr_char p(dsakv->getP()->getValue());\r
178                     dsa->loadPBase64BigNums(p.get(), strlen(p.get()));\r
179                 }\r
180                 if (dsakv->getQ()) {\r
181                     auto_ptr_char q(dsakv->getQ()->getValue());\r
182                     dsa->loadQBase64BigNums(q.get(), strlen(q.get()));\r
183                 }\r
184                 if (dsakv->getG()) {\r
185                     auto_ptr_char g(dsakv->getG()->getValue());\r
186                     dsa->loadGBase64BigNums(g.get(), strlen(g.get()));\r
187                 }\r
188                 return dsa.release();\r
189             }\r
190         }\r
191         catch (ValidationException& ex) {\r
192             log.warn("skipping invalid ds:KeyValue (%s)", ex.what());\r
193         }\r
194         catch(XSECException& e) {\r
195             auto_ptr_char temp(e.getMsg());\r
196             log.error("caught XML-Security exception loading key: %s", temp.get());\r
197         }\r
198         catch(XSECCryptoException& e) {\r
199             log.error("caught XML-Security exception loading key: %s", e.getMsg());\r
200         }\r
201     }\r
202 \r
203     log.warn("unable to resolve key");\r
204     return NULL;\r
205 }\r
206 \r
207 vector<XSECCryptoX509*>::size_type InlineKeyResolver::_resolveCertificates(\r
208     const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs\r
209     ) const\r
210 {\r
211 #ifdef _DEBUG\r
212     NDC ndc("_resolveCertificates");\r
213 #endif\r
214     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver");\r
215 \r
216     if (!keyInfo)\r
217         return 0;\r
218 \r
219     // Check for ds:X509Data\r
220     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();\r
221     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); certs.empty() && j!=x509Datas.end(); ++j) {\r
222         const vector<X509Certificate*> x509Certs=const_cast<const X509Data*>(*j)->getX509Certificates();\r
223         for (vector<X509Certificate*>::const_iterator k=x509Certs.begin(); k!=x509Certs.end(); ++k) {\r
224             try {\r
225                 auto_ptr_char x((*k)->getValue());\r
226                 if (!x.get()) {\r
227                     log.warn("skipping empty ds:X509Certificate");\r
228                 }\r
229                 else {\r
230                     log.debug("resolving ds:X509Certificate");\r
231                     auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());\r
232                     x509->loadX509Base64Bin(x.get(), strlen(x.get()));\r
233                     certs.push_back(x509.release());\r
234                 }\r
235             }\r
236             catch(XSECException& e) {\r
237                 auto_ptr_char temp(e.getMsg());\r
238                 log.error("caught XML-Security exception loading certificate: %s", temp.get());\r
239             }\r
240             catch(XSECCryptoException& e) {\r
241                 log.error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
242             }\r
243         }\r
244     }\r
245     if (log.isDebugEnabled()) {\r
246         log.debug("resolved %d certificate%s", certs.size(), certs.size()==1 ? "" : "s");\r
247     }\r
248     return certs.size();\r
249 }\r
250 \r
251 XSECCryptoX509CRL* InlineKeyResolver::_resolveCRL(const KeyInfo* keyInfo) const\r
252 {\r
253 #ifdef _DEBUG\r
254     NDC ndc("_resolveCRL");\r
255 #endif\r
256     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver");\r
257 \r
258     if (!keyInfo)\r
259         return NULL;\r
260 \r
261     // Check for ds:X509Data\r
262     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();\r
263     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); j!=x509Datas.end(); ++j) {\r
264         const vector<X509CRL*> x509CRLs=const_cast<const X509Data*>(*j)->getX509CRLs();\r
265         for (vector<X509CRL*>::const_iterator k=x509CRLs.begin(); k!=x509CRLs.end(); ++k) {\r
266             try {\r
267                 auto_ptr_char x((*k)->getValue());\r
268                 if (!x.get()) {\r
269                     log.warn("skipping empty ds:X509CRL");\r
270                 }\r
271                 else {\r
272                     log.debug("resolving ds:X509CRL");\r
273                     auto_ptr<XSECCryptoX509CRL> crl(XMLToolingConfig::getConfig().X509CRL());\r
274                     crl->loadX509CRLBase64Bin(x.get(), strlen(x.get()));\r
275                     return crl.release();\r
276                 }\r
277             }\r
278             catch(XSECException& e) {\r
279                 auto_ptr_char temp(e.getMsg());\r
280                 log.error("caught XML-Security exception loading certificate: %s", temp.get());\r
281             }\r
282             catch(XSECCryptoException& e) {\r
283                 log.error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
284             }\r
285         }\r
286     }\r
287     return NULL;\r
288 }\r
289 \r
290 XSECCryptoKey* InlineKeyResolver::resolveKey(const KeyInfo* keyInfo) const\r
291 {\r
292     // Caching?\r
293     if (m_lock) {\r
294         // Get read lock.\r
295         m_lock->rdlock();\r
296         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);\r
297         if (i != m_cache.end()) {\r
298             // Found in cache, so just return the results.\r
299             SharedLock locker(m_lock,false);\r
300             return i->second.m_key ? i->second.m_key->clone() : NULL;\r
301         }\r
302         else {\r
303             // Elevate lock.\r
304             m_lock->unlock();\r
305             m_lock->wrlock();\r
306             SharedLock locker(m_lock,false);\r
307             // Recheck cache.\r
308             i=m_cache.find(keyInfo);\r
309             if (i == m_cache.end()) {\r
310                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;\r
311                 _resolve(i->first, i->second);\r
312             }\r
313             return i->second.m_key ? i->second.m_key->clone() : NULL;\r
314         }\r
315     }\r
316     return _resolveKey(keyInfo);\r
317 }\r
318 \r
319 XSECCryptoX509CRL* InlineKeyResolver::resolveCRL(const KeyInfo* keyInfo) const\r
320 {\r
321     // Caching?\r
322     if (m_lock) {\r
323         // Get read lock.\r
324         m_lock->rdlock();\r
325         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);\r
326         if (i != m_cache.end()) {\r
327             // Found in cache, so just return the results.\r
328             SharedLock locker(m_lock,false);\r
329             return i->second.m_crl ? i->second.m_crl->clone() : NULL;\r
330         }\r
331         else {\r
332             // Elevate lock.\r
333             m_lock->unlock();\r
334             m_lock->wrlock();\r
335             SharedLock locker(m_lock,false);\r
336             // Recheck cache.\r
337             i=m_cache.find(keyInfo);\r
338             if (i == m_cache.end()) {\r
339                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;\r
340                 _resolve(i->first, i->second);\r
341             }\r
342             return i->second.m_crl ? i->second.m_crl->clone() : NULL;\r
343         }\r
344     }\r
345     return _resolveCRL(keyInfo);\r
346 }\r
347 \r
348 vector<XSECCryptoX509*>::size_type InlineKeyResolver::resolveCertificates(\r
349     const KeyInfo* keyInfo, ResolvedCertificates& certs\r
350     ) const\r
351 {\r
352     // Caching?\r
353     if (m_lock) {\r
354         // Get read lock.\r
355         m_lock->rdlock();\r
356         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);\r
357         if (i != m_cache.end()) {\r
358             // Found in cache, so just return the results.\r
359             SharedLock locker(m_lock,false);\r
360             accessCertificates(certs).assign(i->second.m_certs.begin(), i->second.m_certs.end());\r
361             accessOwned(certs) = false;\r
362             return accessCertificates(certs).size();\r
363         }\r
364         else {\r
365             // Elevate lock.\r
366             m_lock->unlock();\r
367             m_lock->wrlock();\r
368             SharedLock locker(m_lock,false);\r
369             // Recheck cache.\r
370             i=m_cache.find(keyInfo);\r
371             if (i == m_cache.end()) {\r
372                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;\r
373                 _resolve(i->first, i->second);\r
374             }\r
375             accessCertificates(certs).assign(i->second.m_certs.begin(), i->second.m_certs.end());\r
376             accessOwned(certs) = false;\r
377             return accessCertificates(certs).size();\r
378         }\r
379     }\r
380     accessOwned(certs) = true;\r
381     return _resolveCertificates(keyInfo, accessCertificates(certs));\r
382 }\r
383 \r
384 XSECCryptoKey* InlineKeyResolver::resolveKey(DSIGKeyInfoList* keyInfo) const\r
385 {\r
386 #ifdef _DEBUG\r
387     NDC ndc("resolveKey");\r
388 #endif\r
389 \r
390     if (!keyInfo)\r
391         return NULL;\r
392 \r
393     // Default resolver handles RSA/DSAKeyValue and X509Certificate elements.\r
394     try {\r
395         XSECKeyInfoResolverDefault def;\r
396         return def.resolveKey(keyInfo);\r
397     }\r
398     catch(XSECException& e) {\r
399         auto_ptr_char temp(e.getMsg());\r
400         Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading certificate: %s", temp.get());\r
401     }\r
402     catch(XSECCryptoException& e) {\r
403         Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
404     }\r
405     return NULL;\r
406 }\r
407 \r
408 vector<XSECCryptoX509*>::size_type InlineKeyResolver::resolveCertificates(\r
409     DSIGKeyInfoList* keyInfo, ResolvedCertificates& certs\r
410     ) const\r
411 {\r
412     accessCertificates(certs).clear();\r
413     accessOwned(certs) = false;\r
414 \r
415     if (!keyInfo)\r
416         return 0;\r
417 \r
418         DSIGKeyInfoList::size_type sz = keyInfo->getSize();\r
419     for (DSIGKeyInfoList::size_type i=0; accessCertificates(certs).empty() && i<sz; ++i) {\r
420         if (keyInfo->item(i)->getKeyInfoType()==DSIGKeyInfo::KEYINFO_X509) {\r
421             DSIGKeyInfoX509* x509 = static_cast<DSIGKeyInfoX509*>(keyInfo->item(i));\r
422             int count = x509->getCertificateListSize();\r
423             for (int j=0; j<count; ++j) {\r
424                 accessCertificates(certs).push_back(x509->getCertificateCryptoItem(j));\r
425             }\r
426         }\r
427     }\r
428     return accessCertificates(certs).size();\r
429 }\r
430 \r
431 XSECCryptoX509CRL* InlineKeyResolver::resolveCRL(DSIGKeyInfoList* keyInfo) const\r
432 {\r
433 #ifdef _DEBUG\r
434     NDC ndc("resolveCRL");\r
435 #endif\r
436 \r
437     if (!keyInfo)\r
438         return NULL;\r
439 \r
440     DSIGKeyInfoList::size_type sz = keyInfo->getSize();\r
441     for (DSIGKeyInfoList::size_type i=0; i<sz; ++i) {\r
442         if (keyInfo->item(i)->getKeyInfoType()==DSIGKeyInfo::KEYINFO_X509) {\r
443             auto_ptr_char buf(static_cast<DSIGKeyInfoX509*>(keyInfo->item(i))->getX509CRL());\r
444             if (buf.get()) {\r
445                 try {\r
446                     auto_ptr<XSECCryptoX509CRL> crlobj(XMLToolingConfig::getConfig().X509CRL());\r
447                     crlobj->loadX509CRLBase64Bin(buf.get(), strlen(buf.get()));\r
448                     return crlobj.release();\r
449                 }\r
450                 catch(XSECException& e) {\r
451                     auto_ptr_char temp(e.getMsg());\r
452                     Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading CRL: %s", temp.get());\r
453                 }\r
454                 catch(XSECCryptoException& e) {\r
455                     Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading CRL: %s", e.getMsg());\r
456                 }\r
457             }\r
458         }\r
459     }\r
460     return NULL;\r
461 }\r