https://issues.shibboleth.net/jira/browse/CPPXT-70
[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 #if defined(XMLTOOLING_LOG4SHIB)
59 # include <log4shib/PropertyConfigurator.hh>
60 # include <log4shib/OstreamAppender.hh>
61 #elif defined(XMLTOOLING_LOG4CPP)
62 # include <log4cpp/PropertyConfigurator.hh>
63 # include <log4cpp/OstreamAppender.hh>
64 #endif
65 #include <xercesc/util/PlatformUtils.hpp>
66 #include <xercesc/util/XMLUniDefs.hpp>
67 #ifndef XMLTOOLING_NO_XMLSEC
68 # include <curl/curl.h>
69 # include <openssl/err.h>
70 # include <openssl/evp.h>
71 # include <xsec/framework/XSECAlgorithmMapper.hpp>
72 # include <xsec/framework/XSECException.hpp>
73 # include <xsec/framework/XSECProvider.hpp>
74 # include <xsec/transformers/TXFMBase.hpp>
75 #endif
76
77 using namespace soap11;
78 using namespace xmltooling::logging;
79 using namespace xmltooling;
80 using namespace xercesc;
81 using namespace std;
82
83 #ifdef WIN32
84 # if (OPENSSL_VERSION_NUMBER >= 0x00908000)
85 #  define XMLTOOLING_OPENSSL_HAVE_SHA2 1
86 # endif
87 #endif
88
89
90 DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLParserException,xmltooling);
91 DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLObjectException,xmltooling);
92 DECL_XMLTOOLING_EXCEPTION_FACTORY(MarshallingException,xmltooling);
93 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnmarshallingException,xmltooling);
94 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownElementException,xmltooling);
95 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownAttributeException,xmltooling);
96 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownExtensionException,xmltooling);
97 DECL_XMLTOOLING_EXCEPTION_FACTORY(ValidationException,xmltooling);
98 DECL_XMLTOOLING_EXCEPTION_FACTORY(IOException,xmltooling);
99
100 #ifndef XMLTOOLING_NO_XMLSEC
101 using namespace xmlencryption;
102 using namespace xmlsignature;
103     DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLSecurityException,xmltooling);
104     DECL_XMLTOOLING_EXCEPTION_FACTORY(SignatureException,xmlsignature);
105     DECL_XMLTOOLING_EXCEPTION_FACTORY(EncryptionException,xmlencryption);
106 #endif
107
108 namespace {
109     static XMLToolingInternalConfig g_config;
110 #ifndef XMLTOOLING_NO_XMLSEC
111     static vector<Mutex*> g_openssl_locks;
112
113     extern "C" void openssl_locking_callback(int mode,int n,const char *file,int line)
114     {
115         if (mode & CRYPTO_LOCK)
116             g_openssl_locks[n]->lock();
117         else
118             g_openssl_locks[n]->unlock();
119     }
120
121 # ifndef WIN32
122     extern "C" unsigned long openssl_thread_id(void)
123     {
124         return (unsigned long)(pthread_self());
125     }
126 # endif
127
128 # ifdef XMLTOOLING_XMLSEC_DEBUGLOGGING
129     class TXFMOutputLog : public TXFMBase {
130             TXFMOutputLog();
131     public:
132         TXFMOutputLog(DOMDocument* doc) : TXFMBase(doc), m_log(Category::getInstance(XMLTOOLING_LOGCAT".Signature.Debugger")) {
133             input = nullptr;
134         }
135         ~TXFMOutputLog() {
136             m_log.debug("\n----- END SIGNATURE DEBUG -----\n");
137         }
138
139             void setInput(TXFMBase *newInput) {
140                 input = newInput;
141                 if (newInput->getOutputType() != TXFMBase::BYTE_STREAM)
142                         throw XSECException(XSECException::TransformInputOutputFail, "OutputLog transform requires BYTE_STREAM input");
143                 keepComments = input->getCommentsStatus();
144             m_log.debug("\n----- BEGIN SIGNATURE DEBUG -----\n");
145         }
146
147             TXFMBase::ioType getInputType() {
148             return TXFMBase::BYTE_STREAM;
149         }
150             TXFMBase::ioType getOutputType() {
151             return TXFMBase::BYTE_STREAM;
152         }
153             TXFMBase::nodeType getNodeType() {
154             return TXFMBase::DOM_NODE_NONE;
155         }
156
157             unsigned int readBytes(XMLByte * const toFill, const unsigned int maxToFill) {
158                 unsigned int sz = input->readBytes(toFill, maxToFill);
159             m_log.debug(string(reinterpret_cast<char* const>(toFill), sz));
160                 return sz;
161         }
162
163             DOMDocument* getDocument() {
164             return nullptr;
165         }
166             DOMNode* getFragmentNode() {
167             return nullptr;
168         }
169             const XMLCh* getFragmentId() {
170             return nullptr;
171         }
172         
173     private:
174         Category& m_log;
175     };
176
177     TXFMBase* TXFMOutputLogFactory(DOMDocument* doc) {
178         if (Category::getInstance(XMLTOOLING_LOGCAT".Signature.Debugger").isDebugEnabled())
179             return new TXFMOutputLog(doc);
180         return nullptr;
181     }
182 # endif
183
184 #endif
185
186 #ifdef WIN32
187     BOOL LogEvent(
188         LPCSTR  lpUNCServerName,
189         WORD  wType,
190         DWORD  dwEventID,
191         PSID  lpUserSid,
192         LPCSTR  message)
193     {
194         LPCSTR  messages[] = {message, nullptr};
195
196         HANDLE hElog = RegisterEventSource(lpUNCServerName, "OpenSAML XMLTooling Library");
197         BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, nullptr);
198         return (DeregisterEventSource(hElog) && res);
199     }
200 #endif
201 }
202
203 XMLToolingConfig& XMLToolingConfig::getConfig()
204 {
205     return g_config;
206 }
207
208 XMLToolingInternalConfig& XMLToolingInternalConfig::getInternalConfig()
209 {
210     return g_config;
211 }
212
213 #ifndef XMLTOOLING_NO_XMLSEC
214 XMLToolingConfig::XMLToolingConfig()
215     : m_keyInfoResolver(nullptr), m_replayCache(nullptr), m_pathResolver(nullptr), m_templateEngine(nullptr), m_urlEncoder(nullptr), clock_skew_secs(180)
216 #else
217 XMLToolingConfig::XMLToolingConfig()
218     : m_pathResolver(nullptr), m_templateEngine(nullptr), m_urlEncoder(nullptr), clock_skew_secs(180)
219 #endif
220 {
221 }
222
223 XMLToolingConfig::~XMLToolingConfig()
224 {
225 }
226
227 #ifndef XMLTOOLING_LITE
228 const KeyInfoResolver* XMLToolingConfig::getKeyInfoResolver() const
229 {
230     return m_keyInfoResolver;
231 }
232
233 ReplayCache* XMLToolingConfig::getReplayCache() const
234 {
235     return m_replayCache;
236 }
237
238 void XMLToolingConfig::setKeyInfoResolver(xmltooling::KeyInfoResolver *keyInfoResolver)
239 {
240     delete m_keyInfoResolver;
241     m_keyInfoResolver = keyInfoResolver;
242 }
243
244 void XMLToolingConfig::setReplayCache(ReplayCache* replayCache)
245 {
246     delete m_replayCache;
247     m_replayCache = replayCache;
248 }
249 #endif
250
251 PathResolver* XMLToolingConfig::getPathResolver() const
252 {
253     return m_pathResolver;
254 }
255
256 TemplateEngine* XMLToolingConfig::getTemplateEngine() const
257 {
258     return m_templateEngine;
259 }
260
261 const URLEncoder* XMLToolingConfig::getURLEncoder() const
262 {
263     return m_urlEncoder;
264 }
265
266 void XMLToolingConfig::setPathResolver(PathResolver* pathResolver)
267 {
268     delete m_pathResolver;
269     m_pathResolver = pathResolver;
270 }
271
272 void XMLToolingConfig::setTemplateEngine(TemplateEngine* templateEngine)
273 {
274     delete m_templateEngine;
275     m_templateEngine = templateEngine;
276 }
277
278 void XMLToolingConfig::setURLEncoder(URLEncoder* urlEncoder)
279 {
280     delete m_urlEncoder;
281     m_urlEncoder = urlEncoder;
282 }
283
284 XMLToolingInternalConfig::XMLToolingInternalConfig() :
285 #ifndef XMLTOOLING_NO_XMLSEC
286     m_xsecProvider(nullptr),
287 #endif
288     m_initCount(0), m_lock(Mutex::create()), m_parserPool(nullptr), m_validatingPool(nullptr)
289 {
290 }
291
292 XMLToolingInternalConfig::~XMLToolingInternalConfig()
293 {
294     delete m_lock;
295 }
296
297 bool XMLToolingInternalConfig::log_config(const char* config)
298 {
299     try {
300         if (!config || !*config)
301             config=getenv("XMLTOOLING_LOG_CONFIG");
302         if (!config || !*config)
303             config="WARN";
304
305         bool level=false;
306         Category& root = Category::getRoot();
307         if (!strcmp(config,"DEBUG")) {
308             root.setPriority(Priority::DEBUG);
309             level=true;
310         }
311         else if (!strcmp(config,"INFO")) {
312             root.setPriority(Priority::INFO);
313             level=true;
314         }
315         else if (!strcmp(config,"NOTICE")) {
316             root.setPriority(Priority::NOTICE);
317             level=true;
318         }
319         else if (!strcmp(config,"WARN")) {
320             root.setPriority(Priority::WARN);
321             level=true;
322         }
323         else if (!strcmp(config,"ERROR")) {
324             root.setPriority(Priority::ERROR);
325             level=true;
326         }
327         else if (!strcmp(config,"CRIT")) {
328             root.setPriority(Priority::CRIT);
329             level=true;
330         }
331         else if (!strcmp(config,"ALERT")) {
332             root.setPriority(Priority::ALERT);
333             level=true;
334         }
335         else if (!strcmp(config,"EMERG")) {
336             root.setPriority(Priority::EMERG);
337             level=true;
338         }
339         else if (!strcmp(config,"FATAL")) {
340             root.setPriority(Priority::FATAL);
341             level=true;
342         }
343         if (level) {
344             root.setAppender(new OstreamAppender("default",&cerr));
345         }
346         else {
347             string path(config);
348             PropertyConfigurator::configure(m_pathResolver ? m_pathResolver->resolve(path, PathResolver::XMLTOOLING_CFG_FILE) : path);
349         }
350
351 #ifndef XMLTOOLING_NO_XMLSEC
352         Category::getInstance(XMLTOOLING_LOGCAT".Signature.Debugger").setAdditivity(false);
353 #endif
354         }
355     catch (const ConfigureFailure& e) {
356         string msg = string("failed to configure logging: ") + e.what();
357         Category::getInstance(XMLTOOLING_LOGCAT".Logging").crit(msg);
358 #ifdef WIN32
359         LogEvent(nullptr, EVENTLOG_ERROR_TYPE, 2100, nullptr, msg.c_str());
360 #endif
361         return false;
362     }
363
364     return true;
365 }
366
367 bool XMLToolingInternalConfig::init()
368 {
369 #ifdef _DEBUG
370     xmltooling::NDC ndc("init");
371 #endif
372     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Config");
373
374     Lock initLock(m_lock);
375
376     if (m_initCount == INT_MAX) {
377         log.crit("library initialized too many times");
378         return false;
379     }
380
381     if (m_initCount >= 1) {
382         ++m_initCount;
383         return true;
384     }
385
386     try {
387         log.debug("library initialization started");
388
389 #ifndef XMLTOOLING_NO_XMLSEC
390         if (curl_global_init(CURL_GLOBAL_ALL)) {
391             log.fatal("failed to initialize libcurl, OpenSSL, or Winsock");
392             return false;
393         }
394         curl_version_info_data* curlver = curl_version_info(CURLVERSION_NOW);
395         if (curlver) {
396             log.debug("libcurl %s initialization complete", curlver->version);
397             if (!(curlver->features & CURL_VERSION_SSL)) {
398                 log.warn("libcurl lacks TLS/SSL support, this will greatly limit functionality");
399             }
400         }
401         else {
402             log.debug("libcurl %s initialization complete", LIBCURL_VERSION);
403         }
404 #endif
405
406         XMLPlatformUtils::Initialize();
407         log.debug("Xerces %s initialization complete", XERCES_FULLVERSIONDOT);
408
409 #ifndef XMLTOOLING_NO_XMLSEC
410         XSECPlatformUtils::Initialise();
411 # ifdef XMLTOOLING_XMLSEC_DEBUGLOGGING
412         XSECPlatformUtils::SetReferenceLoggingSink(TXFMOutputLogFactory);
413 # endif
414         m_xsecProvider=new XSECProvider();
415         log.debug("XML-Security %s initialization complete", XSEC_FULLVERSIONDOT);
416 #endif
417
418         m_parserPool=new ParserPool();
419         m_validatingPool=new ParserPool(true,true);
420
421         // Load catalogs from path.
422         if (!catalog_path.empty()) {
423             char* catpath=strdup(catalog_path.c_str());
424             char* sep=nullptr;
425             char* start=catpath;
426             while (start && *start) {
427                 sep=strchr(start,PATH_SEPARATOR_CHAR);
428                 if (sep)
429                     *sep=0;
430                 auto_ptr_XMLCh temp(start);
431                 m_validatingPool->loadCatalog(temp.get());
432                 start = sep ? sep + 1 : nullptr;
433             }
434             free(catpath);
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     for_each(g_openssl_locks.begin(), g_openssl_locks.end(), xmltooling::cleanup<Mutex>());
523     g_openssl_locks.clear();
524 #endif
525
526     SchemaValidators.destroyValidators();
527     XMLObjectBuilder::destroyBuilders();
528     XMLToolingException::deregisterFactories();
529     AttributeExtensibleXMLObject::deregisterIDAttributes();
530
531     termSOAPTransports();
532     SOAPTransportManager.deregisterFactories();
533
534 #ifndef XMLTOOLING_LITE
535     StorageServiceManager.deregisterFactories();
536 #endif
537
538 #ifndef XMLTOOLING_NO_XMLSEC
539     TrustEngineManager.deregisterFactories();
540     CredentialResolverManager.deregisterFactories();
541     KeyInfoResolverManager.deregisterFactories();
542     m_algorithmMap.clear();
543
544     delete m_keyInfoResolver;
545     m_keyInfoResolver = nullptr;
546
547     delete m_replayCache;
548     m_replayCache = nullptr;
549 #endif
550
551     delete m_pathResolver;
552     m_pathResolver = nullptr;
553
554     delete m_templateEngine;
555     m_templateEngine = nullptr;
556
557     delete m_urlEncoder;
558     m_urlEncoder = nullptr;
559
560     for (vector<void*>::reverse_iterator i=m_libhandles.rbegin(); i!=m_libhandles.rend(); i++) {
561 #if defined(WIN32)
562         FARPROC fn=GetProcAddress(static_cast<HMODULE>(*i),"xmltooling_extension_term");
563         if (fn)
564             fn();
565         FreeLibrary(static_cast<HMODULE>(*i));
566 #elif defined(HAVE_DLFCN_H)
567         void (*fn)()=(void (*)())dlsym(*i,"xmltooling_extension_term");
568         if (fn)
569             fn();
570         dlclose(*i);
571 #else
572 # error "Don't know about dynamic loading on this platform!"
573 #endif
574     }
575     m_libhandles.clear();
576
577     delete m_parserPool;
578     m_parserPool=nullptr;
579     delete m_validatingPool;
580     m_validatingPool=nullptr;
581
582     for_each(m_namedLocks.begin(), m_namedLocks.end(), cleanup_pair<string,Mutex>());
583     m_namedLocks.clear();
584
585 #ifndef XMLTOOLING_NO_XMLSEC
586     delete m_xsecProvider;
587     m_xsecProvider=nullptr;
588     XSECPlatformUtils::Terminate();
589 #endif
590
591     XMLPlatformUtils::Terminate();
592
593 #ifndef XMLTOOLING_NO_XMLSEC
594     curl_global_cleanup();
595 #endif
596    Category::getInstance(XMLTOOLING_LOGCAT".Config").info("%s library shutdown complete", PACKAGE_STRING);
597 }
598
599 Lockable* XMLToolingInternalConfig::lock()
600 {
601     m_lock->lock();
602     return this;
603 }
604
605 void XMLToolingInternalConfig::unlock()
606 {
607     m_lock->unlock();
608 }
609
610 Mutex& XMLToolingInternalConfig::getNamedMutex(const char* name)
611 {
612     Locker glock(this);
613     map<string,Mutex*>::const_iterator m = m_namedLocks.find(name);
614     if (m != m_namedLocks.end())
615         return *(m->second);
616     Mutex* newlock = Mutex::create();
617     m_namedLocks[name] = newlock;
618     return *newlock;
619 }
620
621 bool XMLToolingInternalConfig::load_library(const char* path, void* context)
622 {
623 #ifdef _DEBUG
624     xmltooling::NDC ndc("LoadLibrary");
625 #endif
626     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Config");
627     log.info("loading extension: %s", path);
628
629     Locker locker(this);
630
631     string resolved(path);
632     m_pathResolver->resolve(resolved, PathResolver::XMLTOOLING_LIB_FILE);
633
634 #if defined(WIN32)
635     HMODULE handle=nullptr;
636     for (string::iterator i = resolved.begin(); i != resolved.end(); ++i)
637         if (*i == '/')
638             *i = '\\';
639
640     UINT em=SetErrorMode(SEM_FAILCRITICALERRORS);
641     try {
642         handle=LoadLibraryEx(resolved.c_str(),nullptr,LOAD_WITH_ALTERED_SEARCH_PATH);
643         if (!handle)
644              handle=LoadLibraryEx(resolved.c_str(),nullptr,0);
645         if (!handle)
646             throw runtime_error(string("unable to load extension library: ") + resolved);
647         FARPROC fn=GetProcAddress(handle,"xmltooling_extension_init");
648         if (!fn)
649             throw runtime_error(string("unable to locate xmltooling_extension_init entry point: ") + resolved);
650         if (reinterpret_cast<int(*)(void*)>(fn)(context)!=0)
651             throw runtime_error(string("detected error in xmltooling_extension_init: ") + resolved);
652         SetErrorMode(em);
653     }
654     catch(exception&) {
655         if (handle)
656             FreeLibrary(handle);
657         SetErrorMode(em);
658         throw;
659     }
660
661 #elif defined(HAVE_DLFCN_H)
662     void* handle=dlopen(resolved.c_str(),RTLD_LAZY);
663     if (!handle)
664         throw runtime_error(string("unable to load extension library '") + resolved + "': " + dlerror());
665     int (*fn)(void*)=(int (*)(void*))(dlsym(handle,"xmltooling_extension_init"));
666     if (!fn) {
667         dlclose(handle);
668         throw runtime_error(
669             string("unable to locate xmltooling_extension_init entry point in '") + resolved + "': " +
670                 (dlerror() ? dlerror() : "unknown error")
671             );
672     }
673     try {
674         if (fn(context)!=0)
675             throw runtime_error(string("detected error in xmltooling_extension_init in ") + resolved);
676     }
677     catch(exception&) {
678         if (handle)
679             dlclose(handle);
680         throw;
681     }
682 #else
683 # error "Don't know about dynamic loading on this platform!"
684 #endif
685     m_libhandles.push_back(handle);
686     log.info("loaded extension: %s", resolved.c_str());
687     return true;
688 }
689
690 #ifndef XMLTOOLING_NO_XMLSEC
691
692 void xmltooling::log_openssl()
693 {
694     const char* file;
695     const char* data;
696     int flags,line;
697
698     unsigned long code=ERR_get_error_line_data(&file,&line,&data,&flags);
699     while (code) {
700         Category& log=Category::getInstance("OpenSSL");
701         log.errorStream() << "error code: " << code << " in " << file << ", line " << line << logging::eol;
702         if (data && (flags & ERR_TXT_STRING))
703             log.errorStream() << "error data: " << data << logging::eol;
704         code=ERR_get_error_line_data(&file,&line,&data,&flags);
705     }
706 }
707
708 XSECCryptoX509CRL* XMLToolingInternalConfig::X509CRL() const
709 {
710     return new OpenSSLCryptoX509CRL();
711 }
712
713 pair<const char*,unsigned int> XMLToolingInternalConfig::mapXMLAlgorithmToKeyAlgorithm(const XMLCh* xmlAlgorithm) const
714 {
715     for (algmap_t::const_iterator i = m_algorithmMap.begin(); i != m_algorithmMap.end(); ++i) {
716         algmap_t::value_type::second_type::const_iterator j = i->second.find(xmlAlgorithm);
717         if (j != i->second.end())
718             return pair<const char*,unsigned int>(j->second.first.c_str(), j->second.second);
719     }
720     return pair<const char*,unsigned int>(nullptr, 0);
721 }
722
723 void XMLToolingInternalConfig::registerXMLAlgorithm(
724     const XMLCh* xmlAlgorithm, const char* keyAlgorithm, unsigned int size, XMLSecurityAlgorithmType type
725     )
726 {
727     m_algorithmMap[type][xmlAlgorithm] = pair<string,unsigned int>((keyAlgorithm ? keyAlgorithm : ""), size);
728 }
729
730 bool XMLToolingInternalConfig::isXMLAlgorithmSupported(const XMLCh* xmlAlgorithm, XMLSecurityAlgorithmType type)
731 {
732     try {
733         // First check for basic support from the xmlsec layer.
734         if (XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(xmlAlgorithm)) {
735             // Make sure the algorithm is registered.
736             algmap_t::const_iterator i = m_algorithmMap.find(type);
737             if (i != m_algorithmMap.end()) {
738                 algmap_t::value_type::second_type::const_iterator j = i->second.find(xmlAlgorithm);
739                 if (j != i->second.end())
740                     return true;
741             }
742         }
743     }
744     catch (XSECException&) {
745     }
746     return false;
747 }
748
749 void XMLToolingInternalConfig::registerXMLAlgorithms()
750 {
751     // The deal with all the macros is to try and figure out with no false positives whether
752     // the OpenSSL version *and* the XML-Security version support the algorithms.
753
754     // With ECDSA, XML-Security exports a public macro for OpenSSL's support, and any
755     // versions of XML-Security that didn't provide the macro don't handle ECDSA anyway.
756
757     // With AES, all supported XML-Security versions export a macro for OpenSSL's support.
758
759     // With SHA2, only the very latest XML-Security exports a macro, but all the versions
760     // will handle SHA2 *if* OpenSSL does. So we use our own macro to check OpenSSL's
761     // support, and then add checks to see if specific versions are compiled out.
762
763     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIMD5, nullptr, 0, ALGTYPE_DIGEST);
764     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA1, nullptr, 0, ALGTYPE_DIGEST);
765 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA256)
766     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA224, nullptr, 0, ALGTYPE_DIGEST);
767     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA256, nullptr, 0, ALGTYPE_DIGEST);
768 #endif
769 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA512)
770     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA384, nullptr, 0, ALGTYPE_DIGEST);
771     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURISHA512, nullptr, 0, ALGTYPE_DIGEST);
772 #endif
773
774     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIDSA_SHA1, "DSA", 0, ALGTYPE_SIGN);
775     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_MD5, "RSA", 0, ALGTYPE_SIGN);
776     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA1, "RSA", 0, ALGTYPE_SIGN);
777 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA256)
778     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA224, "RSA", 0, ALGTYPE_SIGN);
779     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA256, "RSA", 0, ALGTYPE_SIGN);
780 #endif
781 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA512)
782     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA384, "RSA", 0, ALGTYPE_SIGN);
783     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA512, "RSA", 0, ALGTYPE_SIGN);
784 #endif
785
786 #ifdef XSEC_OPENSSL_HAVE_EC
787     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIECDSA_SHA1, "EC", 0, ALGTYPE_SIGN);
788 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA256)
789     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIECDSA_SHA256, "EC", 0, ALGTYPE_SIGN);
790 # endif
791 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA512)
792     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIECDSA_SHA384, "EC", 0, ALGTYPE_SIGN);
793     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIECDSA_SHA512, "EC", 0, ALGTYPE_SIGN);
794 # endif
795 #endif
796
797     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA1, "HMAC", 0, ALGTYPE_SIGN);
798 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA256)
799     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA224, "HMAC", 0, ALGTYPE_SIGN);
800     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA256, "HMAC", 0, ALGTYPE_SIGN);
801 #endif
802 #if defined(XMLTOOLING_OPENSSL_HAVE_SHA2) && !defined(OPENSSL_NO_SHA512)
803     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA384, "HMAC", 0, ALGTYPE_SIGN);
804     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA512, "HMAC", 0, ALGTYPE_SIGN);
805 #endif
806
807     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_1_5, "RSA", 0, ALGTYPE_KEYENCRYPT);
808     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_OAEP_MGFP1, "RSA", 0, ALGTYPE_KEYENCRYPT);
809
810     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURI3DES_CBC, "DESede", 192, ALGTYPE_ENCRYPT);
811     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_3DES, "DESede", 192, ALGTYPE_KEYENCRYPT);
812
813 #ifdef XSEC_OPENSSL_HAVE_AES
814     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIAES128_CBC, "AES", 128, ALGTYPE_ENCRYPT);
815     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_AES128, "AES", 128, ALGTYPE_KEYENCRYPT);
816
817     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIAES192_CBC, "AES", 192, ALGTYPE_ENCRYPT);
818     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_AES192, "AES", 192, ALGTYPE_KEYENCRYPT);
819
820     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIAES256_CBC, "AES", 256, ALGTYPE_ENCRYPT);
821     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_AES256, "AES", 256, ALGTYPE_KEYENCRYPT);
822 #endif
823 }
824
825 #endif
826
827 #ifdef WIN32
828
829 extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
830 {
831     if (fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH)
832         ThreadKey::onDetach();
833     return TRUE;
834 }
835
836 #endif