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