Moved key/cred resolution classes out of xmlsig namespace, start cleaning up configure.
[shibboleth/sp.git] / shibsp / impl / XMLServiceProvider.cpp
1 /*\r
2  *  Copyright 2001-2007 Internet2\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * XMLServiceProvider.cpp\r
19  *\r
20  * XML-based SP configuration and mgmt\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "exceptions.h"\r
25 #include "AccessControl.h"\r
26 #include "Application.h"\r
27 #include "Handler.h"\r
28 #include "RequestMapper.h"\r
29 #include "ServiceProvider.h"\r
30 #include "SessionCache.h"\r
31 #include "SPConfig.h"\r
32 #include "SPRequest.h"\r
33 #include "TransactionLog.h"\r
34 #include "attribute/resolver/AttributeResolver.h"\r
35 #include "remoting/ListenerService.h"\r
36 #include "security/PKIXTrustEngine.h"\r
37 #include "util/DOMPropertySet.h"\r
38 #include "util/SPConstants.h"\r
39 \r
40 #include <sys/types.h>\r
41 #include <sys/stat.h>\r
42 #include <log4cpp/Category.hh>\r
43 #include <log4cpp/PropertyConfigurator.hh>\r
44 #include <saml/SAMLConfig.h>\r
45 #include <saml/saml1/core/Assertions.h>\r
46 #include <saml/saml2/metadata/ChainingMetadataProvider.h>\r
47 #include <xmltooling/XMLToolingConfig.h>\r
48 #include <xmltooling/security/ChainingTrustEngine.h>\r
49 #include <xmltooling/util/NDC.h>\r
50 #include <xmltooling/util/ReloadableXMLFile.h>\r
51 #include <xmltooling/util/ReplayCache.h>\r
52 \r
53 using namespace shibsp;\r
54 using namespace opensaml::saml2;\r
55 using namespace opensaml::saml2md;\r
56 using namespace opensaml;\r
57 using namespace xmltooling;\r
58 using namespace log4cpp;\r
59 using namespace std;\r
60 \r
61 namespace {\r
62 \r
63 #if defined (_MSC_VER)\r
64     #pragma warning( push )\r
65     #pragma warning( disable : 4250 )\r
66 #endif\r
67 \r
68     class SHIBSP_DLLLOCAL TokenValidator : public Validator\r
69     {\r
70     public:\r
71         TokenValidator(const Application& app, time_t ts=0, const RoleDescriptor* role=NULL) : m_app(app), m_ts(ts), m_role(role) {}\r
72         void validate(const XMLObject*) const;\r
73 \r
74     private:\r
75         const Application& m_app;\r
76         time_t m_ts;\r
77         const RoleDescriptor* m_role;\r
78     };\r
79 \r
80     static vector<const Handler*> g_noHandlers;\r
81 \r
82     // Application configuration wrapper\r
83     class SHIBSP_DLLLOCAL XMLApplication : public virtual Application, public DOMPropertySet, public DOMNodeFilter\r
84     {\r
85     public:\r
86         XMLApplication(const ServiceProvider*, const DOMElement* e, const XMLApplication* base=NULL);\r
87         ~XMLApplication() { cleanup(); }\r
88     \r
89         // PropertySet\r
90         pair<bool,bool> getBool(const char* name, const char* ns=NULL) const;\r
91         pair<bool,const char*> getString(const char* name, const char* ns=NULL) const;\r
92         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const;\r
93         pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const;\r
94         pair<bool,int> getInt(const char* name, const char* ns=NULL) const;\r
95         const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:sp:config:2.0") const;\r
96 \r
97         // Application\r
98         const ServiceProvider& getServiceProvider() const {return *m_sp;}\r
99         const char* getId() const {return getString("id").second;}\r
100         const char* getHash() const {return m_hash.c_str();}\r
101 \r
102         MetadataProvider* getMetadataProvider() const {\r
103             return (!m_metadata && m_base) ? m_base->getMetadataProvider() : m_metadata;\r
104         }\r
105         TrustEngine* getTrustEngine() const {\r
106             return (!m_trust && m_base) ? m_base->getTrustEngine() : m_trust;\r
107         }\r
108         AttributeResolver* getAttributeResolver() const {\r
109             return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver;\r
110         }\r
111 \r
112         const PropertySet* getCredentialUse(const EntityDescriptor* provider) const;\r
113 \r
114         const Handler* getDefaultSessionInitiator() const;\r
115         const Handler* getSessionInitiatorById(const char* id) const;\r
116         const Handler* getDefaultAssertionConsumerService() const;\r
117         const Handler* getAssertionConsumerServiceByIndex(unsigned short index) const;\r
118         const vector<const Handler*>& getAssertionConsumerServicesByBinding(const XMLCh* binding) const;\r
119         const Handler* getHandler(const char* path) const;\r
120 \r
121         const vector<const XMLCh*>& getAudiences() const {\r
122             return (m_audiences.empty() && m_base) ? m_base->getAudiences() : m_audiences;\r
123         }\r
124         Validator* getTokenValidator(time_t ts=0, const opensaml::saml2md::RoleDescriptor* role=NULL) const {\r
125             return new TokenValidator(*this, ts, role);\r
126         }\r
127 \r
128         // Provides filter to exclude special config elements.\r
129         short acceptNode(const DOMNode* node) const;\r
130     \r
131     private:\r
132         void cleanup();\r
133         const ServiceProvider* m_sp;   // this is ok because its locking scope includes us\r
134         const XMLApplication* m_base;\r
135         string m_hash;\r
136         MetadataProvider* m_metadata;\r
137         TrustEngine* m_trust;\r
138         AttributeResolver* m_attrResolver;\r
139         vector<const XMLCh*> m_audiences;\r
140 \r
141         // manage handler objects\r
142         vector<Handler*> m_handlers;\r
143 \r
144         // maps location (path info) to applicable handlers\r
145         map<string,const Handler*> m_handlerMap;\r
146 \r
147         // maps unique indexes to consumer services\r
148         map<unsigned int,const Handler*> m_acsIndexMap;\r
149         \r
150         // pointer to default consumer service\r
151         const Handler* m_acsDefault;\r
152 \r
153         // maps binding strings to supporting consumer service(s)\r
154 #ifdef HAVE_GOOD_STL\r
155         typedef map<xstring,vector<const Handler*> > ACSBindingMap;\r
156 #else\r
157         typedef map<string,vector<const Handler*> > ACSBindingMap;\r
158 #endif\r
159         ACSBindingMap m_acsBindingMap;\r
160 \r
161         // maps unique ID strings to session initiators\r
162         map<string,const Handler*> m_sessionInitMap;\r
163 \r
164         // pointer to default session initiator\r
165         const Handler* m_sessionInitDefault;\r
166 \r
167         DOMPropertySet* m_credDefault;\r
168 #ifdef HAVE_GOOD_STL\r
169         map<xstring,PropertySet*> m_credMap;\r
170 #else\r
171         map<const XMLCh*,PropertySet*> m_credMap;\r
172 #endif\r
173     };\r
174 \r
175     // Top-level configuration implementation\r
176     class SHIBSP_DLLLOCAL XMLConfig;\r
177     class SHIBSP_DLLLOCAL XMLConfigImpl : public DOMPropertySet, public DOMNodeFilter\r
178     {\r
179     public:\r
180         XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer);\r
181         ~XMLConfigImpl();\r
182         \r
183         RequestMapper* m_requestMapper;\r
184         map<string,Application*> m_appmap;\r
185         map<string,CredentialResolver*> m_credResolverMap;\r
186         map< string,pair< PropertySet*,vector<const SecurityPolicyRule*> > > m_policyMap;\r
187         \r
188         // Provides filter to exclude special config elements.\r
189         short acceptNode(const DOMNode* node) const;\r
190 \r
191         void setDocument(DOMDocument* doc) {\r
192             m_document = doc;\r
193         }\r
194 \r
195     private:\r
196         void doExtensions(const DOMElement* e, const char* label, Category& log);\r
197 \r
198         const XMLConfig* m_outer;\r
199         DOMDocument* m_document;\r
200     };\r
201 \r
202     class SHIBSP_DLLLOCAL XMLConfig : public ServiceProvider, public ReloadableXMLFile\r
203     {\r
204     public:\r
205         XMLConfig(const DOMElement* e)\r
206             : ReloadableXMLFile(e), m_impl(NULL), m_listener(NULL), m_sessionCache(NULL), m_tranLog(NULL) {\r
207         }\r
208         \r
209         void init() {\r
210             load();\r
211         }\r
212 \r
213         ~XMLConfig() {\r
214             delete m_impl;\r
215             delete m_sessionCache;\r
216             delete m_listener;\r
217             delete m_tranLog;\r
218             XMLToolingConfig::getConfig().setReplayCache(NULL);\r
219             for_each(m_storage.begin(), m_storage.end(), cleanup_pair<string,StorageService>());\r
220         }\r
221 \r
222         // PropertySet\r
223         pair<bool,bool> getBool(const char* name, const char* ns=NULL) const {return m_impl->getBool(name,ns);}\r
224         pair<bool,const char*> getString(const char* name, const char* ns=NULL) const {return m_impl->getString(name,ns);}\r
225         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const {return m_impl->getXMLString(name,ns);}\r
226         pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const {return m_impl->getUnsignedInt(name,ns);}\r
227         pair<bool,int> getInt(const char* name, const char* ns=NULL) const {return m_impl->getInt(name,ns);}\r
228         const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:sp:config:2.0") const {return m_impl->getPropertySet(name,ns);}\r
229         const DOMElement* getElement() const {return m_impl->getElement();}\r
230 \r
231         // ServiceProvider\r
232         TransactionLog* getTransactionLog() const {\r
233             if (m_tranLog)\r
234                 return m_tranLog;\r
235             throw ConfigurationException("No TransactionLog available.");\r
236         }\r
237 \r
238         StorageService* getStorageService(const char* id) const {\r
239             if (id) {\r
240                 map<string,StorageService*>::const_iterator i=m_storage.find(id);\r
241                 if (i!=m_storage.end())\r
242                     return i->second;\r
243             }\r
244             return NULL;\r
245         }\r
246 \r
247         ListenerService* getListenerService(bool required=true) const {\r
248             if (required && !m_listener)\r
249                 throw ConfigurationException("No ListenerService available.");\r
250             return m_listener;\r
251         }\r
252 \r
253         SessionCache* getSessionCache(bool required=true) const {\r
254             if (required && !m_sessionCache)\r
255                 throw ConfigurationException("No SessionCache available.");\r
256             return m_sessionCache;\r
257         }\r
258 \r
259         RequestMapper* getRequestMapper(bool required=true) const {\r
260             if (required && !m_impl->m_requestMapper)\r
261                 throw ConfigurationException("No RequestMapper available.");\r
262             return m_impl->m_requestMapper;\r
263         }\r
264 \r
265         const Application* getApplication(const char* applicationId) const {\r
266             map<string,Application*>::const_iterator i=m_impl->m_appmap.find(applicationId);\r
267             return (i!=m_impl->m_appmap.end()) ? i->second : NULL;\r
268         }\r
269 \r
270         CredentialResolver* getCredentialResolver(const char* id) const {\r
271             if (id) {\r
272                 map<string,CredentialResolver*>::const_iterator i=m_impl->m_credResolverMap.find(id);\r
273                 if (i!=m_impl->m_credResolverMap.end())\r
274                     return i->second;\r
275             }\r
276             return NULL;\r
277         }\r
278 \r
279         const PropertySet* getPolicySettings(const char* id) const {\r
280             map<string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::const_iterator i = m_impl->m_policyMap.find(id);\r
281             if (i!=m_impl->m_policyMap.end())\r
282                 return i->second.first;\r
283             throw ConfigurationException("Security Policy ($1) not found, check <SecurityPolicies> element.", params(1,id));\r
284         }\r
285 \r
286         const vector<const SecurityPolicyRule*>& getPolicyRules(const char* id) const {\r
287             map<string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::const_iterator i = m_impl->m_policyMap.find(id);\r
288             if (i!=m_impl->m_policyMap.end())\r
289                 return i->second.second;\r
290             throw ConfigurationException("Security Policy ($1) not found, check <SecurityPolicies> element.", params(1,id));\r
291         }\r
292 \r
293     protected:\r
294         pair<bool,DOMElement*> load();\r
295 \r
296     private:\r
297         friend class XMLConfigImpl;\r
298         XMLConfigImpl* m_impl;\r
299         mutable ListenerService* m_listener;\r
300         mutable SessionCache* m_sessionCache;\r
301         mutable TransactionLog* m_tranLog;\r
302         mutable map<string,StorageService*> m_storage;\r
303     };\r
304 \r
305 #if defined (_MSC_VER)\r
306     #pragma warning( pop )\r
307 #endif\r
308 \r
309     static const XMLCh _Application[] =         UNICODE_LITERAL_11(A,p,p,l,i,c,a,t,i,o,n);\r
310     static const XMLCh Applications[] =         UNICODE_LITERAL_12(A,p,p,l,i,c,a,t,i,o,n,s);\r
311     static const XMLCh _AttributeResolver[] =   UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);\r
312     static const XMLCh Credentials[] =          UNICODE_LITERAL_11(C,r,e,d,e,n,t,i,a,l,s);\r
313     static const XMLCh CredentialUse[] =        UNICODE_LITERAL_13(C,r,e,d,e,n,t,i,a,l,U,s,e);\r
314     static const XMLCh fatal[] =                UNICODE_LITERAL_5(f,a,t,a,l);\r
315     static const XMLCh _Handler[] =             UNICODE_LITERAL_7(H,a,n,d,l,e,r);\r
316     static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);\r
317     static const XMLCh Implementation[] =       UNICODE_LITERAL_14(I,m,p,l,e,m,e,n,t,a,t,i,o,n);\r
318     static const XMLCh InProcess[] =            UNICODE_LITERAL_9(I,n,P,r,o,c,e,s,s);\r
319     static const XMLCh Library[] =              UNICODE_LITERAL_7(L,i,b,r,a,r,y);\r
320     static const XMLCh Listener[] =             UNICODE_LITERAL_8(L,i,s,t,e,n,e,r);\r
321     static const XMLCh logger[] =               UNICODE_LITERAL_6(l,o,g,g,e,r);\r
322     static const XMLCh MemoryListener[] =       UNICODE_LITERAL_14(M,e,m,o,r,y,L,i,s,t,e,n,e,r);\r
323     static const XMLCh Policy[] =               UNICODE_LITERAL_6(P,o,l,i,c,y);\r
324     static const XMLCh RelyingParty[] =         UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y);\r
325     static const XMLCh _ReplayCache[] =         UNICODE_LITERAL_11(R,e,p,l,a,y,C,a,c,h,e);\r
326     static const XMLCh _RequestMapper[] =       UNICODE_LITERAL_13(R,e,q,u,e,s,t,M,a,p,p,e,r);\r
327     static const XMLCh Rule[] =                 UNICODE_LITERAL_4(R,u,l,e);\r
328     static const XMLCh SecurityPolicies[] =     UNICODE_LITERAL_16(S,e,c,u,r,i,t,y,P,o,l,i,c,i,e,s);\r
329     static const XMLCh _SessionCache[] =        UNICODE_LITERAL_12(S,e,s,s,i,o,n,C,a,c,h,e);\r
330     static const XMLCh SessionInitiator[] =     UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r);\r
331     static const XMLCh _StorageService[] =      UNICODE_LITERAL_14(S,t,o,r,a,g,e,S,e,r,v,i,c,e);\r
332     static const XMLCh OutOfProcess[] =         UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s);\r
333     static const XMLCh TCPListener[] =          UNICODE_LITERAL_11(T,C,P,L,i,s,t,e,n,e,r);\r
334     static const XMLCh _TrustEngine[] =         UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e);\r
335     static const XMLCh UnixListener[] =         UNICODE_LITERAL_12(U,n,i,x,L,i,s,t,e,n,e,r);\r
336     static const XMLCh _MetadataProvider[] =    UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r);\r
337     static const XMLCh _path[] =                UNICODE_LITERAL_4(p,a,t,h);\r
338     static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);\r
339 \r
340     class SHIBSP_DLLLOCAL PolicyNodeFilter : public DOMNodeFilter\r
341     {\r
342     public:\r
343         short acceptNode(const DOMNode* node) const {\r
344             if (XMLHelper::isNodeNamed(node,shibspconstants::SHIB2SPCONFIG_NS,Rule))\r
345                 return FILTER_REJECT;\r
346             return FILTER_ACCEPT;\r
347         }\r
348     };\r
349 };\r
350 \r
351 namespace shibsp {\r
352     ServiceProvider* XMLServiceProviderFactory(const DOMElement* const & e)\r
353     {\r
354         return new XMLConfig(e);\r
355     }\r
356 };\r
357 \r
358 void TokenValidator::validate(const XMLObject* xmlObject) const\r
359 {\r
360 #ifdef _DEBUG\r
361     xmltooling::NDC ndc("validate");\r
362 #endif\r
363     Category& log=Category::getInstance(SHIBSP_LOGCAT".Application");\r
364 \r
365     const opensaml::RootObject* root = NULL;\r
366     const opensaml::saml2::Assertion* token2 = dynamic_cast<const opensaml::saml2::Assertion*>(xmlObject);\r
367     if (token2) {\r
368         const opensaml::saml2::Conditions* conds = token2->getConditions();\r
369         // First verify the time conditions, using the specified timestamp, if non-zero.\r
370         if (m_ts>0 && conds) {\r
371             unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;\r
372             time_t t=conds->getNotBeforeEpoch();\r
373             if (m_ts+skew < t)\r
374                 throw ValidationException("Assertion is not yet valid.");\r
375             t=conds->getNotOnOrAfterEpoch();\r
376             if (t <= m_ts-skew)\r
377                 throw ValidationException("Assertion is no longer valid.");\r
378         }\r
379 \r
380         // Now we process conditions. Only audience restrictions at the moment.\r
381         const vector<opensaml::saml2::Condition*>& convec = conds->getConditions();\r
382         for (vector<opensaml::saml2::Condition*>::const_iterator c = convec.begin(); c!=convec.end(); ++c) {\r
383             const opensaml::saml2::AudienceRestriction* ac=dynamic_cast<const opensaml::saml2::AudienceRestriction*>(*c);\r
384             if (!ac) {\r
385                 log.error("unrecognized Condition in assertion (%s)",\r
386                     (*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str());\r
387                 throw ValidationException("Assertion contains an unrecognized condition.");\r
388             }\r
389 \r
390             bool found = false;\r
391             const vector<opensaml::saml2::Audience*>& auds1 = ac->getAudiences();\r
392             const vector<const XMLCh*>& auds2 = m_app.getAudiences();\r
393             for (vector<opensaml::saml2::Audience*>::const_iterator a = auds1.begin(); !found && a!=auds1.end(); ++a) {\r
394                 for (vector<const XMLCh*>::const_iterator a2 = auds2.begin(); !found && a2!=auds2.end(); ++a2) {\r
395                     found = XMLString::equals((*a)->getAudienceURI(), *a2);\r
396                 }\r
397             }\r
398 \r
399             if (!found) {\r
400                 ostringstream os;\r
401                 os << *ac;\r
402                 log.error("unacceptable AudienceRestriction in assertion (%s)", os.str().c_str());\r
403                 throw ValidationException("Assertion contains an unacceptable AudienceRestriction.");\r
404             }\r
405         }\r
406 \r
407         root = token2;\r
408     }\r
409     else {\r
410         const opensaml::saml1::Assertion* token1 = dynamic_cast<const opensaml::saml1::Assertion*>(xmlObject);\r
411         if (token1) {\r
412             const opensaml::saml1::Conditions* conds = token1->getConditions();\r
413             // First verify the time conditions, using the specified timestamp, if non-zero.\r
414             if (m_ts>0 && conds) {\r
415                 unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;\r
416                 time_t t=conds->getNotBeforeEpoch();\r
417                 if (m_ts+skew < t)\r
418                     throw ValidationException("Assertion is not yet valid.");\r
419                 t=conds->getNotOnOrAfterEpoch();\r
420                 if (t <= m_ts-skew)\r
421                     throw ValidationException("Assertion is no longer valid.");\r
422             }\r
423 \r
424             // Now we process conditions. Only audience restrictions at the moment.\r
425             const vector<opensaml::saml1::Condition*>& convec = conds->getConditions();\r
426             for (vector<opensaml::saml1::Condition*>::const_iterator c = convec.begin(); c!=convec.end(); ++c) {\r
427                 const opensaml::saml1::AudienceRestrictionCondition* ac=dynamic_cast<const opensaml::saml1::AudienceRestrictionCondition*>(*c);\r
428                 if (!ac) {\r
429                     log.error("unrecognized Condition in assertion (%s)",\r
430                         (*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str());\r
431                     throw ValidationException("Assertion contains an unrecognized condition.");\r
432                 }\r
433 \r
434                 bool found = false;\r
435                 const vector<opensaml::saml1::Audience*>& auds1 = ac->getAudiences();\r
436                 const vector<const XMLCh*>& auds2 = m_app.getAudiences();\r
437                 for (vector<opensaml::saml1::Audience*>::const_iterator a = auds1.begin(); !found && a!=auds1.end(); ++a) {\r
438                     for (vector<const XMLCh*>::const_iterator a2 = auds2.begin(); !found && a2!=auds2.end(); ++a2) {\r
439                         found = XMLString::equals((*a)->getAudienceURI(), *a2);\r
440                     }\r
441                 }\r
442 \r
443                 if (!found) {\r
444                     ostringstream os;\r
445                     os << *ac;\r
446                     log.error("unacceptable AudienceRestrictionCondition in assertion (%s)", os.str().c_str());\r
447                     throw ValidationException("Assertion contains an unacceptable AudienceRestrictionCondition.");\r
448                 }\r
449             }\r
450 \r
451             root = token1;\r
452         }\r
453         else {\r
454             throw ValidationException("Unknown object type passed to token validator.");\r
455         }\r
456     }\r
457 \r
458     if (!m_role || !m_app.getTrustEngine()) {\r
459         log.warn("no issuer role or TrustEngine provided, so no signature validation performed");\r
460         return;\r
461     }\r
462 \r
463     const PropertySet* policy=m_app.getServiceProvider().getPolicySettings(m_app.getString("policyId").second);\r
464     pair<bool,bool> signedAssertions=policy ? policy->getBool("signedAssertions") : make_pair(false,false);\r
465 \r
466     if (root->getSignature()) {\r
467         if (!m_app.getTrustEngine()->validate(*(root->getSignature()),*m_role))\r
468             throw ValidationException("Assertion signature did not validate.");\r
469     }\r
470     else if (signedAssertions.first && signedAssertions.second)\r
471         throw ValidationException("Assertion was unsigned, violating policy.");\r
472 }\r
473 \r
474 XMLApplication::XMLApplication(\r
475     const ServiceProvider* sp,\r
476     const DOMElement* e,\r
477     const XMLApplication* base\r
478     ) : m_sp(sp), m_base(base), m_metadata(NULL), m_trust(NULL), m_attrResolver(NULL),\r
479         m_credDefault(NULL), m_sessionInitDefault(NULL), m_acsDefault(NULL)\r
480 {\r
481 #ifdef _DEBUG\r
482     xmltooling::NDC ndc("XMLApplication");\r
483 #endif\r
484     Category& log=Category::getInstance(SHIBSP_LOGCAT".Application");\r
485 \r
486     try {\r
487         // First load any property sets.\r
488         load(e,log,this);\r
489 \r
490         SPConfig& conf=SPConfig::getConfig();\r
491         SAMLConfig& samlConf=SAMLConfig::getConfig();\r
492         XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();\r
493 \r
494         m_hash=getId();\r
495         m_hash+=getString("providerId").second;\r
496         m_hash=samlConf.hashSHA1(m_hash.c_str(), true);\r
497 \r
498         const PropertySet* sessions = getPropertySet("Sessions");\r
499 \r
500         // Process handlers.\r
501         Handler* handler=NULL;\r
502         bool hardACS=false, hardSessionInit=false;\r
503         const DOMElement* child = sessions ? XMLHelper::getFirstChildElement(sessions->getElement()) : NULL;\r
504         while (child) {\r
505             try {\r
506                 // A handler is based on the Binding property in conjunction with the element name.\r
507                 // If it's an ACS or SI, also handle index/id mappings and defaulting.\r
508                 if (XMLHelper::isNodeNamed(child,samlconstants::SAML20MD_NS,AssertionConsumerService::LOCAL_NAME)) {\r
509                     auto_ptr_char bindprop(child->getAttributeNS(NULL,EndpointType::BINDING_ATTRIB_NAME));\r
510                     if (!bindprop.get() || !*(bindprop.get())) {\r
511                         log.warn("md:AssertionConsumerService element has no Binding attribute, skipping it...");\r
512                         child = XMLHelper::getNextSiblingElement(child);\r
513                         continue;\r
514                     }\r
515                     handler=conf.AssertionConsumerServiceManager.newPlugin(bindprop.get(),child);\r
516                     // Map by binding (may be > 1 per binding, e.g. SAML 1.0 vs 1.1)\r
517 #ifdef HAVE_GOOD_STL\r
518                     m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler);\r
519 #else\r
520                     m_acsBindingMap[handler->getString("Binding").second].push_back(handler);\r
521 #endif\r
522                     m_acsIndexMap[handler->getUnsignedInt("index").second]=handler;\r
523                     \r
524                     if (!hardACS) {\r
525                         pair<bool,bool> defprop=handler->getBool("isDefault");\r
526                         if (defprop.first) {\r
527                             if (defprop.second) {\r
528                                 hardACS=true;\r
529                                 m_acsDefault=handler;\r
530                             }\r
531                         }\r
532                         else if (!m_acsDefault)\r
533                             m_acsDefault=handler;\r
534                     }\r
535                 }\r
536                 else if (XMLString::equals(child->getLocalName(),SessionInitiator)) {\r
537                     auto_ptr_char bindprop(child->getAttributeNS(NULL,EndpointType::BINDING_ATTRIB_NAME));\r
538                     if (!bindprop.get() || !*(bindprop.get())) {\r
539                         log.warn("SessionInitiator element has no Binding attribute, skipping it...");\r
540                         child = XMLHelper::getNextSiblingElement(child);\r
541                         continue;\r
542                     }\r
543                     handler=conf.SessionInitiatorManager.newPlugin(bindprop.get(),child);\r
544                     pair<bool,const char*> si_id=handler->getString("id");\r
545                     if (si_id.first && si_id.second)\r
546                         m_sessionInitMap[si_id.second]=handler;\r
547                     if (!hardSessionInit) {\r
548                         pair<bool,bool> defprop=handler->getBool("isDefault");\r
549                         if (defprop.first) {\r
550                             if (defprop.second) {\r
551                                 hardSessionInit=true;\r
552                                 m_sessionInitDefault=handler;\r
553                             }\r
554                         }\r
555                         else if (!m_sessionInitDefault)\r
556                             m_sessionInitDefault=handler;\r
557                     }\r
558                 }\r
559                 else if (XMLHelper::isNodeNamed(child,samlconstants::SAML20MD_NS,SingleLogoutService::LOCAL_NAME)) {\r
560                     auto_ptr_char bindprop(child->getAttributeNS(NULL,EndpointType::BINDING_ATTRIB_NAME));\r
561                     if (!bindprop.get() || !*(bindprop.get())) {\r
562                         log.warn("md:SingleLogoutService element has no Binding attribute, skipping it...");\r
563                         child = XMLHelper::getNextSiblingElement(child);\r
564                         continue;\r
565                     }\r
566                     handler=conf.SingleLogoutServiceManager.newPlugin(bindprop.get(),child);\r
567                 }\r
568                 else if (XMLHelper::isNodeNamed(child,samlconstants::SAML20MD_NS,ManageNameIDService::LOCAL_NAME)) {\r
569                     auto_ptr_char bindprop(child->getAttributeNS(NULL,EndpointType::BINDING_ATTRIB_NAME));\r
570                     if (!bindprop.get() || !*(bindprop.get())) {\r
571                         log.warn("md:ManageNameIDService element has no Binding attribute, skipping it...");\r
572                         child = XMLHelper::getNextSiblingElement(child);\r
573                         continue;\r
574                     }\r
575                     handler=conf.ManageNameIDServiceManager.newPlugin(bindprop.get(),child);\r
576                 }\r
577                 else {\r
578                     auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
579                     if (!type.get() || !*(type.get())) {\r
580                         log.warn("Handler element has no type attribute, skipping it...");\r
581                         child = XMLHelper::getNextSiblingElement(child);\r
582                         continue;\r
583                     }\r
584                     handler=conf.HandlerManager.newPlugin(type.get(),child);\r
585                 }\r
586 \r
587                 // Save off the objects after giving the property set to the handler for its use.\r
588                 m_handlers.push_back(handler);\r
589 \r
590                 // Insert into location map.\r
591                 pair<bool,const char*> location=handler->getString("Location");\r
592                 if (location.first && *location.second == '/')\r
593                     m_handlerMap[location.second]=handler;\r
594                 else if (location.first)\r
595                     m_handlerMap[string("/") + location.second]=handler;\r
596 \r
597             }\r
598             catch (exception& ex) {\r
599                 log.error("caught exception processing handler element: %s", ex.what());\r
600             }\r
601             \r
602             child = XMLHelper::getNextSiblingElement(child);\r
603         }\r
604 \r
605         DOMNodeList* nlist=e->getElementsByTagNameNS(samlconstants::SAML20_NS,Audience::LOCAL_NAME);\r
606         for (XMLSize_t i=0; nlist && i<nlist->getLength(); i++)\r
607             if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes())\r
608                 m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue());\r
609 \r
610         // Always include our own providerId as an audience.\r
611         m_audiences.push_back(getXMLString("providerId").second);\r
612 \r
613         if (conf.isEnabled(SPConfig::Metadata)) {\r
614             child = XMLHelper::getFirstChildElement(e,_MetadataProvider);\r
615             if (child) {\r
616                 auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
617                 log.info("building MetadataProvider of type %s...",type.get());\r
618                 try {\r
619                     auto_ptr<MetadataProvider> mp(samlConf.MetadataProviderManager.newPlugin(type.get(),child));\r
620                     mp->init();\r
621                     m_metadata = mp.release();\r
622                 }\r
623                 catch (exception& ex) {\r
624                     log.crit("error building/initializing MetadataProvider: %s", ex.what());\r
625                 }\r
626             }\r
627         }\r
628 \r
629         if (conf.isEnabled(SPConfig::Trust)) {\r
630             child = XMLHelper::getFirstChildElement(e,_TrustEngine);\r
631             if (child) {\r
632                 auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
633                 log.info("building TrustEngine of type %s...",type.get());\r
634                 try {\r
635                     m_trust = xmlConf.TrustEngineManager.newPlugin(type.get(),child);\r
636                 }\r
637                 catch (exception& ex) {\r
638                     log.crit("error building TrustEngine: %s", ex.what());\r
639                 }\r
640             }\r
641         }\r
642 \r
643         if (conf.isEnabled(SPConfig::AttributeResolution)) {\r
644             child = XMLHelper::getFirstChildElement(e,_AttributeResolver);\r
645             if (child) {\r
646                 auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
647                 log.info("building AttributeResolver of type %s...",type.get());\r
648                 try {\r
649                     m_attrResolver = conf.AttributeResolverManager.newPlugin(type.get(),child);\r
650                 }\r
651                 catch (exception& ex) {\r
652                     log.crit("error building AttributeResolver: %s", ex.what());\r
653                 }\r
654             }\r
655         }\r
656 \r
657         // Finally, load credential mappings.\r
658         child = XMLHelper::getFirstChildElement(e,CredentialUse);\r
659         if (child) {\r
660             m_credDefault=new DOMPropertySet();\r
661             m_credDefault->load(child,log,this);\r
662             child = XMLHelper::getFirstChildElement(child,RelyingParty);\r
663             while (child) {\r
664                 DOMPropertySet* rp=new DOMPropertySet();\r
665                 rp->load(child,log,this);\r
666                 m_credMap[child->getAttributeNS(NULL,opensaml::saml2::Attribute::NAME_ATTRIB_NAME)]=rp;\r
667                 child = XMLHelper::getNextSiblingElement(child,RelyingParty);\r
668             }\r
669         }\r
670         \r
671         if (conf.isEnabled(SPConfig::OutOfProcess)) {\r
672             // Really finally, build local browser profile and binding objects.\r
673             // TODO: may need some bits here...\r
674         }\r
675     }\r
676     catch (exception&) {\r
677         cleanup();\r
678         throw;\r
679     }\r
680 #ifndef _DEBUG\r
681     catch (...) {\r
682         cleanup();\r
683         throw;\r
684     }\r
685 #endif\r
686 }\r
687 \r
688 void XMLApplication::cleanup()\r
689 {\r
690     for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup<Handler>());\r
691     \r
692     delete m_credDefault;\r
693 #ifdef HAVE_GOOD_STL\r
694     for_each(m_credMap.begin(),m_credMap.end(),cleanup_pair<xstring,PropertySet>());\r
695 #else\r
696     for_each(m_credMap.begin(),m_credMap.end(),cleanup_pair<const XMLCh*,PropertySet>());\r
697 #endif\r
698 \r
699     delete m_attrResolver;\r
700     delete m_trust;\r
701     delete m_metadata;\r
702 }\r
703 \r
704 short XMLApplication::acceptNode(const DOMNode* node) const\r
705 {\r
706     if (XMLHelper::isNodeNamed(node,samlconstants::SAML20_NS,opensaml::saml2::Attribute::LOCAL_NAME))\r
707         return FILTER_REJECT;\r
708     else if (XMLHelper::isNodeNamed(node,samlconstants::SAML20_NS,Audience::LOCAL_NAME))\r
709         return FILTER_REJECT;\r
710     const XMLCh* name=node->getLocalName();\r
711     if (XMLString::equals(name,_Application) ||\r
712         XMLString::equals(name,AssertionConsumerService::LOCAL_NAME) ||\r
713         XMLString::equals(name,SingleLogoutService::LOCAL_NAME) ||\r
714         XMLString::equals(name,ManageNameIDService::LOCAL_NAME) ||\r
715         XMLString::equals(name,SessionInitiator) ||\r
716         XMLString::equals(name,CredentialUse) ||\r
717         XMLString::equals(name,RelyingParty) ||\r
718         XMLString::equals(name,_MetadataProvider) ||\r
719         XMLString::equals(name,_TrustEngine) ||\r
720         XMLString::equals(name,_AttributeResolver))\r
721         return FILTER_REJECT;\r
722 \r
723     return FILTER_ACCEPT;\r
724 }\r
725 \r
726 pair<bool,bool> XMLApplication::getBool(const char* name, const char* ns) const\r
727 {\r
728     pair<bool,bool> ret=DOMPropertySet::getBool(name,ns);\r
729     if (ret.first)\r
730         return ret;\r
731     return m_base ? m_base->getBool(name,ns) : ret;\r
732 }\r
733 \r
734 pair<bool,const char*> XMLApplication::getString(const char* name, const char* ns) const\r
735 {\r
736     pair<bool,const char*> ret=DOMPropertySet::getString(name,ns);\r
737     if (ret.first)\r
738         return ret;\r
739     return m_base ? m_base->getString(name,ns) : ret;\r
740 }\r
741 \r
742 pair<bool,const XMLCh*> XMLApplication::getXMLString(const char* name, const char* ns) const\r
743 {\r
744     pair<bool,const XMLCh*> ret=DOMPropertySet::getXMLString(name,ns);\r
745     if (ret.first)\r
746         return ret;\r
747     return m_base ? m_base->getXMLString(name,ns) : ret;\r
748 }\r
749 \r
750 pair<bool,unsigned int> XMLApplication::getUnsignedInt(const char* name, const char* ns) const\r
751 {\r
752     pair<bool,unsigned int> ret=DOMPropertySet::getUnsignedInt(name,ns);\r
753     if (ret.first)\r
754         return ret;\r
755     return m_base ? m_base->getUnsignedInt(name,ns) : ret;\r
756 }\r
757 \r
758 pair<bool,int> XMLApplication::getInt(const char* name, const char* ns) const\r
759 {\r
760     pair<bool,int> ret=DOMPropertySet::getInt(name,ns);\r
761     if (ret.first)\r
762         return ret;\r
763     return m_base ? m_base->getInt(name,ns) : ret;\r
764 }\r
765 \r
766 const PropertySet* XMLApplication::getPropertySet(const char* name, const char* ns) const\r
767 {\r
768     const PropertySet* ret=DOMPropertySet::getPropertySet(name,ns);\r
769     if (ret || !m_base)\r
770         return ret;\r
771     return m_base->getPropertySet(name,ns);\r
772 }\r
773 \r
774 const PropertySet* XMLApplication::getCredentialUse(const EntityDescriptor* provider) const\r
775 {\r
776     if (!m_credDefault && m_base)\r
777         return m_base->getCredentialUse(provider);\r
778         \r
779 #ifdef HAVE_GOOD_STL\r
780     map<xstring,PropertySet*>::const_iterator i=m_credMap.find(provider->getEntityID());\r
781     if (i!=m_credMap.end())\r
782         return i->second;\r
783     const EntitiesDescriptor* group=dynamic_cast<const EntitiesDescriptor*>(provider->getParent());\r
784     while (group) {\r
785         if (group->getName()) {\r
786             i=m_credMap.find(group->getName());\r
787             if (i!=m_credMap.end())\r
788                 return i->second;\r
789         }\r
790         group=dynamic_cast<const EntitiesDescriptor*>(group->getParent());\r
791     }\r
792 #else\r
793     map<const XMLCh*,PropertySet*>::const_iterator i=m_credMap.begin();\r
794     for (; i!=m_credMap.end(); i++) {\r
795         if (XMLString::equals(i->first,provider->getId()))\r
796             return i->second;\r
797         const EntitiesDescriptor* group=dynamic_cast<const EntitiesDescriptor*>(provider->getParent());\r
798         while (group) {\r
799             if (XMLString::equals(i->first,group->getName()))\r
800                 return i->second;\r
801             group=dynamic_cast<const EntitiesDescriptor*>(group->getParent());\r
802         }\r
803     }\r
804 #endif\r
805     return m_credDefault;\r
806 }\r
807 \r
808 const Handler* XMLApplication::getDefaultSessionInitiator() const\r
809 {\r
810     if (m_sessionInitDefault) return m_sessionInitDefault;\r
811     return m_base ? m_base->getDefaultSessionInitiator() : NULL;\r
812 }\r
813 \r
814 const Handler* XMLApplication::getSessionInitiatorById(const char* id) const\r
815 {\r
816     map<string,const Handler*>::const_iterator i=m_sessionInitMap.find(id);\r
817     if (i!=m_sessionInitMap.end()) return i->second;\r
818     return m_base ? m_base->getSessionInitiatorById(id) : NULL;\r
819 }\r
820 \r
821 const Handler* XMLApplication::getDefaultAssertionConsumerService() const\r
822 {\r
823     if (m_acsDefault) return m_acsDefault;\r
824     return m_base ? m_base->getDefaultAssertionConsumerService() : NULL;\r
825 }\r
826 \r
827 const Handler* XMLApplication::getAssertionConsumerServiceByIndex(unsigned short index) const\r
828 {\r
829     map<unsigned int,const Handler*>::const_iterator i=m_acsIndexMap.find(index);\r
830     if (i!=m_acsIndexMap.end()) return i->second;\r
831     return m_base ? m_base->getAssertionConsumerServiceByIndex(index) : NULL;\r
832 }\r
833 \r
834 const vector<const Handler*>& XMLApplication::getAssertionConsumerServicesByBinding(const XMLCh* binding) const\r
835 {\r
836 #ifdef HAVE_GOOD_STL\r
837     ACSBindingMap::const_iterator i=m_acsBindingMap.find(binding);\r
838 #else\r
839     auto_ptr_char temp(binding);\r
840     ACSBindingMap::const_iterator i=m_acsBindingMap.find(temp.get());\r
841 #endif\r
842     if (i!=m_acsBindingMap.end())\r
843         return i->second;\r
844     return m_base ? m_base->getAssertionConsumerServicesByBinding(binding) : g_noHandlers;\r
845 }\r
846 \r
847 const Handler* XMLApplication::getHandler(const char* path) const\r
848 {\r
849     string wrap(path);\r
850     map<string,const Handler*>::const_iterator i=m_handlerMap.find(wrap.substr(0,wrap.find('?')));\r
851     if (i!=m_handlerMap.end())\r
852         return i->second;\r
853     return m_base ? m_base->getHandler(path) : NULL;\r
854 }\r
855 \r
856 short XMLConfigImpl::acceptNode(const DOMNode* node) const\r
857 {\r
858     if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB2SPCONFIG_NS))\r
859         return FILTER_ACCEPT;\r
860     const XMLCh* name=node->getLocalName();\r
861     if (XMLString::equals(name,Applications) ||\r
862         XMLString::equals(name,Credentials) ||\r
863         XMLString::equals(name,Extensions::LOCAL_NAME) ||\r
864         XMLString::equals(name,Implementation) ||\r
865         XMLString::equals(name,Listener) ||\r
866         XMLString::equals(name,MemoryListener) ||\r
867         XMLString::equals(name,Policy) ||\r
868         XMLString::equals(name,_RequestMapper) ||\r
869         XMLString::equals(name,_ReplayCache) ||\r
870         XMLString::equals(name,_SessionCache) ||\r
871         XMLString::equals(name,_StorageService) ||\r
872         XMLString::equals(name,TCPListener) ||\r
873         XMLString::equals(name,UnixListener))\r
874         return FILTER_REJECT;\r
875 \r
876     return FILTER_ACCEPT;\r
877 }\r
878 \r
879 void XMLConfigImpl::doExtensions(const DOMElement* e, const char* label, Category& log)\r
880 {\r
881     const DOMElement* exts=XMLHelper::getFirstChildElement(e,Extensions::LOCAL_NAME);\r
882     if (exts) {\r
883         exts=XMLHelper::getFirstChildElement(exts,Library);\r
884         while (exts) {\r
885             auto_ptr_char path(exts->getAttributeNS(NULL,_path));\r
886             try {\r
887                 if (path.get()) {\r
888                     XMLToolingConfig::getConfig().load_library(path.get(),(void*)exts);\r
889                     log.debug("loaded %s extension library (%s)", label, path.get());\r
890                 }\r
891             }\r
892             catch (exception& e) {\r
893                 const XMLCh* fatal=exts->getAttributeNS(NULL,fatal);\r
894                 if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {\r
895                     log.fatal("unable to load mandatory %s extension library %s: %s", label, path.get(), e.what());\r
896                     throw;\r
897                 }\r
898                 else {\r
899                     log.crit("unable to load optional %s extension library %s: %s", label, path.get(), e.what());\r
900                 }\r
901             }\r
902             exts=XMLHelper::getNextSiblingElement(exts,Library);\r
903         }\r
904     }\r
905 }\r
906 \r
907 XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer) : m_requestMapper(NULL), m_outer(outer), m_document(NULL)\r
908 {\r
909 #ifdef _DEBUG\r
910     xmltooling::NDC ndc("XMLConfigImpl");\r
911 #endif\r
912     Category& log=Category::getInstance(SHIBSP_LOGCAT".Config");\r
913 \r
914     try {\r
915         SPConfig& conf=SPConfig::getConfig();\r
916         SAMLConfig& samlConf=SAMLConfig::getConfig();\r
917         XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();\r
918         const DOMElement* SHAR=XMLHelper::getFirstChildElement(e,OutOfProcess);\r
919         const DOMElement* SHIRE=XMLHelper::getFirstChildElement(e,InProcess);\r
920 \r
921         // Initialize log4cpp manually in order to redirect log messages as soon as possible.\r
922         if (conf.isEnabled(SPConfig::Logging)) {\r
923             const XMLCh* logconf=NULL;\r
924             if (conf.isEnabled(SPConfig::OutOfProcess))\r
925                 logconf=SHAR->getAttributeNS(NULL,logger);\r
926             else if (conf.isEnabled(SPConfig::InProcess))\r
927                 logconf=SHIRE->getAttributeNS(NULL,logger);\r
928             if (!logconf || !*logconf)\r
929                 logconf=e->getAttributeNS(NULL,logger);\r
930             if (logconf && *logconf) {\r
931                 auto_ptr_char logpath(logconf);\r
932                 log.debug("loading new logging configuration from (%s), check log destination for status of configuration",logpath.get());\r
933                 XMLToolingConfig::getConfig().log_config(logpath.get());\r
934             }\r
935             \r
936             if (first)\r
937                 m_outer->m_tranLog = new TransactionLog();\r
938         }\r
939         \r
940         // First load any property sets.\r
941         load(e,log,this);\r
942 \r
943         const DOMElement* child;\r
944         string plugtype;\r
945 \r
946         // Much of the processing can only occur on the first instantiation.\r
947         if (first) {\r
948             // Set clock skew.\r
949             pair<bool,unsigned int> skew=getUnsignedInt("clockSkew");\r
950             if (skew.first)\r
951                 xmlConf.clock_skew_secs=skew.second;\r
952 \r
953             // Extensions\r
954             doExtensions(e, "global", log);\r
955             if (conf.isEnabled(SPConfig::OutOfProcess))\r
956                 doExtensions(SHAR, "out of process", log);\r
957 \r
958             if (conf.isEnabled(SPConfig::InProcess))\r
959                 doExtensions(SHIRE, "in process", log);\r
960             \r
961             // Instantiate the ListenerService and SessionCache objects.\r
962             if (conf.isEnabled(SPConfig::Listener)) {\r
963                 child=XMLHelper::getFirstChildElement(SHAR,UnixListener);\r
964                 if (child)\r
965                     plugtype=UNIX_LISTENER_SERVICE;\r
966                 else {\r
967                     child=XMLHelper::getFirstChildElement(SHAR,TCPListener);\r
968                     if (child)\r
969                         plugtype=TCP_LISTENER_SERVICE;\r
970                     else {\r
971                         child=XMLHelper::getFirstChildElement(SHAR,MemoryListener);\r
972                         if (child)\r
973                             plugtype=MEMORY_LISTENER_SERVICE;\r
974                         else {\r
975                             child=XMLHelper::getFirstChildElement(SHAR,Listener);\r
976                             if (child) {\r
977                                 auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
978                                 if (type.get())\r
979                                     plugtype=type.get();\r
980                             }\r
981                         }\r
982                     }\r
983                 }\r
984                 if (child) {\r
985                     log.info("building ListenerService of type %s...", plugtype.c_str());\r
986                     m_outer->m_listener = conf.ListenerServiceManager.newPlugin(plugtype.c_str(),child);\r
987                 }\r
988                 else {\r
989                     log.fatal("can't build ListenerService, missing conf:Listener element?");\r
990                     throw ConfigurationException("Can't build ListenerService, missing conf:Listener element?");\r
991                 }\r
992             }\r
993 \r
994             if (conf.isEnabled(SPConfig::Caching)) {\r
995                 if (conf.isEnabled(SPConfig::OutOfProcess)) {\r
996                     // First build any StorageServices.\r
997                     string inmemID;\r
998                     child=XMLHelper::getFirstChildElement(SHAR,_StorageService);\r
999                     while (child) {\r
1000                         auto_ptr_char id(child->getAttributeNS(NULL,_id));\r
1001                         auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
1002                         try {\r
1003                             log.info("building StorageService (%s) of type %s...", id.get(), type.get());\r
1004                             m_outer->m_storage[id.get()] = xmlConf.StorageServiceManager.newPlugin(type.get(),child);\r
1005                             if (!strcmp(type.get(),MEMORY_STORAGE_SERVICE))\r
1006                                 inmemID = id.get();\r
1007                         }\r
1008                         catch (exception& ex) {\r
1009                             log.crit("failed to instantiate StorageService (%s): %s", id.get(), ex.what());\r
1010                         }\r
1011                         child=XMLHelper::getNextSiblingElement(child,_StorageService);\r
1012                     }\r
1013                 \r
1014                     child=XMLHelper::getFirstChildElement(SHAR,_SessionCache);\r
1015                     if (child) {\r
1016                         auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
1017                         log.info("building SessionCache of type %s...",type.get());\r
1018                         m_outer->m_sessionCache=conf.SessionCacheManager.newPlugin(type.get(),child);\r
1019                     }\r
1020                     else {\r
1021                         log.warn("SessionCache unspecified, building SessionCache of type %s...",STORAGESERVICE_SESSION_CACHE);\r
1022                         if (inmemID.empty()) {\r
1023                             inmemID = "memory";\r
1024                             log.info("no StorageServices configured, providing in-memory version for session cache");\r
1025                             m_outer->m_storage[inmemID] = xmlConf.StorageServiceManager.newPlugin(MEMORY_STORAGE_SERVICE,NULL);\r
1026                         }\r
1027                         child = e->getOwnerDocument()->createElementNS(NULL,_SessionCache);\r
1028                         auto_ptr_XMLCh ssid(inmemID.c_str());\r
1029                         const_cast<DOMElement*>(child)->setAttributeNS(NULL,_StorageService,ssid.get());\r
1030                         m_outer->m_sessionCache=conf.SessionCacheManager.newPlugin(STORAGESERVICE_SESSION_CACHE,child);\r
1031                     }\r
1032 \r
1033                     // Replay cache.\r
1034                     StorageService* replaySS=NULL;\r
1035                     child=XMLHelper::getFirstChildElement(SHAR,_ReplayCache);\r
1036                     if (child) {\r
1037                         auto_ptr_char ssid(child->getAttributeNS(NULL,_StorageService));\r
1038                         if (ssid.get() && *ssid.get()) {\r
1039                             if (m_outer->m_storage.count(ssid.get()))\r
1040                                 replaySS = m_outer->m_storage[ssid.get()];\r
1041                             if (replaySS)\r
1042                                 log.info("building ReplayCache on top of StorageService (%s)...", ssid.get());\r
1043                             else\r
1044                                 log.crit("unable to locate StorageService (%s) in configuration", ssid.get());\r
1045                         }\r
1046                     }\r
1047                     if (!replaySS) {\r
1048                         log.info("building ReplayCache using in-memory StorageService...");\r
1049                         if (inmemID.empty()) {\r
1050                             inmemID = "memory";\r
1051                             log.info("no StorageServices configured, providing in-memory version for legacy config");\r
1052                             m_outer->m_storage[inmemID] = xmlConf.StorageServiceManager.newPlugin(MEMORY_STORAGE_SERVICE,NULL);\r
1053                         }\r
1054                         replaySS = m_outer->m_storage[inmemID];\r
1055                     }\r
1056                     xmlConf.setReplayCache(new ReplayCache(replaySS));\r
1057                 }\r
1058                 else {\r
1059                     log.info("building in-process SessionCache of type %s...",REMOTED_SESSION_CACHE);\r
1060                     m_outer->m_sessionCache=conf.SessionCacheManager.newPlugin(REMOTED_SESSION_CACHE,NULL);\r
1061                 }\r
1062             }\r
1063         } // end of first-time-only stuff\r
1064         \r
1065         // Back to the fully dynamic stuff...next up is the RequestMapper.\r
1066         if (conf.isEnabled(SPConfig::RequestMapping)) {\r
1067             child=XMLHelper::getFirstChildElement(SHIRE,_RequestMapper);\r
1068             if (child) {\r
1069                 auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
1070                 log.info("building RequestMapper of type %s...",type.get());\r
1071                 m_requestMapper=conf.RequestMapperManager.newPlugin(type.get(),child);\r
1072             }\r
1073         }\r
1074         \r
1075         // Now we load the credentials map.\r
1076         if (conf.isEnabled(SPConfig::Credentials)) {\r
1077             child = XMLHelper::getLastChildElement(e,Credentials);\r
1078             if (child) {\r
1079                 // Step down and process resolvers.\r
1080                 child=XMLHelper::getFirstChildElement(child);\r
1081                 while (child) {\r
1082                     auto_ptr_char id(child->getAttributeNS(NULL,_id));\r
1083                     auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
1084                     try {\r
1085                         CredentialResolver* cr=xmlConf.CredentialResolverManager.newPlugin(type.get(),child);\r
1086                         m_credResolverMap[id.get()] = cr;\r
1087                     }\r
1088                     catch (exception& ex) {\r
1089                         log.crit("failed to instantiate CredentialResolver (%s): %s", id.get(), ex.what());\r
1090                     }\r
1091                     child = XMLHelper::getNextSiblingElement(child);\r
1092                 }\r
1093             }\r
1094         }\r
1095 \r
1096         // Load security policies.\r
1097         child = XMLHelper::getLastChildElement(e,SecurityPolicies);\r
1098         if (child) {\r
1099             PolicyNodeFilter filter;\r
1100             child = XMLHelper::getFirstChildElement(child,Policy);\r
1101             while (child) {\r
1102                 auto_ptr_char id(child->getAttributeNS(NULL,_id));\r
1103                 pair< PropertySet*,vector<const SecurityPolicyRule*> >& rules = m_policyMap[id.get()];\r
1104                 rules.first = NULL;\r
1105                 auto_ptr<DOMPropertySet> settings(new DOMPropertySet());\r
1106                 settings->load(child, log, &filter);\r
1107                 rules.first = settings.release();\r
1108                 const DOMElement* rule = XMLHelper::getFirstChildElement(child,Rule);\r
1109                 while (rule) {\r
1110                     auto_ptr_char type(rule->getAttributeNS(NULL,_type));\r
1111                     try {\r
1112                         rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule));\r
1113                     }\r
1114                     catch (exception& ex) {\r
1115                         log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());\r
1116                     }\r
1117                     rule = XMLHelper::getNextSiblingElement(rule,Rule);\r
1118                 }\r
1119                 child = XMLHelper::getNextSiblingElement(child,Policy);\r
1120             }\r
1121         }\r
1122 \r
1123         // Load the default application. This actually has a fixed ID of "default". ;-)\r
1124         child=XMLHelper::getLastChildElement(e,Applications);\r
1125         if (!child) {\r
1126             log.fatal("can't build default Application object, missing conf:Applications element?");\r
1127             throw ConfigurationException("can't build default Application object, missing conf:Applications element?");\r
1128         }\r
1129         XMLApplication* defapp=new XMLApplication(m_outer,child);\r
1130         m_appmap[defapp->getId()]=defapp;\r
1131         \r
1132         // Load any overrides.\r
1133         child = XMLHelper::getFirstChildElement(child,_Application);\r
1134         while (child) {\r
1135             auto_ptr<XMLApplication> iapp(new XMLApplication(m_outer,child,defapp));\r
1136             if (m_appmap.count(iapp->getId()))\r
1137                 log.crit("found conf:Application element with duplicate id attribute (%s), skipping it", iapp->getId());\r
1138             else\r
1139                 m_appmap[iapp->getId()]=iapp.release();\r
1140 \r
1141             child = XMLHelper::getNextSiblingElement(child,_Application);\r
1142         }\r
1143     }\r
1144     catch (exception&) {\r
1145         this->~XMLConfigImpl();\r
1146         throw;\r
1147     }\r
1148 #ifndef _DEBUG\r
1149     catch (...) {\r
1150         this->~XMLConfigImpl();\r
1151         throw;\r
1152     }\r
1153 #endif\r
1154 }\r
1155 \r
1156 XMLConfigImpl::~XMLConfigImpl()\r
1157 {\r
1158     for_each(m_appmap.begin(),m_appmap.end(),cleanup_pair<string,Application>());\r
1159     for_each(m_credResolverMap.begin(),m_credResolverMap.end(),cleanup_pair<string,CredentialResolver>());\r
1160     for (map< string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::iterator i=m_policyMap.begin(); i!=m_policyMap.end(); ++i) {\r
1161         delete i->second.first;\r
1162         for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup<SecurityPolicyRule>());\r
1163     }\r
1164     delete m_requestMapper;\r
1165     if (m_document)\r
1166         m_document->release();\r
1167 }\r
1168 \r
1169 pair<bool,DOMElement*> XMLConfig::load()\r
1170 {\r
1171     // Load from source using base class.\r
1172     pair<bool,DOMElement*> raw = ReloadableXMLFile::load();\r
1173     \r
1174     // If we own it, wrap it.\r
1175     XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);\r
1176 \r
1177     XMLConfigImpl* impl = new XMLConfigImpl(raw.second,(m_impl==NULL),this);\r
1178     \r
1179     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
1180     impl->setDocument(docjanitor.release());\r
1181 \r
1182     delete m_impl;\r
1183     m_impl = impl;\r
1184 \r
1185     return make_pair(false,(DOMElement*)NULL);\r
1186 }\r