Update gitignore to account for subdirs / missing files.
[shibboleth/cpp-opensaml.git] / saml / SAMLConfig.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  * SAMLConfig.cpp
23  * 
24  * Library configuration.
25  */
26
27 #include "internal.h"
28
29 #if defined(XMLTOOLING_LOG4SHIB)
30 # ifndef OPENSAML_LOG4SHIB
31 #  error "Logging library mismatch (XMLTooling is using log4shib)."
32 # endif
33 #elif defined(XMLTOOLING_LOG4CPP)
34 # ifndef OPENSAML_LOG4CPP
35 #  error "Logging library mismatch (XMLTooling is using log4cpp)."
36 # endif
37 #else
38 # error "No supported logging library."
39 #endif
40
41 #include "exceptions.h"
42 #include "SAMLConfig.h"
43 #include "binding/ArtifactMap.h"
44 #include "binding/MessageDecoder.h"
45 #include "binding/MessageEncoder.h"
46 #include "binding/SAMLArtifact.h"
47 #include "binding/SecurityPolicyRule.h"
48 #include "saml1/core/Assertions.h"
49 #include "saml1/core/Protocols.h"
50 #include "saml2/core/Protocols.h"
51 #include "saml2/metadata/EntityMatcher.h"
52 #include "saml2/metadata/Metadata.h"
53 #include "saml2/metadata/MetadataFilter.h"
54 #include "saml2/metadata/MetadataProvider.h"
55 #include "util/SAMLConstants.h"
56
57 #include <xmltooling/logging.h>
58 #include <xmltooling/XMLToolingConfig.h>
59 #include <xmltooling/security/SecurityHelper.h>
60 #include <xmltooling/signature/Signature.h>
61 #include <xmltooling/util/NDC.h>
62 #include <xmltooling/util/PathResolver.h>
63 #include <xmltooling/util/Threads.h>
64
65 #include <boost/lambda/bind.hpp>
66 #include <boost/lambda/casts.hpp>
67 #include <boost/lambda/lambda.hpp>
68
69 #include <xsec/enc/XSECCryptoException.hpp>
70 #include <xsec/enc/XSECCryptoProvider.hpp>
71 #include <xsec/utils/XSECPlatformUtils.hpp>
72 #include <xercesc/util/XMLStringTokenizer.hpp>
73
74 using namespace opensaml;
75 using namespace xmlsignature;
76 using namespace xmltooling::logging;
77 using namespace xmltooling;
78 using namespace boost::lambda;
79 using namespace boost;
80 using namespace std;
81
82 // Expose entry points when used as an extension library
83
84 extern "C" int SAML_API xmltooling_extension_init(void*)
85 {
86     if (SAMLConfig::getConfig().init(false))
87         return 0;
88     return -1;
89 }
90
91 extern "C" void SAML_API xmltooling_extension_term()
92 {
93     SAMLConfig::getConfig().term(false);
94 }
95
96 DECL_XMLTOOLING_EXCEPTION_FACTORY(ArtifactException,opensaml);
97 DECL_XMLTOOLING_EXCEPTION_FACTORY(SecurityPolicyException,opensaml);
98 DECL_XMLTOOLING_EXCEPTION_FACTORY(MetadataException,opensaml::saml2md);
99 DECL_XMLTOOLING_EXCEPTION_FACTORY(MetadataFilterException,opensaml::saml2md);
100 DECL_XMLTOOLING_EXCEPTION_FACTORY(BindingException,opensaml);
101 DECL_XMLTOOLING_EXCEPTION_FACTORY(ProfileException,opensaml);
102 DECL_XMLTOOLING_EXCEPTION_FACTORY(FatalProfileException,opensaml);
103 DECL_XMLTOOLING_EXCEPTION_FACTORY(RetryableProfileException,opensaml);
104
105 namespace opensaml {
106    SAMLInternalConfig g_config;
107 }
108
109 SAMLConfig& SAMLConfig::getConfig()
110 {
111     return g_config;
112 }
113
114 SAMLInternalConfig& SAMLInternalConfig::getInternalConfig()
115 {
116     return g_config;
117 }
118
119 SAMLConfig::SAMLConfig() : m_artifactMap(nullptr)
120 {
121 }
122
123 SAMLConfig::~SAMLConfig()
124 {
125     delete m_artifactMap;
126 }
127
128 ArtifactMap* SAMLConfig::getArtifactMap() const
129 {
130     return m_artifactMap;
131 }
132
133 void SAMLConfig::setArtifactMap(ArtifactMap* artifactMap)
134 {
135     delete m_artifactMap;
136     m_artifactMap = artifactMap;
137 }
138
139 SAMLInternalConfig::SAMLInternalConfig() : m_initCount(0), m_lock(Mutex::create())
140 {
141 }
142
143 SAMLInternalConfig::~SAMLInternalConfig()
144 {
145 }
146
147 bool SAMLInternalConfig::init(bool initXMLTooling)
148 {
149 #ifdef _DEBUG
150     xmltooling::NDC ndc("init");
151 #endif
152     Category& log=Category::getInstance(SAML_LOGCAT ".Config");
153
154     Lock initLock(m_lock);
155
156     if (m_initCount == INT_MAX) {
157         log.crit("library initialized too many times");
158         return false;
159     }
160
161     if (m_initCount >= 1) {
162         ++m_initCount;
163         return true;
164     }
165
166     log.debug("library initialization started");
167
168     if (initXMLTooling && !XMLToolingConfig::getConfig().init()) {
169         return false;
170     }
171
172     XMLToolingConfig::getConfig().getPathResolver()->setDefaultPackageName("opensaml");
173
174     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ArtifactException,opensaml);
175     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(SecurityPolicyException,opensaml);
176     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(MetadataException,opensaml::saml2md);
177     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(MetadataFilterException,opensaml::saml2md);
178     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(BindingException,opensaml);
179     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ProfileException,opensaml);
180     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(FatalProfileException,opensaml);
181     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(RetryableProfileException,opensaml);
182
183     saml1::registerAssertionClasses();
184     saml1p::registerProtocolClasses();
185     saml2::registerAssertionClasses();
186     saml2p::registerProtocolClasses();
187     saml2md::registerMetadataClasses();
188     saml2md::registerMetadataProviders();
189     saml2md::registerMetadataFilters();
190     saml2md::registerEntityMatchers();
191     registerSAMLArtifacts();
192     registerMessageEncoders();
193     registerMessageDecoders();
194     registerSecurityPolicyRules();
195
196     m_contactPriority.push_back(saml2md::ContactPerson::CONTACT_SUPPORT);
197     m_contactPriority.push_back(saml2md::ContactPerson::CONTACT_TECHNICAL);
198
199     log.info("%s library initialization complete", PACKAGE_STRING);
200     ++m_initCount;
201     return true;
202 }
203
204 void SAMLInternalConfig::term(bool termXMLTooling)
205 {
206 #ifdef _DEBUG
207     xmltooling::NDC ndc("term");
208 #endif
209
210     Lock initLock(m_lock);
211     if (m_initCount == 0) {
212         Category::getInstance(SAML_LOGCAT ".Config").crit("term without corresponding init");
213         return;
214     }
215     else if (--m_initCount > 0) {
216         return;
217     }
218
219     MessageDecoderManager.deregisterFactories();
220     MessageEncoderManager.deregisterFactories();
221     SecurityPolicyRuleManager.deregisterFactories();
222     SAMLArtifactManager.deregisterFactories();
223     EntityMatcherManager.deregisterFactories();
224     MetadataFilterManager.deregisterFactories();
225     MetadataProviderManager.deregisterFactories();
226
227     delete m_artifactMap;
228     m_artifactMap = nullptr;
229
230     if (termXMLTooling)
231         XMLToolingConfig::getConfig().term();
232     
233     Category::getInstance(SAML_LOGCAT ".Config").info("%s library shutdown complete", PACKAGE_STRING);
234 }
235
236 void SAMLInternalConfig::generateRandomBytes(void* buf, unsigned int len)
237 {
238     try {
239         if (XSECPlatformUtils::g_cryptoProvider->getRandom(reinterpret_cast<unsigned char*>(buf),len)<len)
240             throw XMLSecurityException("Unable to generate random data; was PRNG seeded?");
241     }
242     catch (XSECCryptoException& e) {
243         throw XMLSecurityException("Unable to generate random data: $1",params(1,e.getMsg()));
244     }
245 }
246
247 void SAMLInternalConfig::generateRandomBytes(std::string& buf, unsigned int len)
248 {
249     buf.erase();
250     auto_arrayptr<unsigned char> hold(new unsigned char[len]);
251     generateRandomBytes(const_cast<unsigned char*>(hold.get()), len);
252     for (unsigned int i=0; i<len; i++)
253         buf+=(hold.get())[i];
254 }
255
256 XMLCh* SAMLInternalConfig::generateIdentifier()
257 {
258     unsigned char key[17];
259     generateRandomBytes(key,16);
260     
261     char hexform[34];
262     sprintf(hexform,"_%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
263             key[0],key[1],key[2],key[3],key[4],key[5],key[6],key[7],
264             key[8],key[9],key[10],key[11],key[12],key[13],key[14],key[15]);
265     hexform[33]=0;
266     return XMLString::transcode(hexform);
267 }
268
269 string SAMLInternalConfig::hashSHA1(const char* s, bool toHex)
270 {
271     return SecurityHelper::doHash("SHA1", s, strlen(s), toHex);
272 }
273
274 void SAMLInternalConfig::setContactPriority(const XMLCh* contactTypes)
275 {
276     const XMLCh* ctype;
277     m_contactPriority.clear();
278     XMLStringTokenizer tokens(contactTypes);
279     while (tokens.hasMoreTokens()) {
280         ctype = tokens.nextToken();
281         if (ctype && *ctype)
282             m_contactPriority.push_back(ctype);
283     }
284 }
285
286 using namespace saml2md;
287
288 const ContactPerson* SAMLInternalConfig::getContactPerson(const EntityDescriptor& entity) const
289 {
290     for (vector<xstring>::const_iterator ctype = m_contactPriority.begin(); ctype != m_contactPriority.end(); ++ctype) {
291         const ContactPerson* cp = find_if(entity.getContactPersons(), *ctype == lambda::bind(&ContactPerson::getContactType, _1));
292         if (cp)
293             return cp;
294     }
295     return nullptr;
296 }
297
298 const ContactPerson* SAMLInternalConfig::getContactPerson(const RoleDescriptor& role) const
299 {
300     for (vector<xstring>::const_iterator ctype = m_contactPriority.begin(); ctype != m_contactPriority.end(); ++ctype) {
301         const ContactPerson* cp = find_if(role.getContactPersons(), *ctype == lambda::bind(&ContactPerson::getContactType, _1));
302         if (cp)
303             return cp;
304     }
305     return getContactPerson(*(dynamic_cast<const EntityDescriptor*>(role.getParent())));
306 }
307
308 SignableObject::SignableObject()
309 {
310 }
311
312 SignableObject::~SignableObject()
313 {
314 }
315
316 RootObject::RootObject()
317 {
318 }
319
320 RootObject::~RootObject()
321 {
322 }
323
324 Assertion::Assertion()
325 {
326 }
327
328 Assertion::~Assertion()
329 {
330 }
331
332 Status::Status()
333 {
334 }
335
336 Status::~Status()
337 {
338 }
339
340 void opensaml::annotateException(XMLToolingException* e, const EntityDescriptor* entity, const Status* status, bool rethrow)
341 {
342     time_t now = time(nullptr);
343     const RoleDescriptor* role = nullptr;
344     static bool (TimeBoundSAMLObject::* isValid)(time_t) const = &TimeBoundSAMLObject::isValid;
345
346     if (entity) {
347         const XMLObject* r = find_if(
348             entity->getOrderedChildren(),
349             (ll_dynamic_cast<const RoleDescriptor*>(_1) != ((const RoleDescriptor*)nullptr) &&
350                     lambda::bind(isValid, ll_dynamic_cast<const TimeBoundSAMLObject*>(_1), now))
351             );
352         if (r)
353             role = dynamic_cast<const RoleDescriptor*>(r);
354     }
355
356     annotateException(e, role, status, rethrow);
357 }
358
359 void opensaml::annotateException(XMLToolingException* e, const RoleDescriptor* role, const Status* status, bool rethrow)
360 {
361     if (role) {
362         auto_ptr_char id(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
363         e->addProperty("entityID",id.get());
364
365         const ContactPerson* cp = SAMLConfig::getConfig().getContactPerson(*role);
366         if (cp) {
367             GivenName* fname = cp->getGivenName();
368             SurName* lname = cp->getSurName();
369             auto_ptr_char first(fname ? fname->getName() : nullptr);
370             auto_ptr_char last(lname ? lname->getName() : nullptr);
371             if (first.get() && last.get()) {
372                 string contact=string(first.get()) + ' ' + last.get();
373                 e->addProperty("contactName", contact.c_str());
374             }
375             else if (first.get())
376                 e->addProperty("contactName", first.get());
377             else if (last.get())
378                 e->addProperty("contactName", last.get());
379             const vector<EmailAddress*>& emails=cp->getEmailAddresss();
380             if (!emails.empty()) {
381                 auto_ptr_char email(emails.front()->getAddress());
382                 if (email.get()) {
383                     if (strstr(email.get(), "mailto:") == email.get()) {
384                         e->addProperty("contactEmail", email.get());
385                     }
386                     else {
387                         string addr = string("mailto:") + email.get();
388                         e->addProperty("contactEmail", addr.c_str());
389                     }
390                 }
391             }
392         }
393
394         auto_ptr_char eurl(role->getErrorURL());
395         if (eurl.get()) {
396             e->addProperty("errorURL",eurl.get());
397         }
398     }
399
400     if (status) {
401         auto_ptr_char sc(status->getTopStatus());
402         if (sc.get() && *sc.get())
403             e->addProperty("statusCode", sc.get());
404         if (status->getSubStatus()) {
405             auto_ptr_char sc2(status->getSubStatus());
406             if (sc2.get() && *sc.get())
407                 e->addProperty("statusCode2", sc2.get());
408         }
409         if (status->getMessage()) {
410             auto_ptr_char msg(status->getMessage());
411             if (msg.get() && *msg.get())
412                 e->addProperty("statusMessage", msg.get());
413         }
414     }
415     
416     if (rethrow)
417         e->raise();
418 }