f20bf92e563ae73b0fa3271baefb0cc3822274c7
[shibboleth/cpp-sp.git] / shib-target / shib-ini.cpp
1 /*
2  *  Copyright 2001-2005 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * shib-ini.h -- config file handling, now XML-based
19  *
20  * $Id$
21  */
22
23 #include "internal.h"
24
25 #include <shibsp/DOMPropertySet.h>
26 #include <shibsp/SPConfig.h>
27 #include <log4cpp/Category.hh>
28 #include <log4cpp/PropertyConfigurator.hh>
29 #include <algorithm>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32
33 using namespace shibsp;
34 using namespace shibtarget;
35 using namespace shibboleth;
36 using namespace saml;
37 using namespace log4cpp;
38 using namespace std;
39
40 namespace shibtarget {
41
42     // Application configuration wrapper
43     class XMLApplication : public virtual IApplication, public DOMPropertySet, public DOMNodeFilter
44     {
45     public:
46         XMLApplication(const IConfig*, const Iterator<ICredentials*>& creds, const DOMElement* e, const XMLApplication* base=NULL);
47         ~XMLApplication() { cleanup(); }
48     
49         // PropertySet
50         pair<bool,bool> getBool(const char* name, const char* ns=NULL) const;
51         pair<bool,const char*> getString(const char* name, const char* ns=NULL) const;
52         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const;
53         pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const;
54         pair<bool,int> getInt(const char* name, const char* ns=NULL) const;
55         const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:target:config:1.0") const;
56
57         // IApplication
58         const char* getId() const {return getString("id").second;}
59         const char* getHash() const {return m_hash.c_str();}
60         Iterator<SAMLAttributeDesignator*> getAttributeDesignators() const;
61         Iterator<IAAP*> getAAPProviders() const;
62         Iterator<IMetadata*> getMetadataProviders() const;
63         Iterator<ITrust*> getTrustProviders() const;
64         Iterator<const XMLCh*> getAudiences() const;
65         const PropertySet* getCredentialUse(const IEntityDescriptor* provider) const;
66         const SAMLBrowserProfile* getBrowserProfile() const {return m_profile;}
67         const SAMLBinding* getBinding(const XMLCh* binding) const
68             {return XMLString::compareString(SAMLBinding::SOAP,binding) ? NULL : m_binding;}
69         SAMLBrowserProfile::ArtifactMapper* getArtifactMapper() const {return new STArtifactMapper(this);}
70         void validateToken(
71             SAMLAssertion* token,
72             time_t t=0,
73             const IRoleDescriptor* role=NULL,
74             const Iterator<ITrust*>& trusts=EMPTY(ITrust*)
75             ) const;
76         const IHandler* getDefaultSessionInitiator() const;
77         const IHandler* getSessionInitiatorById(const char* id) const;
78         const IHandler* getDefaultAssertionConsumerService() const;
79         const IHandler* getAssertionConsumerServiceByIndex(unsigned short index) const;
80         Iterator<const IHandler*> getAssertionConsumerServicesByBinding(const XMLCh* binding) const;
81         const IHandler* getHandler(const char* path) const;
82         
83         // Provides filter to exclude special config elements.
84         short acceptNode(const DOMNode* node) const;
85     
86     private:
87         void cleanup();
88         const IConfig* m_ini;   // this is ok because its locking scope includes us
89         const XMLApplication* m_base;
90         string m_hash;
91         vector<SAMLAttributeDesignator*> m_designators;
92         vector<IAAP*> m_aaps;
93         vector<IMetadata*> m_metadatas;
94         vector<ITrust*> m_trusts;
95         vector<const XMLCh*> m_audiences;
96         ShibBrowserProfile* m_profile;
97         SAMLBinding* m_binding;
98         ShibHTTPHook* m_bindingHook;
99
100         // vectors manage object life for handlers and their property sets
101         vector<IHandler*> m_handlers;
102         vector<PropertySet*> m_handlerProps;
103
104         // maps location (path info) to applicable handlers
105         map<string,const IHandler*> m_handlerMap;
106
107         // maps unique indexes to consumer services
108         map<unsigned int,const IHandler*> m_acsIndexMap;
109         
110         // pointer to default consumer service
111         const IHandler* m_acsDefault;
112
113         // maps binding strings to supporting consumer service(s)
114 #ifdef HAVE_GOOD_STL
115         typedef map<xstring,vector<const IHandler*> > ACSBindingMap;
116 #else
117         typedef map<string,vector<const IHandler*> > ACSBindingMap;
118 #endif
119         ACSBindingMap m_acsBindingMap;
120
121         // maps unique ID strings to session initiators
122         map<string,const IHandler*> m_sessionInitMap;
123
124         // pointer to default session initiator
125         const IHandler* m_sessionInitDefault;
126
127         DOMPropertySet* m_credDefault;
128 #ifdef HAVE_GOOD_STL
129         map<xstring,PropertySet*> m_credMap;
130 #else
131         map<const XMLCh*,PropertySet*> m_credMap;
132 #endif
133     };
134
135     // Top-level configuration implementation
136     class XMLConfig;
137     class XMLConfigImpl : public ReloadableXMLFileImpl, public DOMPropertySet, public DOMNodeFilter
138     {
139     public:
140         XMLConfigImpl(const char* pathname, bool first, const XMLConfig* outer)
141             : ReloadableXMLFileImpl(pathname), m_outer(outer), m_requestMapper(NULL) { init(first); }
142         XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer)
143             : ReloadableXMLFileImpl(e), m_outer(outer), m_requestMapper(NULL) { init(first); }
144         ~XMLConfigImpl();
145         
146         IRequestMapper* m_requestMapper;
147         map<string,IApplication*> m_appmap;
148         vector<ICredentials*> m_creds;
149         vector<IAttributeFactory*> m_attrFactories;
150         
151         // Provides filter to exclude special config elements.
152         short acceptNode(const DOMNode* node) const;
153
154     private:
155         void init(bool first);
156         const XMLConfig* m_outer;
157     };
158     
159     class XMLConfig : public IConfig, public ReloadableXMLFile
160     {
161     public:
162         XMLConfig(const DOMElement* e) : ReloadableXMLFile(e), m_listener(NULL), m_sessionCache(NULL), m_replayCache(NULL) {}
163         ~XMLConfig() {
164             delete m_impl;
165             m_impl=NULL;
166             delete m_sessionCache;
167             m_sessionCache=NULL;
168             delete m_replayCache;
169             m_replayCache=NULL;
170             delete m_listener;
171             m_listener=NULL;
172         }
173
174         void init() { getImplementation(); }
175
176         // PropertySet
177         pair<bool,bool> getBool(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getBool(name,ns);}
178         pair<bool,const char*> getString(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getString(name,ns);}
179         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getXMLString(name,ns);}
180         pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getUnsignedInt(name,ns);}
181         pair<bool,int> getInt(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getInt(name,ns);}
182         const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:target:config:1.0") const {return static_cast<XMLConfigImpl*>(m_impl)->getPropertySet(name,ns);}
183         const DOMElement* getElement() const {return static_cast<XMLConfigImpl*>(m_impl)->getElement();}
184
185         // IConfig
186         ListenerService* getListener() const {return m_listener;}
187         ISessionCache* getSessionCache() const {return m_sessionCache;}
188         IReplayCache* getReplayCache() const {return m_replayCache;}
189         IRequestMapper* getRequestMapper() const {return static_cast<XMLConfigImpl*>(m_impl)->m_requestMapper;}
190         const IApplication* getApplication(const char* applicationId) const {
191             map<string,IApplication*>::const_iterator i=static_cast<XMLConfigImpl*>(m_impl)->m_appmap.find(applicationId);
192             return (i!=static_cast<XMLConfigImpl*>(m_impl)->m_appmap.end()) ? i->second : NULL;
193         }
194         Iterator<ICredentials*> getCredentialsProviders() const {return static_cast<XMLConfigImpl*>(m_impl)->m_creds;}
195
196     protected:
197         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
198         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
199
200     private:
201         friend class XMLConfigImpl;
202         mutable ListenerService* m_listener;
203         mutable ISessionCache* m_sessionCache;
204         mutable IReplayCache* m_replayCache;
205     };
206 }
207
208 IConfig* STConfig::ShibTargetConfigFactory(const DOMElement* e)
209 {
210     return new XMLConfig(e);
211 }
212
213 XMLApplication::XMLApplication(
214     const IConfig* ini,
215     const Iterator<ICredentials*>& creds,
216     const DOMElement* e,
217     const XMLApplication* base
218     ) : m_ini(ini), m_base(base), m_profile(NULL), m_binding(NULL), m_bindingHook(NULL),
219         m_credDefault(NULL), m_sessionInitDefault(NULL), m_acsDefault(NULL)
220 {
221 #ifdef _DEBUG
222     NDC ndc("XMLApplication");
223 #endif
224     Category& log=Category::getInstance("shibtarget.XMLApplication");
225
226     try {
227         // First load any property sets.
228         map<string,string> root_remap;
229         root_remap["shire"]="session";
230         root_remap["shireURL"]="handlerURL";
231         root_remap["shireSSL"]="handlerSSL";
232         load(e,log,this,&root_remap);
233         const PropertySet* propcheck=getPropertySet("Errors");
234         if (propcheck && !propcheck->getString("session").first)
235             throw ConfigurationException("<Errors> element requires 'session' (or deprecated 'shire') attribute");
236         propcheck=getPropertySet("Sessions");
237         if (propcheck && !propcheck->getString("handlerURL").first)
238             throw ConfigurationException("<Sessions> element requires 'handlerURL' (or deprecated 'shireURL') attribute");
239
240         m_hash=getId();
241         m_hash+=getString("providerId").second;
242         m_hash=SAMLArtifact::toHex(SAMLArtifactType0001::generateSourceId(m_hash.c_str()));
243
244         SPConfig& conf=SPConfig::getConfig();
245         SAMLConfig& shibConf=SAMLConfig::getConfig();
246
247         // Process handlers.
248         bool hardACS=false, hardSessionInit=false;
249         DOMElement* handler=saml::XML::getFirstChildElement(propcheck->getElement());
250         while (handler) {
251             // A handler is split across a property set and the plugin itself, which is based on the Binding property.
252             // We build both objects first and then insert them into various structures for lookup.
253             IHandler* hobj=NULL;
254             DOMPropertySet* hprops=new DOMPropertySet();
255             try {
256                 hprops->load(handler,log,this); // filter irrelevant for now, no embedded elements expected
257                 const char* bindprop=hprops->getString("Binding").second;
258                 if (!bindprop)
259                     throw ConfigurationException("Handler element has no Binding attribute, skipping it...");
260                 IPlugIn* hplug=shibConf.getPlugMgr().newPlugin(bindprop,handler);
261                 hobj=dynamic_cast<IHandler*>(hplug);
262                 if (!hobj) {
263                     delete hplug;
264                     throw UnsupportedProfileException(
265                         "Plugin for binding ($1) does not implement IHandler interface.",params(1,bindprop)
266                         );
267                 }
268             }
269             catch (SAMLException& ex) {
270                 // If we get here, the handler's not built, so dispose of the property set.
271                 log.error("caught exception processing a handler element: %s",ex.what());
272                 delete hprops;
273                 hprops=NULL;
274             }
275             
276             const char* location=hprops ? hprops->getString("Location").second : NULL;
277             if (!location) {
278                 delete hprops;
279                 hprops=NULL;
280                 handler=saml::XML::getNextSiblingElement(handler);
281                 continue;
282             }
283             
284             // Save off the objects after giving the property set to the handler for its use.
285             hobj->setProperties(hprops);
286             m_handlers.push_back(hobj);
287             m_handlerProps.push_back(hprops);
288
289             // Insert into location map.
290             if (*location == '/')
291                 m_handlerMap[location]=hobj;
292             else
293                 m_handlerMap[string("/") + location]=hobj;
294
295             // If it's an ACS or SI, handle index/id mappings and defaulting.
296             if (saml::XML::isElementNamed(handler,shibtarget::XML::SAML2META_NS,SHIBT_L(AssertionConsumerService))) {
297                 // Map it.
298 #ifdef HAVE_GOOD_STL
299                 const XMLCh* binding=hprops->getXMLString("Binding").second;
300 #else
301                 const char* binding=hprops->getString("Binding").second;
302 #endif
303                 if (m_acsBindingMap.count(binding)==0)
304                     m_acsBindingMap[binding]=vector<const IHandler*>(1,hobj);
305                 else
306                     m_acsBindingMap[binding].push_back(hobj);
307                 m_acsIndexMap[hprops->getUnsignedInt("index").second]=hobj;
308                 
309                 if (!hardACS) {
310                     pair<bool,bool> defprop=hprops->getBool("isDefault");
311                     if (defprop.first) {
312                         if (defprop.second) {
313                             hardACS=true;
314                             m_acsDefault=hobj;
315                         }
316                     }
317                     else if (!m_acsDefault)
318                         m_acsDefault=hobj;
319                 }
320             }
321             else if (saml::XML::isElementNamed(handler,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(SessionInitiator))) {
322                 pair<bool,const char*> si_id=hprops->getString("id");
323                 if (si_id.first && si_id.second)
324                     m_sessionInitMap[si_id.second]=hobj;
325                 if (!hardSessionInit) {
326                     pair<bool,bool> defprop=hprops->getBool("isDefault");
327                     if (defprop.first) {
328                         if (defprop.second) {
329                             hardSessionInit=true;
330                             m_sessionInitDefault=hobj;
331                         }
332                     }
333                     else if (!m_sessionInitDefault)
334                         m_sessionInitDefault=hobj;
335                 }
336             }
337             handler=saml::XML::getNextSiblingElement(handler);
338         }
339
340         // If no handlers defined at the root, assume a legacy configuration.
341         if (!m_base && m_handlers.empty()) {
342             // A legacy config installs a SAML POST handler at the root handler location.
343             // We use the Sessions element itself as the PropertySet.
344
345             auto_ptr_char b1(Constants::SHIB_SESSIONINIT_PROFILE_URI);
346             IPlugIn* hplug=shibConf.getPlugMgr().newPlugin(b1.get(),propcheck->getElement());
347             IHandler* h1=dynamic_cast<IHandler*>(hplug);
348             if (!h1) {
349                 delete hplug;
350                 throw UnsupportedProfileException(
351                     "Plugin for binding ($1) does not implement IHandler interface.",params(1,b1.get())
352                     );
353             }
354             h1->setProperties(propcheck);
355             m_handlers.push_back(h1);
356             m_sessionInitDefault=h1;
357
358             auto_ptr_char b2(SAMLBrowserProfile::BROWSER_POST);
359             hplug=shibConf.getPlugMgr().newPlugin(b2.get(),propcheck->getElement());
360             IHandler* h2=dynamic_cast<IHandler*>(hplug);
361             if (!h2) {
362                 delete hplug;
363                 throw UnsupportedProfileException(
364                     "Plugin for binding ($1) does not implement IHandler interface.",params(1,b2.get())
365                     );
366             }
367             h2->setProperties(propcheck);
368             m_handlers.push_back(h2);
369             m_handlerMap[""] = h2;
370             m_acsDefault=h2;
371         }
372         
373         // Process general configuration elements.
374         unsigned int i;
375         DOMNodeList* nlist=e->getElementsByTagNameNS(saml::XML::SAML_NS,L(AttributeDesignator));
376         for (i=0; nlist && i<nlist->getLength(); i++)
377             if (nlist->item(i)->getParentNode()->isSameNode(e))
378                 m_designators.push_back(new SAMLAttributeDesignator(static_cast<DOMElement*>(nlist->item(i))));
379
380         nlist=e->getElementsByTagNameNS(saml::XML::SAML_NS,L(Audience));
381         for (i=0; nlist && i<nlist->getLength(); i++)
382             if (nlist->item(i)->getParentNode()->isSameNode(e))
383                 m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue());
384
385         // Always include our own providerId as an audience.
386         m_audiences.push_back(getXMLString("providerId").second);
387
388         if (conf.isEnabled(SPConfig::AAP)) {
389             nlist=e->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,SHIBT_L(AAPProvider));
390             for (i=0; nlist && i<nlist->getLength(); i++) {
391                 if (nlist->item(i)->getParentNode()->isSameNode(e)) {
392                     auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
393                     log.info("building AAP provider of type %s...",type.get());
394                     try {
395                         IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
396                         IAAP* aap=dynamic_cast<IAAP*>(plugin);
397                         if (aap)
398                             m_aaps.push_back(aap);
399                         else {
400                             delete plugin;
401                             log.crit("plugin was not an AAP provider");
402                         }
403                     }
404                     catch (SAMLException& ex) {
405                         log.crit("error building AAP provider: %s",ex.what());
406                     }
407                 }
408             }
409         }
410
411         if (conf.isEnabled(SPConfig::Metadata)) {
412             nlist=e->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,SHIBT_L(MetadataProvider));
413             for (i=0; nlist && i<nlist->getLength(); i++) {
414                 if (nlist->item(i)->getParentNode()->isSameNode(e)) {
415                     auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
416                     log.info("building metadata provider of type %s...",type.get());
417                     try {
418                         IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
419                         IMetadata* md=dynamic_cast<IMetadata*>(plugin);
420                         if (md)
421                             m_metadatas.push_back(md);
422                         else {
423                             delete plugin;
424                             log.crit("plugin was not a metadata provider");
425                         }
426                     }
427                     catch (SAMLException& ex) {
428                         log.crit("error building metadata provider: %s",ex.what());
429                     }
430                 }
431             }
432             nlist=e->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,SHIBT_L(FederationProvider));
433             for (i=0; nlist && i<nlist->getLength(); i++) {
434                 if (nlist->item(i)->getParentNode()->isSameNode(e)) {
435                     auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
436                     log.info("building metadata provider of type %s...",type.get());
437                     try {
438                         IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
439                         IMetadata* md=dynamic_cast<IMetadata*>(plugin);
440                         if (md)
441                             m_metadatas.push_back(md);
442                         else {
443                             delete plugin;
444                             log.crit("plugin was not a metadata provider");
445                         }
446                     }
447                     catch (SAMLException& ex) {
448                         log.crit("error building metadata provider: %s",ex.what());
449                     }
450                 }
451             }
452         }
453
454         if (conf.isEnabled(SPConfig::Trust)) {
455             nlist=e->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,SHIBT_L(TrustProvider));
456             for (i=0; nlist && i<nlist->getLength(); i++) {
457                 if (nlist->item(i)->getParentNode()->isSameNode(e)) {
458                     auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
459                     log.info("building trust provider of type %s...",type.get());
460                     try {
461                         IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
462                         ITrust* trust=dynamic_cast<ITrust*>(plugin);
463                         if (trust)
464                             m_trusts.push_back(trust);
465                         else {
466                             delete plugin;
467                             log.crit("plugin was not a trust provider");
468                         }
469                     }
470                     catch (SAMLException& ex) {
471                         log.crit("error building trust provider: %s",ex.what());
472                     }
473                 }
474             }
475         }
476         
477         // Finally, load credential mappings.
478         const DOMElement* cu=saml::XML::getFirstChildElement(e,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(CredentialUse));
479         if (cu) {
480             m_credDefault=new DOMPropertySet();
481             m_credDefault->load(cu,log,this);
482             cu=saml::XML::getFirstChildElement(cu,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(RelyingParty));
483             while (cu) {
484                 DOMPropertySet* rp=new DOMPropertySet();
485                 rp->load(cu,log,this);
486                 m_credMap[cu->getAttributeNS(NULL,SHIBT_L(Name))]=rp;
487                 cu=saml::XML::getNextSiblingElement(cu,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(RelyingParty));
488             }
489         }
490         
491         if (conf.isEnabled(SPConfig::OutOfProcess)) {
492             // Really finally, build local browser profile and binding objects.
493             m_profile=new ShibBrowserProfile(
494                 this,
495                 getMetadataProviders(),
496                 getTrustProviders()
497                 );
498             m_bindingHook=new ShibHTTPHook(
499                 getTrustProviders(),
500                 creds
501                 );
502             m_binding=SAMLBinding::getInstance(SAMLBinding::SOAP);
503             SAMLSOAPHTTPBinding* bptr=dynamic_cast<SAMLSOAPHTTPBinding*>(m_binding);
504             if (!bptr) {
505                 log.fatal("binding implementation was not SOAP over HTTP");
506                 throw UnsupportedExtensionException("binding implementation was not SOAP over HTTP");
507             }
508             bptr->addHook(m_bindingHook,m_bindingHook); // the hook is its own global context
509         }
510     }
511     catch (SAMLException& e) {
512         log.errorStream() << "Error while processing applicaton element: " << e.what() << CategoryStream::ENDLINE;
513         cleanup();
514         throw;
515     }
516 #ifndef _DEBUG
517     catch (...) {
518         log.error("Unexpected error while processing application element");
519         cleanup();
520         throw;
521     }
522 #endif
523 }
524
525 void XMLApplication::cleanup()
526 {
527     delete m_bindingHook;
528     delete m_binding;
529     delete m_profile;
530     for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup<IHandler>());
531         
532     delete m_credDefault;
533 #ifdef HAVE_GOOD_STL
534     for_each(m_credMap.begin(),m_credMap.end(),xmltooling::cleanup_pair<xstring,PropertySet>());
535 #else
536     for_each(m_credMap.begin(),m_credMap.end(),xmltooling::cleanup_pair<const XMLCh*,PropertySet>());
537 #endif
538     for_each(m_designators.begin(),m_designators.end(),xmltooling::cleanup<SAMLAttributeDesignator>());
539     for_each(m_aaps.begin(),m_aaps.end(),xmltooling::cleanup<IAAP>());
540     for_each(m_metadatas.begin(),m_metadatas.end(),xmltooling::cleanup<IMetadata>());
541     for_each(m_trusts.begin(),m_trusts.end(),xmltooling::cleanup<ITrust>());
542 }
543
544 short XMLApplication::acceptNode(const DOMNode* node) const
545 {
546     if (saml::XML::isElementNamed(static_cast<const DOMElement*>(node),saml::XML::SAML_NS,L(AttributeDesignator)))
547         return FILTER_REJECT;
548     else if (saml::XML::isElementNamed(static_cast<const DOMElement*>(node),saml::XML::SAML_NS,L(Audience)))
549         return FILTER_REJECT;
550     const XMLCh* name=node->getLocalName();
551     if (!XMLString::compareString(name,SHIBT_L(Application)) ||
552         !XMLString::compareString(name,SHIBT_L(AssertionConsumerService)) ||
553         !XMLString::compareString(name,SHIBT_L(SingleLogoutService)) ||
554         !XMLString::compareString(name,SHIBT_L(DiagnosticService)) ||
555         !XMLString::compareString(name,SHIBT_L(SessionInitiator)) ||
556         !XMLString::compareString(name,SHIBT_L(AAPProvider)) ||
557         !XMLString::compareString(name,SHIBT_L(CredentialUse)) ||
558         !XMLString::compareString(name,SHIBT_L(RelyingParty)) ||
559         !XMLString::compareString(name,SHIBT_L(FederationProvider)) ||
560         !XMLString::compareString(name,SHIBT_L(MetadataProvider)) ||
561         !XMLString::compareString(name,SHIBT_L(TrustProvider)))
562         return FILTER_REJECT;
563
564     return FILTER_ACCEPT;
565 }
566
567 pair<bool,bool> XMLApplication::getBool(const char* name, const char* ns) const
568 {
569     pair<bool,bool> ret=DOMPropertySet::getBool(name,ns);
570     if (ret.first)
571         return ret;
572     return m_base ? m_base->getBool(name,ns) : ret;
573 }
574
575 pair<bool,const char*> XMLApplication::getString(const char* name, const char* ns) const
576 {
577     pair<bool,const char*> ret=DOMPropertySet::getString(name,ns);
578     if (ret.first)
579         return ret;
580     return m_base ? m_base->getString(name,ns) : ret;
581 }
582
583 pair<bool,const XMLCh*> XMLApplication::getXMLString(const char* name, const char* ns) const
584 {
585     pair<bool,const XMLCh*> ret=DOMPropertySet::getXMLString(name,ns);
586     if (ret.first)
587         return ret;
588     return m_base ? m_base->getXMLString(name,ns) : ret;
589 }
590
591 pair<bool,unsigned int> XMLApplication::getUnsignedInt(const char* name, const char* ns) const
592 {
593     pair<bool,unsigned int> ret=DOMPropertySet::getUnsignedInt(name,ns);
594     if (ret.first)
595         return ret;
596     return m_base ? m_base->getUnsignedInt(name,ns) : ret;
597 }
598
599 pair<bool,int> XMLApplication::getInt(const char* name, const char* ns) const
600 {
601     pair<bool,int> ret=DOMPropertySet::getInt(name,ns);
602     if (ret.first)
603         return ret;
604     return m_base ? m_base->getInt(name,ns) : ret;
605 }
606
607 const PropertySet* XMLApplication::getPropertySet(const char* name, const char* ns) const
608 {
609     const PropertySet* ret=DOMPropertySet::getPropertySet(name,ns);
610     if (ret || !m_base)
611         return ret;
612     return m_base->getPropertySet(name,ns);
613 }
614
615 Iterator<SAMLAttributeDesignator*> XMLApplication::getAttributeDesignators() const
616 {
617     if (!m_designators.empty() || !m_base)
618         return m_designators;
619     return m_base->getAttributeDesignators();
620 }
621
622 Iterator<IAAP*> XMLApplication::getAAPProviders() const
623 {
624     return (m_aaps.empty() && m_base) ? m_base->getAAPProviders() : m_aaps;
625 }
626
627 Iterator<IMetadata*> XMLApplication::getMetadataProviders() const
628 {
629     return (m_metadatas.empty() && m_base) ? m_base->getMetadataProviders() : m_metadatas;
630 }
631
632 Iterator<ITrust*> XMLApplication::getTrustProviders() const
633 {
634     return (m_trusts.empty() && m_base) ? m_base->getTrustProviders() : m_trusts;
635 }
636
637 Iterator<const XMLCh*> XMLApplication::getAudiences() const
638 {
639     return (m_audiences.empty() && m_base) ? m_base->getAudiences() : m_audiences;
640 }
641
642 const PropertySet* XMLApplication::getCredentialUse(const IEntityDescriptor* provider) const
643 {
644     if (!m_credDefault && m_base)
645         return m_base->getCredentialUse(provider);
646         
647 #ifdef HAVE_GOOD_STL
648     map<xstring,PropertySet*>::const_iterator i=m_credMap.find(provider->getId());
649     if (i!=m_credMap.end())
650         return i->second;
651     const IEntitiesDescriptor* group=provider->getEntitiesDescriptor();
652     while (group) {
653         if (group->getName()) {
654             i=m_credMap.find(group->getName());
655             if (i!=m_credMap.end())
656                 return i->second;
657         }
658         group=group->getEntitiesDescriptor();
659     }
660 #else
661     map<const XMLCh*,PropertySet*>::const_iterator i=m_credMap.begin();
662     for (; i!=m_credMap.end(); i++) {
663         if (!XMLString::compareString(i->first,provider->getId()))
664             return i->second;
665         const IEntitiesDescriptor* group=provider->getEntitiesDescriptor();
666         while (group) {
667             if (!XMLString::compareString(i->first,group->getName()))
668                 return i->second;
669             group=group->getEntitiesDescriptor();
670         }
671     }
672 #endif
673     return m_credDefault;
674 }
675
676 void XMLApplication::validateToken(SAMLAssertion* token, time_t ts, const IRoleDescriptor* role, const Iterator<ITrust*>& trusts) const
677 {
678 #ifdef _DEBUG
679     saml::NDC ndc("validateToken");
680 #endif
681     Category& log=Category::getInstance("shibtarget.XMLApplication");
682
683     // First we verify the time conditions, using the specified timestamp, if non-zero.
684     SAMLConfig& config=SAMLConfig::getConfig();
685     if (ts>0) {
686         const SAMLDateTime* notBefore=token->getNotBefore();
687         if (notBefore && ts+config.clock_skew_secs < notBefore->getEpoch())
688             throw ExpiredAssertionException("Assertion is not yet valid.");
689         const SAMLDateTime* notOnOrAfter=token->getNotOnOrAfter();
690         if (notOnOrAfter && notOnOrAfter->getEpoch() <= ts-config.clock_skew_secs)
691             throw ExpiredAssertionException("Assertion is no longer valid.");
692     }
693
694     // Now we process conditions. Only audience restrictions at the moment.
695     Iterator<SAMLCondition*> conditions=token->getConditions();
696     while (conditions.hasNext()) {
697         SAMLCondition* cond=conditions.next();
698         const SAMLAudienceRestrictionCondition* ac=dynamic_cast<const SAMLAudienceRestrictionCondition*>(cond);
699         if (!ac) {
700             ostringstream os;
701             os << *cond;
702             log.error("unrecognized Condition in assertion (%s)",os.str().c_str());
703             throw UnsupportedExtensionException("Assertion contains an unrecognized condition.");
704         }
705         else if (!ac->eval(getAudiences())) {
706             ostringstream os;
707             os << *ac;
708             log.error("unacceptable AudienceRestrictionCondition in assertion (%s)",os.str().c_str());
709             throw UnsupportedProfileException("Assertion contains an unacceptable AudienceRestrictionCondition.");
710         }
711     }
712
713     if (!role) {
714         log.warn("no metadata provided, so no signature validation was performed");
715         return;
716     }
717
718     const PropertySet* credUse=getCredentialUse(role->getEntityDescriptor());
719     pair<bool,bool> signedAssertions=credUse ? credUse->getBool("signedAssertions") : make_pair(false,false);
720     Trust t(trusts);
721
722     if (token->isSigned() && !t.validate(*token,role))
723         throw TrustException("Assertion signature did not validate.");
724     else if (signedAssertions.first && signedAssertions.second)
725         throw TrustException("Assertion was unsigned, violating policy based on the issuer.");
726 }
727
728 const IHandler* XMLApplication::getDefaultSessionInitiator() const
729 {
730     if (m_sessionInitDefault) return m_sessionInitDefault;
731     return m_base ? m_base->getDefaultSessionInitiator() : NULL;
732 }
733
734 const IHandler* XMLApplication::getSessionInitiatorById(const char* id) const
735 {
736     map<string,const IHandler*>::const_iterator i=m_sessionInitMap.find(id);
737     if (i!=m_sessionInitMap.end()) return i->second;
738     return m_base ? m_base->getSessionInitiatorById(id) : NULL;
739 }
740
741 const IHandler* XMLApplication::getDefaultAssertionConsumerService() const
742 {
743     if (m_acsDefault) return m_acsDefault;
744     return m_base ? m_base->getDefaultAssertionConsumerService() : NULL;
745 }
746
747 const IHandler* XMLApplication::getAssertionConsumerServiceByIndex(unsigned short index) const
748 {
749     map<unsigned int,const IHandler*>::const_iterator i=m_acsIndexMap.find(index);
750     if (i!=m_acsIndexMap.end()) return i->second;
751     return m_base ? m_base->getAssertionConsumerServiceByIndex(index) : NULL;
752 }
753
754 Iterator<const IHandler*> XMLApplication::getAssertionConsumerServicesByBinding(const XMLCh* binding) const
755 {
756 #ifdef HAVE_GOOD_STL
757     ACSBindingMap::const_iterator i=m_acsBindingMap.find(binding);
758 #else
759     auto_ptr_char temp(binding);
760     ACSBindingMap::const_iterator i=m_acsBindingMap.find(temp.get());
761 #endif
762     if (i!=m_acsBindingMap.end())
763         return i->second;
764     return m_base ? m_base->getAssertionConsumerServicesByBinding(binding) : EMPTY(const IHandler*);
765 }
766
767 const IHandler* XMLApplication::getHandler(const char* path) const
768 {
769     string wrap(path);
770     map<string,const IHandler*>::const_iterator i=m_handlerMap.find(wrap.substr(0,wrap.find('?')));
771     if (i!=m_handlerMap.end())
772         return i->second;
773     return m_base ? m_base->getHandler(path) : NULL;
774 }
775
776 ReloadableXMLFileImpl* XMLConfig::newImplementation(const char* pathname, bool first) const
777 {
778     return new XMLConfigImpl(pathname,first,this);
779 }
780
781 ReloadableXMLFileImpl* XMLConfig::newImplementation(const DOMElement* e, bool first) const
782 {
783     return new XMLConfigImpl(e,first,this);
784 }
785
786 short XMLConfigImpl::acceptNode(const DOMNode* node) const
787 {
788     if (XMLString::compareString(node->getNamespaceURI(),shibtarget::XML::SHIBTARGET_NS))
789         return FILTER_ACCEPT;
790     const XMLCh* name=node->getLocalName();
791     if (!XMLString::compareString(name,SHIBT_L(Applications)) ||
792         !XMLString::compareString(name,SHIBT_L(AttributeFactory)) ||
793         !XMLString::compareString(name,SHIBT_L(CredentialsProvider)) ||
794         !XMLString::compareString(name,SHIBT_L(Extensions)) ||
795         !XMLString::compareString(name,SHIBT_L(Implementation)) ||
796         !XMLString::compareString(name,SHIBT_L(Listener)) ||
797         !XMLString::compareString(name,SHIBT_L(MemorySessionCache)) ||
798         !XMLString::compareString(name,SHIBT_L(MySQLReplayCache)) ||
799         !XMLString::compareString(name,SHIBT_L(MySQLSessionCache)) ||
800         !XMLString::compareString(name,SHIBT_L(RequestMap)) ||
801         !XMLString::compareString(name,SHIBT_L(RequestMapProvider)) ||
802         !XMLString::compareString(name,SHIBT_L(ReplayCache)) ||
803         !XMLString::compareString(name,SHIBT_L(SessionCache)) ||
804         !XMLString::compareString(name,SHIBT_L(TCPListener)) ||
805         !XMLString::compareString(name,SHIBT_L(UnixListener)))
806         return FILTER_REJECT;
807
808     return FILTER_ACCEPT;
809 }
810
811 void XMLConfigImpl::init(bool first)
812 {
813 #ifdef _DEBUG
814     saml::NDC ndc("init");
815 #endif
816     Category& log=Category::getInstance("shibtarget.Config");
817
818     try {
819         if (!saml::XML::isElementNamed(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(ShibbolethTargetConfig)) &&
820             !saml::XML::isElementNamed(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(SPConfig))) {
821             log.error("Construction requires a valid configuration file: (conf:SPConfig as root element)");
822             throw ConfigurationException("Construction requires a valid configuration file: (conf:SPConfig as root element)");
823         }
824
825         SAMLConfig& shibConf=SAMLConfig::getConfig();
826         SPConfig& conf=SPConfig::getConfig();
827         const DOMElement* SHAR=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(SHAR));
828         if (!SHAR)
829             SHAR=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Global));
830         if (!SHAR)
831             SHAR=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(OutOfProcess));
832         const DOMElement* SHIRE=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(SHIRE));
833         if (!SHIRE)
834             SHIRE=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Local));
835         if (!SHIRE)
836             SHIRE=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(InProcess));
837
838         // Initialize log4cpp manually in order to redirect log messages as soon as possible.
839         if (conf.isEnabled(SPConfig::Logging)) {
840             const XMLCh* logger=NULL;
841             if (conf.isEnabled(SPConfig::OutOfProcess))
842                 logger=SHAR->getAttributeNS(NULL,SHIBT_L(logger));
843             else if (conf.isEnabled(SPConfig::InProcess))
844                 logger=SHIRE->getAttributeNS(NULL,SHIBT_L(logger));
845             if (!logger || !*logger)
846                 logger=ReloadableXMLFileImpl::m_root->getAttributeNS(NULL,SHIBT_L(logger));
847             if (logger && *logger) {
848                 auto_ptr_char logpath(logger);
849                 log.debug("loading new logging configuration from (%s), check log destination for status of configuration",logpath.get());
850                 try {
851                     PropertyConfigurator::configure(logpath.get());
852                 }
853                 catch (ConfigureFailure& e) {
854                     log.error("Error reading logging configuration: %s",e.what());
855                 }
856             }
857         }
858         
859         // First load any property sets.
860         map<string,string> root_remap;
861         root_remap["SHAR"]="OutOfProcess";
862         root_remap["SHIRE"]="InProcess";
863         root_remap["Global"]="OutOfProcess";
864         root_remap["Local"]="InProcess";
865         load(ReloadableXMLFileImpl::m_root,log,this,&root_remap);
866
867         // Much of the processing can only occur on the first instantiation.
868         if (first) {
869             // Now load any extensions to insure any needed plugins are registered.
870             DOMElement* exts=
871                 saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Extensions));
872             if (exts) {
873                 exts=saml::XML::getFirstChildElement(exts,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Library));
874                 while (exts) {
875                     auto_ptr_char path(exts->getAttributeNS(NULL,SHIBT_L(path)));
876                     try {
877                         SAMLConfig::getConfig().saml_register_extension(path.get(),exts);
878                         log.debug("loaded global extension library %s",path.get());
879                     }
880                     catch (SAMLException& e) {
881                         const XMLCh* fatal=exts->getAttributeNS(NULL,SHIBT_L(fatal));
882                         if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {
883                             log.fatal("unable to load mandatory global extension library %s: %s", path.get(), e.what());
884                             throw;
885                         }
886                         else
887                             log.crit("unable to load optional global extension library %s: %s", path.get(), e.what());
888                     }
889                     exts=saml::XML::getNextSiblingElement(exts,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Library));
890                 }
891             }
892             
893             if (conf.isEnabled(SPConfig::OutOfProcess)) {
894                 exts=saml::XML::getFirstChildElement(SHAR,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Extensions));
895                 if (exts) {
896                     exts=saml::XML::getFirstChildElement(exts,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Library));
897                     while (exts) {
898                         auto_ptr_char path(exts->getAttributeNS(NULL,SHIBT_L(path)));
899                         try {
900                             SAMLConfig::getConfig().saml_register_extension(path.get(),exts);
901                             log.debug("loaded Global extension library %s",path.get());
902                         }
903                         catch (SAMLException& e) {
904                             const XMLCh* fatal=exts->getAttributeNS(NULL,SHIBT_L(fatal));
905                             if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {
906                                 log.fatal("unable to load mandatory Global extension library %s: %s", path.get(), e.what());
907                                 throw;
908                             }
909                             else
910                                 log.crit("unable to load optional Global extension library %s: %s", path.get(), e.what());
911                         }
912                         exts=saml::XML::getNextSiblingElement(exts,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Library));
913                     }
914                 }
915             }
916
917             if (conf.isEnabled(SPConfig::InProcess)) {
918                 exts=saml::XML::getFirstChildElement(SHIRE,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Extensions));
919                 if (exts) {
920                     exts=saml::XML::getFirstChildElement(exts,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Library));
921                     while (exts) {
922                         auto_ptr_char path(exts->getAttributeNS(NULL,SHIBT_L(path)));
923                         try {
924                             SAMLConfig::getConfig().saml_register_extension(path.get(),exts);
925                             log.debug("loaded Local extension library %s",path.get());
926                         }
927                         catch (SAMLException& e) {
928                             const XMLCh* fatal=exts->getAttributeNS(NULL,SHIBT_L(fatal));
929                             if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {
930                                 log.fatal("unable to load mandatory Local extension library %s: %s", path.get(), e.what());
931                                 throw;
932                             }
933                             else
934                                 log.crit("unable to load optional Local extension library %s: %s", path.get(), e.what());
935                         }
936                         exts=saml::XML::getNextSiblingElement(exts,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Library));
937                     }
938                 }
939             }
940             
941             // Instantiate the ListenerService and SessionCache objects.
942             if (conf.isEnabled(SPConfig::Listener)) {
943                 exts=saml::XML::getFirstChildElement(SHAR,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(UnixListener));
944                 if (exts) {
945                     log.info("building Listener of type %s...",UNIX_LISTENER_SERVICE);
946                     m_outer->m_listener=conf.ListenerServiceManager.newPlugin(UNIX_LISTENER_SERVICE,exts);
947                 }
948                 else {
949                     exts=saml::XML::getFirstChildElement(SHAR,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(TCPListener));
950                     if (exts) {
951                         log.info("building Listener of type %s...",TCP_LISTENER_SERVICE);
952                         m_outer->m_listener=conf.ListenerServiceManager.newPlugin(TCP_LISTENER_SERVICE,exts);
953                     }
954                     else {
955                         exts=saml::XML::getFirstChildElement(SHAR,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Listener));
956                         if (exts) {
957                             auto_ptr_char type(exts->getAttributeNS(NULL,SHIBT_L(type)));
958                             log.info("building Listener of type %s...",type.get());
959                             m_outer->m_listener=conf.ListenerServiceManager.newPlugin(type.get(),exts);
960                         }
961                         else {
962                             log.fatal("can't build Listener object, missing conf:Listener element?");
963                             throw ConfigurationException("can't build Listener object, missing conf:Listener element?");
964                         }
965                     }
966                 }
967             }
968
969             if (conf.isEnabled(SPConfig::Caching)) {
970                 IPlugIn* plugin=NULL;
971                 const DOMElement* container=conf.isEnabled(SPConfig::OutOfProcess) ? SHAR : SHIRE;
972                 exts=saml::XML::getFirstChildElement(container,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(MemorySessionCache));
973                 if (exts) {
974                     log.info("building Session Cache of type %s...",shibtarget::XML::MemorySessionCacheType);
975                     plugin=shibConf.getPlugMgr().newPlugin(shibtarget::XML::MemorySessionCacheType,exts);
976                 }
977                 else {
978                     exts=saml::XML::getFirstChildElement(container,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(ODBCSessionCache));
979                     if (exts) {
980                         log.info("building Session Cache of type %s...",shibtarget::XML::ODBCSessionCacheType);
981                         plugin=shibConf.getPlugMgr().newPlugin(shibtarget::XML::ODBCSessionCacheType,exts);
982                     }
983                     else {
984                         exts=saml::XML::getFirstChildElement(container,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(MySQLSessionCache));
985                         if (exts) {
986                             log.info("building Session Cache of type %s...",shibtarget::XML::MySQLSessionCacheType);
987                             plugin=shibConf.getPlugMgr().newPlugin(shibtarget::XML::MySQLSessionCacheType,exts);
988                         }
989                         else {
990                             exts=saml::XML::getFirstChildElement(container,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(SessionCache));
991                             if (exts) {
992                                 auto_ptr_char type(exts->getAttributeNS(NULL,SHIBT_L(type)));
993                                 log.info("building Session Cache of type %s...",type.get());
994                                 plugin=shibConf.getPlugMgr().newPlugin(type.get(),exts);
995                             }
996                             else {
997                                 log.info("session cache not specified, building Session Cache of type %s...",shibtarget::XML::MemorySessionCacheType);
998                                 plugin=shibConf.getPlugMgr().newPlugin(shibtarget::XML::MemorySessionCacheType,exts);
999                             }
1000                         }
1001                     }
1002                 }
1003                 if (plugin) {
1004                     ISessionCache* cache=dynamic_cast<ISessionCache*>(plugin);
1005                     if (cache)
1006                         m_outer->m_sessionCache=cache;
1007                     else {
1008                         delete plugin;
1009                         log.fatal("plugin was not a Session Cache object");
1010                         throw UnsupportedExtensionException("plugin was not a Session Cache object");
1011                     }
1012                 }
1013                 
1014                 // Replay cache.
1015                 container=conf.isEnabled(SPConfig::OutOfProcess) ? SHAR : SHIRE;
1016                 exts=saml::XML::getFirstChildElement(container,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(ODBCReplayCache));
1017                 if (exts) {
1018                     log.info("building Replay Cache of type %s...",shibtarget::XML::ODBCReplayCacheType);
1019                     m_outer->m_replayCache=IReplayCache::getInstance(shibtarget::XML::ODBCReplayCacheType,exts);
1020                 }
1021                 else {
1022                     exts=saml::XML::getFirstChildElement(container,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(MySQLReplayCache));
1023                     if (exts) {
1024                         log.info("building Replay Cache of type %s...",shibtarget::XML::MySQLReplayCacheType);
1025                         m_outer->m_replayCache=IReplayCache::getInstance(shibtarget::XML::MySQLReplayCacheType,exts);
1026                     }
1027                     else {
1028                         exts=saml::XML::getFirstChildElement(container,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(ReplayCache));
1029                         if (exts) {
1030                             auto_ptr_char type(exts->getAttributeNS(NULL,SHIBT_L(type)));
1031                             log.info("building Replay Cache of type %s...",type.get());
1032                             m_outer->m_replayCache=IReplayCache::getInstance(type.get(),exts);
1033                         }
1034                         else {
1035                             // OpenSAML default provider.
1036                             log.info("building default Replay Cache...");
1037                             m_outer->m_replayCache=IReplayCache::getInstance();
1038                         }
1039                     }
1040                 }
1041             }
1042         }
1043         
1044         // Back to the fully dynamic stuff...next up is the Request Mapper.
1045         if (conf.isEnabled(SPConfig::RequestMapper)) {
1046             const DOMElement* child=saml::XML::getFirstChildElement(SHIRE,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(RequestMapProvider));
1047             if (child) {
1048                 auto_ptr_char type(child->getAttributeNS(NULL,SHIBT_L(type)));
1049                 log.info("building Request Mapper of type %s...",type.get());
1050                 IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),child);
1051                 if (plugin) {
1052                     IRequestMapper* reqmap=dynamic_cast<IRequestMapper*>(plugin);
1053                     if (reqmap)
1054                         m_requestMapper=reqmap;
1055                     else {
1056                         delete plugin;
1057                         log.fatal("plugin was not a Request Mapper object");
1058                         throw UnsupportedExtensionException("plugin was not a Request Mapper object");
1059                     }
1060                 }
1061             }
1062             else {
1063                 log.fatal("can't build Request Mapper object, missing conf:RequestMapProvider element?");
1064                 throw ConfigurationException("can't build Request Mapper object, missing conf:RequestMapProvider element?");
1065             }
1066         }
1067         
1068         // Now we load any credentials providers.
1069         DOMNodeList* nlist;
1070         if (conf.isEnabled(SPConfig::Credentials)) {
1071             nlist=ReloadableXMLFileImpl::m_root->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,SHIBT_L(CredentialsProvider));
1072             for (unsigned int i=0; nlist && i<nlist->getLength(); i++) {
1073                 auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
1074                 log.info("building credentials provider of type %s...",type.get());
1075                 try {
1076                     IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
1077                     if (plugin) {
1078                         ICredentials* creds=dynamic_cast<ICredentials*>(plugin);
1079                         if (creds)
1080                             m_creds.push_back(creds);
1081                         else {
1082                             delete plugin;
1083                             log.crit("plugin was not a credentials provider");
1084                         }
1085                     }
1086                 }
1087                 catch (SAMLException& ex) {
1088                     log.crit("error building credentials provider: %s",ex.what());
1089                 }
1090             }
1091         }
1092
1093         // Now we load any attribute factories
1094         nlist=ReloadableXMLFileImpl::m_root->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,SHIBT_L(AttributeFactory));
1095         for (unsigned int i=0; nlist && i<nlist->getLength(); i++) {
1096             auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
1097             log.info("building Attribute factory of type %s...",type.get());
1098             try {
1099                 IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
1100                 if (plugin) {
1101                     IAttributeFactory* fact=dynamic_cast<IAttributeFactory*>(plugin);
1102                     if (fact) {
1103                         m_attrFactories.push_back(fact);
1104                         ShibConfig::getConfig().regAttributeMapping(
1105                             static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,L(AttributeName)),
1106                             fact
1107                             );
1108                     }
1109                     else {
1110                         delete plugin;
1111                         log.crit("plugin was not an Attribute factory");
1112                     }
1113                 }
1114             }
1115             catch (SAMLException& ex) {
1116                 log.crit("error building Attribute factory: %s",ex.what());
1117             }
1118         }
1119
1120         // Load the default application. This actually has a fixed ID of "default". ;-)
1121         const DOMElement* app=saml::XML::getFirstChildElement(
1122             ReloadableXMLFileImpl::m_root,shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Applications)
1123             );
1124         if (!app) {
1125             log.fatal("can't build default Application object, missing conf:Applications element?");
1126             throw ConfigurationException("can't build default Application object, missing conf:Applications element?");
1127         }
1128         XMLApplication* defapp=new XMLApplication(m_outer, m_creds, app);
1129         m_appmap[defapp->getId()]=defapp;
1130         
1131         // Load any overrides.
1132         nlist=app->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,SHIBT_L(Application));
1133         for (unsigned int j=0; nlist && j<nlist->getLength(); j++) {
1134             auto_ptr<XMLApplication> iapp(new XMLApplication(m_outer,m_creds,static_cast<DOMElement*>(nlist->item(j)),defapp));
1135             if (m_appmap.find(iapp->getId())!=m_appmap.end())
1136                 log.crit("found conf:Application element with duplicate Id attribute, ignoring it");
1137             else
1138                 m_appmap[iapp->getId()]=iapp.release();
1139         }
1140     }
1141     catch (xmltooling::XMLToolingException& e) {
1142         log.errorStream() << "Error while loading SP configuration: " << e.what() << CategoryStream::ENDLINE;
1143         throw ConfigurationException(e.what());
1144     }
1145     catch (SAMLException& e) {
1146         log.errorStream() << "Error while loading SP configuration: " << e.what() << CategoryStream::ENDLINE;
1147         throw ConfigurationException(e.what());
1148     }
1149 #ifndef _DEBUG
1150     catch (...) {
1151         log.error("Unexpected error while loading SP configuration");
1152         throw;
1153     }
1154 #endif
1155 }
1156
1157 XMLConfigImpl::~XMLConfigImpl()
1158 {
1159     delete m_requestMapper;
1160     for_each(m_appmap.begin(),m_appmap.end(),xmltooling::cleanup_pair<string,IApplication>());
1161     for_each(m_creds.begin(),m_creds.end(),xmltooling::cleanup<ICredentials>());
1162     ShibConfig::getConfig().clearAttributeMappings();
1163     for_each(m_attrFactories.begin(),m_attrFactories.end(),xmltooling::cleanup<IAttributeFactory>());
1164 }