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