2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
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.
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
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.
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.
50 /* XMLMetadata.cpp - a metadata implementation that uses an XML-based registry
60 #include <sys/types.h>
63 #include <log4cpp/Category.hh>
64 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
66 using namespace shibboleth;
68 using namespace log4cpp;
73 class XMLMetadataImpl : public ReloadableXMLFileImpl
76 XMLMetadataImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { init(); }
77 XMLMetadataImpl(const DOMElement* e) : ReloadableXMLFileImpl(e) { init(); }
81 class ContactPerson : public IContactPerson
84 ContactPerson(const DOMElement* e);
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; }
95 const DOMElement* m_root;
97 auto_ptr<char> m_name;
98 vector<string> m_emails;
103 class KeyDescriptor : public IKeyDescriptor
106 KeyDescriptor() : m_klist(NULL) {}
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; }
116 mutable DSIGKeyInfoList m_klist;
117 friend class Provider;
120 class Role : public virtual IProviderRole
123 Role(const Provider* provider, const DOMElement* e) : m_provider(provider), m_root(e) { }
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;}
137 vector<const XMLCh*> m_protocolEnum;
140 const Provider* m_provider;
141 const DOMElement* m_root;
142 vector<const IKeyDescriptor*> m_keys;
143 friend class Provider;
146 class Endpoint : public IEndpoint
149 Endpoint(const XMLCh* binding, const XMLCh* loc) : m_binding(binding), m_location(loc) {}
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; }
159 const XMLCh* m_binding;
160 const XMLCh* m_location;
163 class SSORole : public Role, public virtual ISSOProviderRole
166 SSORole(const Provider* provider, const DOMElement* e) : Role(provider,e) {}
168 Iterator<const IEndpoint*> getSingleLogoutServices() const {return EMPTY(const IEndpoint*);}
169 Iterator<const IEndpoint*> getManageNameIdentifierServices() const {return EMPTY(const IEndpoint*);}
172 class IDPRole : public SSORole, public virtual IIDPProviderRole
175 IDPRole(const Provider* provider, const DOMElement* e) : SSORole(provider,e) {m_protocolEnum.push_back(::XML::SHIB_NS);}
177 Iterator<const IEndpoint*> getSingleSignOnServices() const {return m_pepv;}
178 Iterator<const IEndpoint*> getNameIdentifierMappingServices() const {return EMPTY(const IEndpoint*);}
181 vector<Endpoint> m_epv;
182 vector<const IEndpoint*> m_pepv;
183 friend class Provider;
186 class AARole : public Role, public virtual IAttributeAuthorityRole
189 AARole(const Provider* provider, const DOMElement* e) : Role(provider,e) {m_protocolEnum.push_back(saml::XML::SAMLP_NS);}
191 Iterator<const IEndpoint*> getAttributeServices() const {return m_pepv;}
192 Iterator<const SAMLAttributeDesignator*> getAttributeDesignators() const {return EMPTY(const SAMLAttributeDesignator*);}
195 vector<Endpoint> m_epv;
196 vector<const IEndpoint*> m_pepv;
197 friend class Provider;
200 class Provider : public IProvider
203 Provider(const DOMElement* e);
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;}
216 const char* getErrorURL() const {return m_errorURL.get();}
218 friend class XMLMetadataImpl;
220 const DOMElement* m_root;
221 auto_ptr_char m_errorURL;
222 vector<const IContactPerson*> m_contacts;
223 vector<const IProviderRole*> m_roles;
226 vector<pair<const XMLCh*,bool> > m_domains;
227 vector<const XMLCh*> m_groups;
231 typedef map<xstring,Provider*> sitemap_t;
233 typedef map<string,Provider*> sitemap_t;
238 class XMLMetadata : public IMetadata, public ReloadableXMLFile
241 XMLMetadata(const DOMElement* e) : ReloadableXMLFile(e) {}
244 const IProvider* lookup(const XMLCh* providerId) const;
247 virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
248 virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
252 IPlugIn* XMLMetadataFactory(const DOMElement* e)
254 XMLMetadata* m=new XMLMetadata(e);
256 m->getImplementation();
265 ReloadableXMLFileImpl* XMLMetadata::newImplementation(const DOMElement* e, bool first) const
267 return new XMLMetadataImpl(e);
270 ReloadableXMLFileImpl* XMLMetadata::newImplementation(const char* pathname, bool first) const
272 return new XMLMetadataImpl(pathname);
275 XMLMetadataImpl::Role::~Role()
277 for (vector<const IKeyDescriptor*>::iterator i=m_keys.begin(); i!=m_keys.end(); i++)
278 delete const_cast<IKeyDescriptor*>(*i);
281 bool XMLMetadataImpl::Role::hasSupport(const XMLCh* version) const
283 Iterator<const XMLCh*> i(m_protocolEnum);
284 while (i.hasNext()) {
285 if (!XMLString::compareString(version,i.next()))
291 XMLMetadataImpl::ContactPerson::ContactPerson(const DOMElement* e)
292 : m_root(e), m_name(toUTF8(e->getAttributeNS(NULL,SHIB_L(Name))))
294 ContactPerson::ContactType type;
295 if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(technical)))
296 m_type=IContactPerson::technical;
297 else if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(support)))
298 type=IContactPerson::support;
299 else if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(administrative)))
300 type=IContactPerson::administrative;
301 else if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(billing)))
302 type=IContactPerson::billing;
303 else if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(Type)),SHIB_L(other)))
304 type=IContactPerson::other;
306 if (e->hasAttributeNS(NULL,SHIB_L(Email))) {
307 auto_ptr<char> temp(toUTF8(e->getAttributeNS(NULL,SHIB_L(Email))));
309 m_emails.push_back(temp.get());
313 XMLMetadataImpl::Provider::Provider(const DOMElement* e) : m_root(e), m_IDP(NULL), m_AA(NULL),
314 m_id(e->getAttributeNS(NULL,SHIB_L(Name))), m_errorURL(e->getAttributeNS(NULL,SHIB_L(ErrorURL)))
316 // Record all the SiteGroups containing this site.
317 DOMNode* group=e->getParentNode();
318 while (group && group->getNodeType()==DOMNode::ELEMENT_NODE) {
319 m_groups.push_back(static_cast<DOMElement*>(group)->getAttributeNS(NULL,SHIB_L(Name)));
320 group=group->getParentNode();
323 DOMElement* child=saml::XML::getFirstChildElement(e);
325 // Process the various kinds of OriginSite children that we care about...
326 if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(Contact))) {
327 m_contacts.push_back(new ContactPerson(child));
329 else if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(HandleService))) {
330 // Create the IDP role if needed.
332 m_IDP=new IDPRole(this, child);
333 m_IDP->m_keys.push_back(new KeyDescriptor());
335 m_roles.push_back(m_IDP);
337 // Manufacture an endpoint for this role.
338 m_IDP->m_epv.push_back(Endpoint(::XML::SHIB_NS,child->getAttributeNS(NULL,SHIB_L(Location))));
339 m_IDP->m_pepv.push_back(&(m_IDP->m_epv.back()));
341 // We're going to "mock up" a KeyInfo that contains the specified Name as KeyName.
342 DOMElement* kne=e->getOwnerDocument()->createElementNS(saml::XML::XMLSIG_NS,SHIB_L(KeyName));
343 kne->appendChild(e->getOwnerDocument()->createTextNode(child->getAttributeNS(NULL,SHIB_L(Name))));
344 KeyDescriptor* kd=const_cast<KeyDescriptor*>(static_cast<const KeyDescriptor*>(m_IDP->m_keys.back()));
345 if (!kd->m_klist.addXMLKeyInfo(kne))
346 throw MetadataException("Provider::Provider() unable to mock up ds:KeyName");
348 else if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(AttributeAuthority))) {
349 // Create the AA role if needed.
351 m_AA=new AARole(this, child);
352 m_AA->m_keys.push_back(new KeyDescriptor());
354 m_roles.push_back(m_AA);
356 // Manufacture an endpoint for this role.
357 m_AA->m_epv.push_back(Endpoint(SAMLBinding::SAML_SOAP_HTTPS,child->getAttributeNS(NULL,SHIB_L(Location))));
358 m_AA->m_pepv.push_back(&(m_AA->m_epv.back()));
360 // We're going to "mock up" a KeyInfo that contains the specified Name as KeyName.
361 DOMElement* kne=e->getOwnerDocument()->createElementNS(saml::XML::XMLSIG_NS,SHIB_L(KeyName));
362 kne->appendChild(e->getOwnerDocument()->createTextNode(child->getAttributeNS(NULL,SHIB_L(Name))));
363 KeyDescriptor* kd=const_cast<KeyDescriptor*>(static_cast<const KeyDescriptor*>(m_AA->m_keys.back()));
364 if (!kd->m_klist.addXMLKeyInfo(kne))
365 throw MetadataException("Provider::Provider() unable to mock up ds:KeyName");
367 else if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(Domain))) {
368 const XMLCh* dom=child->getFirstChild()->getNodeValue();
370 static const XMLCh one[]={ chDigit_1, chNull };
371 static const XMLCh tru[]={ chLatin_t, chLatin_r, chLatin_u, chLatin_e, chNull };
372 const XMLCh* regexp=child->getAttributeNS(NULL,SHIB_L(regexp));
373 bool flag=(!XMLString::compareString(regexp,one) || !XMLString::compareString(regexp,tru));
374 m_domains.push_back(pair<const XMLCh*,bool>(dom,flag));
377 child = saml::XML::getNextSiblingElement(child);
381 XMLMetadataImpl::Provider::~Provider()
383 for (vector<const IContactPerson*>::iterator i=m_contacts.begin(); i!=m_contacts.end(); i++)
384 delete const_cast<IContactPerson*>(*i);
389 void XMLMetadataImpl::init()
391 NDC ndc("XMLMetadataImpl");
392 Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLMetadataImpl");
396 if (!saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(SiteGroup))) {
397 log.error("Construction requires a valid site file: (shib:SiteGroup as root element)");
398 throw MetadataException("Construction requires a valid site file: (shib:SiteGroup as root element)");
401 // Loop over the OriginSite elements.
402 DOMNodeList* nlist = m_root->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(OriginSite));
403 for (int i=0; nlist && i<nlist->getLength(); i++) {
404 const XMLCh* os_name=static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIB_L(Name));
405 if (!os_name || !*os_name)
408 Provider* p = new Provider(static_cast<DOMElement*>(nlist->item(i)));
412 auto_ptr_char os_name2(os_name);
413 m_sites[os_name2.get()]=p;
417 catch (SAMLException& e)
419 log.errorStream() << "Error while parsing site configuration: " << e.what() << CategoryStream::ENDLINE;
420 for (sitemap_t::iterator i=m_sites.begin(); i!=m_sites.end(); i++)
426 log.error("Unexpected error while parsing site configuration");
427 for (sitemap_t::iterator i=m_sites.begin(); i!=m_sites.end(); i++)
433 XMLMetadataImpl::~XMLMetadataImpl()
435 for (sitemap_t::iterator i=m_sites.begin(); i!=m_sites.end(); i++)
439 const IProvider* XMLMetadata::lookup(const XMLCh* providerId) const
441 XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
443 XMLMetadataImpl::sitemap_t::const_iterator i=impl->m_sites.find(providerId);
445 auto_ptr_char temp(providerId);
446 XMLMetadataImpl::sitemap_t::const_iterator i=impl->m_sites.find(temp.get());
448 return (i==impl->m_sites.end()) ? NULL : i->second;