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