More boostisms
[shibboleth/cpp-xmltooling.git] / xmltooling / XMLToolingConfig.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * XMLToolingConfig.cpp
23  *
24  * Library configuration.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "logging.h"
30 #include "XMLToolingConfig.h"
31 #include "encryption/Encryption.h"
32 #include "encryption/Encrypter.h"
33 #include "impl/UnknownElement.h"
34 #include "io/HTTPResponse.h"
35 #include "security/TrustEngine.h"
36 #include "security/OpenSSLCryptoX509CRL.h"
37 #include "security/CredentialResolver.h"
38 #include "security/KeyInfoResolver.h"
39 #include "security/PathValidator.h"
40 #include "signature/KeyInfo.h"
41 #include "signature/Signature.h"
42 #include "soap/SOAP.h"
43 #include "soap/SOAPTransport.h"
44 #include "util/NDC.h"
45 #include "util/PathResolver.h"
46 #include "util/ReplayCache.h"
47 #include "util/StorageService.h"
48 #include "util/TemplateEngine.h"
49 #include "util/Threads.h"
50 #include "util/URLEncoder.h"
51 #include "validation/ValidatorSuite.h"
52
53 #ifdef HAVE_DLFCN_H
54 # include <dlfcn.h>
55 #endif
56
57 #include <stdexcept>
58 #include <boost/bind.hpp>
59 #include <boost/ptr_container/ptr_vector.hpp>
60 #include <boost/tokenizer.hpp>
61
62 #if defined(XMLTOOLING_LOG4SHIB)
63 # include <log4shib/PropertyConfigurator.hh>
64 # include <log4shib/OstreamAppender.hh>
65 #elif defined(XMLTOOLING_LOG4CPP)
66 # include <log4cpp/PropertyConfigurator.hh>
67 # include <log4cpp/OstreamAppender.hh>
68 #endif
69 #include <xercesc/util/PlatformUtils.hpp>
70 #include <xercesc/util/XMLUniDefs.hpp>
71 #ifndef XMLTOOLING_NO_XMLSEC
72 # include <curl/curl.h>
73 # include <openssl/err.h>
74 # include <openssl/evp.h>
75 # include <xsec/framework/XSECAlgorithmMapper.hpp>
76 # include <xsec/framework/XSECException.hpp>
77 # include <xsec/framework/XSECProvider.hpp>
78 # include <xsec/transformers/TXFMBase.hpp>
79 #endif
80
81 using namespace soap11;
82 using namespace xmltooling::logging;
83 using namespace xmltooling;
84 using namespace xercesc;
85 using namespace boost;
86 using namespace std;
87
88 #ifdef WIN32
89 # if (OPENSSL_VERSION_NUMBER >= 0x00908000)
90 #  define XMLTOOLING_OPENSSL_HAVE_SHA2 1
91 # endif
92 #endif
93
94
95 DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLParserException,xmltooling);
96 DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLObjectException,xmltooling);
97 DECL_XMLTOOLING_EXCEPTION_FACTORY(MarshallingException,xmltooling);
98 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnmarshallingException,xmltooling);
99 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownElementException,xmltooling);
100 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownAttributeException,xmltooling);
101 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownExtensionException,xmltooling);
102 DECL_XMLTOOLING_EXCEPTION_FACTORY(ValidationException,xmltooling);
103 DECL_XMLTOOLING_EXCEPTION_FACTORY(IOException,xmltooling);
104
105 #ifndef XMLTOOLING_NO_XMLSEC
106 using namespace xmlencryption;
107 using namespace xmlsignature;
108     DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLSecurityException,xmltooling);
109     DECL_XMLTOOLING_EXCEPTION_FACTORY(SignatureException,xmlsignature);
110     DECL_XMLTOOLING_EXCEPTION_FACTORY(EncryptionException,xmlencryption);
111 #endif
112
113 namespace {
114     static XMLToolingInternalConfig g_config;
115 #ifndef XMLTOOLING_NO_XMLSEC
116     static ptr_vector<Mutex> g_openssl_locks;
117
118     extern "C" void openssl_locking_callback(int mode,int n,const char *file,int line)
119     {
120         if (mode & CRYPTO_LOCK)
121             g_openssl_locks[n].lock();
122         else
123             g_openssl_locks[n].unlock();
124     }
125
126 # ifndef WIN32
127     extern "C" unsigned long openssl_thread_id(void)
128     {
129         return (unsigned long)(pthread_self());
130     }
131 # endif
132
133 # ifdef XMLTOOLING_XMLSEC_DEBUGLOGGING
134     class TXFMOutputLog : public TXFMBase {
135             TXFMOutputLog();
136     public:
137         TXFMOutputLog(DOMDocument* doc) : TXFMBase(doc), m_log(Category::getInstance(XMLTOOLING_LOGCAT".Signature.Debugger")) {
138             input = nullptr;
139         }
140         ~TXFMOutputLog() {
141             m_log.debug("\n----- END SIGNATURE DEBUG -----\n");
142         }
143
144             void setInput(TXFMBase *newInput) {
145                 input = newInput;
146                 if (newInput->getOutputType() != TXFMBase::BYTE_STREAM)
147                         throw XSECException(XSECException::TransformInputOutputFail, "OutputLog transform requires BYTE_STREAM input");
148                 keepComments = input->getCommentsStatus();
149             m_log.debug("\n----- BEGIN SIGNATURE DEBUG -----\n");
150         }
151
152             TXFMBase::ioType getInputType() {
153             return TXFMBase::BYTE_STREAM;
154         }
155             TXFMBase::ioType getOutputType() {
156             return TXFMBase::BYTE_STREAM;
157         }
158             TXFMBase::nodeType getNodeType() {
159             return TXFMBase::DOM_NODE_NONE;
160         }
161
162             unsigned int readBytes(XMLByte * const toFill, const unsigned int maxToFill) {
163                 unsigned int sz = input->readBytes(toFill, maxToFill);
164             m_log.debug(string(reinterpret_cast<char* const>(toFill), sz));
165                 return sz;
166         }
167
168             DOMDocument* getDocument() {
169             return nullptr;
170         }
171             DOMNode* getFragmentNode() {
172             return nullptr;
173         }
174             const XMLCh* getFragmentId() {
175             return nullptr;
176         }
177         
178     private:
179         Category& m_log;
180     };
181
182     TXFMBase* TXFMOutputLogFactory(DOMDocument* doc) {
183         if (Category::getInstance(XMLTOOLING_LOGCAT".Signature.Debugger").isDebugEnabled())
184             return new TXFMOutputLog(doc);
185         return nullptr;
186     }
187 # endif
188
189 #endif
190
191 #ifdef WIN32
192     BOOL LogEvent(
193         LPCSTR  lpUNCServerName,
194         WORD  wType,
195         DWORD  dwEventID,
196         PSID  lpUserSid,
197         LPCSTR  message)
198     {
199         LPCSTR  messages[] = {message, nullptr};
200
201         HANDLE hElog = RegisterEventSource(lpUNCServerName, "OpenSAML XMLTooling Library");
202         BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, nullptr);
203         return (DeregisterEventSource(hElog) && res);
204     }
205 #endif
206 }
207
208 XMLToolingConfig& XMLToolingConfig::getConfig()
209 {
210     return g_config;
211 }
212
213 XMLToolingInternalConfig& XMLToolingInternalConfig::getInternalConfig()
214 {
215     return g_config;
216 }
217
218 #ifndef XMLTOOLING_NO_XMLSEC
219 XMLToolingConfig::XMLToolingConfig()
220     : m_keyInfoResolver(nullptr), m_replayCache(nullptr), m_pathResolver(nullptr), m_templateEngine(nullptr), m_urlEncoder(nullptr), clock_skew_secs(180)
221 #else
222 XMLToolingConfig::XMLToolingConfig()
223     : m_pathResolver(nullptr), m_templateEngine(nullptr), m_urlEncoder(nullptr), clock_skew_secs(180)
224 #endif
225 {
226 }
227
228 XMLToolingConfig::~XMLToolingConfig()
229 {
230 }
231
232 #ifndef XMLTOOLING_LITE
233 const KeyInfoResolver* XMLToolingConfig::getKeyInfoResolver() const
234 {
235     return m_keyInfoResolver;
236 }
237
238 ReplayCache* XMLToolingConfig::getReplayCache() const
239 {
240     return m_replayCache;
241 }
242
243 void XMLToolingConfig::setKeyInfoResolver(xmltooling::KeyInfoResolver *keyInfoResolver)
244 {
245     delete m_keyInfoResolver;
246     m_keyInfoResolver = keyInfoResolver;
247 }
248
249 void XMLToolingConfig::setReplayCache(ReplayCache* replayCache)
250 {
251     delete m_replayCache;
252     m_replayCache = replayCache;
253 }
254 #endif
255
256 PathResolver* XMLToolingConfig::getPathResolver() const
257 {
258     return m_pathResolver;
259 }
260
261 TemplateEngine* XMLToolingConfig::getTemplateEngine() const
262 {
263     return m_templateEngine;
264 }
265
266 const URLEncoder* XMLToolingConfig::getURLEncoder() const
267 {
268     return m_urlEncoder;
269 }
270
271 void XMLToolingConfig::setPathResolver(PathResolver* pathResolver)
272 {
273     delete m_pathResolver;
274     m_pathResolver = pathResolver;
275 }
276
277 void XMLToolingConfig::setTemplateEngine(TemplateEngine* templateEngine)
278 {
279     delete m_templateEngine;
280     m_templateEngine = templateEngine;
281 }
282
283 void XMLToolingConfig::setURLEncoder(URLEncoder* urlEncoder)
284 {
285     delete m_urlEncoder;
286     m_urlEncoder = urlEncoder;
287 }
288
289 XMLToolingInternalConfig::XMLToolingInternalConfig() :
290 #ifndef XMLTOOLING_NO_XMLSEC
291     m_xsecProvider(nullptr),
292 #endif
293     m_initCount(0), m_lock(Mutex::create()), m_parserPool(nullptr), m_validatingPool(nullptr)
294 {
295 }
296
297 XMLToolingInternalConfig::~XMLToolingInternalConfig()
298 {
299     delete m_lock;
300 }
301
302 bool XMLToolingInternalConfig::log_config(const char* config)
303 {
304     try {
305         if (!config || !*config)
306             config=getenv("XMLTOOLING_LOG_CONFIG");
307         if (!config || !*config)
308             config="WARN";
309
310         bool level=false;
311         Category& root = Category::getRoot();
312         if (!strcmp(config,"DEBUG")) {
313             root.setPriority(Priority::DEBUG);
314             level=true;
315         }
316         else if (!strcmp(config,"INFO")) {
317             root.setPriority(Priority::INFO);
318             level=true;
319         }
320         else if (!strcmp(config,"NOTICE")) {
321             root.setPriority(Priority::NOTICE);
322             level=true;
323         }
324         else if (!strcmp(config,"WARN")) {
325             root.setPriority(Priority::WARN);
326             level=true;
327         }
328         else if (!strcmp(config,"ERROR")) {
329             root.setPriority(Priority::ERROR);
330             level=true;
331         }
332         else if (!strcmp(config,"CRIT")) {
333             root.setPriority(Priority::CRIT);
334             level=true;
335         }
336         else if (!strcmp(config,"ALERT")) {
337             root.setPriority(Priority::ALERT);
338             level=true;
339         }
340         else if (!strcmp(config,"EMERG")) {
341             root.setPriority(Priority::EMERG);
342             level=true;
343         }
344         else if (!strcmp(config,"FATAL")) {
345             root.setPriority(Priority::FATAL);
346             level=true;
347         }
348         if (level) {
349             root.setAppender(new OstreamAppender("default",&cerr));
350         }
351         else {
352             string path(config);
353             PropertyConfigurator::configure(m_pathResolver ? m_pathResolver->resolve(path, PathResolver::XMLTOOLING_CFG_FILE) : path);
354         }
355
356 #ifndef XMLTOOLING_NO_XMLSEC
357         Category::getInstance(XMLTOOLING_LOGCAT".Signature.Debugger").setAdditivity(false);
358 #endif
359         }
360     catch (const ConfigureFailure& e) {
361         string msg = string("error in file permissions or logging configuration: ") + e.what();
362         Category::getInstance(XMLTOOLING_LOGCAT".Logging").crit(msg);
363 #ifdef WIN32
364         LogEvent(nullptr, EVENTLOG_ERROR_TYPE, 2100, nullptr, msg.c_str());
365 #endif
366         return false;
367     }
368
369     return true;
370 }
371
372 bool XMLToolingInternalConfig::init()
373 {
374 #ifdef _DEBUG
375     xmltooling::NDC ndc("init");
376 #endif
377     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Config");
378
379     Lock initLock(m_lock);
380
381     if (m_initCount == INT_MAX) {
382         log.crit("library initialized too many times");
383         return false;
384     }
385
386     if (m_initCount >= 1) {
387         ++m_initCount;
388         return true;
389     }
390
391     try {
392         log.debug("library initialization started");
393
394 #ifndef XMLTOOLING_NO_XMLSEC
395         if (curl_global_init(CURL_GLOBAL_ALL)) {
396             log.fatal("failed to initialize libcurl, OpenSSL, or Winsock");
397             return false;
398         }
399         curl_version_info_data* curlver = curl_version_info(CURLVERSION_NOW);
400         if (curlver) {
401             log.debug("libcurl %s initialization complete", curlver->version);
402             if (!(curlver->features & CURL_VERSION_SSL)) {
403                 log.warn("libcurl lacks TLS/SSL support, this will greatly limit functionality");
404             }
405         }
406         else {
407             log.debug("libcurl %s initialization complete", LIBCURL_VERSION);
408         }
409 #endif
410
411         XMLPlatformUtils::Initialize();
412         log.debug("Xerces %s initialization complete", XERCES_FULLVERSIONDOT);
413
414 #ifndef XMLTOOLING_NO_XMLSEC
415         XSECPlatformUtils::Initialise();
416 # ifdef XMLTOOLING_XMLSEC_DEBUGLOGGING
417         XSECPlatformUtils::SetReferenceLoggingSink(TXFMOutputLogFactory);
418 # endif
419         m_xsecProvider=new XSECProvider();
420         log.debug("XML-Security %s initialization complete", XSEC_FULLVERSIONDOT);
421 #endif
422
423         m_parserPool=new ParserPool();
424         m_validatingPool=new ParserPool(true,true);
425
426         // Load catalogs from path.
427         if (!catalog_path.empty()) {
428             boost::tokenizer< char_separator<char> > catpaths(catalog_path, char_separator<char>(PATH_SEPARATOR_STR));
429             for_each(
430                 catpaths.begin(), catpaths.end(),
431                 // Call loadCatalog with an inner call to s->c_str() on each entry.
432                 boost::bind(static_cast<bool (ParserPool::*)(const char*)>(&ParserPool::loadCatalog),
433                     m_validatingPool, boost::bind(&string::c_str,_1))
434                 );
435         }
436
437         // default registrations
438         XMLObjectBuilder::registerDefaultBuilder(new UnknownElementBuilder());
439
440         registerSOAPClasses();
441
442         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(XMLParserException,xmltooling);
443         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(XMLObjectException,xmltooling);
444         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(MarshallingException,xmltooling);
445         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(UnmarshallingException,xmltooling);
446         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(UnknownElementException,xmltooling);
447         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(UnknownAttributeException,xmltooling);
448         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ValidationException,xmltooling);
449         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(IOException,xmltooling);
450
451 #ifndef XMLTOOLING_NO_XMLSEC
452         XMLObjectBuilder::registerBuilder(QName(xmlconstants::XMLSIG_NS,Signature::LOCAL_NAME),new SignatureBuilder());
453         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(XMLSecurityException,xmltooling);
454         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(SignatureException,xmlsignature);
455         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(EncryptionException,xmlencryption);
456         registerKeyInfoClasses();
457         registerEncryptionClasses();
458         registerCredentialResolvers();
459         registerKeyInfoResolvers();
460         registerPathValidators();
461         registerTrustEngines();
462         registerXMLAlgorithms();
463         m_keyInfoResolver = KeyInfoResolverManager.newPlugin(INLINE_KEYINFO_RESOLVER,nullptr);
464 #endif
465
466 #ifndef XMLTOOLING_LITE
467         registerStorageServices();
468 #endif
469         registerSOAPTransports();
470         initSOAPTransports();
471
472         m_pathResolver = new PathResolver();
473         m_urlEncoder = new URLEncoder();
474
475         HTTPResponse::getAllowedSchemes().push_back("https");
476         HTTPResponse::getAllowedSchemes().push_back("http");
477
478         // Register xml:id as an ID attribute.
479         static const XMLCh xmlid[] = UNICODE_LITERAL_2(i,d);
480         AttributeExtensibleXMLObject::registerIDAttribute(QName(xmlconstants::XML_NS, xmlid));
481     }
482     catch (const xercesc::XMLException&) {
483         log.fatal("caught exception while initializing Xerces");
484 #ifndef XMLTOOLING_NO_XMLSEC
485         curl_global_cleanup();
486 #endif
487         return false;
488     }
489
490 #ifndef XMLTOOLING_NO_XMLSEC
491     // Set up OpenSSL locking.
492     for (int i=0; i<CRYPTO_num_locks(); i++)
493         g_openssl_locks.push_back(Mutex::create());
494     CRYPTO_set_locking_callback(openssl_locking_callback);
495 # ifndef WIN32
496     CRYPTO_set_id_callback(openssl_thread_id);
497 # endif
498 #endif
499
500     log.info("%s library initialization complete", PACKAGE_STRING);
501     ++m_initCount;
502     return true;
503 }
504
505 void XMLToolingInternalConfig::term()
506 {
507 #ifdef _DEBUG
508     xmltooling::NDC ndc("term");
509 #endif
510
511     Lock initLock(m_lock);
512     if (m_initCount == 0) {
513         Category::getInstance(XMLTOOLING_LOGCAT".Config").crit("term without corresponding init");
514         return;
515     }
516     else if (--m_initCount > 0) {
517         return;
518     }
519
520 #ifndef XMLTOOLING_NO_XMLSEC
521     CRYPTO_set_locking_callback(nullptr);
522     g_openssl_locks.clear();
523 #endif
524
525     SchemaValidators.destroyValidators();
526     XMLObjectBuilder::destroyBuilders();
527     XMLToolingException::deregisterFactories();
528     AttributeExtensibleXMLObject::deregisterIDAttributes();
529
530     termSOAPTransports();
531     SOAPTransportManager.deregisterFactories();
532
533 #ifndef XMLTOOLING_LITE
534     StorageServiceManager.deregisterFactories();
535 #endif
536
537 #ifndef XMLTOOLING_NO_XMLSEC
538     TrustEngineManager.deregisterFactories();
539     CredentialResolverManager.deregisterFactories();
540     KeyInfoResolverManager.deregisterFactories();
541     m_algorithmMap.clear();
542
543     delete m_keyInfoResolver;
544     m_keyInfoResolver = nullptr;
545
546     delete m_replayCache;
547     m_replayCache = nullptr;
548 #endif
549
550     delete m_pathResolver;
551     m_pathResolver = nullptr;
552
553     delete m_templateEngine;
554     m_templateEngine = nullptr;
555
556     delete m_urlEncoder;
557     m_urlEncoder = nullptr;
558
559     for (vector<void*>::reverse_iterator i=m_libhandles.rbegin(); i!=m_libhandles.rend(); i++) {
560 #if defined(WIN32)
561         FARPROC fn=GetProcAddress(static_cast<HMODULE>(*i),"xmltooling_extension_term");
562         if (fn)
563             fn();
564         FreeLibrary(static_cast<HMODULE>(*i));
565 #elif defined(HAVE_DLFCN_H)
566         void (*fn)()=(void (*)())dlsym(*i,"xmltooling_extension_term");
567         if (fn)
568             fn();
569         dlclose(*i);
570 #else
571 # error "Don't know about dynamic loading on this platform!"
572 #endif
573     }
574     m_libhandles.clear();
575
576     delete m_parserPool;
577     m_parserPool=nullptr;
578     delete m_validatingPool;
579     m_validatingPool=nullptr;
580
581     for_each(m_namedLocks.begin(), m_namedLocks.end(), cleanup_pair<string,Mutex>());
582     m_namedLocks.clear();
583
584 #ifndef XMLTOOLING_NO_XMLSEC
585     delete m_xsecProvider;
586     m_xsecProvider=nullptr;
587     XSECPlatformUtils::Terminate();
588 #endif
589
590     XMLPlatformUtils::Terminate();
591
592 #ifndef XMLTOOLING_NO_XMLSEC
593     curl_global_cleanup();
594 #endif
595    Category::getInstance(XMLTOOLING_LOGCAT".Config").info("%s library shutdown complete", PACKAGE_STRING);
596 }
597
598 Lockable* XMLToolingInternalConfig::lock()
599 {
600     m_lock->lock();
601     return this;
602 }
603
604 void XMLToolingInternalConfig::unlock()
605 {
606     m_lock->unlock();
607 }
608
609 Mutex& XMLToolingInternalConfig::getNamedMutex(const char* name)
610 {
611     Locker glock(this);
612     map<string,Mutex*>::const_iterator m = m_namedLocks.find(name);
613     if (m != m_namedLocks.end())
614         return *(m->second);
615     Mutex* newlock = Mutex::create();
616     m_namedLocks[name] = newlock;
617     return *newlock;
618 }
619
620 bool XMLToolingInternalConfig::load_library(const char* path, void* context)
621 {
622 #ifdef _DEBUG
623     xmltooling::NDC ndc("LoadLibrary");
624 #endif
625     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Config");
626     log.info("loading extension: %s", path);
627
628     Locker locker(this);
629
630     string resolved(path);
631     m_pathResolver->resolve(resolved, PathResolver::XMLTOOLING_LIB_FILE);
632
633 #if defined(WIN32)
634     HMODULE handle=nullptr;
635     for (string::iterator i = resolved.begin(); i != resolved.end(); ++i)
636         if (*i == '/')
637             *i = '\\';
638
639     UINT em=SetErrorMode(SEM_FAILCRITICALERRORS);
640     try {
641         handle=LoadLibraryEx(resolved.c_str(),nullptr,LOAD_WITH_ALTERED_SEARCH_PATH);
642         if (!handle)
643              handle=LoadLibraryEx(resolved.c_str(),nullptr,0);
644         if (!handle)
645             throw runtime_error(string("unable to load extension library: ") + resolved);
646         FARPROC fn=GetProcAddress(handle,"xmltooling_extension_init");
647         if (!fn)
648             throw runtime_error(string("unable to locate xmltooling_extension_init entry point: ") + resolved);
649         if (reinterpret_cast<int(*)(void*)>(fn)(context)!=0)
650             throw runtime_error(string("detected error in xmltooling_extension_init: ") + resolved);
651         SetErrorMode(em);
652     }
653     catch(std::exception&) {
654         if (handle)
655             FreeLibrary(handle);
656         SetErrorMode(em);
657         throw;
658     }
659
660 #elif defined(HAVE_DLFCN_H)
661     void* handle=dlopen(resolved.c_str(),RTLD_LAZY);
662     if (!handle)
663         throw runtime_error(string("unable to load extension library '") + resolved + "': " + dlerror());
664     int (*fn)(void*)=(int (*)(void*))(dlsym(handle,"xmltooling_extension_init"));
665     if (!fn) {
666         dlclose(handle);
667         throw runtime_error(
668             string("unable to locate xmltooling_extension_init entry point in '") + resolved + "': " +
669                 (dlerror() ? dlerror() : "unknown error")
670             );
671     }
672     try {
673         if (fn(context)!=0)
674             throw runtime_error(string("detected error in xmltooling_extension_init in ") + resolved);
675     }
676     catch(std::exception&) {
677         if (handle)
678             dlclose(handle);
679         throw;
680     }
681 #else
682 # error "Don't know about dynamic loading on this platform!"
683 #endif
684     m_libhandles.push_back(handle);
685     log.info("loaded extension: %s", resolved.c_str());
686     return true;
687 }
688
689 #ifndef XMLTOOLING_NO_XMLSEC
690
691 void xmltooling::log_openssl()
692 {
693     const char* file;
694     const char* data;
695     int flags,line;
696
697     unsigned long code=ERR_get_error_line_data(&file,&line,&data,&flags);
698     while (code) {
699         Category& log=Category::getInstance("OpenSSL");
700         log.errorStream() << "error code: " << code << " in " << file << ", line " << line << logging::eol;
701         if (data && (flags & ERR_TXT_STRING))
702             log.errorStream() << "error data: " << data << logging::eol;
703         code=ERR_get_error_line_data(&file,&line,&data,&flags);
704     }
705 }
706
707 XSECCryptoX509CRL* XMLToolingInternalConfig::X509CRL() const
708 {
709     return new OpenSSLCryptoX509CRL();
710 }
711
712 pair<const char*,unsigned int> XMLToolingInternalConfig::mapXMLAlgorithmToKeyAlgorithm(const XMLCh* xmlAlgorithm) const
713 {
714     for (algmap_t::const_iterator i = m_algorithmMap.begin(); i != m_algorithmMap.end(); ++i) {
715         algmap_t::value_type::second_type::const_iterator j = i->second.find(xmlAlgorithm);
716         if (j != i->second.end())
717             return pair<const char*,unsigned int>(j->second.first.c_str(), j->second.second);
718     }
719     return pair<const char*,unsigned int>(nullptr, 0);
720 }
721
722 void XMLToolingInternalConfig::registerXMLAlgorithm(
723     const XMLCh* xmlAlgorithm, const char* keyAlgorithm, unsigned int size, XMLSecurityAlgorithmType type
724     )
725 {
726     m_algorithmMap[type][xmlAlgorithm] = pair<string,unsigned int>((keyAlgorithm ? keyAlgorithm : ""), size);
727 }
728
729 bool XMLToolingInternalConfig::isXMLAlgorithmSupported(const XMLCh* xmlAlgorithm, XMLSecurityAlgorithmType type)
730 {
731     try {
732         // First check for basic support from the xmlsec layer.
733         if (XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(xmlAlgorithm)) {
734             // Make sure the algorithm is registered.
735             algmap_t::const_iterator i = m_algorithmMap.find(type);
736             if (i != m_algorithmMap.end()) {
737                 algmap_t::value_type::second_type::const_iterator j = i->second.find(xmlAlgorithm);
738                 if (j != i->second.end())
739                     return true;
740             }
741         }
742     }
743     catch (XSECException&) {
744     }
745     return false;
746 }
747
748 void XMLToolingInternalConfig::registerXMLAlgorithms()
749 {
750     // The deal with all the macros is to try and figure out with no false positives whether
751     // the OpenSSL version *and* the XML-Security version support the algorithms.
752
753     // With ECDSA, XML-Security exports a public macro for OpenSSL's support, and any
754     // versions of XML-Security that didn't provide the macro don't handle ECDSA anyway.
755
756     // With AES, all supported XML-Security versions export a macro for OpenSSL's support.
757
758     // With SHA2, only the very latest XML-Security exports a macro, but all the versions
759     // will handle SHA2 *if* OpenSSL does. So we use our own macro to check OpenSSL's
760     // support, and then add checks to see if specific versions are compiled out.
761
762     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIMD5, nullptr, 0, ALGTYPE_DIGEST);
763     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA1, nullptr, 0, ALGTYPE_DIGEST);
764 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA256)
765     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA224, nullptr, 0, ALGTYPE_DIGEST);
766     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA256, nullptr, 0, ALGTYPE_DIGEST);
767 #endif
768 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA512)
769     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA384, nullptr, 0, ALGTYPE_DIGEST);
770     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA512, nullptr, 0, ALGTYPE_DIGEST);
771 #endif
772
773     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIDSA_SHA1, "DSA", 0, ALGTYPE_SIGN);
774     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_MD5, "RSA", 0, ALGTYPE_SIGN);
775     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA1, "RSA", 0, ALGTYPE_SIGN);
776 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA256)
777     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA224, "RSA", 0, ALGTYPE_SIGN);
778     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA256, "RSA", 0, ALGTYPE_SIGN);
779 #endif
780 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA512)
781     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA384, "RSA", 0, ALGTYPE_SIGN);
782     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA512, "RSA", 0, ALGTYPE_SIGN);
783 #endif
784
785 #ifdef XSEC_OPENSSL_HAVE_EC
786     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIECDSA_SHA1, "EC", 0, ALGTYPE_SIGN);
787 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA256)
788     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIECDSA_SHA256, "EC", 0, ALGTYPE_SIGN);
789 # endif
790 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA512)
791     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIECDSA_SHA384, "EC", 0, ALGTYPE_SIGN);
792     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIECDSA_SHA512, "EC", 0, ALGTYPE_SIGN);
793 # endif
794 #endif
795
796     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA1, "HMAC", 0, ALGTYPE_SIGN);
797 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA256)
798     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA224, "HMAC", 0, ALGTYPE_SIGN);
799     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA256, "HMAC", 0, ALGTYPE_SIGN);
800 #endif
801 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA512)
802     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA384, "HMAC", 0, ALGTYPE_SIGN);
803     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA512, "HMAC", 0, ALGTYPE_SIGN);
804 #endif
805
806     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_1_5, "RSA", 0, ALGTYPE_KEYENCRYPT);
807     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_OAEP_MGFP1, "RSA", 0, ALGTYPE_KEYENCRYPT);
808
809     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURI3DES_CBC, "DESede", 192, ALGTYPE_ENCRYPT);
810     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_3DES, "DESede", 192, ALGTYPE_KEYENCRYPT);
811
812 #ifdef XSEC_OPENSSL_HAVE_AES
813     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIAES128_CBC, "AES", 128, ALGTYPE_ENCRYPT);
814     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_AES128, "AES", 128, ALGTYPE_KEYENCRYPT);
815
816     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIAES192_CBC, "AES", 192, ALGTYPE_ENCRYPT);
817     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_AES192, "AES", 192, ALGTYPE_KEYENCRYPT);
818
819     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIAES256_CBC, "AES", 256, ALGTYPE_ENCRYPT);
820     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_AES256, "AES", 256, ALGTYPE_KEYENCRYPT);
821 #endif
822 }
823
824 #endif
825
826 #ifdef WIN32
827
828 extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
829 {
830     if (fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH)
831         ThreadKey::onDetach();
832     return TRUE;
833 }
834
835 #endif