Correct location of TransportOption feature, makes no sense inside policies.
[shibboleth/sp.git] / shibsp / impl / XMLServiceProvider.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * XMLServiceProvider.cpp
19  *
20  * XML-based SP configuration and mgmt
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "AccessControl.h"
26 #include "Application.h"
27 #include "RequestMapper.h"
28 #include "ServiceProvider.h"
29 #include "SessionCache.h"
30 #include "SPConfig.h"
31 #include "handler/SessionInitiator.h"
32 #include "remoting/ListenerService.h"
33 #include "util/DOMPropertySet.h"
34 #include "util/SPConstants.h"
35
36 #if defined(XMLTOOLING_LOG4SHIB)
37 # include <log4shib/PropertyConfigurator.hh>
38 #elif defined(XMLTOOLING_LOG4CPP)
39 # include <log4cpp/PropertyConfigurator.hh>
40 #else
41 # error "Supported logging library not available."
42 #endif
43 #include <xercesc/util/XMLUniDefs.hpp>
44 #include <xmltooling/XMLToolingConfig.h>
45 #include <xmltooling/util/NDC.h>
46 #include <xmltooling/util/ReloadableXMLFile.h>
47 #include <xmltooling/util/XMLHelper.h>
48
49 #ifndef SHIBSP_LITE
50 # include "TransactionLog.h"
51 # include "attribute/filtering/AttributeFilter.h"
52 # include "attribute/resolver/AttributeExtractor.h"
53 # include "attribute/resolver/AttributeResolver.h"
54 # include "security/PKIXTrustEngine.h"
55 # include <saml/SAMLConfig.h>
56 # include <saml/binding/ArtifactMap.h>
57 # include <saml/binding/SAMLArtifact.h>
58 # include <saml/saml1/core/Assertions.h>
59 # include <saml/saml2/binding/SAML2ArtifactType0004.h>
60 # include <saml/saml2/metadata/ChainingMetadataProvider.h>
61 # include <xmltooling/security/ChainingTrustEngine.h>
62 # include <xmltooling/util/ReplayCache.h>
63 using namespace opensaml::saml2;
64 using namespace opensaml::saml2p;
65 using namespace opensaml::saml2md;
66 using namespace opensaml;
67 #endif
68
69 using namespace shibsp;
70 using namespace xmltooling;
71 using namespace std;
72
73 namespace {
74
75 #if defined (_MSC_VER)
76     #pragma warning( push )
77     #pragma warning( disable : 4250 )
78 #endif
79
80     static vector<const Handler*> g_noHandlers;
81
82     // Application configuration wrapper
83     class SHIBSP_DLLLOCAL XMLApplication : public Application, public Remoted, public DOMPropertySet, public DOMNodeFilter
84     {
85     public:
86         XMLApplication(const ServiceProvider*, const DOMElement* e, const XMLApplication* base=NULL);
87         ~XMLApplication() { cleanup(); }
88     
89         const char* getHash() const {return m_hash.c_str();}
90
91 #ifndef SHIBSP_LITE
92         SAMLArtifact* generateSAML1Artifact(const EntityDescriptor* relyingParty) const {
93             throw ConfigurationException("No support for SAML 1.x artifact generation.");
94         }
95         SAML2Artifact* generateSAML2Artifact(const EntityDescriptor* relyingParty) const {
96             pair<bool,int> index = make_pair(false,0);
97             const PropertySet* props = getRelyingParty(relyingParty);
98             index = props->getInt("artifactEndpointIndex");
99             if (!index.first)
100                 index = getArtifactEndpointIndex();
101             return new SAML2ArtifactType0004(SAMLConfig::getConfig().hashSHA1(props->getString("entityID").second),index.first ? index.second : 1);
102         }
103
104         MetadataProvider* getMetadataProvider(bool required=true) const {
105             if (required && !m_base && !m_metadata)
106                 throw ConfigurationException("No MetadataProvider available.");
107             return (!m_metadata && m_base) ? m_base->getMetadataProvider() : m_metadata;
108         }
109         TrustEngine* getTrustEngine(bool required=true) const {
110             if (required && !m_base && !m_trust)
111                 throw ConfigurationException("No TrustEngine available.");
112             return (!m_trust && m_base) ? m_base->getTrustEngine() : m_trust;
113         }
114         AttributeExtractor* getAttributeExtractor() const {
115             return (!m_attrExtractor && m_base) ? m_base->getAttributeExtractor() : m_attrExtractor;
116         }
117         AttributeFilter* getAttributeFilter() const {
118             return (!m_attrFilter && m_base) ? m_base->getAttributeFilter() : m_attrFilter;
119         }
120         AttributeResolver* getAttributeResolver() const {
121             return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver;
122         }
123         CredentialResolver* getCredentialResolver() const {
124             return (!m_credResolver && m_base) ? m_base->getCredentialResolver() : m_credResolver;
125         }
126         const PropertySet* getRelyingParty(const EntityDescriptor* provider) const;
127         const vector<const XMLCh*>* getAudiences() const {
128             return (m_audiences.empty() && m_base) ? m_base->getAudiences() : &m_audiences;
129         }
130 #endif
131         string getNotificationURL(const char* resource, bool front, unsigned int index) const;
132
133         const vector<string>& getRemoteUserAttributeIds() const {
134             return (m_remoteUsers.empty() && m_base) ? m_base->getRemoteUserAttributeIds() : m_remoteUsers;
135         }
136
137         const SessionInitiator* getDefaultSessionInitiator() const;
138         const SessionInitiator* getSessionInitiatorById(const char* id) const;
139         const Handler* getDefaultAssertionConsumerService() const;
140         const Handler* getAssertionConsumerServiceByIndex(unsigned short index) const;
141         const vector<const Handler*>& getAssertionConsumerServicesByBinding(const XMLCh* binding) const;
142         const Handler* getHandler(const char* path) const;
143         void getHandlers(vector<const Handler*>& handlers) const;
144
145         void receive(DDF& in, ostream& out) {
146             // Only current function is to return the headers to clear.
147             DDF header;
148             DDF ret=DDF(NULL).list();
149             DDFJanitor jret(ret);
150             for (vector< pair<string,string> >::const_iterator i = m_unsetHeaders.begin(); i!=m_unsetHeaders.end(); ++i) {
151                 header = DDF(i->first.c_str()).string(i->second.c_str());
152                 ret.add(header);
153             }
154             out << ret;
155         }
156
157         // Provides filter to exclude special config elements.
158         short acceptNode(const DOMNode* node) const;
159     
160     private:
161         void cleanup();
162         const XMLApplication* m_base;
163         string m_hash;
164 #ifndef SHIBSP_LITE
165         MetadataProvider* m_metadata;
166         TrustEngine* m_trust;
167         AttributeExtractor* m_attrExtractor;
168         AttributeFilter* m_attrFilter;
169         AttributeResolver* m_attrResolver;
170         CredentialResolver* m_credResolver;
171         vector<const XMLCh*> m_audiences;
172
173         // RelyingParty properties
174 #ifdef HAVE_GOOD_STL
175         map<xstring,PropertySet*> m_partyMap;
176 #else
177         map<const XMLCh*,PropertySet*> m_partyMap;
178 #endif
179 #endif
180         vector<string> m_remoteUsers,m_frontLogout,m_backLogout;
181
182         // manage handler objects
183         vector<Handler*> m_handlers;
184
185         // maps location (path info) to applicable handlers
186         map<string,const Handler*> m_handlerMap;
187
188         // maps unique indexes to consumer services
189         map<unsigned int,const Handler*> m_acsIndexMap;
190         
191         // pointer to default consumer service
192         const Handler* m_acsDefault;
193
194         // maps binding strings to supporting consumer service(s)
195 #ifdef HAVE_GOOD_STL
196         typedef map<xstring,vector<const Handler*> > ACSBindingMap;
197 #else
198         typedef map<string,vector<const Handler*> > ACSBindingMap;
199 #endif
200         ACSBindingMap m_acsBindingMap;
201
202         // pointer to default session initiator
203         const SessionInitiator* m_sessionInitDefault;
204
205         // maps unique ID strings to session initiators
206         map<string,const SessionInitiator*> m_sessionInitMap;
207
208         // pointer to default artifact resolution service
209         const Handler* m_artifactResolutionDefault;
210
211         pair<bool,int> getArtifactEndpointIndex() const {
212             if (m_artifactResolutionDefault) return m_artifactResolutionDefault->getInt("index");
213             return m_base ? m_base->getArtifactEndpointIndex() : make_pair(false,0);
214         }
215     };
216
217     // Top-level configuration implementation
218     class SHIBSP_DLLLOCAL XMLConfig;
219     class SHIBSP_DLLLOCAL XMLConfigImpl : public DOMPropertySet, public DOMNodeFilter
220     {
221     public:
222         XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log);
223         ~XMLConfigImpl();
224         
225         RequestMapper* m_requestMapper;
226         map<string,Application*> m_appmap;
227 #ifndef SHIBSP_LITE
228         map< string,pair< PropertySet*,vector<const SecurityPolicyRule*> > > m_policyMap;
229         vector< pair< string, pair<string,string> > > m_transportOptions;
230 #endif
231         
232         // Provides filter to exclude special config elements.
233         short acceptNode(const DOMNode* node) const;
234
235         void setDocument(DOMDocument* doc) {
236             m_document = doc;
237         }
238
239     private:
240         void doExtensions(const DOMElement* e, const char* label, Category& log);
241         void cleanup();
242
243         const XMLConfig* m_outer;
244         DOMDocument* m_document;
245     };
246
247     class SHIBSP_DLLLOCAL XMLConfig : public ServiceProvider, public ReloadableXMLFile
248 #ifndef SHIBSP_LITE
249         ,public Remoted
250 #endif
251     {
252     public:
253         XMLConfig(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".Config")),
254             m_impl(NULL), m_listener(NULL), m_sessionCache(NULL)
255 #ifndef SHIBSP_LITE
256             , m_tranLog(NULL)
257 #endif
258         {
259         }
260         
261         void init() {
262             load();
263         }
264
265         ~XMLConfig() {
266             delete m_impl;
267             delete m_sessionCache;
268             delete m_listener;
269 #ifndef SHIBSP_LITE
270             delete m_tranLog;
271             SAMLConfig::getConfig().setArtifactMap(NULL);
272             XMLToolingConfig::getConfig().setReplayCache(NULL);
273             for_each(m_storage.begin(), m_storage.end(), cleanup_pair<string,StorageService>());
274 #endif
275         }
276
277         // PropertySet
278         const PropertySet* getParent() const { return m_impl->getParent(); }
279         void setParent(const PropertySet* parent) {return m_impl->setParent(parent);}
280         pair<bool,bool> getBool(const char* name, const char* ns=NULL) const {return m_impl->getBool(name,ns);}
281         pair<bool,const char*> getString(const char* name, const char* ns=NULL) const {return m_impl->getString(name,ns);}
282         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const {return m_impl->getXMLString(name,ns);}
283         pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const {return m_impl->getUnsignedInt(name,ns);}
284         pair<bool,int> getInt(const char* name, const char* ns=NULL) const {return m_impl->getInt(name,ns);}
285         void getAll(map<string,const char*>& properties) const {return m_impl->getAll(properties);}
286         const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:2.0:native:sp:config") const {return m_impl->getPropertySet(name,ns);}
287         const DOMElement* getElement() const {return m_impl->getElement();}
288
289         // ServiceProvider
290 #ifndef SHIBSP_LITE
291         // Remoted
292         void receive(DDF& in, ostream& out);
293
294         TransactionLog* getTransactionLog() const {
295             if (m_tranLog)
296                 return m_tranLog;
297             throw ConfigurationException("No TransactionLog available.");
298         }
299
300         StorageService* getStorageService(const char* id) const {
301             if (id) {
302                 map<string,StorageService*>::const_iterator i=m_storage.find(id);
303                 if (i!=m_storage.end())
304                     return i->second;
305             }
306             return NULL;
307         }
308 #endif
309
310         ListenerService* getListenerService(bool required=true) const {
311             if (required && !m_listener)
312                 throw ConfigurationException("No ListenerService available.");
313             return m_listener;
314         }
315
316         SessionCache* getSessionCache(bool required=true) const {
317             if (required && !m_sessionCache)
318                 throw ConfigurationException("No SessionCache available.");
319             return m_sessionCache;
320         }
321
322         RequestMapper* getRequestMapper(bool required=true) const {
323             if (required && !m_impl->m_requestMapper)
324                 throw ConfigurationException("No RequestMapper available.");
325             return m_impl->m_requestMapper;
326         }
327
328         const Application* getApplication(const char* applicationId) const {
329             map<string,Application*>::const_iterator i=m_impl->m_appmap.find(applicationId);
330             return (i!=m_impl->m_appmap.end()) ? i->second : NULL;
331         }
332
333 #ifndef SHIBSP_LITE
334         const PropertySet* getPolicySettings(const char* id) const {
335             map<string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::const_iterator i = m_impl->m_policyMap.find(id);
336             if (i!=m_impl->m_policyMap.end())
337                 return i->second.first;
338             throw ConfigurationException("Security Policy ($1) not found, check <SecurityPolicies> element.", params(1,id));
339         }
340
341         const vector<const SecurityPolicyRule*>& getPolicyRules(const char* id) const {
342             map<string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::const_iterator i = m_impl->m_policyMap.find(id);
343             if (i!=m_impl->m_policyMap.end())
344                 return i->second.second;
345             throw ConfigurationException("Security Policy ($1) not found, check <SecurityPolicies> element.", params(1,id));
346         }
347
348         bool setTransportOptions(SOAPTransport& transport) const {
349             bool ret = true;
350             vector< pair< string, pair<string,string> > >::const_iterator opt;
351             for (opt = m_impl->m_transportOptions.begin(); opt != m_impl->m_transportOptions.end(); ++opt) {
352                 if (!transport.setProviderOption(opt->first.c_str(), opt->second.first.c_str(), opt->second.second.c_str())) {
353                     m_log.error("failed to set SOAPTransport option (%s)", opt->second.first.c_str());
354                     ret = false;
355                 }
356             }
357             return ret;
358         }
359 #endif
360
361     protected:
362         pair<bool,DOMElement*> load();
363
364     private:
365         friend class XMLConfigImpl;
366         XMLConfigImpl* m_impl;
367         mutable ListenerService* m_listener;
368         mutable SessionCache* m_sessionCache;
369 #ifndef SHIBSP_LITE
370         mutable TransactionLog* m_tranLog;
371         mutable map<string,StorageService*> m_storage;
372 #endif
373     };
374
375 #if defined (_MSC_VER)
376     #pragma warning( pop )
377 #endif
378
379     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);
380     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);
381     static const XMLCh _ArtifactMap[] =         UNICODE_LITERAL_11(A,r,t,i,f,a,c,t,M,a,p);
382     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);
383     static const XMLCh _AttributeFilter[] =     UNICODE_LITERAL_15(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r);
384     static const XMLCh _AttributeResolver[] =   UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);
385     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);
386     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);
387     static const XMLCh _Audience[] =            UNICODE_LITERAL_8(A,u,d,i,e,n,c,e);
388     static const XMLCh Binding[] =              UNICODE_LITERAL_7(B,i,n,d,i,n,g);
389     static const XMLCh Channel[]=               UNICODE_LITERAL_7(C,h,a,n,n,e,l);
390     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);
391     static const XMLCh _Extensions[] =          UNICODE_LITERAL_10(E,x,t,e,n,s,i,o,n,s);
392     static const XMLCh _fatal[] =               UNICODE_LITERAL_5(f,a,t,a,l);
393     static const XMLCh _Handler[] =             UNICODE_LITERAL_7(H,a,n,d,l,e,r);
394     static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);
395     static const XMLCh InProcess[] =            UNICODE_LITERAL_9(I,n,P,r,o,c,e,s,s);
396     static const XMLCh Library[] =              UNICODE_LITERAL_7(L,i,b,r,a,r,y);
397     static const XMLCh Listener[] =             UNICODE_LITERAL_8(L,i,s,t,e,n,e,r);
398     static const XMLCh Location[] =             UNICODE_LITERAL_8(L,o,c,a,t,i,o,n);
399     static const XMLCh logger[] =               UNICODE_LITERAL_6(l,o,g,g,e,r);
400     static const XMLCh _LogoutInitiator[] =     UNICODE_LITERAL_15(L,o,g,o,u,t,I,n,i,t,i,a,t,o,r);
401     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);
402     static const XMLCh _MetadataProvider[] =    UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r);
403     static const XMLCh Notify[] =               UNICODE_LITERAL_6(N,o,t,i,f,y);
404     static const XMLCh _option[] =              UNICODE_LITERAL_6(o,p,t,i,o,n);
405     static const XMLCh OutOfProcess[] =         UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s);
406     static const XMLCh _path[] =                UNICODE_LITERAL_4(p,a,t,h);
407     static const XMLCh Policy[] =               UNICODE_LITERAL_6(P,o,l,i,c,y);
408     static const XMLCh _provider[] =            UNICODE_LITERAL_8(p,r,o,v,i,d,e,r);
409     static const XMLCh RelyingParty[] =         UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y);
410     static const XMLCh _ReplayCache[] =         UNICODE_LITERAL_11(R,e,p,l,a,y,C,a,c,h,e);
411     static const XMLCh _RequestMapper[] =       UNICODE_LITERAL_13(R,e,q,u,e,s,t,M,a,p,p,e,r);
412     static const XMLCh Rule[] =                 UNICODE_LITERAL_4(R,u,l,e);
413     static const XMLCh SecurityPolicies[] =     UNICODE_LITERAL_16(S,e,c,u,r,i,t,y,P,o,l,i,c,i,e,s);
414     static const XMLCh _SessionCache[] =        UNICODE_LITERAL_12(S,e,s,s,i,o,n,C,a,c,h,e);
415     static const XMLCh _SessionInitiator[] =    UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r);
416     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);
417     static const XMLCh Site[] =                 UNICODE_LITERAL_4(S,i,t,e);
418     static const XMLCh _StorageService[] =      UNICODE_LITERAL_14(S,t,o,r,a,g,e,S,e,r,v,i,c,e);
419     static const XMLCh TCPListener[] =          UNICODE_LITERAL_11(T,C,P,L,i,s,t,e,n,e,r);
420     static const XMLCh TransportOption[] =      UNICODE_LITERAL_15(T,r,a,n,s,p,o,r,t,O,p,t,i,o,n);
421     static const XMLCh _TrustEngine[] =         UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e);
422     static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);
423     static const XMLCh UnixListener[] =         UNICODE_LITERAL_12(U,n,i,x,L,i,s,t,e,n,e,r);
424
425 #ifndef SHIBSP_LITE
426     class SHIBSP_DLLLOCAL PolicyNodeFilter : public DOMNodeFilter
427     {
428     public:
429         short acceptNode(const DOMNode* node) const {
430             return FILTER_REJECT;
431         }
432     };
433 #endif
434 };
435
436 namespace shibsp {
437     ServiceProvider* XMLServiceProviderFactory(const DOMElement* const & e)
438     {
439         return new XMLConfig(e);
440     }
441 };
442
443 XMLApplication::XMLApplication(
444     const ServiceProvider* sp,
445     const DOMElement* e,
446     const XMLApplication* base
447     ) : Application(sp), m_base(base),
448 #ifndef SHIBSP_LITE
449         m_metadata(NULL), m_trust(NULL),
450         m_attrExtractor(NULL), m_attrFilter(NULL), m_attrResolver(NULL),
451         m_credResolver(NULL),
452 #endif
453         m_acsDefault(NULL), m_sessionInitDefault(NULL), m_artifactResolutionDefault(NULL)
454 {
455 #ifdef _DEBUG
456     xmltooling::NDC ndc("XMLApplication");
457 #endif
458     Category& log=Category::getInstance(SHIBSP_LOGCAT".Application");
459
460     try {
461         // First load any property sets.
462         load(e,NULL,this);
463         if (base)
464             setParent(base);
465
466         SPConfig& conf=SPConfig::getConfig();
467 #ifndef SHIBSP_LITE
468         SAMLConfig& samlConf=SAMLConfig::getConfig();
469         XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();
470 #endif
471
472         // This used to be an actual hash, but now it's just a hex-encode to avoid xmlsec.
473         static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
474         string tohash=getId();
475         tohash+=getString("entityID").second;
476         for (const char* ch = tohash.c_str(); *ch; ++ch) {
477             m_hash += (DIGITS[((unsigned char)(0xF0 & *ch)) >> 4 ]);
478             m_hash += (DIGITS[0x0F & *ch]);
479         }
480
481         // Load attribute ID lists for REMOTE_USER and header clearing.
482         if (conf.isEnabled(SPConfig::InProcess)) {
483             pair<bool,const char*> attributes = getString("REMOTE_USER");
484             if (attributes.first) {
485                 char* dup = strdup(attributes.second);
486                 char* pos;
487                 char* start = dup;
488                 while (start && *start) {
489                     while (*start && isspace(*start))
490                         start++;
491                     if (!*start)
492                         break;
493                     pos = strchr(start,' ');
494                     if (pos)
495                         *pos=0;
496                     m_remoteUsers.push_back(start);
497                     start = pos ? pos+1 : NULL;
498                 }
499                 free(dup);
500             }
501
502             attributes = getString("unsetHeaders");
503             if (attributes.first) {
504                 string transformedprefix("HTTP_");
505                 const char* pch;
506                 pair<bool,const char*> prefix = getString("metadataAttributePrefix");
507                 if (prefix.first) {
508                     pch = prefix.second;
509                     while (*pch) {
510                         transformedprefix += (isalnum(*pch) ? toupper(*pch) : '_');
511                         pch++;
512                     }
513                 }
514                 char* dup = strdup(attributes.second);
515                 char* pos;
516                 char* start = dup;
517                 while (start && *start) {
518                     while (*start && isspace(*start))
519                         start++;
520                     if (!*start)
521                         break;
522                     pos = strchr(start,' ');
523                     if (pos)
524                         *pos=0;
525
526                     string transformed;
527                     pch = start;
528                     while (*pch) {
529                         transformed += (isalnum(*pch) ? toupper(*pch) : '_');
530                         pch++;
531                     }
532                     m_unsetHeaders.push_back(pair<string,string>(start,string("HTTP_") + transformed));
533                     if (prefix.first)
534                         m_unsetHeaders.push_back(pair<string,string>(string(prefix.second) + start, transformedprefix + transformed));
535                     start = pos ? pos+1 : NULL;
536                 }
537                 free(dup);
538                 m_unsetHeaders.push_back(pair<string,string>("Shib-Application-ID","HTTP_SHIB_APPLICATION_ID"));
539             }
540         }
541
542         Handler* handler=NULL;
543         const PropertySet* sessions = getPropertySet("Sessions");
544
545         // Process assertion export handler.
546         pair<bool,const char*> location = sessions ? sessions->getString("exportLocation") : pair<bool,const char*>(false,NULL);
547         if (location.first) {
548             try {
549                 DOMElement* exportElement = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,_Handler);
550                 exportElement->setAttributeNS(NULL,Location,sessions->getXMLString("exportLocation").second);
551                 pair<bool,const XMLCh*> exportACL = sessions->getXMLString("exportACL");
552                 if (exportACL.first) {
553                     static const XMLCh _acl[] = UNICODE_LITERAL_9(e,x,p,o,r,t,A,C,L);
554                     exportElement->setAttributeNS(NULL,_acl,exportACL.second);
555                 }
556                 handler = conf.HandlerManager.newPlugin(
557                     samlconstants::SAML20_BINDING_URI, pair<const DOMElement*,const char*>(exportElement, getId())
558                     );
559                 m_handlers.push_back(handler);
560
561                 // Insert into location map. If it contains the handlerURL, we skip past that part.
562                 const char* pch = strstr(location.second, sessions->getString("handlerURL").second);
563                 if (pch)
564                     location.second = pch + strlen(sessions->getString("handlerURL").second);
565                 if (*location.second == '/')
566                     m_handlerMap[location.second]=handler;
567                 else
568                     m_handlerMap[string("/") + location.second]=handler;
569             }
570             catch (exception& ex) {
571                 log.error("caught exception installing assertion lookup handler: %s", ex.what());
572             }
573         }
574
575         // Process other handlers.
576         bool hardACS=false, hardSessionInit=false, hardArt=false;
577         const DOMElement* child = sessions ? XMLHelper::getFirstChildElement(sessions->getElement()) : NULL;
578         while (child) {
579             try {
580                 // A handler is based on the Binding property in conjunction with the element name.
581                 // If it's an ACS or SI, also handle index/id mappings and defaulting.
582                 if (XMLString::equals(child->getLocalName(),_AssertionConsumerService)) {
583                     auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));
584                     if (!bindprop.get() || !*(bindprop.get())) {
585                         log.warn("md:AssertionConsumerService element has no Binding attribute, skipping it...");
586                         child = XMLHelper::getNextSiblingElement(child);
587                         continue;
588                     }
589                     handler=conf.AssertionConsumerServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));
590                     // Map by binding (may be > 1 per binding, e.g. SAML 1.0 vs 1.1)
591 #ifdef HAVE_GOOD_STL
592                     m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler);
593 #else
594                     m_acsBindingMap[handler->getString("Binding").second].push_back(handler);
595 #endif
596                     m_acsIndexMap[handler->getUnsignedInt("index").second]=handler;
597                     
598                     if (!hardACS) {
599                         pair<bool,bool> defprop=handler->getBool("isDefault");
600                         if (defprop.first) {
601                             if (defprop.second) {
602                                 hardACS=true;
603                                 m_acsDefault=handler;
604                             }
605                         }
606                         else if (!m_acsDefault)
607                             m_acsDefault=handler;
608                     }
609                 }
610                 else if (XMLString::equals(child->getLocalName(),_SessionInitiator)) {
611                     auto_ptr_char type(child->getAttributeNS(NULL,_type));
612                     if (!type.get() || !*(type.get())) {
613                         log.warn("SessionInitiator element has no type attribute, skipping it...");
614                         child = XMLHelper::getNextSiblingElement(child);
615                         continue;
616                     }
617                     SessionInitiator* sihandler=conf.SessionInitiatorManager.newPlugin(type.get(),make_pair(child, getId()));
618                     handler=sihandler;
619                     pair<bool,const char*> si_id=handler->getString("id");
620                     if (si_id.first && si_id.second)
621                         m_sessionInitMap[si_id.second]=sihandler;
622                     if (!hardSessionInit) {
623                         pair<bool,bool> defprop=handler->getBool("isDefault");
624                         if (defprop.first) {
625                             if (defprop.second) {
626                                 hardSessionInit=true;
627                                 m_sessionInitDefault=sihandler;
628                             }
629                         }
630                         else if (!m_sessionInitDefault)
631                             m_sessionInitDefault=sihandler;
632                     }
633                 }
634                 else if (XMLString::equals(child->getLocalName(),_LogoutInitiator)) {
635                     auto_ptr_char type(child->getAttributeNS(NULL,_type));
636                     if (!type.get() || !*(type.get())) {
637                         log.warn("LogoutInitiator element has no type attribute, skipping it...");
638                         child = XMLHelper::getNextSiblingElement(child);
639                         continue;
640                     }
641                     handler=conf.LogoutInitiatorManager.newPlugin(type.get(),make_pair(child, getId()));
642                 }
643                 else if (XMLString::equals(child->getLocalName(),_ArtifactResolutionService)) {
644                     auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));
645                     if (!bindprop.get() || !*(bindprop.get())) {
646                         log.warn("md:ArtifactResolutionService element has no Binding attribute, skipping it...");
647                         child = XMLHelper::getNextSiblingElement(child);
648                         continue;
649                     }
650                     handler=conf.ArtifactResolutionServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));
651                     
652                     if (!hardArt) {
653                         pair<bool,bool> defprop=handler->getBool("isDefault");
654                         if (defprop.first) {
655                             if (defprop.second) {
656                                 hardArt=true;
657                                 m_artifactResolutionDefault=handler;
658                             }
659                         }
660                         else if (!m_artifactResolutionDefault)
661                             m_artifactResolutionDefault=handler;
662                     }
663                 }
664                 else if (XMLString::equals(child->getLocalName(),_SingleLogoutService)) {
665                     auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));
666                     if (!bindprop.get() || !*(bindprop.get())) {
667                         log.warn("md:SingleLogoutService element has no Binding attribute, skipping it...");
668                         child = XMLHelper::getNextSiblingElement(child);
669                         continue;
670                     }
671                     handler=conf.SingleLogoutServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));
672                 }
673                 else if (XMLString::equals(child->getLocalName(),_ManageNameIDService)) {
674                     auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));
675                     if (!bindprop.get() || !*(bindprop.get())) {
676                         log.warn("md:ManageNameIDService element has no Binding attribute, skipping it...");
677                         child = XMLHelper::getNextSiblingElement(child);
678                         continue;
679                     }
680                     handler=conf.ManageNameIDServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));
681                 }
682                 else {
683                     auto_ptr_char type(child->getAttributeNS(NULL,_type));
684                     if (!type.get() || !*(type.get())) {
685                         log.warn("Handler element has no type attribute, skipping it...");
686                         child = XMLHelper::getNextSiblingElement(child);
687                         continue;
688                     }
689                     handler=conf.HandlerManager.newPlugin(type.get(),make_pair(child, getId()));
690                 }
691
692                 m_handlers.push_back(handler);
693
694                 // Insert into location map.
695                 location=handler->getString("Location");
696                 if (location.first && *location.second == '/')
697                     m_handlerMap[location.second]=handler;
698                 else if (location.first)
699                     m_handlerMap[string("/") + location.second]=handler;
700
701             }
702             catch (exception& ex) {
703                 log.error("caught exception processing handler element: %s", ex.what());
704             }
705             
706             child = XMLHelper::getNextSiblingElement(child);
707         }
708
709         // Notification.
710         DOMNodeList* nlist=e->getElementsByTagNameNS(shibspconstants::SHIB2SPCONFIG_NS,Notify);
711         for (XMLSize_t i=0; nlist && i<nlist->getLength(); i++) {
712             if (nlist->item(i)->getParentNode()->isSameNode(e)) {
713                 const XMLCh* channel = static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,Channel);
714                 auto_ptr_char loc(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,Location));
715                 if (loc.get() && *loc.get()) {
716                     if (channel && *channel == chLatin_f)
717                         m_frontLogout.push_back(loc.get());
718                     else
719                         m_backLogout.push_back(loc.get());
720                 }
721             }
722         }
723
724 #ifndef SHIBSP_LITE
725         nlist=e->getElementsByTagNameNS(samlconstants::SAML20_NS,Audience::LOCAL_NAME);
726         for (XMLSize_t i=0; nlist && i<nlist->getLength(); i++)
727             if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes())
728                 m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue());
729
730         if (conf.isEnabled(SPConfig::Metadata)) {
731             child = XMLHelper::getFirstChildElement(e,_MetadataProvider);
732             if (child) {
733                 auto_ptr_char type(child->getAttributeNS(NULL,_type));
734                 log.info("building MetadataProvider of type %s...",type.get());
735                 try {
736                     auto_ptr<MetadataProvider> mp(samlConf.MetadataProviderManager.newPlugin(type.get(),child));
737                     mp->init();
738                     m_metadata = mp.release();
739                 }
740                 catch (exception& ex) {
741                     log.crit("error building/initializing MetadataProvider: %s", ex.what());
742                 }
743             }
744         }
745
746         if (conf.isEnabled(SPConfig::Trust)) {
747             child = XMLHelper::getFirstChildElement(e,_TrustEngine);
748             if (child) {
749                 auto_ptr_char type(child->getAttributeNS(NULL,_type));
750                 log.info("building TrustEngine of type %s...",type.get());
751                 try {
752                     m_trust = xmlConf.TrustEngineManager.newPlugin(type.get(),child);
753                 }
754                 catch (exception& ex) {
755                     log.crit("error building TrustEngine: %s", ex.what());
756                 }
757             }
758         }
759
760         if (conf.isEnabled(SPConfig::AttributeResolution)) {
761             child = XMLHelper::getFirstChildElement(e,_AttributeExtractor);
762             if (child) {
763                 auto_ptr_char type(child->getAttributeNS(NULL,_type));
764                 log.info("building AttributeExtractor of type %s...",type.get());
765                 try {
766                     m_attrExtractor = conf.AttributeExtractorManager.newPlugin(type.get(),child);
767                 }
768                 catch (exception& ex) {
769                     log.crit("error building AttributeExtractor: %s", ex.what());
770                 }
771             }
772
773             child = XMLHelper::getFirstChildElement(e,_AttributeFilter);
774             if (child) {
775                 auto_ptr_char type(child->getAttributeNS(NULL,_type));
776                 log.info("building AttributeFilter of type %s...",type.get());
777                 try {
778                     m_attrFilter = conf.AttributeFilterManager.newPlugin(type.get(),child);
779                 }
780                 catch (exception& ex) {
781                     log.crit("error building AttributeFilter: %s", ex.what());
782                 }
783             }
784
785             child = XMLHelper::getFirstChildElement(e,_AttributeResolver);
786             if (child) {
787                 auto_ptr_char type(child->getAttributeNS(NULL,_type));
788                 log.info("building AttributeResolver of type %s...",type.get());
789                 try {
790                     m_attrResolver = conf.AttributeResolverManager.newPlugin(type.get(),child);
791                 }
792                 catch (exception& ex) {
793                     log.crit("error building AttributeResolver: %s", ex.what());
794                 }
795             }
796
797             if (m_unsetHeaders.empty()) {
798                 vector<string> unsetHeaders;
799                 if (m_attrExtractor) {
800                     Locker extlock(m_attrExtractor);
801                     m_attrExtractor->getAttributeIds(unsetHeaders);
802                 }
803                 if (m_attrResolver) {
804                     Locker reslock(m_attrResolver);
805                     m_attrResolver->getAttributeIds(unsetHeaders);
806                 }
807                 if (unsetHeaders.empty()) {
808                     if (m_base)
809                         m_unsetHeaders.insert(m_unsetHeaders.end(), m_base->m_unsetHeaders.begin(), m_base->m_unsetHeaders.end());
810                     else
811                         m_unsetHeaders.push_back(pair<string,string>("Shib-Application-ID","HTTP_SHIB_APPLICATION_ID"));
812                 }
813                 else {
814                     string transformedprefix("HTTP_");
815                     const char* pch;
816                     pair<bool,const char*> prefix = getString("metadataAttributePrefix");
817                     if (prefix.first) {
818                         pch = prefix.second;
819                         while (*pch) {
820                             transformedprefix += (isalnum(*pch) ? toupper(*pch) : '_');
821                             pch++;
822                         }
823                     }
824                     for (vector<string>::const_iterator hdr = unsetHeaders.begin(); hdr!=unsetHeaders.end(); ++hdr) {
825                         string transformed;
826                         pch = hdr->c_str();
827                         while (*pch) {
828                             transformed += (isalnum(*pch) ? toupper(*pch) : '_');
829                             pch++;
830                         }
831                         m_unsetHeaders.push_back(pair<string,string>(*hdr, string("HTTP_") + transformed));
832                         if (prefix.first)
833                             m_unsetHeaders.push_back(pair<string,string>(string(prefix.second) + *hdr, transformedprefix + transformed));
834                     }
835                     m_unsetHeaders.push_back(pair<string,string>("Shib-Application-ID","HTTP_SHIB_APPLICATION_ID"));
836                 }
837             }
838         }
839
840         if (conf.isEnabled(SPConfig::Credentials)) {
841             child = XMLHelper::getFirstChildElement(e,_CredentialResolver);
842             if (child) {
843                 auto_ptr_char type(child->getAttributeNS(NULL,_type));
844                 log.info("building CredentialResolver of type %s...",type.get());
845                 try {
846                     m_credResolver = xmlConf.CredentialResolverManager.newPlugin(type.get(),child);
847                 }
848                 catch (exception& ex) {
849                     log.crit("error building CredentialResolver: %s", ex.what());
850                 }
851             }
852         }
853
854         // Finally, load relying parties.
855         child = XMLHelper::getFirstChildElement(e,RelyingParty);
856         while (child) {
857             auto_ptr<DOMPropertySet> rp(new DOMPropertySet());
858             rp->load(child,NULL,this);
859             rp->setParent(this);
860             m_partyMap[child->getAttributeNS(NULL,saml2::Attribute::NAME_ATTRIB_NAME)]=rp.release();
861             child = XMLHelper::getNextSiblingElement(child,RelyingParty);
862         }
863 #endif
864
865         // Out of process only, we register a listener endpoint.
866         if (!conf.isEnabled(SPConfig::InProcess)) {
867             ListenerService* listener = sp->getListenerService(false);
868             if (listener) {
869                 string addr=string(getId()) + "::getHeaders::Application";
870                 listener->regListener(addr.c_str(),this);
871             }
872             else
873                 log.info("no ListenerService available, Application remoting disabled");
874         }
875     }
876     catch (exception&) {
877         cleanup();
878         throw;
879     }
880 #ifndef _DEBUG
881     catch (...) {
882         cleanup();
883         throw;
884     }
885 #endif
886 }
887
888 void XMLApplication::cleanup()
889 {
890     ListenerService* listener=getServiceProvider().getListenerService(false);
891     if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess) && !SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
892         string addr=string(getId()) + "::getHeaders::Application";
893         listener->unregListener(addr.c_str(),this);
894     }
895     for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup<Handler>());
896     m_handlers.clear();
897 #ifndef SHIBSP_LITE
898 #ifdef HAVE_GOOD_STL
899     for_each(m_partyMap.begin(),m_partyMap.end(),cleanup_pair<xstring,PropertySet>());
900 #else
901     for_each(m_partyMap.begin(),m_partyMap.end(),cleanup_pair<const XMLCh*,PropertySet>());
902 #endif
903     m_partyMap.clear();
904     delete m_credResolver;
905     m_credResolver = NULL;
906     delete m_attrResolver;
907     m_attrResolver = NULL;
908     delete m_attrFilter;
909     m_attrFilter = NULL;
910     delete m_attrExtractor;
911     m_attrExtractor = NULL;
912     delete m_trust;
913     m_trust = NULL;
914     delete m_metadata;
915     m_metadata = NULL;
916 #endif
917 }
918
919 short XMLApplication::acceptNode(const DOMNode* node) const
920 {
921     const XMLCh* name=node->getLocalName();
922     if (XMLString::equals(name,ApplicationOverride) ||
923         XMLString::equals(name,_Audience) ||
924         XMLString::equals(name,Notify) ||
925         XMLString::equals(name,_Handler) ||
926         XMLString::equals(name,_AssertionConsumerService) ||
927         XMLString::equals(name,_ArtifactResolutionService) ||
928         XMLString::equals(name,_LogoutInitiator) ||
929         XMLString::equals(name,_ManageNameIDService) ||
930         XMLString::equals(name,_SessionInitiator) ||
931         XMLString::equals(name,_SingleLogoutService) ||
932         XMLString::equals(name,RelyingParty) ||
933         XMLString::equals(name,_MetadataProvider) ||
934         XMLString::equals(name,_TrustEngine) ||
935         XMLString::equals(name,_CredentialResolver) ||
936         XMLString::equals(name,_AttributeFilter) ||
937         XMLString::equals(name,_AttributeExtractor) ||
938         XMLString::equals(name,_AttributeResolver))
939         return FILTER_REJECT;
940
941     return FILTER_ACCEPT;
942 }
943
944 #ifndef SHIBSP_LITE
945
946 const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provider) const
947 {
948     if (!provider)
949         return this;
950         
951 #ifdef HAVE_GOOD_STL
952     map<xstring,PropertySet*>::const_iterator i=m_partyMap.find(provider->getEntityID());
953     if (i!=m_partyMap.end())
954         return i->second;
955     const EntitiesDescriptor* group=dynamic_cast<const EntitiesDescriptor*>(provider->getParent());
956     while (group) {
957         if (group->getName()) {
958             i=m_partyMap.find(group->getName());
959             if (i!=m_partyMap.end())
960                 return i->second;
961         }
962         group=dynamic_cast<const EntitiesDescriptor*>(group->getParent());
963     }
964 #else
965     map<const XMLCh*,PropertySet*>::const_iterator i=m_partyMap.begin();
966     for (; i!=m_partyMap.end(); i++) {
967         if (XMLString::equals(i->first,provider->getEntityID()))
968             return i->second;
969         const EntitiesDescriptor* group=dynamic_cast<const EntitiesDescriptor*>(provider->getParent());
970         while (group) {
971             if (XMLString::equals(i->first,group->getName()))
972                 return i->second;
973             group=dynamic_cast<const EntitiesDescriptor*>(group->getParent());
974         }
975     }
976 #endif
977     return this;
978 }
979
980 #endif
981
982 string XMLApplication::getNotificationURL(const char* resource, bool front, unsigned int index) const
983 {
984     const vector<string>& locs = front ? m_frontLogout : m_backLogout;
985     if (locs.empty())
986         return m_base ? m_base->getNotificationURL(resource, front, index) : string();
987     else if (index >= locs.size())
988         return string();
989
990 #ifdef HAVE_STRCASECMP
991     if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
992 #else
993     if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
994 #endif
995         throw ConfigurationException("Request URL was not absolute.");
996
997     const char* handler=locs[index].c_str();
998     
999     // Should never happen...
1000     if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
1001         throw ConfigurationException(
1002             "Invalid Location property ($1) in Notify element for Application ($2)",
1003             params(2, handler ? handler : "null", getId())
1004             );
1005
1006     // The "Location" property can be in one of three formats:
1007     //
1008     // 1) a full URI:       http://host/foo/bar
1009     // 2) a hostless URI:   http:///foo/bar
1010     // 3) a relative path:  /foo/bar
1011     //
1012     // #  Protocol  Host        Path
1013     // 1  handler   handler     handler
1014     // 2  handler   resource    handler
1015     // 3  resource  resource    handler
1016
1017     const char* path = NULL;
1018
1019     // Decide whether to use the handler or the resource for the "protocol"
1020     const char* prot;
1021     if (*handler != '/') {
1022         prot = handler;
1023     }
1024     else {
1025         prot = resource;
1026         path = handler;
1027     }
1028
1029     // break apart the "protocol" string into protocol, host, and "the rest"
1030     const char* colon=strchr(prot,':');
1031     colon += 3;
1032     const char* slash=strchr(colon,'/');
1033     if (!path)
1034         path = slash;
1035
1036     // Compute the actual protocol and store.
1037     string notifyURL(prot, colon-prot);
1038
1039     // create the "host" from either the colon/slash or from the target string
1040     // If prot == handler then we're in either #1 or #2, else #3.
1041     // If slash == colon then we're in #2.
1042     if (prot != handler || slash == colon) {
1043         colon = strchr(resource, ':');
1044         colon += 3;      // Get past the ://
1045         slash = strchr(colon, '/');
1046     }
1047     string host(colon, (slash ? slash-colon : strlen(colon)));
1048
1049     // Build the URL
1050     notifyURL += host + path;
1051     return notifyURL;
1052 }
1053
1054 const SessionInitiator* XMLApplication::getDefaultSessionInitiator() const
1055 {
1056     if (m_sessionInitDefault) return m_sessionInitDefault;
1057     return m_base ? m_base->getDefaultSessionInitiator() : NULL;
1058 }
1059
1060 const SessionInitiator* XMLApplication::getSessionInitiatorById(const char* id) const
1061 {
1062     map<string,const SessionInitiator*>::const_iterator i=m_sessionInitMap.find(id);
1063     if (i!=m_sessionInitMap.end()) return i->second;
1064     return m_base ? m_base->getSessionInitiatorById(id) : NULL;
1065 }
1066
1067 const Handler* XMLApplication::getDefaultAssertionConsumerService() const
1068 {
1069     if (m_acsDefault) return m_acsDefault;
1070     return m_base ? m_base->getDefaultAssertionConsumerService() : NULL;
1071 }
1072
1073 const Handler* XMLApplication::getAssertionConsumerServiceByIndex(unsigned short index) const
1074 {
1075     map<unsigned int,const Handler*>::const_iterator i=m_acsIndexMap.find(index);
1076     if (i!=m_acsIndexMap.end()) return i->second;
1077     return m_base ? m_base->getAssertionConsumerServiceByIndex(index) : NULL;
1078 }
1079
1080 const vector<const Handler*>& XMLApplication::getAssertionConsumerServicesByBinding(const XMLCh* binding) const
1081 {
1082 #ifdef HAVE_GOOD_STL
1083     ACSBindingMap::const_iterator i=m_acsBindingMap.find(binding);
1084 #else
1085     auto_ptr_char temp(binding);
1086     ACSBindingMap::const_iterator i=m_acsBindingMap.find(temp.get());
1087 #endif
1088     if (i!=m_acsBindingMap.end())
1089         return i->second;
1090     return m_base ? m_base->getAssertionConsumerServicesByBinding(binding) : g_noHandlers;
1091 }
1092
1093 const Handler* XMLApplication::getHandler(const char* path) const
1094 {
1095     string wrap(path);
1096     map<string,const Handler*>::const_iterator i=m_handlerMap.find(wrap.substr(0,wrap.find('?')));
1097     if (i!=m_handlerMap.end())
1098         return i->second;
1099     return m_base ? m_base->getHandler(path) : NULL;
1100 }
1101
1102 void XMLApplication::getHandlers(vector<const Handler*>& handlers) const
1103 {
1104     handlers.insert(handlers.end(), m_handlers.begin(), m_handlers.end());
1105     if (m_base) {
1106         for (map<string,const Handler*>::const_iterator h = m_base->m_handlerMap.begin(); h != m_base->m_handlerMap.end(); ++h) {
1107             if (m_handlerMap.count(h->first) == 0)
1108                 handlers.push_back(h->second);
1109         }
1110     }
1111 }
1112
1113 short XMLConfigImpl::acceptNode(const DOMNode* node) const
1114 {
1115     if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB2SPCONFIG_NS))
1116         return FILTER_ACCEPT;
1117     const XMLCh* name=node->getLocalName();
1118     if (XMLString::equals(name,ApplicationDefaults) ||
1119         XMLString::equals(name,_ArtifactMap) ||
1120         XMLString::equals(name,_Extensions) ||
1121         XMLString::equals(name,Listener) ||
1122         XMLString::equals(name,_RequestMapper) ||
1123         XMLString::equals(name,_ReplayCache) ||
1124         XMLString::equals(name,SecurityPolicies) ||
1125         XMLString::equals(name,_SessionCache) ||
1126         XMLString::equals(name,Site) ||
1127         XMLString::equals(name,_StorageService) ||
1128         XMLString::equals(name,TCPListener) ||
1129         XMLString::equals(name,TransportOption) ||
1130         XMLString::equals(name,UnixListener))
1131         return FILTER_REJECT;
1132
1133     return FILTER_ACCEPT;
1134 }
1135
1136 void XMLConfigImpl::doExtensions(const DOMElement* e, const char* label, Category& log)
1137 {
1138     const DOMElement* exts=XMLHelper::getFirstChildElement(e,_Extensions);
1139     if (exts) {
1140         exts=XMLHelper::getFirstChildElement(exts,Library);
1141         while (exts) {
1142             auto_ptr_char path(exts->getAttributeNS(NULL,_path));
1143             try {
1144                 if (path.get()) {
1145                     XMLToolingConfig::getConfig().load_library(path.get(),(void*)exts);
1146                     log.debug("loaded %s extension library (%s)", label, path.get());
1147                 }
1148             }
1149             catch (exception& e) {
1150                 const XMLCh* fatal=exts->getAttributeNS(NULL,_fatal);
1151                 if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {
1152                     log.fatal("unable to load mandatory %s extension library %s: %s", label, path.get(), e.what());
1153                     throw;
1154                 }
1155                 else {
1156                     log.crit("unable to load optional %s extension library %s: %s", label, path.get(), e.what());
1157                 }
1158             }
1159             exts=XMLHelper::getNextSiblingElement(exts,Library);
1160         }
1161     }
1162 }
1163
1164 XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log)
1165     : m_requestMapper(NULL), m_outer(outer), m_document(NULL)
1166 {
1167 #ifdef _DEBUG
1168     xmltooling::NDC ndc("XMLConfigImpl");
1169 #endif
1170
1171     try {
1172         SPConfig& conf=SPConfig::getConfig();
1173 #ifndef SHIBSP_LITE
1174         SAMLConfig& samlConf=SAMLConfig::getConfig();
1175 #endif
1176         XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();
1177         const DOMElement* SHAR=XMLHelper::getFirstChildElement(e,OutOfProcess);
1178         const DOMElement* SHIRE=XMLHelper::getFirstChildElement(e,InProcess);
1179
1180         // Initialize log4cpp manually in order to redirect log messages as soon as possible.
1181         if (conf.isEnabled(SPConfig::Logging)) {
1182             const XMLCh* logconf=NULL;
1183             if (conf.isEnabled(SPConfig::OutOfProcess))
1184                 logconf=SHAR->getAttributeNS(NULL,logger);
1185             else if (conf.isEnabled(SPConfig::InProcess))
1186                 logconf=SHIRE->getAttributeNS(NULL,logger);
1187             if (!logconf || !*logconf)
1188                 logconf=e->getAttributeNS(NULL,logger);
1189             if (logconf && *logconf) {
1190                 auto_ptr_char logpath(logconf);
1191                 log.debug("loading new logging configuration from (%s), check log destination for status of configuration",logpath.get());
1192                 XMLToolingConfig::getConfig().log_config(logpath.get());
1193             }
1194             
1195 #ifndef SHIBSP_LITE
1196             if (first)
1197                 m_outer->m_tranLog = new TransactionLog();
1198 #endif
1199         }
1200         
1201         // First load any property sets.
1202         load(e,NULL,this);
1203
1204         const DOMElement* child;
1205         string plugtype;
1206
1207         // Much of the processing can only occur on the first instantiation.
1208         if (first) {
1209             // Set clock skew.
1210             pair<bool,unsigned int> skew=getUnsignedInt("clockSkew");
1211             if (skew.first)
1212                 xmlConf.clock_skew_secs=skew.second;
1213
1214             // Extensions
1215             doExtensions(e, "global", log);
1216             if (conf.isEnabled(SPConfig::OutOfProcess))
1217                 doExtensions(SHAR, "out of process", log);
1218
1219             if (conf.isEnabled(SPConfig::InProcess))
1220                 doExtensions(SHIRE, "in process", log);
1221             
1222             // Instantiate the ListenerService and SessionCache objects.
1223             if (conf.isEnabled(SPConfig::Listener)) {
1224                 child=XMLHelper::getFirstChildElement(e,UnixListener);
1225                 if (child)
1226                     plugtype=UNIX_LISTENER_SERVICE;
1227                 else {
1228                     child=XMLHelper::getFirstChildElement(e,TCPListener);
1229                     if (child)
1230                         plugtype=TCP_LISTENER_SERVICE;
1231                     else {
1232                         child=XMLHelper::getFirstChildElement(e,Listener);
1233                         if (child) {
1234                             auto_ptr_char type(child->getAttributeNS(NULL,_type));
1235                             if (type.get())
1236                                 plugtype=type.get();
1237                         }
1238                     }
1239                 }
1240                 if (child) {
1241                     log.info("building ListenerService of type %s...", plugtype.c_str());
1242                     m_outer->m_listener = conf.ListenerServiceManager.newPlugin(plugtype.c_str(), child);
1243                 }
1244                 else {
1245                     log.fatal("can't build ListenerService, missing conf:Listener element?");
1246                     throw ConfigurationException("Can't build ListenerService, missing conf:Listener element?");
1247                 }
1248             }
1249
1250 #ifndef SHIBSP_LITE
1251             if (m_outer->m_listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess)) {
1252                 m_outer->m_listener->regListener("set::RelayState", m_outer->m_listener);
1253                 m_outer->m_listener->regListener("get::RelayState", m_outer->m_listener);
1254             }
1255 #endif
1256
1257             if (conf.isEnabled(SPConfig::Caching)) {
1258                 if (conf.isEnabled(SPConfig::OutOfProcess)) {
1259 #ifndef SHIBSP_LITE
1260                     // First build any StorageServices.
1261                     child=XMLHelper::getFirstChildElement(e,_StorageService);
1262                     while (child) {
1263                         auto_ptr_char id(child->getAttributeNS(NULL,_id));
1264                         auto_ptr_char type(child->getAttributeNS(NULL,_type));
1265                         try {
1266                             log.info("building StorageService (%s) of type %s...", id.get(), type.get());
1267                             m_outer->m_storage[id.get()] = xmlConf.StorageServiceManager.newPlugin(type.get(),child);
1268                         }
1269                         catch (exception& ex) {
1270                             log.crit("failed to instantiate StorageService (%s): %s", id.get(), ex.what());
1271                         }
1272                         child=XMLHelper::getNextSiblingElement(child,_StorageService);
1273                     }
1274
1275                     // Replay cache.
1276                     StorageService* replaySS=NULL;
1277                     child=XMLHelper::getFirstChildElement(e,_ReplayCache);
1278                     if (child) {
1279                         auto_ptr_char ssid(child->getAttributeNS(NULL,_StorageService));
1280                         if (ssid.get() && *ssid.get()) {
1281                             if (m_outer->m_storage.count(ssid.get()))
1282                                 replaySS = m_outer->m_storage[ssid.get()];
1283                             if (replaySS)
1284                                 log.info("building ReplayCache on top of StorageService (%s)...", ssid.get());
1285                             else
1286                                 log.warn("unable to locate StorageService (%s) for ReplayCache, using dedicated in-memory instance", ssid.get());
1287                         }
1288                         xmlConf.setReplayCache(new ReplayCache(replaySS));
1289                     }
1290                     else {
1291                         log.warn("no ReplayCache built, missing conf:ReplayCache element?");
1292                     }
1293                     
1294                     // ArtifactMap
1295                     child=XMLHelper::getFirstChildElement(e,_ArtifactMap);
1296                     if (child) {
1297                         auto_ptr_char ssid(child->getAttributeNS(NULL,_StorageService));
1298                         if (ssid.get() && *ssid.get() && m_outer->m_storage.count(ssid.get())) {
1299                             log.info("building ArtifactMap on top of StorageService (%s)...", ssid.get());
1300                             samlConf.setArtifactMap(new ArtifactMap(child, m_outer->m_storage[ssid.get()]));
1301                         }
1302                     }
1303                     if (samlConf.getArtifactMap()==NULL) {
1304                         log.info("building in-memory ArtifactMap...");
1305                         samlConf.setArtifactMap(new ArtifactMap(child));
1306                     }
1307 #endif
1308                 }
1309                 child=XMLHelper::getFirstChildElement(e,_SessionCache);
1310                 if (child) {
1311                     auto_ptr_char type(child->getAttributeNS(NULL,_type));
1312                     log.info("building SessionCache of type %s...",type.get());
1313                     m_outer->m_sessionCache=conf.SessionCacheManager.newPlugin(type.get(), child);
1314                 }
1315                 else {
1316                     log.fatal("can't build SessionCache, missing conf:SessionCache element?");
1317                     throw ConfigurationException("Can't build SessionCache, missing conf:SessionCache element?");
1318                 }
1319             }
1320         } // end of first-time-only stuff
1321         
1322         // Back to the fully dynamic stuff...next up is the RequestMapper.
1323         if (conf.isEnabled(SPConfig::RequestMapping)) {
1324             child=XMLHelper::getFirstChildElement(e,_RequestMapper);
1325             if (child) {
1326                 auto_ptr_char type(child->getAttributeNS(NULL,_type));
1327                 log.info("building RequestMapper of type %s...",type.get());
1328                 m_requestMapper=conf.RequestMapperManager.newPlugin(type.get(),child);
1329             }
1330             else {
1331                 log.fatal("can't build RequestMapper, missing conf:RequestMapper element?");
1332                 throw ConfigurationException("Can't build RequestMapper, missing conf:RequestMapper element?");
1333             }
1334         }
1335         
1336 #ifndef SHIBSP_LITE
1337         // Load security policies.
1338         child = XMLHelper::getLastChildElement(e,SecurityPolicies);
1339         if (child) {
1340             PolicyNodeFilter filter;
1341             child = XMLHelper::getFirstChildElement(child,Policy);
1342             while (child) {
1343                 auto_ptr_char id(child->getAttributeNS(NULL,_id));
1344                 pair< PropertySet*,vector<const SecurityPolicyRule*> >& rules = m_policyMap[id.get()];
1345                 rules.first = NULL;
1346                 auto_ptr<DOMPropertySet> settings(new DOMPropertySet());
1347                 settings->load(child, NULL, &filter);
1348                 rules.first = settings.release();
1349                 
1350                 // Process Rule elements.
1351                 const DOMElement* rule = XMLHelper::getFirstChildElement(child,Rule);
1352                 while (rule) {
1353                     auto_ptr_char type(rule->getAttributeNS(NULL,_type));
1354                     try {
1355                         rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule));
1356                     }
1357                     catch (exception& ex) {
1358                         log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());
1359                     }
1360                     rule = XMLHelper::getNextSiblingElement(rule,Rule);
1361                 }
1362                 
1363                 child = XMLHelper::getNextSiblingElement(child,Policy);
1364             }
1365         }
1366
1367         // Process TransportOption elements.
1368         child = XMLHelper::getLastChildElement(e,TransportOption);
1369         while (child) {
1370             if (child->hasChildNodes()) {
1371                 auto_ptr_char provider(child->getAttributeNS(NULL,_provider));
1372                 auto_ptr_char option(child->getAttributeNS(NULL,_option));
1373                 auto_ptr_char value(child->getFirstChild()->getNodeValue());
1374                 if (provider.get() && *provider.get() && option.get() && *option.get() && value.get() && *value.get()) {
1375                     m_transportOptions.push_back(make_pair(string(provider.get()), make_pair(string(option.get()), string(value.get()))));
1376                 }
1377             }
1378             child = XMLHelper::getPreviousSiblingElement(child,TransportOption);
1379         }
1380 #endif
1381
1382         // Load the default application. This actually has a fixed ID of "default". ;-)
1383         child=XMLHelper::getLastChildElement(e,ApplicationDefaults);
1384         if (!child) {
1385             log.fatal("can't build default Application object, missing conf:ApplicationDefaults element?");
1386             throw ConfigurationException("can't build default Application object, missing conf:ApplicationDefaults element?");
1387         }
1388         XMLApplication* defapp=new XMLApplication(m_outer,child);
1389         m_appmap[defapp->getId()]=defapp;
1390         
1391         // Load any overrides.
1392         child = XMLHelper::getFirstChildElement(child,ApplicationOverride);
1393         while (child) {
1394             auto_ptr<XMLApplication> iapp(new XMLApplication(m_outer,child,defapp));
1395             if (m_appmap.count(iapp->getId()))
1396                 log.crit("found conf:ApplicationOverride element with duplicate id attribute (%s), skipping it", iapp->getId());
1397             else
1398                 m_appmap[iapp->getId()]=iapp.release();
1399
1400             child = XMLHelper::getNextSiblingElement(child,ApplicationOverride);
1401         }
1402     }
1403     catch (exception&) {
1404         cleanup();
1405         throw;
1406     }
1407 }
1408
1409 XMLConfigImpl::~XMLConfigImpl()
1410 {
1411     cleanup();
1412 }
1413
1414 void XMLConfigImpl::cleanup()
1415 {
1416     for_each(m_appmap.begin(),m_appmap.end(),cleanup_pair<string,Application>());
1417     m_appmap.clear();
1418 #ifndef SHIBSP_LITE
1419     for (map< string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::iterator i=m_policyMap.begin(); i!=m_policyMap.end(); ++i) {
1420         delete i->second.first;
1421         for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup<SecurityPolicyRule>());
1422     }
1423     m_policyMap.clear();
1424 #endif
1425     delete m_requestMapper;
1426     m_requestMapper = NULL;
1427     if (m_document)
1428         m_document->release();
1429     m_document = NULL;
1430 }
1431
1432 #ifndef SHIBSP_LITE
1433 void XMLConfig::receive(DDF& in, ostream& out)
1434 {
1435     if (!strcmp(in.name(), "get::RelayState")) {
1436         const char* id = in["id"].string();
1437         const char* key = in["key"].string();
1438         if (!id || !key)
1439             throw ListenerException("Required parameters missing for RelayState recovery.");
1440
1441         string relayState;
1442         StorageService* storage = getStorageService(id);
1443         if (storage) {
1444             if (storage->readString("RelayState",key,&relayState)>0) {
1445                 if (in["clear"].integer())
1446                     storage->deleteString("RelayState",key);
1447             }
1448         }
1449         else {
1450             Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
1451                 "Storage-backed RelayState with invalid StorageService ID (%s)", id
1452                 );
1453         }
1454
1455         // Repack for return to caller.
1456         DDF ret=DDF(NULL).string(relayState.c_str());
1457         DDFJanitor jret(ret);
1458         out << ret;
1459     }
1460     else if (!strcmp(in.name(), "set::RelayState")) {
1461         const char* id = in["id"].string();
1462         const char* value = in["value"].string();
1463         if (!id || !value)
1464             throw ListenerException("Required parameters missing for RelayState creation.");
1465
1466         string rsKey;
1467         StorageService* storage = getStorageService(id);
1468         if (storage) {
1469             SAMLConfig::getConfig().generateRandomBytes(rsKey,20);
1470             rsKey = SAMLArtifact::toHex(rsKey);
1471             storage->createString("RelayState", rsKey.c_str(), value, time(NULL) + 600);
1472         }
1473         else {
1474             Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
1475                 "Storage-backed RelayState with invalid StorageService ID (%s)", id
1476                 );
1477         }
1478
1479         // Repack for return to caller.
1480         DDF ret=DDF(NULL).string(rsKey.c_str());
1481         DDFJanitor jret(ret);
1482         out << ret;
1483     }
1484 }
1485 #endif
1486
1487 pair<bool,DOMElement*> XMLConfig::load()
1488 {
1489     // Load from source using base class.
1490     pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
1491     
1492     // If we own it, wrap it.
1493     XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);
1494
1495     XMLConfigImpl* impl = new XMLConfigImpl(raw.second,(m_impl==NULL),this,m_log);
1496     
1497     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
1498     impl->setDocument(docjanitor.release());
1499
1500     delete m_impl;
1501     m_impl = impl;
1502
1503     return make_pair(false,(DOMElement*)NULL);
1504 }