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