Reducing header overuse, non-inlining selected methods (CPPOST-35).
[shibboleth/cpp-xmltooling.git] / xmltooling / XMLToolingConfig.cpp
1 /*
2  *  Copyright 2001-2009 Internet2
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * XMLToolingConfig.cpp
19  *
20  * Library configuration
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "logging.h"
26 #include "XMLToolingConfig.h"
27 #include "encryption/Encryption.h"
28 #include "encryption/Encrypter.h"
29 #include "io/HTTPRequest.h"
30 #include "io/HTTPResponse.h"
31 #include "impl/UnknownElement.h"
32 #include "security/TrustEngine.h"
33 #include "security/OpenSSLCryptoX509CRL.h"
34 #include "security/CredentialResolver.h"
35 #include "security/KeyInfoResolver.h"
36 #include "signature/KeyInfo.h"
37 #include "signature/Signature.h"
38 #include "soap/SOAP.h"
39 #include "soap/SOAPTransport.h"
40 #include "util/NDC.h"
41 #include "util/PathResolver.h"
42 #include "util/ReplayCache.h"
43 #include "util/StorageService.h"
44 #include "util/TemplateEngine.h"
45 #include "util/Threads.h"
46 #include "util/URLEncoder.h"
47 #include "util/XMLConstants.h"
48 #include "validation/ValidatorSuite.h"
49
50 #ifdef HAVE_DLFCN_H
51 # include <dlfcn.h>
52 #endif
53
54 #include <stdexcept>
55 #if defined(XMLTOOLING_LOG4SHIB)
56 # include <log4shib/PropertyConfigurator.hh>
57 # include <log4shib/OstreamAppender.hh>
58 #elif defined(XMLTOOLING_LOG4CPP)
59 # include <log4cpp/PropertyConfigurator.hh>
60 # include <log4cpp/OstreamAppender.hh>
61 #endif
62 #include <xercesc/util/PlatformUtils.hpp>
63 #ifndef XMLTOOLING_NO_XMLSEC
64 # include <curl/curl.h>
65 # include <openssl/err.h>
66 # include <xsec/framework/XSECProvider.hpp>
67 #endif
68
69 using namespace soap11;
70 using namespace xmltooling::logging;
71 using namespace xmltooling;
72 using namespace std;
73
74 using xercesc::XMLPlatformUtils;
75
76 DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLParserException,xmltooling);
77 DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLObjectException,xmltooling);
78 DECL_XMLTOOLING_EXCEPTION_FACTORY(MarshallingException,xmltooling);
79 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnmarshallingException,xmltooling);
80 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownElementException,xmltooling);
81 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownAttributeException,xmltooling);
82 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownExtensionException,xmltooling);
83 DECL_XMLTOOLING_EXCEPTION_FACTORY(ValidationException,xmltooling);
84 DECL_XMLTOOLING_EXCEPTION_FACTORY(IOException,xmltooling);
85
86 #ifndef XMLTOOLING_NO_XMLSEC
87 using namespace xmlencryption;
88 using namespace xmlsignature;
89     DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLSecurityException,xmltooling);
90     DECL_XMLTOOLING_EXCEPTION_FACTORY(SignatureException,xmlsignature);
91     DECL_XMLTOOLING_EXCEPTION_FACTORY(EncryptionException,xmlencryption);
92 #endif
93
94 namespace xmltooling {
95     static XMLToolingInternalConfig g_config;
96 #ifndef XMLTOOLING_NO_XMLSEC
97     static vector<Mutex*> g_openssl_locks;
98
99     extern "C" void openssl_locking_callback(int mode,int n,const char *file,int line)
100     {
101         if (mode & CRYPTO_LOCK)
102             g_openssl_locks[n]->lock();
103         else
104             g_openssl_locks[n]->unlock();
105     }
106
107 # ifndef WIN32
108     extern "C" unsigned long openssl_thread_id(void)
109     {
110         return (unsigned long)(pthread_self());
111     }
112 # endif
113 #endif
114
115 #ifdef WIN32
116     BOOL LogEvent(
117         LPCSTR  lpUNCServerName,
118         WORD  wType,
119         DWORD  dwEventID,
120         PSID  lpUserSid,
121         LPCSTR  message)
122     {
123         LPCSTR  messages[] = {message, NULL};
124
125         HANDLE hElog = RegisterEventSource(lpUNCServerName, "OpenSAML XMLTooling Library");
126         BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, NULL);
127         return (DeregisterEventSource(hElog) && res);
128     }
129 #endif
130 }
131
132 XMLToolingConfig& XMLToolingConfig::getConfig()
133 {
134     return g_config;
135 }
136
137 XMLToolingInternalConfig& XMLToolingInternalConfig::getInternalConfig()
138 {
139     return g_config;
140 }
141
142 bool XMLToolingInternalConfig::log_config(const char* config)
143 {
144     try {
145         if (!config || !*config)
146             config=getenv("XMLTOOLING_LOG_CONFIG");
147         if (!config || !*config)
148             config="WARN";
149
150         bool level=false;
151         Category& root = Category::getRoot();
152         if (!strcmp(config,"DEBUG")) {
153             root.setPriority(Priority::DEBUG);
154             level=true;
155         }
156         else if (!strcmp(config,"INFO")) {
157             root.setPriority(Priority::INFO);
158             level=true;
159         }
160         else if (!strcmp(config,"NOTICE")) {
161             root.setPriority(Priority::NOTICE);
162             level=true;
163         }
164         else if (!strcmp(config,"WARN")) {
165             root.setPriority(Priority::WARN);
166             level=true;
167         }
168         else if (!strcmp(config,"ERROR")) {
169             root.setPriority(Priority::ERROR);
170             level=true;
171         }
172         else if (!strcmp(config,"CRIT")) {
173             root.setPriority(Priority::CRIT);
174             level=true;
175         }
176         else if (!strcmp(config,"ALERT")) {
177             root.setPriority(Priority::ALERT);
178             level=true;
179         }
180         else if (!strcmp(config,"EMERG")) {
181             root.setPriority(Priority::EMERG);
182             level=true;
183         }
184         else if (!strcmp(config,"FATAL")) {
185             root.setPriority(Priority::FATAL);
186             level=true;
187         }
188         if (level) {
189             root.setAppender(new OstreamAppender("default",&cerr));
190         }
191         else {
192             string path(config);
193             PropertyConfigurator::configure(m_pathResolver ? m_pathResolver->resolve(path, PathResolver::XMLTOOLING_CFG_FILE) : path);
194         }
195     }
196     catch (const ConfigureFailure& e) {
197         string msg = string("failed to configure logging: ") + e.what();
198         Category::getInstance(XMLTOOLING_LOGCAT".Logging").crit(msg);
199 #ifdef WIN32
200         LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str());
201 #endif
202         return false;
203     }
204
205     return true;
206 }
207
208 #ifndef XMLTOOLING_LITE
209 const KeyInfoResolver* XMLToolingConfig::getKeyInfoResolver() const
210 {
211     return m_keyInfoResolver;
212 }
213
214 ReplayCache* XMLToolingConfig::getReplayCache() const
215 {
216     return m_replayCache;
217 }
218
219 void XMLToolingConfig::setKeyInfoResolver(xmltooling::KeyInfoResolver *keyInfoResolver)
220 {
221     delete m_keyInfoResolver;
222     m_keyInfoResolver = keyInfoResolver;
223 }
224
225 void XMLToolingConfig::setReplayCache(ReplayCache* replayCache)
226 {
227     delete m_replayCache;
228     m_replayCache = replayCache;
229 }
230 #endif
231
232 PathResolver* XMLToolingConfig::getPathResolver() const
233 {
234     return m_pathResolver;
235 }
236
237 TemplateEngine* XMLToolingConfig::getTemplateEngine() const
238 {
239     return m_templateEngine;
240 }
241
242 const URLEncoder* XMLToolingConfig::getURLEncoder() const
243 {
244     return m_urlEncoder;
245 }
246
247 void XMLToolingConfig::setPathResolver(PathResolver* pathResolver)
248 {
249     delete m_pathResolver;
250     m_pathResolver = pathResolver;
251 }
252
253 void XMLToolingConfig::setTemplateEngine(TemplateEngine* templateEngine)
254 {
255     delete m_templateEngine;
256     m_templateEngine = templateEngine;
257 }
258
259 void XMLToolingConfig::setURLEncoder(URLEncoder* urlEncoder)
260 {
261     delete m_urlEncoder;
262     m_urlEncoder = urlEncoder;
263 }
264
265 bool XMLToolingInternalConfig::init()
266 {
267 #ifdef _DEBUG
268     xmltooling::NDC ndc("init");
269 #endif
270     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLToolingConfig");
271     try {
272         log.debug("library initialization started");
273
274 #ifndef XMLTOOLING_NO_XMLSEC
275         if (curl_global_init(CURL_GLOBAL_ALL)) {
276             log.fatal("failed to initialize libcurl, OpenSSL, or Winsock");
277             return false;
278         }
279         log.debug("libcurl %s initialization complete", LIBCURL_VERSION);
280 #endif
281
282         XMLPlatformUtils::Initialize();
283         log.debug("Xerces %s initialization complete", XERCES_FULLVERSIONDOT);
284
285 #ifndef XMLTOOLING_NO_XMLSEC
286         XSECPlatformUtils::Initialise();
287         m_xsecProvider=new XSECProvider();
288         log.debug("XML-Security %s initialization complete", XSEC_FULLVERSIONDOT);
289 #endif
290
291         m_parserPool=new ParserPool();
292         m_validatingPool=new ParserPool(true,true);
293         m_lock=XMLPlatformUtils::makeMutex();
294
295         // Load catalogs from path.
296         if (!catalog_path.empty()) {
297             char* catpath=strdup(catalog_path.c_str());
298             char* sep=NULL;
299             char* start=catpath;
300             while (start && *start) {
301                 sep=strchr(start,PATH_SEPARATOR_CHAR);
302                 if (sep)
303                     *sep=0;
304                 auto_ptr_XMLCh temp(start);
305                 m_validatingPool->loadCatalog(temp.get());
306                 start = sep ? sep + 1 : NULL;
307             }
308             free(catpath);
309         }
310
311         // default registrations
312         XMLObjectBuilder::registerDefaultBuilder(new UnknownElementBuilder());
313
314         registerSOAPClasses();
315
316         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(XMLParserException,xmltooling);
317         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(XMLObjectException,xmltooling);
318         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(MarshallingException,xmltooling);
319         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(UnmarshallingException,xmltooling);
320         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(UnknownElementException,xmltooling);
321         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(UnknownAttributeException,xmltooling);
322         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ValidationException,xmltooling);
323         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(IOException,xmltooling);
324
325 #ifndef XMLTOOLING_NO_XMLSEC
326         XMLObjectBuilder::registerBuilder(QName(xmlconstants::XMLSIG_NS,Signature::LOCAL_NAME),new SignatureBuilder());
327         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(XMLSecurityException,xmltooling);
328         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(SignatureException,xmlsignature);
329         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(EncryptionException,xmlencryption);
330         registerKeyInfoClasses();
331         registerEncryptionClasses();
332         registerKeyInfoResolvers();
333         registerCredentialResolvers();
334         registerTrustEngines();
335         registerXMLAlgorithms();
336         registerSOAPTransports();
337         initSOAPTransports();
338         registerStorageServices();
339         m_keyInfoResolver = KeyInfoResolverManager.newPlugin(INLINE_KEYINFO_RESOLVER,NULL);
340 #endif
341
342         m_pathResolver = new PathResolver();
343         m_urlEncoder = new URLEncoder();
344
345         // Register xml:id as an ID attribute.
346         static const XMLCh xmlid[] = UNICODE_LITERAL_2(i,d);
347         AttributeExtensibleXMLObject::registerIDAttribute(QName(xmlconstants::XML_NS, xmlid));
348     }
349     catch (const xercesc::XMLException&) {
350         log.fatal("caught exception while initializing Xerces");
351 #ifndef XMLTOOLING_NO_XMLSEC
352         curl_global_cleanup();
353 #endif
354         return false;
355     }
356
357 #ifndef XMLTOOLING_NO_XMLSEC
358     // Set up OpenSSL locking.
359     for (int i=0; i<CRYPTO_num_locks(); i++)
360         g_openssl_locks.push_back(Mutex::create());
361     CRYPTO_set_locking_callback(openssl_locking_callback);
362 # ifndef WIN32
363     CRYPTO_set_id_callback(openssl_thread_id);
364 # endif
365 #endif
366
367     log.info("%s library initialization complete", PACKAGE_STRING);
368     return true;
369 }
370
371 void XMLToolingInternalConfig::term()
372 {
373 #ifndef XMLTOOLING_NO_XMLSEC
374     CRYPTO_set_locking_callback(NULL);
375     for_each(g_openssl_locks.begin(), g_openssl_locks.end(), xmltooling::cleanup<Mutex>());
376     g_openssl_locks.clear();
377 #endif
378
379     SchemaValidators.destroyValidators();
380     XMLObjectBuilder::destroyBuilders();
381     XMLToolingException::deregisterFactories();
382     AttributeExtensibleXMLObject::deregisterIDAttributes();
383
384 #ifndef XMLTOOLING_NO_XMLSEC
385     StorageServiceManager.deregisterFactories();
386     termSOAPTransports();
387     SOAPTransportManager.deregisterFactories();
388     TrustEngineManager.deregisterFactories();
389     CredentialResolverManager.deregisterFactories();
390     KeyInfoResolverManager.deregisterFactories();
391     m_algorithmMap.clear();
392
393     delete m_keyInfoResolver;
394     m_keyInfoResolver = NULL;
395
396     delete m_replayCache;
397     m_replayCache = NULL;
398 #endif
399
400     delete m_pathResolver;
401     m_pathResolver = NULL;
402
403     delete m_templateEngine;
404     m_templateEngine = NULL;
405
406     delete m_urlEncoder;
407     m_urlEncoder = NULL;
408
409     for (vector<void*>::reverse_iterator i=m_libhandles.rbegin(); i!=m_libhandles.rend(); i++) {
410 #if defined(WIN32)
411         FARPROC fn=GetProcAddress(static_cast<HMODULE>(*i),"xmltooling_extension_term");
412         if (fn)
413             fn();
414         FreeLibrary(static_cast<HMODULE>(*i));
415 #elif defined(HAVE_DLFCN_H)
416         void (*fn)()=(void (*)())dlsym(*i,"xmltooling_extension_term");
417         if (fn)
418             fn();
419         dlclose(*i);
420 #else
421 # error "Don't know about dynamic loading on this platform!"
422 #endif
423     }
424     m_libhandles.clear();
425
426     delete m_parserPool;
427     m_parserPool=NULL;
428     delete m_validatingPool;
429     m_validatingPool=NULL;
430
431 #ifndef XMLTOOLING_NO_XMLSEC
432     delete m_xsecProvider;
433     m_xsecProvider=NULL;
434     XSECPlatformUtils::Terminate();
435 #endif
436
437     XMLPlatformUtils::closeMutex(m_lock);
438     m_lock=NULL;
439     XMLPlatformUtils::Terminate();
440
441 #ifndef XMLTOOLING_NO_XMLSEC
442     curl_global_cleanup();
443 #endif
444 #ifdef _DEBUG
445     xmltooling::NDC ndc("term");
446 #endif
447    Category::getInstance(XMLTOOLING_LOGCAT".XMLToolingConfig").info("%s library shutdown complete", PACKAGE_STRING);
448 }
449
450 Lockable* XMLToolingInternalConfig::lock()
451 {
452     xercesc::XMLPlatformUtils::lockMutex(m_lock);
453     return this;
454 }
455
456 void XMLToolingInternalConfig::unlock()
457 {
458     xercesc::XMLPlatformUtils::unlockMutex(m_lock);
459 }
460
461 bool XMLToolingInternalConfig::load_library(const char* path, void* context)
462 {
463 #ifdef _DEBUG
464     xmltooling::NDC ndc("LoadLibrary");
465 #endif
466     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLToolingConfig");
467     log.info("loading extension: %s", path);
468
469     Locker locker(this);
470
471     string resolved(path);
472     m_pathResolver->resolve(resolved, PathResolver::XMLTOOLING_LIB_FILE);
473
474 #if defined(WIN32)
475     HMODULE handle=NULL;
476     for (string::iterator i = resolved.begin(); i != resolved.end(); ++i)
477         if (*i == '/')
478             *i = '\\';
479
480     UINT em=SetErrorMode(SEM_FAILCRITICALERRORS);
481     try {
482         handle=LoadLibraryEx(resolved.c_str(),NULL,LOAD_WITH_ALTERED_SEARCH_PATH);
483         if (!handle)
484              handle=LoadLibraryEx(resolved.c_str(),NULL,0);
485         if (!handle)
486             throw runtime_error(string("unable to load extension library: ") + resolved);
487         FARPROC fn=GetProcAddress(handle,"xmltooling_extension_init");
488         if (!fn)
489             throw runtime_error(string("unable to locate xmltooling_extension_init entry point: ") + resolved);
490         if (reinterpret_cast<int(*)(void*)>(fn)(context)!=0)
491             throw runtime_error(string("detected error in xmltooling_extension_init: ") + resolved);
492         SetErrorMode(em);
493     }
494     catch(exception&) {
495         if (handle)
496             FreeLibrary(handle);
497         SetErrorMode(em);
498         throw;
499     }
500
501 #elif defined(HAVE_DLFCN_H)
502     void* handle=dlopen(resolved.c_str(),RTLD_LAZY);
503     if (!handle)
504         throw runtime_error(string("unable to load extension library '") + resolved + "': " + dlerror());
505     int (*fn)(void*)=(int (*)(void*))(dlsym(handle,"xmltooling_extension_init"));
506     if (!fn) {
507         dlclose(handle);
508         throw runtime_error(
509             string("unable to locate xmltooling_extension_init entry point in '") + resolved + "': " +
510                 (dlerror() ? dlerror() : "unknown error")
511             );
512     }
513     try {
514         if (fn(context)!=0)
515             throw runtime_error(string("detected error in xmltooling_extension_init in ") + resolved);
516     }
517     catch(exception&) {
518         if (handle)
519             dlclose(handle);
520         throw;
521     }
522 #else
523 # error "Don't know about dynamic loading on this platform!"
524 #endif
525     m_libhandles.push_back(handle);
526     log.info("loaded extension: %s", resolved.c_str());
527     return true;
528 }
529
530 #ifndef XMLTOOLING_NO_XMLSEC
531 void xmltooling::log_openssl()
532 {
533     const char* file;
534     const char* data;
535     int flags,line;
536
537     unsigned long code=ERR_get_error_line_data(&file,&line,&data,&flags);
538     while (code) {
539         Category& log=Category::getInstance("OpenSSL");
540         log.errorStream() << "error code: " << code << " in " << file << ", line " << line << logging::eol;
541         if (data && (flags & ERR_TXT_STRING))
542             log.errorStream() << "error data: " << data << logging::eol;
543         code=ERR_get_error_line_data(&file,&line,&data,&flags);
544     }
545 }
546
547 XSECCryptoX509CRL* XMLToolingInternalConfig::X509CRL() const
548 {
549     return new OpenSSLCryptoX509CRL();
550 }
551
552 void XMLToolingInternalConfig::registerXMLAlgorithms()
553 {
554     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_MD5, "RSA", 0);
555     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA1, "RSA", 0);
556     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA224, "RSA", 0);
557     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA256, "RSA", 0);
558     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA384, "RSA", 0);
559     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_SHA512, "RSA", 0);
560
561     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_1_5, "RSA", 0);
562     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIRSA_OAEP_MGFP1, "RSA", 0);
563
564     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIDSA_SHA1, "DSA", 0);
565
566     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA1, "HMAC", 0);
567     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA224, "HMAC", 0);
568     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA256, "HMAC", 0);
569     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA384, "HMAC", 0);
570     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIHMAC_SHA512, "HMAC", 0);
571
572     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURI3DES_CBC, "DESede", 192);
573     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_3DES, "DESede", 192);
574
575     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIAES128_CBC, "AES", 128);
576     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_AES128, "AES", 128);
577
578     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIAES192_CBC, "AES", 192);
579     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_AES192, "AES", 192);
580
581     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIAES256_CBC, "AES", 256);
582     registerXMLAlgorithm(DSIGConstants::s_unicodeStrURIKW_AES256, "AES", 256);
583 }
584 #endif
585
586 #ifdef WIN32
587
588 extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
589 {
590     if (fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH)
591         ThreadKey::onDetach();
592     return TRUE;
593 }
594
595 #endif