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