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