Moved URLEncoder down to tooling lib, added exception->querystring method.
[shibboleth/cpp-opensaml.git] / saml / SAMLConfig.cpp
1
2 /*
3  *  Copyright 2001-2007 Internet2
4  * 
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 /**
19  * SAMLConfig.cpp
20  * 
21  * Library configuration 
22  */
23
24 #include "internal.h"
25 #include "exceptions.h"
26 #include "SAMLConfig.h"
27 #include "binding/ArtifactMap.h"
28 #include "binding/MessageDecoder.h"
29 #include "binding/MessageEncoder.h"
30 #include "binding/SAMLArtifact.h"
31 #include "binding/SecurityPolicyRule.h"
32 #include "saml1/core/Assertions.h"
33 #include "saml1/core/Protocols.h"
34 #include "saml2/core/Protocols.h"
35 #include "saml2/metadata/Metadata.h"
36 #include "saml2/metadata/MetadataProvider.h"
37 #include "util/SAMLConstants.h"
38
39 #include <xmltooling/XMLToolingConfig.h>
40 #include <xmltooling/signature/Signature.h>
41 #include <xmltooling/util/NDC.h>
42
43 #include <log4cpp/Category.hh>
44 #include <xsec/enc/XSECCryptoException.hpp>
45 #include <xsec/enc/XSECCryptoProvider.hpp>
46 #include <xsec/utils/XSECPlatformUtils.hpp>
47 #include <openssl/err.h>
48
49 using namespace opensaml;
50 using namespace xmlsignature;
51 using namespace xmltooling;
52 using namespace log4cpp;
53 using namespace std;
54
55 // Expose entry points when used as an extension library
56
57 extern "C" int SAML_API xmltooling_extension_init(void*)
58 {
59     if (SAMLConfig::getConfig().init(false))
60         return 0;
61     return -1;
62 }
63
64 extern "C" void SAML_API xmltooling_extension_term()
65 {
66     SAMLConfig::getConfig().term(false);
67 }
68
69 DECL_XMLTOOLING_EXCEPTION_FACTORY(ArtifactException,opensaml);
70 DECL_XMLTOOLING_EXCEPTION_FACTORY(MetadataException,opensaml::saml2md);
71 DECL_XMLTOOLING_EXCEPTION_FACTORY(MetadataFilterException,opensaml::saml2md);
72 DECL_XMLTOOLING_EXCEPTION_FACTORY(BindingException,opensaml);
73 DECL_XMLTOOLING_EXCEPTION_FACTORY(ProfileException,opensaml);
74 DECL_XMLTOOLING_EXCEPTION_FACTORY(FatalProfileException,opensaml);
75 DECL_XMLTOOLING_EXCEPTION_FACTORY(RetryableProfileException,opensaml);
76
77 namespace opensaml {
78    SAMLInternalConfig g_config;
79 }
80
81 SAMLConfig& SAMLConfig::getConfig()
82 {
83     return g_config;
84 }
85
86 SAMLInternalConfig& SAMLInternalConfig::getInternalConfig()
87 {
88     return g_config;
89 }
90
91 void SAMLConfig::setArtifactMap(ArtifactMap* artifactMap)
92 {
93     delete m_artifactMap;
94     m_artifactMap = artifactMap;
95 }
96
97 bool SAMLInternalConfig::init(bool initXMLTooling)
98 {
99 #ifdef _DEBUG
100     xmltooling::NDC ndc("init");
101 #endif
102     Category& log=Category::getInstance(SAML_LOGCAT".SAMLConfig");
103     log.debug("library initialization started");
104
105     if (initXMLTooling) {
106         XMLToolingConfig::getConfig().init();
107         log.debug("XMLTooling library initialized");
108     }
109
110     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ArtifactException,opensaml);
111     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(MetadataException,opensaml::saml2md);
112     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(MetadataFilterException,opensaml::saml2md);
113     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(BindingException,opensaml);
114     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ProfileException,opensaml);
115     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(FatalProfileException,opensaml);
116     REGISTER_XMLTOOLING_EXCEPTION_FACTORY(RetryableProfileException,opensaml);
117
118     saml1::registerAssertionClasses();
119     saml1p::registerProtocolClasses();
120     saml2::registerAssertionClasses();
121     saml2p::registerProtocolClasses();
122     saml2md::registerMetadataClasses();
123     saml2md::registerMetadataProviders();
124     saml2md::registerMetadataFilters();
125     registerSAMLArtifacts();
126     registerMessageEncoders();
127     registerMessageDecoders();
128     registerSecurityPolicyRules();
129
130     log.info("library initialization complete");
131     return true;
132 }
133
134 void SAMLInternalConfig::term(bool termXMLTooling)
135 {
136 #ifdef _DEBUG
137     xmltooling::NDC ndc("term");
138 #endif
139     Category& log=Category::getInstance(SAML_LOGCAT".SAMLConfig");
140
141     MessageDecoderManager.deregisterFactories();
142     MessageEncoderManager.deregisterFactories();
143     SecurityPolicyRuleManager.deregisterFactories();
144     SAMLArtifactManager.deregisterFactories();
145     MetadataFilterManager.deregisterFactories();
146     MetadataProviderManager.deregisterFactories();
147
148     delete m_artifactMap;
149     m_artifactMap = NULL;
150
151     if (termXMLTooling) {
152         XMLToolingConfig::getConfig().term();
153         log.debug("XMLTooling library shut down");
154     }
155     log.info("library shutdown complete");
156 }
157
158 void SAMLInternalConfig::generateRandomBytes(void* buf, unsigned int len)
159 {
160     try {
161         if (XSECPlatformUtils::g_cryptoProvider->getRandom(reinterpret_cast<unsigned char*>(buf),len)<len)
162             throw XMLSecurityException("Unable to generate random data; was PRNG seeded?");
163     }
164     catch (XSECCryptoException& e) {
165         throw XMLSecurityException("Unable to generate random data: $1",params(1,e.getMsg()));
166     }
167 }
168
169 void SAMLInternalConfig::generateRandomBytes(std::string& buf, unsigned int len)
170 {
171     buf.erase();
172     auto_ptr<unsigned char> hold(new unsigned char[len]);
173     generateRandomBytes(hold.get(),len);
174     for (unsigned int i=0; i<len; i++)
175         buf+=(hold.get())[i];
176 }
177
178 XMLCh* SAMLInternalConfig::generateIdentifier()
179 {
180     unsigned char key[17];
181     generateRandomBytes(key,16);
182     
183     char hexform[34];
184     sprintf(hexform,"_%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
185             key[0],key[1],key[2],key[3],key[4],key[5],key[6],key[7],
186             key[8],key[9],key[10],key[11],key[12],key[13],key[14],key[15]);
187     hexform[33]=0;
188     return XMLString::transcode(hexform);
189 }
190
191 string SAMLInternalConfig::hashSHA1(const char* s, bool toHex)
192 {
193     static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
194
195     auto_ptr<XSECCryptoHash> hasher(XSECPlatformUtils::g_cryptoProvider->hashSHA1());
196     if (hasher.get()) {
197         unsigned char buf[21];
198         hasher->hash(reinterpret_cast<unsigned char*>(const_cast<char*>(s)),strlen(s));
199         if (hasher->finish(buf,20)==20) {
200             string ret;
201             if (toHex) {
202                 for (unsigned int i=0; i<20; i++) {
203                     ret+=(DIGITS[((unsigned char)(0xF0 & buf[i])) >> 4 ]);
204                     ret+=(DIGITS[0x0F & buf[i]]);
205                 }
206             }
207             else {
208                 for (unsigned int i=0; i<20; i++) {
209                     ret+=buf[i];
210                 }
211             }
212             return ret;
213         }
214     }
215     throw XMLSecurityException("Unable to generate SHA-1 hash.");
216 }
217
218 void opensaml::log_openssl()
219 {
220     const char* file;
221     const char* data;
222     int flags,line;
223
224     unsigned long code=ERR_get_error_line_data(&file,&line,&data,&flags);
225     while (code) {
226         Category& log=Category::getInstance("OpenSSL");
227         log.errorStream() << "error code: " << code << " in " << file << ", line " << line << CategoryStream::ENDLINE;
228         if (data && (flags & ERR_TXT_STRING))
229             log.errorStream() << "error data: " << data << CategoryStream::ENDLINE;
230         code=ERR_get_error_line_data(&file,&line,&data,&flags);
231     }
232 }
233
234 using namespace saml2md;
235
236 void opensaml::annotateException(XMLToolingException* e, const EntityDescriptor* entity, bool rethrow)
237 {
238     if (entity) {
239         auto_ptr_char id(entity->getEntityID());
240         e->addProperty("entityID",id.get());
241         const list<XMLObject*>& roles=entity->getOrderedChildren();
242         for (list<XMLObject*>::const_iterator child=roles.begin(); child!=roles.end(); ++child) {
243             const RoleDescriptor* role=dynamic_cast<RoleDescriptor*>(*child);
244             if (role && role->isValid()) {
245                 const vector<ContactPerson*>& contacts=role->getContactPersons();
246                 for (vector<ContactPerson*>::const_iterator c=contacts.begin(); c!=contacts.end(); ++c) {
247                     const XMLCh* ctype=(*c)->getContactType();
248                     if (ctype && (XMLString::equals(ctype,ContactPerson::CONTACT_SUPPORT)
249                             || XMLString::equals(ctype,ContactPerson::CONTACT_TECHNICAL))) {
250                         GivenName* fname=(*c)->getGivenName();
251                         SurName* lname=(*c)->getSurName();
252                         auto_ptr_char first(fname ? fname->getName() : NULL);
253                         auto_ptr_char last(lname ? lname->getName() : NULL);
254                         if (first.get() && last.get()) {
255                             string contact=string(first.get()) + ' ' + last.get();
256                             e->addProperty("contactName",contact.c_str());
257                         }
258                         else if (first.get())
259                             e->addProperty("contactName",first.get());
260                         else if (last.get())
261                             e->addProperty("contactName",last.get());
262                         const vector<EmailAddress*>& emails=const_cast<const ContactPerson*>(*c)->getEmailAddresss();
263                         if (!emails.empty()) {
264                             auto_ptr_char email(emails.front()->getAddress());
265                             if (email.get())
266                                 e->addProperty("contactEmail",email.get());
267                         }
268                         break;
269                     }
270                 }
271                 if (e->getProperty("contactName") || e->getProperty("contactEmail")) {
272                     auto_ptr_char eurl(role->getErrorURL());
273                     if (eurl.get()) {
274                         e->addProperty("errorURL",eurl.get());
275                     }
276                 }
277                 break;
278             }
279         }
280     }
281     
282     if (rethrow)
283         e->raise();
284 }
285
286 void opensaml::annotateException(XMLToolingException* e, const RoleDescriptor* role, bool rethrow)
287 {
288     if (role) {
289         auto_ptr_char id(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
290         e->addProperty("entityID",id.get());
291
292         const vector<ContactPerson*>& contacts=role->getContactPersons();
293         for (vector<ContactPerson*>::const_iterator c=contacts.begin(); c!=contacts.end(); ++c) {
294             const XMLCh* ctype=(*c)->getContactType();
295             if (ctype && (XMLString::equals(ctype,ContactPerson::CONTACT_SUPPORT)
296                     || XMLString::equals(ctype,ContactPerson::CONTACT_TECHNICAL))) {
297                 GivenName* fname=(*c)->getGivenName();
298                 SurName* lname=(*c)->getSurName();
299                 auto_ptr_char first(fname ? fname->getName() : NULL);
300                 auto_ptr_char last(lname ? lname->getName() : NULL);
301                 if (first.get() && last.get()) {
302                     string contact=string(first.get()) + ' ' + last.get();
303                     e->addProperty("contactName",contact.c_str());
304                 }
305                 else if (first.get())
306                     e->addProperty("contactName",first.get());
307                 else if (last.get())
308                     e->addProperty("contactName",last.get());
309                 const vector<EmailAddress*>& emails=const_cast<const ContactPerson*>(*c)->getEmailAddresss();
310                 if (!emails.empty()) {
311                     auto_ptr_char email(emails.front()->getAddress());
312                     if (email.get())
313                         e->addProperty("contactEmail",email.get());
314                 }
315                 break;
316             }
317         }
318
319         auto_ptr_char eurl(role->getErrorURL());
320         if (eurl.get()) {
321             e->addProperty("errorURL",eurl.get());
322         }
323     }
324     
325     if (rethrow)
326         e->raise();
327 }