SSPCPP-616 - fix tuple namespaces and string literal errors
[shibboleth/cpp-sp.git] / shibsp / impl / XMLServiceProvider.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  * XMLServiceProvider.cpp
23  *
24  * XML-based SP configuration and mgmt.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "version.h"
30 #include "AccessControl.h"
31 #include "Application.h"
32 #include "RequestMapper.h"
33 #include "ServiceProvider.h"
34 #include "SessionCache.h"
35 #include "SPConfig.h"
36 #include "SPRequest.h"
37 #include "binding/ProtocolProvider.h"
38 #include "handler/LogoutInitiator.h"
39 #include "handler/SessionInitiator.h"
40 #include "remoting/ListenerService.h"
41 #include "util/DOMPropertySet.h"
42 #include "util/SPConstants.h"
43
44 #if defined(XMLTOOLING_LOG4SHIB)
45 # include <log4shib/PropertyConfigurator.hh>
46 #elif defined(XMLTOOLING_LOG4CPP)
47 # include <log4cpp/PropertyConfigurator.hh>
48 #else
49 # error "Supported logging library not available."
50 #endif
51 #include <algorithm>
52 #include <boost/bind.hpp>
53 #include <boost/lexical_cast.hpp>
54 #include <boost/shared_ptr.hpp>
55 #include <boost/algorithm/string.hpp>
56 #include <boost/tuple/tuple.hpp>
57 #include <xercesc/util/XMLUniDefs.hpp>
58 #include <xercesc/util/XMLStringTokenizer.hpp>
59 #include <xmltooling/XMLToolingConfig.h>
60 #include <xmltooling/version.h>
61 #include <xmltooling/util/NDC.h>
62 #include <xmltooling/util/ReloadableXMLFile.h>
63 #include <xmltooling/util/TemplateEngine.h>
64 #include <xmltooling/util/Threads.h>
65 #include <xmltooling/util/XMLHelper.h>
66
67 #ifndef SHIBSP_LITE
68 # include "attribute/filtering/AttributeFilter.h"
69 # include "attribute/resolver/AttributeExtractor.h"
70 # include "attribute/resolver/AttributeResolver.h"
71 # include "security/PKIXTrustEngine.h"
72 # include "security/SecurityPolicyProvider.h"
73 # include <saml/exceptions.h>
74 # include <saml/version.h>
75 # include <saml/SAMLConfig.h>
76 # include <saml/binding/ArtifactMap.h>
77 # include <saml/binding/SAMLArtifact.h>
78 # include <saml/saml1/core/Assertions.h>
79 # include <saml/saml2/core/Assertions.h>
80 # include <saml/saml2/binding/SAML2ArtifactType0004.h>
81 # include <saml/saml2/metadata/EntityMatcher.h>
82 # include <saml/saml2/metadata/Metadata.h>
83 # include <saml/saml2/metadata/MetadataProvider.h>
84 # include <saml/util/SAMLConstants.h>
85 # include <xmltooling/security/ChainingTrustEngine.h>
86 # include <xmltooling/security/CredentialResolver.h>
87 # include <xmltooling/security/SecurityHelper.h>
88 # include <xmltooling/util/ReplayCache.h>
89 # include <xmltooling/util/StorageService.h>
90 # include <xsec/utils/XSECPlatformUtils.hpp>
91 using namespace opensaml::saml2;
92 using namespace opensaml::saml2p;
93 using namespace opensaml::saml2md;
94 using namespace opensaml;
95 #else
96 # include "lite/SAMLConstants.h"
97 #endif
98
99 using namespace shibsp;
100 using namespace xmltooling;
101 using namespace boost;
102 using namespace std;
103
104 #ifndef min
105 # define min(a,b)            (((a) < (b)) ? (a) : (b))
106 #endif
107
108 namespace {
109
110 #if defined (_MSC_VER)
111     #pragma warning( push )
112     #pragma warning( disable : 4250 )
113 #endif
114
115     static vector<const Handler*> g_noHandlers;
116
117     // Application configuration wrapper
118     class SHIBSP_DLLLOCAL XMLApplication : public Application, public Remoted, public DOMPropertySet, public DOMNodeFilter
119     {
120     public:
121         XMLApplication(const ServiceProvider*, const ProtocolProvider*, DOMElement*, const XMLApplication* base=nullptr);
122         ~XMLApplication();
123
124         const char* getHash() const {return m_hash.c_str();}
125
126 #ifndef SHIBSP_LITE
127         SAMLArtifact* generateSAML1Artifact(const EntityDescriptor* relyingParty) const {
128             throw ConfigurationException("No support for SAML 1.x artifact generation.");
129         }
130         SAML2Artifact* generateSAML2Artifact(const EntityDescriptor* relyingParty) const {
131             pair<bool,int> index = make_pair(false,0);
132             const PropertySet* props = getRelyingParty(relyingParty);
133             index = props->getInt("artifactEndpointIndex");
134             if (!index.first)
135                 index = getArtifactEndpointIndex();
136             pair<bool,const char*> entityID = props->getString("entityID");
137             return new SAML2ArtifactType0004(
138                 SecurityHelper::doHash("SHA1", entityID.second, strlen(entityID.second), false),
139                 index.first ? index.second : 1
140                 );
141         }
142
143         MetadataProvider* getMetadataProvider(bool required=true) const {
144             if (required && !m_base && !m_metadata)
145                 throw ConfigurationException("No MetadataProvider available.");
146             return (!m_metadata && m_base) ? m_base->getMetadataProvider(required) : m_metadata.get();
147         }
148         TrustEngine* getTrustEngine(bool required=true) const {
149             if (required && !m_base && !m_trust)
150                 throw ConfigurationException("No TrustEngine available.");
151             return (!m_trust && m_base) ? m_base->getTrustEngine(required) : m_trust.get();
152         }
153         AttributeExtractor* getAttributeExtractor() const {
154             return (!m_attrExtractor && m_base) ? m_base->getAttributeExtractor() : m_attrExtractor.get();
155         }
156         AttributeFilter* getAttributeFilter() const {
157             return (!m_attrFilter && m_base) ? m_base->getAttributeFilter() : m_attrFilter.get();
158         }
159         AttributeResolver* getAttributeResolver() const {
160             return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver.get();
161         }
162         CredentialResolver* getCredentialResolver() const {
163             return (!m_credResolver && m_base) ? m_base->getCredentialResolver() : m_credResolver.get();
164         }
165         const PropertySet* getRelyingParty(const EntityDescriptor* provider) const;
166         const PropertySet* getRelyingParty(const XMLCh* entityID) const;
167
168         const vector<const XMLCh*>* getAudiences() const {
169             return (m_audiences.empty() && m_base) ? m_base->getAudiences() : &m_audiences;
170         }
171 #endif
172         string getNotificationURL(const char* resource, bool front, unsigned int index) const;
173
174         const vector<string>& getRemoteUserAttributeIds() const {
175             return (m_remoteUsers.empty() && m_base) ? m_base->getRemoteUserAttributeIds() : m_remoteUsers;
176         }
177
178         void clearHeader(SPRequest& request, const char* rawname, const char* cginame) const;
179         void setHeader(SPRequest& request, const char* name, const char* value) const;
180         string getSecureHeader(const SPRequest& request, const char* name) const;
181
182         const SessionInitiator* getDefaultSessionInitiator() const;
183         const SessionInitiator* getSessionInitiatorById(const char* id) const;
184         const Handler* getDefaultAssertionConsumerService() const;
185         const Handler* getAssertionConsumerServiceByIndex(unsigned short index) const;
186         const Handler* getAssertionConsumerServiceByProtocol(const XMLCh* protocol, const char* binding=nullptr) const;
187         const vector<const Handler*>& getAssertionConsumerServicesByBinding(const XMLCh* binding) const;
188         const Handler* getHandler(const char* path) const;
189         void getHandlers(vector<const Handler*>& handlers) const;
190         void limitRedirect(const GenericRequest& request, const char* url) const;
191
192         void receive(DDF& in, ostream& out) {
193             // Only current function is to return the headers to clear.
194             DDF header;
195             DDF ret=DDF(nullptr).list();
196             DDFJanitor jret(ret);
197             for (vector< pair<string,string> >::const_iterator i = m_unsetHeaders.begin(); i!=m_unsetHeaders.end(); ++i) {
198                 header = DDF(i->first.c_str()).string(i->second.c_str());
199                 ret.add(header);
200             }
201             out << ret;
202         }
203
204         // Provides filter to exclude special config elements.
205 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
206         short
207 #else
208         FilterAction
209 #endif
210         acceptNode(const DOMNode* node) const;
211
212     private:
213         template <class T> T* doChainedPlugins(
214             PluginManager<T,string,const DOMElement*>& pluginMgr,
215             const char* pluginType,
216             const char* chainingType,
217             const XMLCh* localName,
218             DOMElement* e,
219             Category& log,
220             const char* dummyType=nullptr
221             );
222         void doAttributeInfo();
223         void doHandlers(const ProtocolProvider*, const DOMElement*, Category&);
224         void doSSO(const ProtocolProvider&, set<string>&, DOMElement*, Category&);
225         void doLogout(const ProtocolProvider&, set<string>&, DOMElement*, Category&);
226         void doNameIDMgmt(const ProtocolProvider&, set<string>&, DOMElement*, Category&);
227         void doArtifactResolution(const ProtocolProvider&, const char*, DOMElement*, Category&);
228         const XMLApplication* m_base;
229         string m_hash;
230         std::pair<std::string,std::string> m_attributePrefix;
231 #ifndef SHIBSP_LITE
232         void doAttributePlugins(DOMElement*, Category&);
233         scoped_ptr<MetadataProvider> m_metadata;
234         scoped_ptr<TrustEngine> m_trust;
235         scoped_ptr<AttributeExtractor> m_attrExtractor;
236         scoped_ptr<AttributeFilter> m_attrFilter;
237         scoped_ptr<AttributeResolver> m_attrResolver;
238         scoped_ptr<CredentialResolver> m_credResolver;
239         vector<const XMLCh*> m_audiences;
240
241         // RelyingParty properties
242         map< xstring,boost::shared_ptr<PropertySet> > m_partyMap;   // name-based matching
243         vector< pair< boost::shared_ptr<EntityMatcher>,boost::shared_ptr<PropertySet> > > m_partyVec;  // plugin-based matching
244 #endif
245         vector<string> m_remoteUsers,m_frontLogout,m_backLogout;
246
247         // manage handler objects
248         vector< boost::shared_ptr<Handler> > m_handlers;
249
250         // maps location (path info) to applicable handlers
251         map<string,const Handler*> m_handlerMap;
252
253         // maps unique indexes to consumer services
254         map<unsigned int,const Handler*> m_acsIndexMap;
255
256         // pointer to default consumer service
257         const Handler* m_acsDefault;
258
259         // maps binding strings to supporting consumer service(s)
260         typedef map< xstring,vector<const Handler*> > ACSBindingMap;
261         ACSBindingMap m_acsBindingMap;
262
263         // maps protocol strings to supporting consumer service(s)
264         typedef map< xstring,vector<const Handler*> > ACSProtocolMap;
265         ACSProtocolMap m_acsProtocolMap;
266
267         // pointer to default session initiator
268         const SessionInitiator* m_sessionInitDefault;
269
270         // maps unique ID strings to session initiators
271         map<string,const SessionInitiator*> m_sessionInitMap;
272
273         // pointer to default artifact resolution service
274         const Handler* m_artifactResolutionDefault;
275
276         pair<bool,int> getArtifactEndpointIndex() const {
277             if (m_artifactResolutionDefault) return m_artifactResolutionDefault->getInt("index");
278             return m_base ? m_base->getArtifactEndpointIndex() : make_pair(false,0);
279         }
280
281         enum {
282             REDIRECT_LIMIT_INHERIT,
283             REDIRECT_LIMIT_NONE,
284             REDIRECT_LIMIT_EXACT,
285             REDIRECT_LIMIT_HOST,
286             REDIRECT_LIMIT_WHITELIST,
287             REDIRECT_LIMIT_EXACT_WHITELIST,
288             REDIRECT_LIMIT_HOST_WHITELIST
289         } m_redirectLimit;
290         vector<string> m_redirectWhitelist;
291     };
292
293     // Top-level configuration implementation
294     class SHIBSP_DLLLOCAL XMLConfig;
295     class SHIBSP_DLLLOCAL XMLConfigImpl : public DOMPropertySet, public DOMNodeFilter
296     {
297     public:
298         XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer, Category& log);
299         ~XMLConfigImpl() {
300             if (m_document)
301                 m_document->release();
302         }
303
304 #ifndef SHIBSP_LITE
305         scoped_ptr<TransactionLog> m_tranLog;
306         scoped_ptr<SecurityPolicyProvider> m_policy;
307         vector< boost::tuple<string,string,string> > m_transportOptions;
308 #endif
309         scoped_ptr<RequestMapper> m_requestMapper;
310         map< string,boost::shared_ptr<Application> > m_appmap;
311
312         // Provides filter to exclude special config elements.
313 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
314         short
315 #else
316         FilterAction
317 #endif
318         acceptNode(const DOMNode* node) const;
319
320         void setDocument(DOMDocument* doc) {
321             m_document = doc;
322         }
323
324     private:
325         void doExtensions(const DOMElement*, const char*, Category&);
326         void doListener(const DOMElement*, XMLConfig*, Category&);
327         void doCaching(const DOMElement*, XMLConfig*, Category&);
328
329         DOMDocument* m_document;
330     };
331
332     class SHIBSP_DLLLOCAL XMLConfig : public ServiceProvider, public ReloadableXMLFile
333 #ifndef SHIBSP_LITE
334         ,public Remoted
335 #endif
336     {
337     public:
338         XMLConfig(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT ".Config")) {}
339
340         void init() {
341             background_load();
342         }
343
344         ~XMLConfig() {
345             shutdown();
346 #ifndef SHIBSP_LITE
347             SAMLConfig::getConfig().setArtifactMap(nullptr);
348             XMLToolingConfig::getConfig().setReplayCache(nullptr);
349 #endif
350         }
351
352 #ifndef SHIBSP_LITE
353         // Lockable
354         Lockable* lock() {
355             ReloadableXMLFile::lock();
356             if (m_impl->m_policy)
357                 m_impl->m_policy->lock();
358             return this;
359         }
360         void unlock() {
361             if (m_impl->m_policy)
362                 m_impl->m_policy->unlock();
363             ReloadableXMLFile::unlock();
364         }
365 #endif
366
367         // PropertySet
368         const PropertySet* getParent() const { return m_impl->getParent(); }
369         void setParent(const PropertySet* parent) {return m_impl->setParent(parent);}
370         pair<bool,bool> getBool(const char* name, const char* ns=nullptr) const {return m_impl->getBool(name,ns);}
371         pair<bool,const char*> getString(const char* name, const char* ns=nullptr) const {return m_impl->getString(name,ns);}
372         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=nullptr) const {return m_impl->getXMLString(name,ns);}
373         pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=nullptr) const {return m_impl->getUnsignedInt(name,ns);}
374         pair<bool,int> getInt(const char* name, const char* ns=nullptr) const {return m_impl->getInt(name,ns);}
375         void getAll(map<string,const char*>& properties) const {return m_impl->getAll(properties);}
376         const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:2.0:native:sp:config") const {return m_impl->getPropertySet(name,ns);}
377         const DOMElement* getElement() const {return m_impl->getElement();}
378
379         // ServiceProvider
380 #ifndef SHIBSP_LITE
381         // Remoted
382         void receive(DDF& in, ostream& out);
383
384         TransactionLog* getTransactionLog() const {
385             if (m_impl->m_tranLog)
386                 return m_impl->m_tranLog.get();
387             throw ConfigurationException("No TransactionLog available.");
388         }
389
390         StorageService* getStorageService(const char* id) const {
391             if (id) {
392                 map< string,boost::shared_ptr<StorageService> >::const_iterator i = m_storage.find(id);
393                 if (i != m_storage.end())
394                     return i->second.get();
395             }
396             else if (!m_storage.empty())
397                 return m_storage.begin()->second.get();
398             return nullptr;
399         }
400 #endif
401
402         ListenerService* getListenerService(bool required=true) const {
403             if (required && !m_listener)
404                 throw ConfigurationException("No ListenerService available.");
405             return m_listener.get();
406         }
407
408         SessionCache* getSessionCache(bool required=true) const {
409             if (required && !m_sessionCache)
410                 throw ConfigurationException("No SessionCache available.");
411             return m_sessionCache.get();
412         }
413
414         RequestMapper* getRequestMapper(bool required=true) const {
415             if (required && !m_impl->m_requestMapper)
416                 throw ConfigurationException("No RequestMapper available.");
417             return m_impl->m_requestMapper.get();
418         }
419
420         const Application* getApplication(const char* applicationId) const {
421             map< string,boost::shared_ptr<Application> >::const_iterator i = m_impl->m_appmap.find(applicationId ? applicationId : "default");
422             return (i != m_impl->m_appmap.end()) ? i->second.get() : nullptr;
423         }
424
425 #ifndef SHIBSP_LITE
426         SecurityPolicyProvider* getSecurityPolicyProvider(bool required=true) const {
427             if (required && !m_impl->m_policy)
428                 throw ConfigurationException("No SecurityPolicyProvider available.");
429             return m_impl->m_policy.get();
430         }
431
432         const PropertySet* getPolicySettings(const char* id) const {
433             return getSecurityPolicyProvider()->getPolicySettings(id);
434         }
435
436         const vector<const SecurityPolicyRule*>& getPolicyRules(const char* id) const {
437             return getSecurityPolicyProvider()->getPolicyRules(id);
438         }
439
440         bool setTransportOptions(SOAPTransport& transport) const {
441             bool ret = true;
442             for (vector< boost::tuple<string,string,string> >::const_iterator opt = m_impl->m_transportOptions.begin();
443                     opt != m_impl->m_transportOptions.end(); ++opt) {
444                 if (!transport.setProviderOption(opt->get<0>().c_str(), opt->get<1>().c_str(), opt->get<2>().c_str())) {
445                     m_log.error("failed to set SOAPTransport option (%s)", opt->get<1>().c_str());
446                     ret = false;
447                 }
448             }
449             return ret;
450         }
451 #endif
452
453     protected:
454         pair<bool,DOMElement*> background_load();
455
456     private:
457         friend class XMLConfigImpl;
458         // The order of these members actually matters. If we want to rely on auto-destruction, then
459         // anything dependent on anything else has to come later in the object so it will pop first.
460         // Storage is the lowest, then remoting, then the cache, and finally the rest.
461 #ifndef SHIBSP_LITE
462         map< string,boost::shared_ptr<StorageService> > m_storage;
463 #endif
464         scoped_ptr<ListenerService> m_listener;
465         scoped_ptr<SessionCache> m_sessionCache;
466         scoped_ptr<XMLConfigImpl> m_impl;
467     };
468
469 #if defined (_MSC_VER)
470     #pragma warning( pop )
471 #endif
472
473     static const XMLCh applicationId[] =        UNICODE_LITERAL_13(a,p,p,l,i,c,a,t,i,o,n,I,d);
474     static const XMLCh ApplicationOverride[] =  UNICODE_LITERAL_19(A,p,p,l,i,c,a,t,i,o,n,O,v,e,r,r,i,d,e);
475     static const XMLCh ApplicationDefaults[] =  UNICODE_LITERAL_19(A,p,p,l,i,c,a,t,i,o,n,D,e,f,a,u,l,t,s);
476     static const XMLCh _ArtifactMap[] =         UNICODE_LITERAL_11(A,r,t,i,f,a,c,t,M,a,p);
477     static const XMLCh _AttributeExtractor[] =  UNICODE_LITERAL_18(A,t,t,r,i,b,u,t,e,E,x,t,r,a,c,t,o,r);
478     static const XMLCh _AttributeFilter[] =     UNICODE_LITERAL_15(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r);
479     static const XMLCh _AttributeResolver[] =   UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);
480     static const XMLCh _AssertionConsumerService[] = UNICODE_LITERAL_24(A,s,s,e,r,t,i,o,n,C,o,n,s,u,m,e,r,S,e,r,v,i,c,e);
481     static const XMLCh _ArtifactResolutionService[] =UNICODE_LITERAL_25(A,r,t,i,f,a,c,t,R,e,s,o,l,u,t,i,o,n,S,e,r,v,i,c,e);
482     static const XMLCh _Audience[] =            UNICODE_LITERAL_8(A,u,d,i,e,n,c,e);
483     static const XMLCh Binding[] =              UNICODE_LITERAL_7(B,i,n,d,i,n,g);
484     static const XMLCh Channel[]=               UNICODE_LITERAL_7(C,h,a,n,n,e,l);
485     static const XMLCh _CredentialResolver[] =  UNICODE_LITERAL_18(C,r,e,d,e,n,t,i,a,l,R,e,s,o,l,v,e,r);
486     static const XMLCh _default[] =             UNICODE_LITERAL_7(d,e,f,a,u,l,t);
487     static const XMLCh _Extensions[] =          UNICODE_LITERAL_10(E,x,t,e,n,s,i,o,n,s);
488     static const XMLCh _fatal[] =               UNICODE_LITERAL_5(f,a,t,a,l);
489     static const XMLCh _Handler[] =             UNICODE_LITERAL_7(H,a,n,d,l,e,r);
490     static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);
491     static const XMLCh _index[] =               UNICODE_LITERAL_5(i,n,d,e,x);
492     static const XMLCh InProcess[] =            UNICODE_LITERAL_9(I,n,P,r,o,c,e,s,s);
493     static const XMLCh Library[] =              UNICODE_LITERAL_7(L,i,b,r,a,r,y);
494     static const XMLCh Listener[] =             UNICODE_LITERAL_8(L,i,s,t,e,n,e,r);
495     static const XMLCh Location[] =             UNICODE_LITERAL_8(L,o,c,a,t,i,o,n);
496     static const XMLCh logger[] =               UNICODE_LITERAL_6(l,o,g,g,e,r);
497     static const XMLCh Logout[] =               UNICODE_LITERAL_6(L,o,g,o,u,t);
498     static const XMLCh _LogoutInitiator[] =     UNICODE_LITERAL_15(L,o,g,o,u,t,I,n,i,t,i,a,t,o,r);
499     static const XMLCh _ManageNameIDService[] = UNICODE_LITERAL_19(M,a,n,a,g,e,N,a,m,e,I,D,S,e,r,v,i,c,e);
500     static const XMLCh _MetadataProvider[] =    UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r);
501     static const XMLCh NameIDMgmt[] =           UNICODE_LITERAL_10(N,a,m,e,I,D,M,g,m,t);
502     static const XMLCh Notify[] =               UNICODE_LITERAL_6(N,o,t,i,f,y);
503     static const XMLCh _option[] =              UNICODE_LITERAL_6(o,p,t,i,o,n);
504     static const XMLCh OutOfProcess[] =         UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s);
505     static const XMLCh _path[] =                UNICODE_LITERAL_4(p,a,t,h);
506     static const XMLCh _policyId[] =            UNICODE_LITERAL_8(p,o,l,i,c,y,I,d);
507     static const XMLCh _ProtocolProvider[] =    UNICODE_LITERAL_16(P,r,o,t,o,c,o,l,P,r,o,v,i,d,e,r);
508     static const XMLCh _provider[] =            UNICODE_LITERAL_8(p,r,o,v,i,d,e,r);
509     static const XMLCh RelyingParty[] =         UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y);
510     static const XMLCh _ReplayCache[] =         UNICODE_LITERAL_11(R,e,p,l,a,y,C,a,c,h,e);
511     static const XMLCh _RequestMapper[] =       UNICODE_LITERAL_13(R,e,q,u,e,s,t,M,a,p,p,e,r);
512     static const XMLCh RequestMap[] =           UNICODE_LITERAL_10(R,e,q,u,e,s,t,M,a,p);
513     static const XMLCh SecurityPolicies[] =     UNICODE_LITERAL_16(S,e,c,u,r,i,t,y,P,o,l,i,c,i,e,s);
514     static const XMLCh _SecurityPolicyProvider[] = UNICODE_LITERAL_22(S,e,c,u,r,i,t,y,P,o,l,i,c,y,P,r,o,v,i,d,e,r);
515     static const XMLCh _SessionCache[] =        UNICODE_LITERAL_12(S,e,s,s,i,o,n,C,a,c,h,e);
516     static const XMLCh _SessionInitiator[] =    UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r);
517     static const XMLCh _SingleLogoutService[] = UNICODE_LITERAL_19(S,i,n,g,l,e,L,o,g,o,u,t,S,e,r,v,i,c,e);
518     static const XMLCh Site[] =                 UNICODE_LITERAL_4(S,i,t,e);
519     static const XMLCh SSO[] =                  UNICODE_LITERAL_3(S,S,O);
520     static const XMLCh _StorageService[] =      UNICODE_LITERAL_14(S,t,o,r,a,g,e,S,e,r,v,i,c,e);
521     static const XMLCh TCPListener[] =          UNICODE_LITERAL_11(T,C,P,L,i,s,t,e,n,e,r);
522     static const XMLCh tranLogFiller[] =        UNICODE_LITERAL_13(t,r,a,n,L,o,g,F,i,l,l,e,r);
523     static const XMLCh tranLogFormat[] =        UNICODE_LITERAL_13(t,r,a,n,L,o,g,F,o,r,m,a,t);
524     static const XMLCh TransportOption[] =      UNICODE_LITERAL_15(T,r,a,n,s,p,o,r,t,O,p,t,i,o,n);
525     static const XMLCh _TrustEngine[] =         UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e);
526     static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);
527     static const XMLCh UnixListener[] =         UNICODE_LITERAL_12(U,n,i,x,L,i,s,t,e,n,e,r);
528 };
529
530 namespace shibsp {
531     ServiceProvider* XMLServiceProviderFactory(const DOMElement* const & e)
532     {
533         return new XMLConfig(e);
534     }
535 };
536
537 XMLApplication::XMLApplication(
538     const ServiceProvider* sp,
539     const ProtocolProvider* pp,
540     DOMElement* e,
541     const XMLApplication* base
542     ) : Application(sp), m_base(base), m_acsDefault(nullptr), m_sessionInitDefault(nullptr), m_artifactResolutionDefault(nullptr)
543 {
544 #ifdef _DEBUG
545     xmltooling::NDC ndc("XMLApplication");
546 #endif
547     Category& log = Category::getInstance(SHIBSP_LOGCAT ".Application");
548
549     // First load any property sets.
550     map<string,string> remapper;
551     remapper["relayStateLimit"] = "redirectLimit";
552     remapper["relayStateWhitelist"] = "redirectWhitelist";
553     load(e, nullptr, this, &remapper);
554
555     // Process redirect limit policy. Do this before assigning the parent pointer
556     // to ensure we get only our Sessions element.
557     const PropertySet* sessionProps = getPropertySet("Sessions");
558     if (sessionProps) {
559         pair<bool,const char*> prop = sessionProps->getString("redirectLimit");
560         if (prop.first) {
561             if (!strcmp(prop.second, "none"))
562                 m_redirectLimit = REDIRECT_LIMIT_NONE;
563             else if (!strcmp(prop.second, "exact"))
564                 m_redirectLimit = REDIRECT_LIMIT_EXACT;
565             else if (!strcmp(prop.second, "host"))
566                 m_redirectLimit = REDIRECT_LIMIT_HOST;
567             else {
568                 if (!strcmp(prop.second, "exact+whitelist"))
569                     m_redirectLimit = REDIRECT_LIMIT_EXACT_WHITELIST;
570                 else if (!strcmp(prop.second, "host+whitelist"))
571                     m_redirectLimit = REDIRECT_LIMIT_HOST_WHITELIST;
572                 else if (!strcmp(prop.second, "whitelist"))
573                     m_redirectLimit = REDIRECT_LIMIT_WHITELIST;
574                 else
575                     throw ConfigurationException("Unrecognized redirectLimit setting ($1)", params(1, prop.second));
576                 prop = sessionProps->getString("redirectWhitelist");
577                 if (prop.first) {
578                     string dup(prop.second);
579                     trim(dup);
580                     split(m_redirectWhitelist, dup, is_space(), algorithm::token_compress_on);
581                 }
582             }
583         }
584         else {
585             m_redirectLimit = base ? REDIRECT_LIMIT_INHERIT : REDIRECT_LIMIT_NONE;
586         }
587
588         // Audit some additional settings for logging purposes.
589         prop = sessionProps->getString("cookieProps");
590         if (!prop.first) {
591             log.warn("empty/missing cookieProps setting, set to \"https\" for SSL/TLS-only usage");
592         }
593         else if (!strcmp(prop.second, "http")) {
594             log.warn("insecure cookieProps setting, set to \"https\" for SSL/TLS-only usage");
595         }
596         else if (strcmp(prop.second, "https")) {
597             if (!strstr(prop.second, ";secure") && !strstr(prop.second, "; secure"))
598                 log.warn("custom cookieProps setting should include \"; secure\" for SSL/TLS-only usage");
599             else if (!strstr(prop.second, ";HttpOnly") && !strstr(prop.second, "; HttpOnly"))
600                 log.warn("custom cookieProps setting should include \"; HttpOnly\", site is vulnerable to client-side cookie theft");
601         }
602
603         pair<bool,bool> handlerSSL = sessionProps->getBool("handlerSSL");
604         if (handlerSSL.first && !handlerSSL.second)
605             log.warn("handlerSSL should be enabled for SSL/TLS-enabled web sites");
606     }
607     else {
608         m_redirectLimit = base ? REDIRECT_LIMIT_INHERIT : REDIRECT_LIMIT_NONE;
609     }
610
611     // Assign parent.
612     if (base)
613         setParent(base);
614
615     SPConfig& conf=SPConfig::getConfig();
616 #ifndef SHIBSP_LITE
617     XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();
618 #endif
619
620     // This used to be an actual hash, but now it's just a hex-encode to avoid xmlsec dependency.
621     static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
622     string tohash=getId();
623     tohash+=getString("entityID").second;
624     for (const char* ch = tohash.c_str(); *ch; ++ch) {
625         m_hash += (DIGITS[((unsigned char)(0xF0 & *ch)) >> 4 ]);
626         m_hash += (DIGITS[0x0F & *ch]);
627     }
628
629     doAttributeInfo();
630
631     if (conf.isEnabled(SPConfig::Handlers))
632         doHandlers(pp, e, log);
633
634     // Notification.
635     DOMNodeList* nlist = e->getElementsByTagNameNS(shibspconstants::SHIB2SPCONFIG_NS, Notify);
636     for (XMLSize_t i = 0; nlist && i < nlist->getLength(); ++i) {
637         if (nlist->item(i)->getParentNode()->isSameNode(e)) {
638             const XMLCh* channel = static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(nullptr, Channel);
639             string loc(XMLHelper::getAttrString(static_cast<DOMElement*>(nlist->item(i)), nullptr, Location));
640             if (!loc.empty()) {
641                 if (channel && *channel == chLatin_f)
642                     m_frontLogout.push_back(loc);
643                 else
644                     m_backLogout.push_back(loc);
645             }
646         }
647     }
648
649 #ifndef SHIBSP_LITE
650     nlist = e->getElementsByTagNameNS(samlconstants::SAML20_NS, Audience::LOCAL_NAME);
651     if (nlist && nlist->getLength()) {
652         log.warn("use of <saml:Audience> elements outside of a Security Policy Rule is deprecated");
653         for (XMLSize_t i = 0; i < nlist->getLength(); ++i)
654             if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes())
655                 m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue());
656     }
657
658     if (conf.isEnabled(SPConfig::Metadata)) {
659         m_metadata.reset(
660             doChainedPlugins(
661                 SAMLConfig::getConfig().MetadataProviderManager, "MetadataProvider", CHAINING_METADATA_PROVIDER, _MetadataProvider, e, log
662                 )
663             );
664         try {
665             if (m_metadata)
666                 m_metadata->init();
667             else if (!m_base)
668                 log.warn("no MetadataProvider available, configure at least one for standard SSO usage");
669         }
670         catch (std::exception& ex) {
671             log.crit("error initializing MetadataProvider: %s", ex.what());
672         }
673     }
674
675     if (conf.isEnabled(SPConfig::Trust)) {
676         m_trust.reset(doChainedPlugins(xmlConf.TrustEngineManager, "TrustEngine", CHAINING_TRUSTENGINE, _TrustEngine, e, log));
677         if (!m_trust && !m_base) {
678             log.info(
679                 "no TrustEngine specified or installed, using default chain {%s, %s}",
680                 EXPLICIT_KEY_TRUSTENGINE, SHIBBOLETH_PKIX_TRUSTENGINE
681                 );
682             m_trust.reset(xmlConf.TrustEngineManager.newPlugin(CHAINING_TRUSTENGINE, nullptr));
683             ChainingTrustEngine* trustchain = dynamic_cast<ChainingTrustEngine*>(m_trust.get());
684             if (trustchain) {
685                 trustchain->addTrustEngine(xmlConf.TrustEngineManager.newPlugin(EXPLICIT_KEY_TRUSTENGINE, nullptr));
686                 trustchain->addTrustEngine(xmlConf.TrustEngineManager.newPlugin(SHIBBOLETH_PKIX_TRUSTENGINE, nullptr));
687             }
688         }
689     }
690
691     if (conf.isEnabled(SPConfig::AttributeResolution)) {
692         doAttributePlugins(e, log);
693     }
694
695     if (conf.isEnabled(SPConfig::Credentials)) {
696         m_credResolver.reset(
697             doChainedPlugins(xmlConf.CredentialResolverManager, "CredentialResolver", CHAINING_CREDENTIAL_RESOLVER, _CredentialResolver, e, log)
698             );
699     }
700
701     // Finally, load relying parties.
702     const DOMElement* child = XMLHelper::getFirstChildElement(e, RelyingParty);
703     while (child) {
704         if (child->hasAttributeNS(nullptr, saml2::Attribute::NAME_ATTRIB_NAME)) {
705             boost::shared_ptr<DOMPropertySet> rp(new DOMPropertySet());
706             rp->load(child, nullptr, this);
707             rp->setParent(this);
708             m_partyMap[child->getAttributeNS(nullptr, saml2::Attribute::NAME_ATTRIB_NAME)] = rp;
709         }
710         else if (child->hasAttributeNS(nullptr, _type)) {
711             string emtype(XMLHelper::getAttrString(child, nullptr, _type));
712             boost::shared_ptr<EntityMatcher> em(SAMLConfig::getConfig().EntityMatcherManager.newPlugin(emtype, child));
713             boost::shared_ptr<DOMPropertySet> rp(new DOMPropertySet());
714             rp->load(child, nullptr, this);
715             rp->setParent(this);
716             m_partyVec.push_back(make_pair(em, rp));
717         }
718         child = XMLHelper::getNextSiblingElement(child, RelyingParty);
719     }
720     if (base && m_partyMap.empty() && m_partyVec.empty() && (!base->m_partyMap.empty() || !base->m_partyVec.empty())) {
721         // For inheritance of RPs to work, we have to pull them in to the override by cloning the DOM.
722         child = XMLHelper::getFirstChildElement(base->getElement(), RelyingParty);
723         while (child) {
724             if (child->hasAttributeNS(nullptr, saml2::Attribute::NAME_ATTRIB_NAME)) {
725                 DOMElement* rpclone = static_cast<DOMElement*>(child->cloneNode(true));
726                 boost::shared_ptr<DOMPropertySet> rp(new DOMPropertySet());
727                 rp->load(rpclone, nullptr, this);
728                 rp->setParent(this);
729                 m_partyMap[rpclone->getAttributeNS(nullptr, saml2::Attribute::NAME_ATTRIB_NAME)] = rp;
730             }
731             else if (child->hasAttributeNS(nullptr, _type)) {
732                 DOMElement* rpclone = static_cast<DOMElement*>(child->cloneNode(true));
733                 string emtype(XMLHelper::getAttrString(rpclone, nullptr, _type));
734                 boost::shared_ptr<EntityMatcher> em(SAMLConfig::getConfig().EntityMatcherManager.newPlugin(emtype, rpclone));
735                 boost::shared_ptr<DOMPropertySet> rp(new DOMPropertySet());
736                 rp->load(rpclone, nullptr, this);
737                 rp->setParent(this);
738                 m_partyVec.push_back(make_pair(em, rp));
739             }
740             child = XMLHelper::getNextSiblingElement(child, RelyingParty);
741         }
742     }
743 #endif
744
745     // Out of process only, we register a listener endpoint.
746     if (!conf.isEnabled(SPConfig::InProcess)) {
747         ListenerService* listener = sp->getListenerService(false);
748         if (listener) {
749             string addr=string(getId()) + "::getHeaders::Application";
750             listener->regListener(addr.c_str(), this);
751         }
752         else {
753             log.info("no ListenerService available, Application remoting disabled");
754         }
755     }
756 }
757
758 XMLApplication::~XMLApplication()
759 {
760     ListenerService* listener=getServiceProvider().getListenerService(false);
761     if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess) && !SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
762         string addr=string(getId()) + "::getHeaders::Application";
763         listener->unregListener(addr.c_str(), this);
764     }
765 }
766
767 template <class T> T* XMLApplication::doChainedPlugins(
768     PluginManager<T,string,const DOMElement*>& pluginMgr,
769     const char* pluginType,
770     const char* chainingType,
771     const XMLCh* localName,
772     DOMElement* e,
773     Category& log,
774     const char* dummyType
775     )
776 {
777     string t;
778     DOMElement* child = XMLHelper::getFirstChildElement(e, localName);
779     if (child) {
780         // Check for multiple.
781         if (XMLHelper::getNextSiblingElement(child, localName)) {
782             log.info("multiple %s plugins, wrapping in a chain", pluginType);
783             DOMElement* chain = child->getOwnerDocument()->createElementNS(nullptr, localName);
784             while (child) {
785                 chain->appendChild(child);
786                 child = XMLHelper::getFirstChildElement(e, localName);
787             }
788             t = chainingType;
789             child = chain;
790             e->appendChild(chain);
791         }
792         else {
793             // Only a single one.
794             t = XMLHelper::getAttrString(child, nullptr, _type);
795         }
796
797         try {
798             if (!t.empty()) {
799                 log.info("building %s of type %s...", pluginType, t.c_str());
800                 return pluginMgr.newPlugin(t.c_str(), child);
801             }
802             else {
803                 throw ConfigurationException("$1 element had no type attribute.", params(1, pluginType));
804             }
805         }
806         catch (std::exception& ex) {
807             log.crit("error building %s: %s", pluginType, ex.what());
808             if (dummyType) {
809                 // Install a dummy version as a safety valve.
810                 log.crit("installing safe %s in place of failed version", pluginType);
811                 return pluginMgr.newPlugin(dummyType, nullptr);
812             }
813         }
814     }
815
816     return nullptr;
817 }
818
819 void XMLApplication::doAttributeInfo()
820 {
821     // Populate prefix pair.
822     m_attributePrefix.second = "HTTP_";
823     pair<bool,const char*> prefix = getString("attributePrefix");
824     if (prefix.first) {
825         m_attributePrefix.first = prefix.second;
826         const char* pch = prefix.second;
827         while (*pch) {
828             m_attributePrefix.second += (isalnum(*pch) ? toupper(*pch) : '_');
829             pch++;
830         }
831     }
832
833     pair<bool,const char*> attributes = getString("REMOTE_USER");
834     if (attributes.first) {
835         string dup(attributes.second);
836         trim(dup);
837         split(m_remoteUsers, dup, is_space(), algorithm::token_compress_on);
838     }
839
840     // Load attribute ID lists for REMOTE_USER and header clearing.
841     if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
842         attributes = getString("unsetHeaders");
843         if (attributes.first) {
844             string transformedprefix(m_attributePrefix.second);
845             const char* pch;
846             prefix = getString("metadataAttributePrefix");
847             if (prefix.first) {
848                 pch = prefix.second;
849                 while (*pch) {
850                     transformedprefix += (isalnum(*pch) ? toupper(*pch) : '_');
851                     pch++;
852                 }
853             }
854
855             string dup(attributes.second);
856             trim(dup);
857             vector<string> headerNames;
858             split(headerNames, dup, is_space(), algorithm::token_compress_on);
859             for (vector<string>::const_iterator h = headerNames.begin(); h != headerNames.end(); ++h) {
860                 string transformed;
861                 const char* pch = h->c_str();
862                 while (*pch) {
863                     transformed += (isalnum(*pch) ? toupper(*pch) : '_');
864                     pch++;
865                 }
866                 m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + *h, m_attributePrefix.second + transformed));
867                 if (prefix.first)
868                     m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + prefix.second + *h, transformedprefix + transformed));
869             }
870             m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + "Shib-Application-ID", m_attributePrefix.second + "SHIB_APPLICATION_ID"));
871         }
872     }
873 }
874
875 void XMLApplication::doHandlers(const ProtocolProvider* pp, const DOMElement* e, Category& log)
876 {
877     SPConfig& conf = SPConfig::getConfig();
878
879     const PropertySet* sessions = getPropertySet("Sessions");
880
881     // Process assertion export handler.
882     pair<bool,const char*> location = sessions ? sessions->getString("exportLocation") : pair<bool,const char*>(false,nullptr);
883     if (location.first) {
884         try {
885             DOMElement* exportElement = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, _Handler);
886             exportElement->setAttributeNS(nullptr,Location,sessions->getXMLString("exportLocation").second);
887             pair<bool,const XMLCh*> exportACL = sessions->getXMLString("exportACL");
888             if (exportACL.first) {
889                 static const XMLCh _acl[] = UNICODE_LITERAL_9(e,x,p,o,r,t,A,C,L);
890                 exportElement->setAttributeNS(nullptr,_acl,exportACL.second);
891             }
892             boost::shared_ptr<Handler> exportHandler(
893                 conf.HandlerManager.newPlugin(samlconstants::SAML20_BINDING_URI, pair<const DOMElement*,const char*>(exportElement, getId()))
894                 );
895             m_handlers.push_back(exportHandler);
896
897             // Insert into location map. If it contains the handlerURL, we skip past that part.
898             const char* hurl = sessions->getString("handlerURL").second;
899             if (!hurl)
900                 hurl = "/Shibboleth.sso";
901             const char* pch = strstr(location.second, hurl);
902             if (pch)
903                 location.second = pch + strlen(hurl);
904             if (*location.second == '/')
905                 m_handlerMap[location.second] = exportHandler.get();
906             else
907                 m_handlerMap[string("/") + location.second] = exportHandler.get();
908         }
909         catch (std::exception& ex) {
910             log.error("caught exception installing assertion lookup handler: %s", ex.what());
911         }
912     }
913
914     // Look for "shorthand" elements first.
915     set<string> protocols;
916     DOMElement* child = sessions ? XMLHelper::getFirstChildElement(sessions->getElement()) : nullptr;
917     while (child) {
918         if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2SPCONFIG_NS, SSO)) {
919             if (pp)
920                 doSSO(*pp, protocols, child, log);
921             else
922                 log.error("no ProtocolProvider, SSO auto-configure unsupported");
923         }
924         else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2SPCONFIG_NS, Logout)) {
925             if (pp)
926                 doLogout(*pp, protocols, child, log);
927             else
928                 log.error("no ProtocolProvider, Logout auto-configure unsupported");
929         }
930         else if (XMLHelper::isNodeNamed(child, shibspconstants::SHIB2SPCONFIG_NS, NameIDMgmt)) {
931             if (pp)
932                 doNameIDMgmt(*pp, protocols, child, log);
933             else
934                 log.error("no ProtocolProvider, NameIDMgmt auto-configure unsupported");
935         }
936         else {
937             break;  // drop into next while loop
938         }
939         child = XMLHelper::getNextSiblingElement(child);
940     }
941
942     // Process other handlers.
943     bool hardACS=false, hardSessionInit=false, hardArt=false;
944     while (child) {
945         if (!child->hasAttributeNS(nullptr, Location)) {
946             auto_ptr_char hclass(child->getLocalName());
947             log.error("%s handler with no Location property cannot be processed", hclass.get());
948             child = XMLHelper::getNextSiblingElement(child);
949             continue;
950         }
951         try {
952             boost::shared_ptr<Handler> handler;
953             if (XMLString::equals(child->getLocalName(), _AssertionConsumerService)) {
954                 string bindprop(XMLHelper::getAttrString(child, nullptr, Binding));
955                 if (bindprop.empty()) {
956                     log.error("AssertionConsumerService element has no Binding attribute, skipping it...");
957                     child = XMLHelper::getNextSiblingElement(child);
958                     continue;
959                 }
960                 handler.reset(conf.AssertionConsumerServiceManager.newPlugin(bindprop.c_str(), pair<const DOMElement*,const char*>(child, getId())));
961                 // Map by binding and protocol (may be > 1 per protocol and binding)
962                 m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler.get());
963                 const XMLCh* protfamily = handler->getProtocolFamily();
964                 if (protfamily)
965                     m_acsProtocolMap[protfamily].push_back(handler.get());
966                 m_acsIndexMap[handler->getUnsignedInt("index").second] = handler.get();
967
968                 if (!hardACS) {
969                     pair<bool,bool> defprop = handler->getBool("isDefault");
970                     if (defprop.first) {
971                         if (defprop.second) {
972                             hardACS = true;
973                             m_acsDefault = handler.get();
974                         }
975                     }
976                     else if (!m_acsDefault)
977                         m_acsDefault = handler.get();
978                 }
979             }
980             else if (XMLString::equals(child->getLocalName(), _SessionInitiator)) {
981                 string t(XMLHelper::getAttrString(child, nullptr, _type));
982                 if (t.empty()) {
983                     log.error("SessionInitiator element has no type attribute, skipping it...");
984                     child = XMLHelper::getNextSiblingElement(child);
985                     continue;
986                 }
987                 boost::shared_ptr<SessionInitiator> sihandler(
988                     conf.SessionInitiatorManager.newPlugin(t.c_str(), pair<const DOMElement*,const char*>(child, getId()))
989                     );
990                 handler = sihandler;
991                 pair<bool,const char*> si_id = handler->getString("id");
992                 if (si_id.first && si_id.second)
993                     m_sessionInitMap[si_id.second] = sihandler.get();
994                 if (!hardSessionInit) {
995                     pair<bool,bool> defprop = handler->getBool("isDefault");
996                     if (defprop.first) {
997                         if (defprop.second) {
998                             hardSessionInit = true;
999                             m_sessionInitDefault = sihandler.get();
1000                         }
1001                     }
1002                     else if (!m_sessionInitDefault) {
1003                         m_sessionInitDefault = sihandler.get();
1004                     }
1005                 }
1006             }
1007             else if (XMLString::equals(child->getLocalName(), _LogoutInitiator)) {
1008                 string t(XMLHelper::getAttrString(child, nullptr, _type));
1009                 if (t.empty()) {
1010                     log.error("LogoutInitiator element has no type attribute, skipping it...");
1011                     child = XMLHelper::getNextSiblingElement(child);
1012                     continue;
1013                 }
1014                 handler.reset(conf.LogoutInitiatorManager.newPlugin(t.c_str(), pair<const DOMElement*,const char*>(child, getId())));
1015             }
1016             else if (XMLString::equals(child->getLocalName(), _ArtifactResolutionService)) {
1017                 string bindprop(XMLHelper::getAttrString(child, nullptr, Binding));
1018                 if (bindprop.empty()) {
1019                     log.error("ArtifactResolutionService element has no Binding attribute, skipping it...");
1020                     child = XMLHelper::getNextSiblingElement(child);
1021                     continue;
1022                 }
1023                 handler.reset(conf.ArtifactResolutionServiceManager.newPlugin(bindprop.c_str(), pair<const DOMElement*,const char*>(child, getId())));
1024
1025                 if (!hardArt) {
1026                     pair<bool,bool> defprop = handler->getBool("isDefault");
1027                     if (defprop.first) {
1028                         if (defprop.second) {
1029                             hardArt = true;
1030                             m_artifactResolutionDefault = handler.get();
1031                         }
1032                     }
1033                     else if (!m_artifactResolutionDefault)
1034                         m_artifactResolutionDefault = handler.get();
1035                 }
1036             }
1037             else if (XMLString::equals(child->getLocalName(), _SingleLogoutService)) {
1038                 string bindprop(XMLHelper::getAttrString(child, nullptr, Binding));
1039                 if (bindprop.empty()) {
1040                     log.error("SingleLogoutService element has no Binding attribute, skipping it...");
1041                     child = XMLHelper::getNextSiblingElement(child);
1042                     continue;
1043                 }
1044                 handler.reset(conf.SingleLogoutServiceManager.newPlugin(bindprop.c_str(), pair<const DOMElement*,const char*>(child, getId())));
1045             }
1046             else if (XMLString::equals(child->getLocalName(), _ManageNameIDService)) {
1047                 string bindprop(XMLHelper::getAttrString(child, nullptr, Binding));
1048                 if (bindprop.empty()) {
1049                     log.error("ManageNameIDService element has no Binding attribute, skipping it...");
1050                     child = XMLHelper::getNextSiblingElement(child);
1051                     continue;
1052                 }
1053                 handler.reset(conf.ManageNameIDServiceManager.newPlugin(bindprop.c_str(), pair<const DOMElement*,const char*>(child, getId())));
1054             }
1055             else {
1056                 string t(XMLHelper::getAttrString(child, nullptr, _type));
1057                 if (t.empty()) {
1058                     log.error("Handler element has no type attribute, skipping it...");
1059                     child = XMLHelper::getNextSiblingElement(child);
1060                     continue;
1061                 }
1062                 handler.reset(conf.HandlerManager.newPlugin(t.c_str(), pair<const DOMElement*,const char*>(child, getId())));
1063             }
1064
1065             m_handlers.push_back(handler);
1066
1067             // Insert into location map.
1068             location = handler->getString("Location");
1069             if (location.first && *location.second == '/')
1070                 m_handlerMap[location.second] = handler.get();
1071             else if (location.first)
1072                 m_handlerMap[string("/") + location.second] = handler.get();
1073         }
1074         catch (std::exception& ex) {
1075             log.error("caught exception processing handler element: %s", ex.what());
1076         }
1077
1078         child = XMLHelper::getNextSiblingElement(child);
1079     }
1080 }
1081
1082 void XMLApplication::doSSO(const ProtocolProvider& pp, set<string>& protocols, DOMElement* e, Category& log)
1083 {
1084     if (!e->hasChildNodes())
1085         return;
1086     DOMNamedNodeMap* ssoprops = e->getAttributes();
1087     XMLSize_t ssopropslen = ssoprops ? ssoprops->getLength() : 0;
1088
1089     SPConfig& conf = SPConfig::getConfig();
1090
1091     int index = 0; // track ACS indexes globally across all protocols
1092
1093     // Tokenize the protocol list inside the element.
1094     XMLStringTokenizer prottokens(e->getTextContent());
1095     while (prottokens.hasMoreTokens()) {
1096         auto_ptr_char prot(prottokens.nextToken());
1097
1098         // Look for initiator.
1099         const PropertySet* initiator = pp.getInitiator(prot.get(), "SSO");
1100         if (initiator) {
1101             log.info("auto-configuring SSO initiation for protocol (%s)", prot.get());
1102             pair<bool,const XMLCh*> inittype = initiator->getXMLString("id");
1103             if (inittype.first) {
1104                 // Append a session initiator element of the designated type to the root element.
1105                 DOMElement* sidom = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, _SessionInitiator);
1106                 sidom->setAttributeNS(nullptr, _type, inittype.second);
1107                 e->appendChild(sidom);
1108                 log.info("adding SessionInitiator of type (%s) to chain (/Login)", initiator->getString("id").second);
1109
1110                 doArtifactResolution(pp, prot.get(), e, log);
1111                 protocols.insert(prot.get());
1112             }
1113             else {
1114                 log.error("missing id property on Initiator element, check config for protocol (%s)", prot.get());
1115             }
1116         }
1117
1118         // Look for incoming bindings.
1119         const vector<const PropertySet*>& bindings = pp.getBindings(prot.get(), "SSO");
1120         if (!bindings.empty()) {
1121             log.info("auto-configuring SSO endpoints for protocol (%s)", prot.get());
1122             pair<bool,const XMLCh*> idprop,pathprop;
1123             for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b, ++index) {
1124                 idprop = (*b)->getXMLString("id");
1125                 pathprop = (*b)->getXMLString("path");
1126                 if (idprop.first && pathprop.first) {
1127                     DOMElement* acsdom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _AssertionConsumerService);
1128
1129                     // Copy in any attributes from the <SSO> element so they can be accessed as properties in the ACS handler.
1130                     for (XMLSize_t p = 0; p < ssopropslen; ++p) {
1131                         DOMNode* ssoprop = ssoprops->item(p);
1132                         if (ssoprop->getNodeType() == DOMNode::ATTRIBUTE_NODE) {
1133                             acsdom->setAttributeNS(
1134                                 ((DOMAttr*)ssoprop)->getNamespaceURI(),
1135                                 ((DOMAttr*)ssoprop)->getLocalName(),
1136                                 ((DOMAttr*)ssoprop)->getValue()
1137                                 );
1138                         }
1139                     }
1140
1141                     // Set necessary properties based on context.
1142                     acsdom->setAttributeNS(nullptr, Binding, idprop.second);
1143                     acsdom->setAttributeNS(nullptr, Location, pathprop.second);
1144                     xstring indexbuf(1, chDigit_1 + (index % 10));
1145                     if (index / 10)
1146                         indexbuf = (XMLCh)(chDigit_1 + (index / 10)) + indexbuf;
1147                     acsdom->setAttributeNS(nullptr, _index, indexbuf.c_str());
1148
1149                     log.info("adding AssertionConsumerService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
1150                     boost::shared_ptr<Handler> handler(
1151                         conf.AssertionConsumerServiceManager.newPlugin(
1152                             (*b)->getString("id").second, pair<const DOMElement*,const char*>(acsdom, getId())
1153                             )
1154                         );
1155                     m_handlers.push_back(handler);
1156
1157                     // Setup maps and defaults.
1158                     m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler.get());
1159                     const XMLCh* protfamily = handler->getProtocolFamily();
1160                     if (protfamily)
1161                         m_acsProtocolMap[protfamily].push_back(handler.get());
1162                     m_acsIndexMap[handler->getUnsignedInt("index").second] = handler.get();
1163                     if (!m_acsDefault)
1164                         m_acsDefault = handler.get();
1165
1166                     // Insert into location map.
1167                     pair<bool,const char*> location = handler->getString("Location");
1168                     if (location.first && *location.second == '/')
1169                         m_handlerMap[location.second] = handler.get();
1170                     else if (location.first)
1171                         m_handlerMap[string("/") + location.second] = handler.get();
1172                 }
1173                 else {
1174                     log.error("missing id or path property on Binding element, check config for protocol (%s)", prot.get());
1175                 }
1176             }
1177         }
1178
1179         if (!initiator && bindings.empty()) {
1180             log.error("no SSO Initiator or Binding config for protocol (%s)", prot.get());
1181         }
1182     }
1183
1184     // Handle discovery.
1185     static const XMLCh discoveryProtocol[] = UNICODE_LITERAL_17(d,i,s,c,o,v,e,r,y,P,r,o,t,o,c,o,l);
1186     static const XMLCh discoveryURL[] = UNICODE_LITERAL_12(d,i,s,c,o,v,e,r,y,U,R,L);
1187     static const XMLCh _URL[] = UNICODE_LITERAL_3(U,R,L);
1188     const XMLCh* discop = e->getAttributeNS(nullptr, discoveryProtocol);
1189     if (discop && *discop) {
1190         const XMLCh* discou = e->getAttributeNS(nullptr, discoveryURL);
1191         if (discou && *discou) {
1192             // Append a session initiator element of the designated type to the root element.
1193             DOMElement* sidom = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, _SessionInitiator);
1194             sidom->setAttributeNS(nullptr, _type, discop);
1195             sidom->setAttributeNS(nullptr, _URL, discou);
1196             e->appendChild(sidom);
1197             if (log.isInfoEnabled()) {
1198                 auto_ptr_char dp(discop);
1199                 log.info("adding SessionInitiator of type (%s) to chain (/Login)", dp.get());
1200             }
1201         }
1202         else {
1203             log.error("SSO discoveryProtocol specified without discoveryURL");
1204         }
1205     }
1206
1207     // Attach default Location to SSO element.
1208     static const XMLCh _loc[] = { chForwardSlash, chLatin_L, chLatin_o, chLatin_g, chLatin_i, chLatin_n, chNull };
1209     e->setAttributeNS(nullptr, Location, _loc);
1210
1211     // Instantiate Chaining initiator around the SSO element.
1212     boost::shared_ptr<SessionInitiator> chain(
1213         conf.SessionInitiatorManager.newPlugin(CHAINING_SESSION_INITIATOR, pair<const DOMElement*,const char*>(e, getId()))
1214         );
1215     m_handlers.push_back(chain);
1216     m_sessionInitDefault = chain.get();
1217     m_handlerMap["/Login"] = chain.get();
1218 }
1219
1220 void XMLApplication::doLogout(const ProtocolProvider& pp, set<string>& protocols, DOMElement* e, Category& log)
1221 {
1222     if (!e->hasChildNodes())
1223         return;
1224     DOMNamedNodeMap* sloprops = e->getAttributes();
1225     XMLSize_t slopropslen = sloprops ? sloprops->getLength() : 0;
1226
1227     SPConfig& conf = SPConfig::getConfig();
1228
1229     // Tokenize the protocol list inside the element.
1230     XMLStringTokenizer prottokens(e->getTextContent());
1231     while (prottokens.hasMoreTokens()) {
1232         auto_ptr_char prot(prottokens.nextToken());
1233
1234         // Look for initiator.
1235         const PropertySet* initiator = pp.getInitiator(prot.get(), "Logout");
1236         if (initiator) {
1237             log.info("auto-configuring Logout initiation for protocol (%s)", prot.get());
1238             pair<bool,const XMLCh*> inittype = initiator->getXMLString("id");
1239             if (inittype.first) {
1240                 // Append a logout initiator element of the designated type to the root element.
1241                 DOMElement* lidom = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, _LogoutInitiator);
1242                 lidom->setAttributeNS(nullptr, _type, inittype.second);
1243                 e->appendChild(lidom);
1244                 log.info("adding LogoutInitiator of type (%s) to chain (/Logout)", initiator->getString("id").second);
1245
1246                 if (protocols.count(prot.get()) == 0) {
1247                     doArtifactResolution(pp, prot.get(), e, log);
1248                     protocols.insert(prot.get());
1249                 }
1250             }
1251             else {
1252                 log.error("missing id property on Initiator element, check config for protocol (%s)", prot.get());
1253             }
1254         }
1255
1256         // Look for incoming bindings.
1257         const vector<const PropertySet*>& bindings = pp.getBindings(prot.get(), "Logout");
1258         if (!bindings.empty()) {
1259             log.info("auto-configuring Logout endpoints for protocol (%s)", prot.get());
1260             pair<bool,const XMLCh*> idprop,pathprop;
1261             for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
1262                 idprop = (*b)->getXMLString("id");
1263                 pathprop = (*b)->getXMLString("path");
1264                 if (idprop.first && pathprop.first) {
1265                     DOMElement* slodom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _SingleLogoutService);
1266
1267                     // Copy in any attributes from the <Logout> element so they can be accessed as properties in the SLO handler.
1268                     for (XMLSize_t p = 0; p < slopropslen; ++p) {
1269                         DOMNode* sloprop = sloprops->item(p);
1270                         if (sloprop->getNodeType() == DOMNode::ATTRIBUTE_NODE) {
1271                             slodom->setAttributeNS(
1272                                 ((DOMAttr*)sloprop)->getNamespaceURI(),
1273                                 ((DOMAttr*)sloprop)->getLocalName(),
1274                                 ((DOMAttr*)sloprop)->getValue()
1275                                 );
1276                         }
1277                     }
1278
1279                     // Set necessary properties based on context.
1280                     slodom->setAttributeNS(nullptr, Binding, idprop.second);
1281                     slodom->setAttributeNS(nullptr, Location, pathprop.second);
1282                     if (e->hasAttributeNS(nullptr, _policyId))
1283                         slodom->setAttributeNS(shibspconstants::SHIB2SPCONFIG_NS, _policyId, e->getAttributeNS(nullptr, _policyId));
1284
1285                     log.info("adding SingleLogoutService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
1286                     boost::shared_ptr<Handler> handler(
1287                         conf.SingleLogoutServiceManager.newPlugin((*b)->getString("id").second, pair<const DOMElement*,const char*>(slodom, getId()))
1288                         );
1289                     m_handlers.push_back(handler);
1290
1291                     // Insert into location map.
1292                     pair<bool,const char*> location = handler->getString("Location");
1293                     if (location.first && *location.second == '/')
1294                         m_handlerMap[location.second] = handler.get();
1295                     else if (location.first)
1296                         m_handlerMap[string("/") + location.second] = handler.get();
1297                 }
1298                 else {
1299                     log.error("missing id or path property on Binding element, check config for protocol (%s)", prot.get());
1300                 }
1301             }
1302
1303             if (protocols.count(prot.get()) == 0) {
1304                 doArtifactResolution(pp, prot.get(), e, log);
1305                 protocols.insert(prot.get());
1306             }
1307         }
1308
1309         if (!initiator && bindings.empty()) {
1310             log.error("no Logout Initiator or Binding config for protocol (%s)", prot.get());
1311         }
1312     }
1313
1314     // Attach default Location to Logout element.
1315     static const XMLCh _loc[] = { chForwardSlash, chLatin_L, chLatin_o, chLatin_g, chLatin_o, chLatin_u, chLatin_t, chNull };
1316     e->setAttributeNS(nullptr, Location, _loc);
1317
1318     // Instantiate Chaining initiator around the SSO element.
1319     boost::shared_ptr<Handler> chain(
1320         conf.LogoutInitiatorManager.newPlugin(CHAINING_LOGOUT_INITIATOR, pair<const DOMElement*,const char*>(e, getId()))
1321         );
1322     m_handlers.push_back(chain);
1323     m_handlerMap["/Logout"] = chain.get();
1324 }
1325
1326 void XMLApplication::doNameIDMgmt(const ProtocolProvider& pp, set<string>& protocols, DOMElement* e, Category& log)
1327 {
1328     if (!e->hasChildNodes())
1329         return;
1330     DOMNamedNodeMap* nimprops = e->getAttributes();
1331     XMLSize_t nimpropslen = nimprops ? nimprops->getLength() : 0;
1332
1333     SPConfig& conf = SPConfig::getConfig();
1334
1335     // Tokenize the protocol list inside the element.
1336     XMLStringTokenizer prottokens(e->getTextContent());
1337     while (prottokens.hasMoreTokens()) {
1338         auto_ptr_char prot(prottokens.nextToken());
1339
1340         // Look for incoming bindings.
1341         const vector<const PropertySet*>& bindings = pp.getBindings(prot.get(), "NameIDMgmt");
1342         if (!bindings.empty()) {
1343             log.info("auto-configuring NameIDMgmt endpoints for protocol (%s)", prot.get());
1344             pair<bool,const XMLCh*> idprop,pathprop;
1345             for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
1346                 idprop = (*b)->getXMLString("id");
1347                 pathprop = (*b)->getXMLString("path");
1348                 if (idprop.first && pathprop.first) {
1349                     DOMElement* nimdom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _ManageNameIDService);
1350
1351                     // Copy in any attributes from the <NameIDMgmt> element so they can be accessed as properties in the NIM handler.
1352                     for (XMLSize_t p = 0; p < nimpropslen; ++p) {
1353                         DOMNode* nimprop = nimprops->item(p);
1354                         if (nimprop->getNodeType() == DOMNode::ATTRIBUTE_NODE) {
1355                             nimdom->setAttributeNS(
1356                                 ((DOMAttr*)nimprop)->getNamespaceURI(),
1357                                 ((DOMAttr*)nimprop)->getLocalName(),
1358                                 ((DOMAttr*)nimprop)->getValue()
1359                                 );
1360                         }
1361                     }
1362
1363                     // Set necessary properties based on context.
1364                     nimdom->setAttributeNS(nullptr, Binding, idprop.second);
1365                     nimdom->setAttributeNS(nullptr, Location, pathprop.second);
1366                     if (e->hasAttributeNS(nullptr, _policyId))
1367                         nimdom->setAttributeNS(shibspconstants::SHIB2SPCONFIG_NS, _policyId, e->getAttributeNS(nullptr, _policyId));
1368
1369                     log.info("adding ManageNameIDService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
1370                     boost::shared_ptr<Handler> handler(
1371                         conf.ManageNameIDServiceManager.newPlugin((*b)->getString("id").second, pair<const DOMElement*,const char*>(nimdom, getId()))
1372                         );
1373                     m_handlers.push_back(handler);
1374
1375                     // Insert into location map.
1376                     pair<bool,const char*> location = handler->getString("Location");
1377                     if (location.first && *location.second == '/')
1378                         m_handlerMap[location.second] = handler.get();
1379                     else if (location.first)
1380                         m_handlerMap[string("/") + location.second] = handler.get();
1381                 }
1382                 else {
1383                     log.error("missing id or path property on Binding element, check config for protocol (%s)", prot.get());
1384                 }
1385             }
1386
1387             if (protocols.count(prot.get()) == 0) {
1388                 doArtifactResolution(pp, prot.get(), e, log);
1389                 protocols.insert(prot.get());
1390             }
1391         }
1392         else {
1393             log.error("no NameIDMgmt Binding config for protocol (%s)", prot.get());
1394         }
1395     }
1396 }
1397
1398 void XMLApplication::doArtifactResolution(const ProtocolProvider& pp, const char* protocol, DOMElement* e, Category& log)
1399 {
1400     SPConfig& conf = SPConfig::getConfig();
1401
1402     int index = 0; // track indexes globally across all protocols
1403
1404     // Look for incoming bindings.
1405     const vector<const PropertySet*>& bindings = pp.getBindings(protocol, "ArtifactResolution");
1406     if (!bindings.empty()) {
1407         log.info("auto-configuring ArtifactResolution endpoints for protocol (%s)", protocol);
1408         pair<bool,const XMLCh*> idprop,pathprop;
1409         for (vector<const PropertySet*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b, ++index) {
1410             idprop = (*b)->getXMLString("id");
1411             pathprop = (*b)->getXMLString("path");
1412             if (idprop.first && pathprop.first) {
1413                 DOMElement* artdom = e->getOwnerDocument()->createElementNS(samlconstants::SAML20MD_NS, _ArtifactResolutionService);
1414                 artdom->setAttributeNS(nullptr, Binding, idprop.second);
1415                 artdom->setAttributeNS(nullptr, Location, pathprop.second);
1416                 xstring indexbuf(1, chDigit_1 + (index % 10));
1417                 if (index / 10)
1418                     indexbuf = (XMLCh)(chDigit_1 + (index / 10)) + indexbuf;
1419                 artdom->setAttributeNS(nullptr, _index, indexbuf.c_str());
1420
1421                 log.info("adding ArtifactResolutionService for Binding (%s) at (%s)", (*b)->getString("id").second, (*b)->getString("path").second);
1422                 boost::shared_ptr<Handler> handler(
1423                     conf.ArtifactResolutionServiceManager.newPlugin((*b)->getString("id").second, pair<const DOMElement*,const char*>(artdom, getId()))
1424                     );
1425                 m_handlers.push_back(handler);
1426
1427                 if (!m_artifactResolutionDefault)
1428                     m_artifactResolutionDefault = handler.get();
1429
1430                 // Insert into location map.
1431                 pair<bool,const char*> location = handler->getString("Location");
1432                 if (location.first && *location.second == '/')
1433                     m_handlerMap[location.second] = handler.get();
1434                 else if (location.first)
1435                     m_handlerMap[string("/") + location.second] = handler.get();
1436             }
1437             else {
1438                 log.error("missing id or path property on Binding element, check config for protocol (%s)", protocol);
1439             }
1440         }
1441     }
1442 }
1443
1444 #ifndef SHIBSP_LITE
1445 void XMLApplication::doAttributePlugins(DOMElement* e, Category& log)
1446 {
1447     SPConfig& conf = SPConfig::getConfig();
1448
1449     m_attrExtractor.reset(
1450         doChainedPlugins(conf.AttributeExtractorManager, "AttributeExtractor", CHAINING_ATTRIBUTE_EXTRACTOR, _AttributeExtractor, e, log)
1451         );
1452
1453     m_attrFilter.reset(
1454         doChainedPlugins(conf.AttributeFilterManager, "AttributeFilter", CHAINING_ATTRIBUTE_FILTER, _AttributeFilter, e, log, DUMMY_ATTRIBUTE_FILTER)
1455         );
1456
1457     m_attrResolver.reset(
1458         doChainedPlugins(conf.AttributeResolverManager, "AttributeResolver", CHAINING_ATTRIBUTE_RESOLVER, _AttributeResolver, e, log)
1459         );
1460
1461     if (m_unsetHeaders.empty()) {
1462         vector<string> unsetHeaders;
1463         if (m_attrExtractor) {
1464             Locker extlock(m_attrExtractor.get());
1465             m_attrExtractor->getAttributeIds(unsetHeaders);
1466         }
1467         else if (m_base && m_base->m_attrExtractor) {
1468             Locker extlock(m_base->m_attrExtractor.get());
1469             m_base->m_attrExtractor->getAttributeIds(unsetHeaders);
1470         }
1471         if (m_attrResolver) {
1472             Locker reslock(m_attrResolver.get());
1473             m_attrResolver->getAttributeIds(unsetHeaders);
1474         }
1475         else if (m_base && m_base->m_attrResolver) {
1476             Locker extlock(m_base->m_attrResolver.get());
1477             m_base->m_attrResolver->getAttributeIds(unsetHeaders);
1478         }
1479         if (!unsetHeaders.empty()) {
1480             string transformedprefix(m_attributePrefix.second);
1481             const char* pch;
1482             pair<bool,const char*> prefix = getString("metadataAttributePrefix");
1483             if (prefix.first) {
1484                 pch = prefix.second;
1485                 while (*pch) {
1486                     transformedprefix += (isalnum(*pch) ? toupper(*pch) : '_');
1487                     pch++;
1488                 }
1489             }
1490             for (vector<string>::const_iterator hdr = unsetHeaders.begin(); hdr!=unsetHeaders.end(); ++hdr) {
1491                 string transformed;
1492                 pch = hdr->c_str();
1493                 while (*pch) {
1494                     transformed += (isalnum(*pch) ? toupper(*pch) : '_');
1495                     pch++;
1496                 }
1497                 m_unsetHeaders.push_back(make_pair(m_attributePrefix.first + *hdr, m_attributePrefix.second + transformed));
1498                 if (prefix.first)
1499                     m_unsetHeaders.push_back(make_pair(m_attributePrefix.first + prefix.second + *hdr, transformedprefix + transformed));
1500             }
1501         }
1502         m_unsetHeaders.push_back(make_pair(m_attributePrefix.first + "Shib-Application-ID", m_attributePrefix.second + "SHIB_APPLICATION_ID"));
1503     }
1504 }
1505 #endif
1506
1507 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
1508 short
1509 #else
1510 DOMNodeFilter::FilterAction
1511 #endif
1512 XMLApplication::acceptNode(const DOMNode* node) const
1513 {
1514     const XMLCh* name=node->getLocalName();
1515     if (XMLString::equals(name,ApplicationOverride) ||
1516         XMLString::equals(name,_Audience) ||
1517         XMLString::equals(name,Notify) ||
1518         XMLString::equals(name,_Handler) ||
1519         XMLString::equals(name,_AssertionConsumerService) ||
1520         XMLString::equals(name,_ArtifactResolutionService) ||
1521         XMLString::equals(name,Logout) ||
1522         XMLString::equals(name,_LogoutInitiator) ||
1523         XMLString::equals(name,_ManageNameIDService) ||
1524         XMLString::equals(name,NameIDMgmt) ||
1525         XMLString::equals(name,_SessionInitiator) ||
1526         XMLString::equals(name,_SingleLogoutService) ||
1527         XMLString::equals(name,SSO) ||
1528         XMLString::equals(name,RelyingParty) ||
1529         XMLString::equals(name,_MetadataProvider) ||
1530         XMLString::equals(name,_TrustEngine) ||
1531         XMLString::equals(name,_CredentialResolver) ||
1532         XMLString::equals(name,_AttributeFilter) ||
1533         XMLString::equals(name,_AttributeExtractor) ||
1534         XMLString::equals(name,_AttributeResolver))
1535         return FILTER_REJECT;
1536
1537     return FILTER_ACCEPT;
1538 }
1539
1540 #ifndef SHIBSP_LITE
1541
1542 const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provider) const
1543 {
1544     if (!provider)
1545         return this;
1546
1547     // Check for exact match on name.
1548     map< xstring,boost::shared_ptr<PropertySet> >::const_iterator i = m_partyMap.find(provider->getEntityID());
1549     if (i != m_partyMap.end())
1550         return i->second.get();
1551
1552     // Check for extensible matching.
1553     vector < pair< boost::shared_ptr<EntityMatcher>,boost::shared_ptr<PropertySet> > >::const_iterator j;
1554     for (j = m_partyVec.begin(); j != m_partyVec.end(); ++j) {
1555         if (j->first->matches(*provider))
1556             return j->second.get();
1557     }
1558
1559     // Check for group match.
1560     const EntitiesDescriptor* group = dynamic_cast<const EntitiesDescriptor*>(provider->getParent());
1561     while (group) {
1562         if (group->getName()) {
1563             i = m_partyMap.find(group->getName());
1564             if (i != m_partyMap.end())
1565                 return i->second.get();
1566         }
1567         group = dynamic_cast<const EntitiesDescriptor*>(group->getParent());
1568     }
1569     return this;
1570 }
1571
1572 const PropertySet* XMLApplication::getRelyingParty(const XMLCh* entityID) const
1573 {
1574     if (!entityID)
1575         return this;
1576     map< xstring,boost::shared_ptr<PropertySet> >::const_iterator i = m_partyMap.find(entityID);
1577     return (i != m_partyMap.end()) ? i->second.get() : this;
1578 }
1579
1580 #endif
1581
1582 string XMLApplication::getNotificationURL(const char* resource, bool front, unsigned int index) const
1583 {
1584     const vector<string>& locs = front ? m_frontLogout : m_backLogout;
1585     if (locs.empty())
1586         return m_base ? m_base->getNotificationURL(resource, front, index) : string();
1587     else if (index >= locs.size())
1588         return string();
1589
1590 #ifdef HAVE_STRCASECMP
1591     if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
1592 #else
1593     if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
1594 #endif
1595         throw ConfigurationException("Request URL was not absolute.");
1596
1597     const char* handler = locs[index].c_str();
1598
1599     // Should never happen...
1600     if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
1601         throw ConfigurationException(
1602             "Invalid Location property ($1) in Notify element for Application ($2)",
1603             params(2, handler ? handler : "null", getId())
1604             );
1605
1606     // The "Location" property can be in one of three formats:
1607     //
1608     // 1) a full URI:       http://host/foo/bar
1609     // 2) a hostless URI:   http:///foo/bar
1610     // 3) a relative path:  /foo/bar
1611     //
1612     // #  Protocol  Host        Path
1613     // 1  handler   handler     handler
1614     // 2  handler   resource    handler
1615     // 3  resource  resource    handler
1616
1617     const char* path = nullptr;
1618
1619     // Decide whether to use the handler or the resource for the "protocol"
1620     const char* prot;
1621     if (*handler != '/') {
1622         prot = handler;
1623     }
1624     else {
1625         prot = resource;
1626         path = handler;
1627     }
1628
1629     // break apart the "protocol" string into protocol, host, and "the rest"
1630     const char* colon=strchr(prot,':');
1631     colon += 3;
1632     const char* slash=strchr(colon,'/');
1633     if (!path)
1634         path = slash;
1635
1636     // Compute the actual protocol and store.
1637     string notifyURL(prot, colon-prot);
1638
1639     // create the "host" from either the colon/slash or from the target string
1640     // If prot == handler then we're in either #1 or #2, else #3.
1641     // If slash == colon then we're in #2.
1642     if (prot != handler || slash == colon) {
1643         colon = strchr(resource, ':');
1644         colon += 3;      // Get past the ://
1645         slash = strchr(colon, '/');
1646     }
1647     string host(colon, (slash ? slash-colon : strlen(colon)));
1648
1649     // Build the URL
1650     notifyURL += host + path;
1651     return notifyURL;
1652 }
1653
1654 void XMLApplication::clearHeader(SPRequest& request, const char* rawname, const char* cginame) const
1655 {
1656     if (!m_attributePrefix.first.empty()) {
1657         string temp = m_attributePrefix.first + rawname;
1658         string temp2 = m_attributePrefix.second + (cginame + 5);
1659         request.clearHeader(temp.c_str(), temp2.c_str());
1660     }
1661     else if (m_base) {
1662         m_base->clearHeader(request, rawname, cginame);
1663     }
1664     else {
1665         request.clearHeader(rawname, cginame);
1666     }
1667 }
1668
1669 void XMLApplication::setHeader(SPRequest& request, const char* name, const char* value) const
1670 {
1671     if (!m_attributePrefix.first.empty()) {
1672         string temp = m_attributePrefix.first + name;
1673         request.setHeader(temp.c_str(), value);
1674     }
1675     else if (m_base) {
1676         m_base->setHeader(request, name, value);
1677     }
1678     else {
1679         request.setHeader(name, value);
1680     }
1681 }
1682
1683 string XMLApplication::getSecureHeader(const SPRequest& request, const char* name) const
1684 {
1685     if (!m_attributePrefix.first.empty()) {
1686         string temp = m_attributePrefix.first + name;
1687         return request.getSecureHeader(temp.c_str());
1688     }
1689     else if (m_base) {
1690         return m_base->getSecureHeader(request,name);
1691     }
1692     else {
1693         return request.getSecureHeader(name);
1694     }
1695 }
1696
1697 const SessionInitiator* XMLApplication::getDefaultSessionInitiator() const
1698 {
1699     if (m_sessionInitDefault) return m_sessionInitDefault;
1700     return m_base ? m_base->getDefaultSessionInitiator() : nullptr;
1701 }
1702
1703 const SessionInitiator* XMLApplication::getSessionInitiatorById(const char* id) const
1704 {
1705     map<string,const SessionInitiator*>::const_iterator i = m_sessionInitMap.find(id);
1706     if (i != m_sessionInitMap.end()) return i->second;
1707     return m_base ? m_base->getSessionInitiatorById(id) : nullptr;
1708 }
1709
1710 const Handler* XMLApplication::getDefaultAssertionConsumerService() const
1711 {
1712     if (m_acsDefault) return m_acsDefault;
1713     return m_base ? m_base->getDefaultAssertionConsumerService() : nullptr;
1714 }
1715
1716 const Handler* XMLApplication::getAssertionConsumerServiceByIndex(unsigned short index) const
1717 {
1718     map<unsigned int,const Handler*>::const_iterator i = m_acsIndexMap.find(index);
1719     if (i != m_acsIndexMap.end()) return i->second;
1720     return m_base ? m_base->getAssertionConsumerServiceByIndex(index) : nullptr;
1721 }
1722
1723 const Handler* XMLApplication::getAssertionConsumerServiceByProtocol(const XMLCh* protocol, const char* binding) const
1724 {
1725     ACSProtocolMap::const_iterator i = m_acsProtocolMap.find(protocol);
1726     if (i != m_acsProtocolMap.end() && !i->second.empty()) {
1727         if (!binding || !*binding)
1728             return i->second.front();
1729         for (ACSProtocolMap::value_type::second_type::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
1730             if (!strcmp(binding, (*j)->getString("Binding").second))
1731                 return *j;
1732         }
1733     }
1734     return m_base ? m_base->getAssertionConsumerServiceByProtocol(protocol, binding) : nullptr;
1735 }
1736
1737 const vector<const Handler*>& XMLApplication::getAssertionConsumerServicesByBinding(const XMLCh* binding) const
1738 {
1739     ACSBindingMap::const_iterator i = m_acsBindingMap.find(binding);
1740     if (i != m_acsBindingMap.end())
1741         return i->second;
1742     return m_base ? m_base->getAssertionConsumerServicesByBinding(binding) : g_noHandlers;
1743 }
1744
1745 const Handler* XMLApplication::getHandler(const char* path) const
1746 {
1747     string wrap(path);
1748     wrap = wrap.substr(0, wrap.find(';'));
1749     map<string,const Handler*>::const_iterator i = m_handlerMap.find(wrap.substr(0, wrap.find('?')));
1750     if (i != m_handlerMap.end())
1751         return i->second;
1752     return m_base ? m_base->getHandler(path) : nullptr;
1753 }
1754
1755 void XMLApplication::getHandlers(vector<const Handler*>& handlers) const
1756 {
1757     static void (vector<const Handler*>::* pb)(const Handler* const&) = &vector<const Handler*>::push_back;
1758     for_each(m_handlers.begin(), m_handlers.end(), boost::bind(pb, boost::ref(handlers), boost::bind(&boost::shared_ptr<Handler>::get, _1)));
1759     if (m_base) {
1760         for (map<string,const Handler*>::const_iterator h = m_base->m_handlerMap.begin(); h != m_base->m_handlerMap.end(); ++h) {
1761             if (m_handlerMap.count(h->first) == 0)
1762                 handlers.push_back(h->second);
1763         }
1764     }
1765 }
1766
1767 void XMLApplication::limitRedirect(const GenericRequest& request, const char* url) const
1768 {
1769     if (!url || *url == '/')
1770         return;
1771     if (m_redirectLimit == REDIRECT_LIMIT_INHERIT)
1772         return m_base->limitRedirect(request, url);
1773     if (m_redirectLimit != REDIRECT_LIMIT_NONE) {
1774         vector<string> whitelist;
1775         if (m_redirectLimit == REDIRECT_LIMIT_EXACT || m_redirectLimit == REDIRECT_LIMIT_EXACT_WHITELIST) {
1776             // Scheme and hostname have to match.
1777             if (request.isDefaultPort()) {
1778                 whitelist.push_back(string(request.getScheme()) + "://" + request.getHostname() + '/');
1779             }
1780             whitelist.push_back(string(request.getScheme()) + "://" + request.getHostname() + ':' + lexical_cast<string>(request.getPort()) + '/');
1781         }
1782         else if (m_redirectLimit == REDIRECT_LIMIT_HOST || m_redirectLimit == REDIRECT_LIMIT_HOST_WHITELIST) {
1783             // Allow any scheme or port.
1784             whitelist.push_back(string("https://") + request.getHostname() + '/');
1785             whitelist.push_back(string("http://") + request.getHostname() + '/');
1786             whitelist.push_back(string("https://") + request.getHostname() + ':');
1787             whitelist.push_back(string("http://") + request.getHostname() + ':');
1788         }
1789
1790         static bool (*startsWithI)(const char*,const char*) = XMLString::startsWithI;
1791         if (!whitelist.empty() && find_if(whitelist.begin(), whitelist.end(),
1792                 boost::bind(startsWithI, url, boost::bind(&string::c_str, _1))) != whitelist.end()) {
1793             return;
1794         }
1795         else if (!m_redirectWhitelist.empty() && find_if(m_redirectWhitelist.begin(), m_redirectWhitelist.end(),
1796                 boost::bind(startsWithI, url, boost::bind(&string::c_str, _1))) != m_redirectWhitelist.end()) {
1797             return;
1798         }
1799         Category::getInstance(SHIBSP_LOGCAT ".Application").warn("redirectLimit policy enforced, blocked redirect to (%s)", url);
1800         throw opensaml::SecurityPolicyException("Blocked unacceptable redirect location.");
1801     }
1802 }
1803
1804 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
1805 short
1806 #else
1807 DOMNodeFilter::FilterAction
1808 #endif
1809 XMLConfigImpl::acceptNode(const DOMNode* node) const
1810 {
1811     if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB2SPCONFIG_NS))
1812         return FILTER_ACCEPT;
1813     const XMLCh* name=node->getLocalName();
1814     if (XMLString::equals(name,ApplicationDefaults) ||
1815         XMLString::equals(name,_ArtifactMap) ||
1816         XMLString::equals(name,_Extensions) ||
1817         XMLString::equals(name,Listener) ||
1818         XMLString::equals(name,_ProtocolProvider) ||
1819         XMLString::equals(name,_RequestMapper) ||
1820         XMLString::equals(name,_ReplayCache) ||
1821         XMLString::equals(name,SecurityPolicies) ||
1822         XMLString::equals(name,_SecurityPolicyProvider) ||
1823         XMLString::equals(name,_SessionCache) ||
1824         XMLString::equals(name,Site) ||
1825         XMLString::equals(name,_StorageService) ||
1826         XMLString::equals(name,TCPListener) ||
1827         XMLString::equals(name,TransportOption) ||
1828         XMLString::equals(name,UnixListener))
1829         return FILTER_REJECT;
1830
1831     return FILTER_ACCEPT;
1832 }
1833
1834 void XMLConfigImpl::doExtensions(const DOMElement* e, const char* label, Category& log)
1835 {
1836     const DOMElement* exts = XMLHelper::getFirstChildElement(e, _Extensions);
1837     if (exts) {
1838         exts = XMLHelper::getFirstChildElement(exts, Library);
1839         while (exts) {
1840             string path(XMLHelper::getAttrString(exts, nullptr, _path));
1841             try {
1842                 if (!path.empty()) {
1843                     if (!XMLToolingConfig::getConfig().load_library(path.c_str(), (void*)exts))
1844                         throw ConfigurationException("XMLToolingConfig::load_library failed.");
1845                     log.debug("loaded %s extension library (%s)", label, path.c_str());
1846                 }
1847             }
1848             catch (std::exception& e) {
1849                 if (XMLHelper::getAttrBool(exts, false, _fatal)) {
1850                     log.fatal("unable to load mandatory %s extension library %s: %s", label, path.c_str(), e.what());
1851                     throw;
1852                 }
1853                 else {
1854                     log.crit("unable to load optional %s extension library %s: %s", label, path.c_str(), e.what());
1855                 }
1856             }
1857             exts = XMLHelper::getNextSiblingElement(exts, Library);
1858         }
1859     }
1860 }
1861
1862 void XMLConfigImpl::doListener(const DOMElement* e, XMLConfig* conf, Category& log)
1863 {
1864 #ifdef WIN32
1865     string plugtype(TCP_LISTENER_SERVICE);
1866 #else
1867     string plugtype(UNIX_LISTENER_SERVICE);
1868 #endif
1869     DOMElement* child = XMLHelper::getFirstChildElement(e, UnixListener);
1870     if (child)
1871         plugtype = UNIX_LISTENER_SERVICE;
1872     else {
1873         child = XMLHelper::getFirstChildElement(e, TCPListener);
1874         if (child)
1875             plugtype = TCP_LISTENER_SERVICE;
1876         else {
1877             child = XMLHelper::getFirstChildElement(e, Listener);
1878             if (child) {
1879                 auto_ptr_char type(child->getAttributeNS(nullptr, _type));
1880                 if (type.get() && *type.get())
1881                     plugtype = type.get();
1882             }
1883         }
1884     }
1885
1886     log.info("building ListenerService of type %s...", plugtype.c_str());
1887     conf->m_listener.reset(SPConfig::getConfig().ListenerServiceManager.newPlugin(plugtype.c_str(), child));
1888 }
1889
1890 void XMLConfigImpl::doCaching(const DOMElement* e, XMLConfig* conf, Category& log)
1891 {
1892     SPConfig& spConf = SPConfig::getConfig();
1893 #ifndef SHIBSP_LITE
1894     SAMLConfig& samlConf = SAMLConfig::getConfig();
1895 #endif
1896
1897     DOMElement* child;
1898 #ifndef SHIBSP_LITE
1899     if (spConf.isEnabled(SPConfig::OutOfProcess)) {
1900         XMLToolingConfig& xmlConf = XMLToolingConfig::getConfig();
1901         // First build any StorageServices.
1902         child = XMLHelper::getFirstChildElement(e, _StorageService);
1903         while (child) {
1904             string id(XMLHelper::getAttrString(child, nullptr, _id));
1905             string t(XMLHelper::getAttrString(child, nullptr, _type));
1906             if (!t.empty()) {
1907                 try {
1908                     log.info("building StorageService (%s) of type %s...", id.c_str(), t.c_str());
1909                     conf->m_storage[id] = boost::shared_ptr<StorageService>(xmlConf.StorageServiceManager.newPlugin(t.c_str(), child));
1910                 }
1911                 catch (std::exception& ex) {
1912                     log.crit("failed to instantiate StorageService (%s): %s", id.c_str(), ex.what());
1913                 }
1914             }
1915             child = XMLHelper::getNextSiblingElement(child, _StorageService);
1916         }
1917
1918         if (conf->m_storage.empty()) {
1919             log.info("no StorageService plugin(s) installed, using (mem) in-memory instance");
1920             conf->m_storage["mem"] = boost::shared_ptr<StorageService>(xmlConf.StorageServiceManager.newPlugin(MEMORY_STORAGE_SERVICE, nullptr));
1921         }
1922
1923         // Replay cache.
1924         StorageService* replaySS = nullptr;
1925         child = XMLHelper::getFirstChildElement(e, _ReplayCache);
1926         if (child) {
1927             string ssid(XMLHelper::getAttrString(child, nullptr, _StorageService));
1928             if (!ssid.empty()) {
1929                 if (conf->m_storage.count(ssid)) {
1930                     log.info("building ReplayCache on top of StorageService (%s)...", ssid.c_str());
1931                     replaySS = conf->m_storage[ssid].get();
1932                 }
1933                 else {
1934                     log.error("unable to locate StorageService (%s), using arbitrary instance for ReplayCache", ssid.c_str());
1935                     replaySS = conf->m_storage.begin()->second.get();
1936                 }
1937             }
1938             else {
1939                 log.info("no StorageService specified for ReplayCache, using arbitrary instance");
1940                 replaySS = conf->m_storage.begin()->second.get();
1941             }
1942         }
1943         else {
1944             log.info("no ReplayCache specified, using arbitrary StorageService instance");
1945             replaySS = conf->m_storage.begin()->second.get();
1946         }
1947         xmlConf.setReplayCache(new ReplayCache(replaySS));
1948
1949         // ArtifactMap
1950         child = XMLHelper::getFirstChildElement(e, _ArtifactMap);
1951         if (child) {
1952             string ssid(XMLHelper::getAttrString(child, nullptr, _StorageService));
1953             if (!ssid.empty()) {
1954                 if (conf->m_storage.count(ssid)) {
1955                     log.info("building ArtifactMap on top of StorageService (%s)...", ssid.c_str());
1956                     samlConf.setArtifactMap(new ArtifactMap(child, conf->m_storage[ssid].get()));
1957                 }
1958                 else {
1959                     log.error("unable to locate StorageService (%s), using in-memory ArtifactMap", ssid.c_str());
1960                     samlConf.setArtifactMap(new ArtifactMap(child));
1961                 }
1962             }
1963             else {
1964                 log.info("no StorageService specified, using in-memory ArtifactMap");
1965                 samlConf.setArtifactMap(new ArtifactMap(child));
1966             }
1967         }
1968         else {
1969             log.info("no ArtifactMap specified, building in-memory ArtifactMap...");
1970             samlConf.setArtifactMap(new ArtifactMap(child));
1971         }
1972     }   // end of out of process caching components
1973 #endif
1974
1975     child = XMLHelper::getFirstChildElement(e, _SessionCache);
1976     if (child) {
1977         string t(XMLHelper::getAttrString(child, nullptr, _type));
1978         if (!t.empty()) {
1979             log.info("building SessionCache of type %s...", t.c_str());
1980             conf->m_sessionCache.reset(spConf.SessionCacheManager.newPlugin(t.c_str(), child));
1981         }
1982     }
1983     if (!conf->m_sessionCache) {
1984         log.info("no SessionCache specified, using StorageService-backed instance");
1985         conf->m_sessionCache.reset(spConf.SessionCacheManager.newPlugin(STORAGESERVICE_SESSION_CACHE, nullptr));
1986     }
1987 }
1988
1989 XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer, Category& log) : m_document(nullptr)
1990 {
1991 #ifdef _DEBUG
1992     xmltooling::NDC ndc("XMLConfigImpl");
1993 #endif
1994     SPConfig& conf=SPConfig::getConfig();
1995     XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();
1996     const DOMElement* SHAR=XMLHelper::getFirstChildElement(e, OutOfProcess);
1997     const DOMElement* SHIRE=XMLHelper::getFirstChildElement(e, InProcess);
1998
1999     // Initialize logging manually in order to redirect log messages as soon as possible.
2000     // If no explicit config is supplied, we now assume the caller has done this, so that
2001     // setuid processes can potentially do this as root.
2002     if (conf.isEnabled(SPConfig::Logging)) {
2003         string logconf;
2004         if (conf.isEnabled(SPConfig::OutOfProcess))
2005             logconf = XMLHelper::getAttrString(SHAR, nullptr, logger);
2006         else if (conf.isEnabled(SPConfig::InProcess))
2007             logconf = XMLHelper::getAttrString(SHIRE, nullptr, logger);
2008         if (logconf.empty())
2009             logconf = XMLHelper::getAttrString(e, nullptr, logger);
2010         if (!logconf.empty()) {
2011             log.debug("loading new logging configuration from (%s), check log destination for status of configuration", logconf.c_str());
2012             if (!XMLToolingConfig::getConfig().log_config(logconf.c_str()))
2013                 log.crit("failed to load new logging configuration from (%s)", logconf.c_str());
2014         }
2015
2016 #ifndef SHIBSP_LITE
2017         m_tranLog.reset(
2018             new TransactionLog(
2019                 XMLHelper::getAttrString(SHAR, nullptr, tranLogFormat).c_str(),
2020                 XMLHelper::getAttrString(SHAR, nullptr, tranLogFiller).c_str()
2021                 )
2022             );
2023 #endif
2024     }
2025
2026     // Re-log library versions now that logging is set up.
2027     log.info("Shibboleth SP Version %s", PACKAGE_VERSION);
2028 #ifndef SHIBSP_LITE
2029     log.info(
2030         "Library versions: %s %s, Xerces-C %s, XML-Security-C %s, XMLTooling-C %s, OpenSAML-C %s, Shibboleth %s",
2031 # if defined(LOG4SHIB_VERSION)
2032     "log4shib", LOG4SHIB_VERSION,
2033 # elif defined(LOG4CPP_VERSION)
2034     "log4cpp", LOG4CPP_VERSION,
2035 # else
2036     "", "",
2037 # endif
2038         XERCES_FULLVERSIONDOT, XSEC_FULLVERSIONDOT, gXMLToolingDotVersionStr, gOpenSAMLDotVersionStr, gShibSPDotVersionStr
2039         );
2040 #else
2041     log.info(
2042         "Library versions: %s %s, Xerces-C %s, XMLTooling-C %s, Shibboleth %s",
2043 # if defined(LOG4SHIB_VERSION)
2044     "log4shib", LOG4SHIB_VERSION,
2045 # elif defined(LOG4CPP_VERSION)
2046     "log4cpp", LOG4CPP_VERSION,
2047 # else
2048     "", "",
2049 # endif
2050         XERCES_FULLVERSIONDOT, gXMLToolingDotVersionStr, gShibSPDotVersionStr
2051         );
2052 #endif
2053
2054     // First load any property sets.
2055     load(e, nullptr, this);
2056
2057     DOMElement* child;
2058
2059     // Much of the processing can only occur on the first instantiation.
2060     if (first) {
2061         // Set clock skew.
2062         pair<bool,unsigned int> skew=getUnsignedInt("clockSkew");
2063         if (skew.first)
2064             xmlConf.clock_skew_secs=min(skew.second,(60*60*24*7*28));
2065
2066         pair<bool,const char*> unsafe = getString("unsafeChars");
2067         if (unsafe.first)
2068             TemplateEngine::unsafe_chars = unsafe.second;
2069
2070         unsafe = getString("allowedSchemes");
2071         if (unsafe.first) {
2072             HTTPResponse::getAllowedSchemes().clear();
2073             string schemes(unsafe.second);
2074             trim(schemes);
2075             split(HTTPResponse::getAllowedSchemes(), schemes, is_space(), algorithm::token_compress_on);
2076         }
2077
2078         // Default language handling.
2079         pair<bool,bool> langFromClient = getBool("langFromClient");
2080         pair<bool,const XMLCh*> langPriority = getXMLString("langPriority");
2081         GenericRequest::setLangDefaults(!langFromClient.first || langFromClient.second, langPriority.second);
2082
2083 #ifndef SHIBSP_LITE
2084         langPriority = getXMLString("contactPriority");
2085         if (langPriority.first)
2086             SAMLConfig::getConfig().setContactPriority(langPriority.second);
2087 #endif
2088
2089         // Extensions
2090         doExtensions(e, "global", log);
2091         if (conf.isEnabled(SPConfig::OutOfProcess))
2092             doExtensions(SHAR, "out of process", log);
2093
2094         if (conf.isEnabled(SPConfig::InProcess))
2095             doExtensions(SHIRE, "in process", log);
2096
2097         // Instantiate the ListenerService and SessionCache objects.
2098         if (conf.isEnabled(SPConfig::Listener))
2099             doListener(e, outer, log);
2100
2101 #ifndef SHIBSP_LITE
2102         if (outer->m_listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess)) {
2103             outer->m_listener->regListener("set::RelayState", outer);
2104             outer->m_listener->regListener("get::RelayState", outer);
2105             outer->m_listener->regListener("set::PostData", outer);
2106             outer->m_listener->regListener("get::PostData", outer);
2107         }
2108 #endif
2109         if (conf.isEnabled(SPConfig::Caching))
2110             doCaching(e, outer, log);
2111     } // end of first-time-only stuff
2112
2113     // Back to the fully dynamic stuff...next up is the RequestMapper.
2114     if (conf.isEnabled(SPConfig::RequestMapping)) {
2115         if (child = XMLHelper::getFirstChildElement(e, _RequestMapper)) {
2116             string t(XMLHelper::getAttrString(child, nullptr, _type));
2117             if (!t.empty()) {
2118                 log.info("building RequestMapper of type %s...", t.c_str());
2119                 m_requestMapper.reset(conf.RequestMapperManager.newPlugin(t.c_str(), child));
2120             }
2121         }
2122         if (!m_requestMapper) {
2123             log.info("no RequestMapper specified, using 'Native' plugin with empty/default map");
2124             child = e->getOwnerDocument()->createElementNS(nullptr, _RequestMapper);
2125             DOMElement* mapperDummy = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS, RequestMap);
2126             mapperDummy->setAttributeNS(nullptr, applicationId, _default);
2127             child->appendChild(mapperDummy);
2128             m_requestMapper.reset(conf.RequestMapperManager.newPlugin(NATIVE_REQUEST_MAPPER, child));
2129         }
2130     }
2131
2132 #ifndef SHIBSP_LITE
2133     // Load security policies.
2134     if (child = XMLHelper::getLastChildElement(e, _SecurityPolicyProvider)) {
2135         string t(XMLHelper::getAttrString(child, nullptr, _type));
2136         if (!t.empty()) {
2137             log.info("building SecurityPolicyProvider of type %s...", t.c_str());
2138             m_policy.reset(conf.SecurityPolicyProviderManager.newPlugin(t.c_str(), child));
2139         }
2140         else {
2141             throw ConfigurationException("can't build SecurityPolicyProvider, no type specified");
2142         }
2143     }
2144     else if (child = XMLHelper::getLastChildElement(e, SecurityPolicies)) {
2145         // For backward compatibility, wrap in a plugin element.
2146         DOMElement* polwrapper = e->getOwnerDocument()->createElementNS(nullptr, _SecurityPolicyProvider);
2147         polwrapper->appendChild(child);
2148         log.warn("deprecated/legacy SecurityPolicy configuration, consider externalizing with <SecurityPolicyProvider>");
2149         m_policy.reset(conf.SecurityPolicyProviderManager.newPlugin(XML_SECURITYPOLICY_PROVIDER, polwrapper));
2150     }
2151     else {
2152         log.fatal("can't build SecurityPolicyProvider, missing conf:SecurityPolicyProvider element?");
2153         throw ConfigurationException("Can't build SecurityPolicyProvider, missing conf:SecurityPolicyProvider element?");
2154     }
2155
2156     if (first) {
2157         if (!m_policy->getAlgorithmWhitelist().empty()) {
2158 #ifdef SHIBSP_XMLSEC_WHITELISTING
2159             for (vector<xstring>::const_iterator white = m_policy->getAlgorithmWhitelist().begin();
2160                     white != m_policy->getAlgorithmWhitelist().end(); ++white) {
2161                 XSECPlatformUtils::whitelistAlgorithm(white->c_str());
2162                 auto_ptr_char whitelog(white->c_str());
2163                 log.info("explicitly whitelisting security algorithm (%s)", whitelog.get());
2164             }
2165 #else
2166             log.crit("XML-Security-C library prior to 1.6.0 does not support algorithm white/blacklists");
2167 #endif
2168         }
2169         else if (!m_policy->getDefaultAlgorithmBlacklist().empty() || !m_policy->getAlgorithmBlacklist().empty()) {
2170 #ifdef SHIBSP_XMLSEC_WHITELISTING
2171             for (vector<xstring>::const_iterator black = m_policy->getDefaultAlgorithmBlacklist().begin();
2172                     black != m_policy->getDefaultAlgorithmBlacklist().end(); ++black) {
2173                 XSECPlatformUtils::blacklistAlgorithm(black->c_str());
2174                 auto_ptr_char blacklog(black->c_str());
2175                 log.info("automatically blacklisting security algorithm (%s)", blacklog.get());
2176             }
2177             for (vector<xstring>::const_iterator black = m_policy->getAlgorithmBlacklist().begin();
2178                     black != m_policy->getAlgorithmBlacklist().end(); ++black) {
2179                 XSECPlatformUtils::blacklistAlgorithm(black->c_str());
2180                 auto_ptr_char blacklog(black->c_str());
2181                 log.info("explicitly blacklisting security algorithm (%s)", blacklog.get());
2182             }
2183 #else
2184             log.crit("XML-Security-C library prior to 1.6.0 does not support algorithm white/blacklists");
2185 #endif
2186         }
2187     }
2188
2189     // Process TransportOption elements.
2190     child = XMLHelper::getLastChildElement(e, TransportOption);
2191     while (child) {
2192         if (child->hasChildNodes()) {
2193             string provider(XMLHelper::getAttrString(child, nullptr, _provider));
2194             string option(XMLHelper::getAttrString(child, nullptr, _option));
2195             auto_ptr_char value(child->getFirstChild()->getNodeValue());
2196             if (!provider.empty() && !option.empty() && value.get() && *value.get()) {
2197                 m_transportOptions.push_back(boost::make_tuple(provider, option, string(value.get())));
2198             }
2199         }
2200         child = XMLHelper::getPreviousSiblingElement(child, TransportOption);
2201     }
2202 #endif
2203
2204     scoped_ptr<ProtocolProvider> pp;
2205     if (conf.isEnabled(SPConfig::Handlers)) {
2206         if (child = XMLHelper::getLastChildElement(e, _ProtocolProvider)) {
2207             string t(XMLHelper::getAttrString(child, nullptr, _type));
2208             if (!t.empty()) {
2209                 log.info("building ProtocolProvider of type %s...", t.c_str());
2210                 pp.reset(conf.ProtocolProviderManager.newPlugin(t.c_str(), child));
2211             }
2212         }
2213     }
2214     Locker pplocker(pp.get());
2215
2216     // Load the default application.
2217     child = XMLHelper::getLastChildElement(e, ApplicationDefaults);
2218     if (!child) {
2219         log.fatal("can't build default Application object, missing conf:ApplicationDefaults element?");
2220         throw ConfigurationException("can't build default Application object, missing conf:ApplicationDefaults element?");
2221     }
2222     boost::shared_ptr<XMLApplication> defapp(new XMLApplication(outer, pp.get(), child));
2223     m_appmap[defapp->getId()] = defapp;
2224
2225     // Load any overrides.
2226     child = XMLHelper::getFirstChildElement(child, ApplicationOverride);
2227     while (child) {
2228         boost::shared_ptr<XMLApplication> iapp(new XMLApplication(outer, pp.get(), child, defapp.get()));
2229         if (m_appmap.count(iapp->getId()))
2230             log.crit("found conf:ApplicationOverride element with duplicate id attribute (%s), skipping it", iapp->getId());
2231         else
2232             m_appmap[iapp->getId()] = iapp;
2233
2234         child = XMLHelper::getNextSiblingElement(child, ApplicationOverride);
2235     }
2236
2237     // Check for extra AuthTypes to recognize.
2238     if (conf.isEnabled(SPConfig::InProcess)) {
2239         const PropertySet* inprocs = getPropertySet("InProcess");
2240         if (inprocs) {
2241             pair<bool,const char*> extraAuthTypes = inprocs->getString("extraAuthTypes");
2242             if (extraAuthTypes.first) {
2243                 string types(extraAuthTypes.second);
2244                 trim(types);
2245                 split(outer->m_authTypes, types, is_space(), algorithm::token_compress_on);
2246                 outer->m_authTypes.insert("shibboleth");
2247             }
2248         }
2249     }
2250 }
2251
2252 #ifndef SHIBSP_LITE
2253 void XMLConfig::receive(DDF& in, ostream& out)
2254 {
2255     if (!strcmp(in.name(), "get::RelayState")) {
2256         const char* id = in["id"].string();
2257         const char* key = in["key"].string();
2258         if (!id || !key)
2259             throw ListenerException("Required parameters missing for RelayState recovery.");
2260
2261         string relayState;
2262         StorageService* storage = getStorageService(id);
2263         if (storage) {
2264             if (storage->readString("RelayState",key,&relayState)>0) {
2265                 if (in["clear"].integer())
2266                     storage->deleteString("RelayState",key);
2267             }
2268             else if (storage->readText("RelayState",key,&relayState)>0) {
2269                 if (in["clear"].integer())
2270                     storage->deleteText("RelayState",key);
2271             }
2272         }
2273         else {
2274             Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").error(
2275                 "Storage-backed RelayState with invalid StorageService ID (%s)", id
2276                 );
2277         }
2278
2279         // Repack for return to caller.
2280         DDF ret=DDF(nullptr).unsafe_string(relayState.c_str());
2281         DDFJanitor jret(ret);
2282         out << ret;
2283     }
2284     else if (!strcmp(in.name(), "set::RelayState")) {
2285         const char* id = in["id"].string();
2286         const char* value = in["value"].string();
2287         if (!id || !value)
2288             throw ListenerException("Required parameters missing for RelayState creation.");
2289
2290         string rsKey;
2291         StorageService* storage = getStorageService(id);
2292         if (storage) {
2293             SAMLConfig::getConfig().generateRandomBytes(rsKey,32);
2294             rsKey = SAMLArtifact::toHex(rsKey);
2295             if (strlen(value) <= storage->getCapabilities().getStringSize())
2296                 storage->createString("RelayState", rsKey.c_str(), value, time(nullptr) + 600);
2297             else
2298                 storage->createText("RelayState", rsKey.c_str(), value, time(nullptr) + 600);
2299         }
2300         else {
2301             Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").error(
2302                 "Storage-backed RelayState with invalid StorageService ID (%s)", id
2303                 );
2304         }
2305
2306         // Repack for return to caller.
2307         DDF ret=DDF(nullptr).string(rsKey.c_str());
2308         DDFJanitor jret(ret);
2309         out << ret;
2310     }
2311     else if (!strcmp(in.name(), "get::PostData")) {
2312         const char* id = in["id"].string();
2313         const char* key = in["key"].string();
2314         if (!id || !key)
2315             throw ListenerException("Required parameters missing for PostData recovery.");
2316
2317         string postData;
2318         StorageService* storage = getStorageService(id);
2319         if (storage) {
2320             if (storage->readText("PostData",key,&postData) > 0) {
2321                 storage->deleteText("PostData",key);
2322             }
2323         }
2324         else {
2325             Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").error(
2326                 "Storage-backed PostData with invalid StorageService ID (%s)", id
2327                 );
2328         }
2329         // If the data's empty, we'll send nothing back.
2330         // If not, we don't need to round trip it, just send back the serialized DDF list.
2331         if (postData.empty()) {
2332             DDF ret(nullptr);
2333             DDFJanitor jret(ret);
2334             out << ret;
2335         }
2336         else {
2337             out << postData;
2338         }
2339     }
2340     else if (!strcmp(in.name(), "set::PostData")) {
2341         const char* id = in["id"].string();
2342         if (!id || !in["parameters"].islist())
2343             throw ListenerException("Required parameters missing for PostData creation.");
2344
2345         string rsKey;
2346         StorageService* storage = getStorageService(id);
2347         if (storage) {
2348             SAMLConfig::getConfig().generateRandomBytes(rsKey,32);
2349             rsKey = SAMLArtifact::toHex(rsKey);
2350             ostringstream params;
2351             params << in["parameters"];
2352             storage->createText("PostData", rsKey.c_str(), params.str().c_str(), time(nullptr) + 600);
2353         }
2354         else {
2355             Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").error(
2356                 "Storage-backed PostData with invalid StorageService ID (%s)", id
2357                 );
2358         }
2359
2360         // Repack for return to caller.
2361         DDF ret=DDF(nullptr).string(rsKey.c_str());
2362         DDFJanitor jret(ret);
2363         out << ret;
2364     }
2365 }
2366 #endif
2367
2368 pair<bool,DOMElement*> XMLConfig::background_load()
2369 {
2370     // Load from source using base class.
2371     pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
2372
2373     // If we own it, wrap it.
2374     XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);
2375
2376     scoped_ptr<XMLConfigImpl> impl(new XMLConfigImpl(raw.second, (m_impl==nullptr), this, m_log));
2377
2378     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
2379     impl->setDocument(docjanitor.release());
2380
2381     // Perform the swap inside a lock.
2382     if (m_lock)
2383         m_lock->wrlock();
2384     SharedLock locker(m_lock, false);
2385     m_impl.swap(impl);
2386
2387     return make_pair(false,(DOMElement*)nullptr);
2388 }