Last sec draft std adjustment
[shibboleth/sp.git] / xmlproviders / XMLMetadata.cpp
1 /* 
2  * The Shibboleth License, Version 1. 
3  * Copyright (c) 2002 
4  * University Corporation for Advanced Internet Development, Inc. 
5  * All rights reserved
6  * 
7  * 
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions are met:
10  * 
11  * Redistributions of source code must retain the above copyright notice, this 
12  * list of conditions and the following disclaimer.
13  * 
14  * Redistributions in binary form must reproduce the above copyright notice, 
15  * this list of conditions and the following disclaimer in the documentation 
16  * and/or other materials provided with the distribution, if any, must include 
17  * the following acknowledgment: "This product includes software developed by 
18  * the University Corporation for Advanced Internet Development 
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
20  * may appear in the software itself, if and wherever such third-party 
21  * acknowledgments normally appear.
22  * 
23  * Neither the name of Shibboleth nor the names of its contributors, nor 
24  * Internet2, nor the University Corporation for Advanced Internet Development, 
25  * Inc., nor UCAID may be used to endorse or promote products derived from this 
26  * software without specific prior written permission. For written permission, 
27  * please contact shibboleth@shibboleth.org
28  * 
29  * Products derived from this software may not be called Shibboleth, Internet2, 
30  * UCAID, or the University Corporation for Advanced Internet Development, nor 
31  * may Shibboleth appear in their name, without prior written permission of the 
32  * University Corporation for Advanced Internet Development.
33  * 
34  * 
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 /* XMLMetadata.cpp - a metadata implementation that uses an XML-based registry
51
52    Scott Cantor
53    9/27/02
54
55    $History:$
56 */
57
58 #include "internal.h"
59
60 #include <sys/types.h>
61 #include <sys/stat.h>
62
63 #include <log4cpp/Category.hh>
64 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
65
66 using namespace shibboleth;
67 using namespace saml;
68 using namespace log4cpp;
69 using namespace std;
70
71 namespace {
72
73     class XMLMetadataImpl : public ReloadableXMLFileImpl
74     {
75     public:
76         XMLMetadataImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { init(); }
77         XMLMetadataImpl(const DOMElement* e) : ReloadableXMLFileImpl(e) { init(); }
78         void init();
79         ~XMLMetadataImpl();
80     
81         class ContactPerson : public IContactPerson
82         {
83         public:
84             ContactPerson(const DOMElement* e);
85             ~ContactPerson() {}
86         
87             ContactType getType() const { return m_type; }
88             const char* getCompany() const { return NULL; }
89             const char* getName() const { return m_name.get(); }
90             Iterator<string> getEmails() const { return m_emails; }
91             Iterator<string> getTelephones() const { return EMPTY(string); }
92             const DOMElement* getElement() const { return m_root; }
93         
94         private:
95             const DOMElement* m_root;
96             ContactType m_type;
97             auto_ptr_char m_name;
98             vector<string> m_emails;
99         };
100
101         class Provider;
102         
103         class KeyDescriptor : public IKeyDescriptor
104         {
105         public:
106             KeyDescriptor() : m_klist(NULL) {}
107             ~KeyDescriptor() {}
108             
109             KeyUse getUse() const { return signing; }
110             const XMLCh* getEncryptionMethod() const { return NULL; }
111             int getKeySize() const { return 0; }
112             DSIGKeyInfoList* getKeyInfo() const { return &m_klist; }
113             const DOMElement* getElement() const { return NULL; }
114         
115         private:
116             mutable DSIGKeyInfoList m_klist;
117             friend class Provider;
118         };
119         
120         class Role : public virtual IProviderRole
121         {
122         public:
123             Role(const Provider* provider, const DOMElement* e) : m_provider(provider), m_root(e) { }
124             ~Role();
125             
126             // External contract
127             const IProvider* getProvider() const {return m_provider;}
128             Iterator<const XMLCh*> getProtocolSupportEnumeration() const {return m_protocolEnum;}
129             bool hasSupport(const XMLCh* version) const;
130             Iterator<const IKeyDescriptor*> getKeyDescriptors() const {return m_keys;}
131             const IOrganization* getOrganization() const {return NULL;}
132             Iterator<const IContactPerson*> getContacts() const {return m_provider->getContacts();}
133             const char* getErrorURL() const {return m_provider->getErrorURL();}
134             const DOMElement* getElement() const {return m_root;}
135         
136         protected:
137             vector<const XMLCh*> m_protocolEnum;
138
139         private:
140             const Provider* m_provider;
141             const DOMElement* m_root;
142             vector<const IKeyDescriptor*> m_keys;
143             friend class Provider;
144         };
145         
146         class Endpoint : public IEndpoint
147         {
148         public:
149             Endpoint(const XMLCh* binding, const XMLCh* loc) : m_binding(binding), m_location(loc) {}
150             ~Endpoint() {}
151             
152             const XMLCh* getBinding() const { return m_binding; }
153             const XMLCh* getVersion() const { return NULL; }
154             const XMLCh* getLocation() const { return m_location; }
155             const XMLCh* getResponseLocation() const { return NULL; }
156             const DOMElement* getElement() const { return NULL; }
157         
158         private:
159             const XMLCh* m_binding;
160             const XMLCh* m_location;
161         };
162         
163         class SSORole : public Role, public virtual ISSOProviderRole
164         {
165         public:
166             SSORole(const Provider* provider, const DOMElement* e) : Role(provider,e) {}
167             ~SSORole() {}
168             Iterator<const IEndpoint*> getSingleLogoutServices() const {return EMPTY(const IEndpoint*);}
169             Iterator<const IEndpoint*> getManageNameIdentifierServices() const {return EMPTY(const IEndpoint*);}
170         };
171         
172         class IDPRole : public SSORole, public virtual IIDPProviderRole
173         {
174         public:
175             IDPRole(const Provider* provider, const DOMElement* e) : SSORole(provider,e) {m_protocolEnum.push_back(::XML::SHIB_NS);}
176             ~IDPRole() {}
177             Iterator<const IEndpoint*> getSingleSignOnServices() const {return m_pepv;}
178             Iterator<const IEndpoint*> getNameIdentifierMappingServices() const {return EMPTY(const IEndpoint*);}
179         
180         private:
181             vector<Endpoint> m_epv;
182             vector<const IEndpoint*> m_pepv;
183             friend class Provider;
184         };
185
186         class AARole : public Role, public virtual IAttributeAuthorityRole
187         {
188         public:
189             AARole(const Provider* provider, const DOMElement* e) : Role(provider,e) {m_protocolEnum.push_back(saml::XML::SAMLP_NS);}
190             ~AARole() {}
191             Iterator<const IEndpoint*> getAttributeServices() const {return m_pepv;}
192             Iterator<const SAMLAttributeDesignator*> getAttributeDesignators() const {return EMPTY(const SAMLAttributeDesignator*);}
193         
194         private:
195             vector<Endpoint> m_epv;
196             vector<const IEndpoint*> m_pepv;
197             friend class Provider;
198         };
199     
200         class Provider : public IProvider
201         {
202         public:
203             Provider(const DOMElement* e);
204             ~Provider();
205         
206             // External contract
207             const XMLCh* getId() const {return m_id;}
208             Iterator<const XMLCh*> getGroups() const {return m_groups;}
209             const IOrganization* getOrganization() const {return NULL;}
210             Iterator<const IContactPerson*> getContacts() const {return m_contacts;}
211             Iterator<const IProviderRole*> getRoles() const {return m_roles;}
212             const DOMElement* getElement() const {return m_root;}
213             Iterator<std::pair<const XMLCh*,bool> > getSecurityDomains() const {return m_domains;}
214
215             // Used internally
216             const char* getErrorURL() const {return m_errorURL.get();}
217         private:
218             friend class XMLMetadataImpl;
219             const XMLCh* m_id;
220             const DOMElement* m_root;
221             auto_ptr_char m_errorURL;
222             vector<const IContactPerson*> m_contacts;
223             vector<const IProviderRole*> m_roles;
224             IDPRole* m_IDP;
225             AARole* m_AA;
226             vector<pair<const XMLCh*,bool> > m_domains;
227             vector<const XMLCh*> m_groups;
228         };
229
230     #ifdef HAVE_GOOD_STL
231         typedef map<xstring,Provider*> sitemap_t;
232     #else
233         typedef map<string,Provider*> sitemap_t;
234     #endif
235         sitemap_t m_sites;
236     };
237
238     class XMLMetadata : public IMetadata, public ReloadableXMLFile
239     {
240     public:
241         XMLMetadata(const DOMElement* e) : ReloadableXMLFile(e) {}
242         ~XMLMetadata() {}
243
244         const IProvider* lookup(const XMLCh* providerId) const;
245         
246     protected:
247         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
248         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
249     };
250 }
251
252 IPlugIn* XMLMetadataFactory(const DOMElement* e)
253 {
254     XMLMetadata* m=new XMLMetadata(e);
255     try {
256         m->getImplementation();
257     }
258     catch (...) {
259         delete m;
260         throw;
261     }
262     return m;    
263 }
264
265 ReloadableXMLFileImpl* XMLMetadata::newImplementation(const DOMElement* e, bool first) const
266 {
267     return new XMLMetadataImpl(e);
268 }
269
270 ReloadableXMLFileImpl* XMLMetadata::newImplementation(const char* pathname, bool first) const
271 {
272     return new XMLMetadataImpl(pathname);
273 }
274
275 XMLMetadataImpl::Role::~Role()
276 {
277     for (vector<const IKeyDescriptor*>::iterator i=m_keys.begin(); i!=m_keys.end(); i++)
278         delete const_cast<IKeyDescriptor*>(*i);
279 }
280
281 bool XMLMetadataImpl::Role::hasSupport(const XMLCh* version) const
282 {
283     Iterator<const XMLCh*> i(m_protocolEnum);
284     while (i.hasNext()) {
285         if (!XMLString::compareString(version,i.next()))
286             return true;
287     }
288     return false;
289 }
290
291 XMLMetadataImpl::ContactPerson::ContactPerson(const DOMElement* e) : m_root(e), m_name(e->getAttributeNS(NULL,SHIB_L(Name)))
292 {
293     ContactPerson::ContactType type;
294     if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(technical)))
295         m_type=IContactPerson::technical;
296     else if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(support)))
297         type=IContactPerson::support;
298     else if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(administrative)))
299         type=IContactPerson::administrative;
300     else if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(billing)))
301         type=IContactPerson::billing;
302     else if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(other)))
303         type=IContactPerson::other;
304     
305     auto_ptr_char temp(e->getAttributeNS(NULL,SHIB_L(Email)));
306     if (temp.get())
307         m_emails.push_back(temp.get());
308 }
309
310 XMLMetadataImpl::Provider::Provider(const DOMElement* e) : m_root(e), m_IDP(NULL), m_AA(NULL),
311     m_id(e->getAttributeNS(NULL,SHIB_L(Name))), m_errorURL(e->getAttributeNS(NULL,SHIB_L(ErrorURL)))
312 {
313     // Record all the SiteGroups containing this site.
314     DOMNode* group=e->getParentNode();
315     while (group && group->getNodeType()==DOMNode::ELEMENT_NODE) {
316         m_groups.push_back(static_cast<DOMElement*>(group)->getAttributeNS(NULL,SHIB_L(Name)));
317         group=group->getParentNode();
318     }
319
320     DOMElement* child=saml::XML::getFirstChildElement(e);
321     while (child) {
322         // Process the various kinds of OriginSite children that we care about...
323         if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(Contact))) {
324             m_contacts.push_back(new ContactPerson(child));
325         }
326         else if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(HandleService))) {
327             // Create the IDP role if needed.
328             if (!m_IDP) {
329                 m_IDP=new IDPRole(this, child);
330                 m_IDP->m_keys.push_back(new KeyDescriptor());
331             }
332             m_roles.push_back(m_IDP);
333             
334             // Manufacture an endpoint for this role.
335             m_IDP->m_epv.push_back(Endpoint(::XML::SHIB_NS,child->getAttributeNS(NULL,SHIB_L(Location))));
336             m_IDP->m_pepv.push_back(&(m_IDP->m_epv.back()));
337
338             // We're going to "mock up" a KeyInfo that contains the specified Name as KeyName.
339             DOMElement* kne=e->getOwnerDocument()->createElementNS(saml::XML::XMLSIG_NS,SHIB_L(KeyName));
340             kne->appendChild(e->getOwnerDocument()->createTextNode(child->getAttributeNS(NULL,SHIB_L(Name))));
341             KeyDescriptor* kd=const_cast<KeyDescriptor*>(static_cast<const KeyDescriptor*>(m_IDP->m_keys.back()));
342             if (!kd->m_klist.addXMLKeyInfo(kne))
343                 throw MetadataException("Provider::Provider() unable to mock up ds:KeyName");
344         }
345         else if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(AttributeAuthority))) {
346             // Create the AA role if needed.
347             if (!m_AA) {
348                 m_AA=new AARole(this, child);
349                 m_AA->m_keys.push_back(new KeyDescriptor());
350             }
351             m_roles.push_back(m_AA);
352             
353             // Manufacture an endpoint for this role.
354             m_AA->m_epv.push_back(Endpoint(SAMLBinding::SAML_SOAP_HTTPS,child->getAttributeNS(NULL,SHIB_L(Location))));
355             m_AA->m_pepv.push_back(&(m_AA->m_epv.back()));
356
357             // We're going to "mock up" a KeyInfo that contains the specified Name as KeyName.
358             DOMElement* kne=e->getOwnerDocument()->createElementNS(saml::XML::XMLSIG_NS,SHIB_L(KeyName));
359             kne->appendChild(e->getOwnerDocument()->createTextNode(child->getAttributeNS(NULL,SHIB_L(Name))));
360             KeyDescriptor* kd=const_cast<KeyDescriptor*>(static_cast<const KeyDescriptor*>(m_AA->m_keys.back()));
361             if (!kd->m_klist.addXMLKeyInfo(kne))
362                 throw MetadataException("Provider::Provider() unable to mock up ds:KeyName");
363         }
364         else if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(Domain))) {
365             const XMLCh* dom=child->getFirstChild()->getNodeValue();
366             if (dom && *dom) {
367                 static const XMLCh one[]={ chDigit_1, chNull };
368                 static const XMLCh tru[]={ chLatin_t, chLatin_r, chLatin_u, chLatin_e, chNull };
369                 const XMLCh* regexp=child->getAttributeNS(NULL,SHIB_L(regexp));
370                 bool flag=(!XMLString::compareString(regexp,one) || !XMLString::compareString(regexp,tru));
371                 m_domains.push_back(pair<const XMLCh*,bool>(dom,flag));
372             }
373         }
374         child = saml::XML::getNextSiblingElement(child);
375     }
376 }
377
378 XMLMetadataImpl::Provider::~Provider()
379 {
380     for (vector<const IContactPerson*>::iterator i=m_contacts.begin(); i!=m_contacts.end(); i++)
381         delete const_cast<IContactPerson*>(*i);
382     delete m_IDP;
383     delete m_AA;
384 }
385
386 void XMLMetadataImpl::init()
387 {
388     NDC ndc("XMLMetadataImpl");
389     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLMetadataImpl");
390
391     try
392     {
393         if (!saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(SiteGroup))) {
394             log.error("Construction requires a valid site file: (shib:SiteGroup as root element)");
395             throw MetadataException("Construction requires a valid site file: (shib:SiteGroup as root element)");
396         }
397
398         // Loop over the OriginSite elements.
399         DOMNodeList* nlist = m_root->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(OriginSite));
400         for (int i=0; nlist && i<nlist->getLength(); i++) {
401             const XMLCh* os_name=static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIB_L(Name));
402             if (!os_name || !*os_name)
403                 continue;
404
405             Provider* p = new Provider(static_cast<DOMElement*>(nlist->item(i)));
406 #ifdef HAVE_GOOD_STL
407             m_sites[os_name]=p;
408 #else
409             auto_ptr_char os_name2(os_name);
410             m_sites[os_name2.get()]=p;
411 #endif
412         }
413     }
414     catch (SAMLException& e)
415     {
416         log.errorStream() << "Error while parsing site configuration: " << e.what() << CategoryStream::ENDLINE;
417         for (sitemap_t::iterator i=m_sites.begin(); i!=m_sites.end(); i++)
418             delete i->second;
419         throw;
420     }
421     catch (...)
422     {
423         log.error("Unexpected error while parsing site configuration");
424         for (sitemap_t::iterator i=m_sites.begin(); i!=m_sites.end(); i++)
425             delete i->second;
426         throw;
427     }
428 }
429
430 XMLMetadataImpl::~XMLMetadataImpl()
431 {
432     for (sitemap_t::iterator i=m_sites.begin(); i!=m_sites.end(); i++)
433         delete i->second;
434 }
435
436 const IProvider* XMLMetadata::lookup(const XMLCh* providerId) const
437 {
438     XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
439 #ifdef HAVE_GOOD_STL
440     XMLMetadataImpl::sitemap_t::const_iterator i=impl->m_sites.find(providerId);
441 #else
442     auto_ptr_char temp(providerId);
443     XMLMetadataImpl::sitemap_t::const_iterator i=impl->m_sites.find(temp.get());
444 #endif
445     return (i==impl->m_sites.end()) ? NULL : i->second;
446 }