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