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