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 /* XMLCredentials.cpp - a credentials implementation that uses an XML file
60 #include <sys/types.h>
63 #include <log4cpp/Category.hh>
64 #include <xercesc/framework/URLInputSource.hpp>
65 #include <xercesc/util/regx/RegularExpression.hpp>
66 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
68 using namespace shibboleth;
70 using namespace log4cpp;
73 namespace shibboleth {
75 class XMLCredentialsImpl : public ReloadableXMLFileImpl
78 XMLCredentialsImpl(const char* pathname);
79 ~XMLCredentialsImpl();
81 typedef map<string,ICredResolver*> resolvermap_t;
82 resolvermap_t m_resolverMap;
86 KeyUse(resolvermap_t& resolverMap, const XMLCh* keyref, const XMLCh* certref=NULL);
89 ICredResolver* m_cert;
90 vector<pair<const XMLCh*,bool> > m_relying;
93 vector<KeyUse*> m_keyuses;
94 typedef multimap<pair<const XMLCh*,bool>,KeyUse*> BindingMap;
95 BindingMap m_bindings;
98 class XMLCredentials : public ICredentials, public ReloadableXMLFile
101 XMLCredentials(const char* pathname) : ReloadableXMLFile(pathname) {}
104 bool attach(const XMLCh* subject, const ISite* relyingParty, SSL_CTX* ctx) const;
107 virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const;
112 extern "C" ICredentials* XMLCredentialsFactory(const char* source)
114 XMLCredentials* creds=new XMLCredentials(source);
117 creds->getImplementation();
127 ReloadableXMLFileImpl* XMLCredentials::newImplementation(const char* pathname) const
129 return new XMLCredentialsImpl(pathname);
132 XMLCredentialsImpl::KeyUse::KeyUse(resolvermap_t& resolverMap, const XMLCh* keyref, const XMLCh* certref) : m_key(NULL), m_cert(NULL)
134 auto_ptr<char> temp(XMLString::transcode(keyref));
135 resolvermap_t::iterator i=resolverMap.find(temp.get());
136 if (i==resolverMap.end())
137 throw MetadataException(string("XMLCredentialsImpl::KeyUse::KeyUse() unable to find valid key reference (") + temp.get() + ")");
140 if (certref && *certref)
142 auto_ptr<char> temp2(XMLString::transcode(certref));
143 i=resolverMap.find(temp2.get());
144 if (i==resolverMap.end())
145 throw MetadataException(string("XMLCredentialsImpl::KeyUse::KeyUse() unable to find valid certificate reference (") + temp2.get() + ")");
150 XMLCredentialsImpl::XMLCredentialsImpl(const char* pathname) : ReloadableXMLFileImpl(pathname)
152 NDC ndc("XMLCredentialsImpl");
153 Category& log=Category::getInstance(SHIB_LOGCAT".XMLCredentialsImpl");
157 DOMElement* e = m_doc->getDocumentElement();
158 if (XMLString::compareString(XML::SHIB_NS,e->getNamespaceURI()) ||
159 XMLString::compareString(SHIB_L(Credentials),e->getLocalName()))
161 log.error("Construction requires a valid creds file: (shib:Credentials as root element)");
162 throw MetadataException("Construction requires a valid creds file: (shib:Credentials as root element)");
165 // Process everything up to the first shib:KeyUse as a resolver.
166 DOMElement* child=saml::XML::getFirstChildElement(e);
167 while (!saml::XML::isElementNamed(child,XML::SHIB_NS,SHIB_L(KeyUse)))
170 auto_ptr<char> id(XMLString::transcode(child->getAttributeNS(NULL,SHIB_L(Id))));
172 if (saml::XML::isElementNamed(child,XML::SHIB_NS,SHIB_L(FileCredResolver)))
173 cr_type="edu.internet2.middleware.shibboleth.creds.provider.FileCredResolver";
174 else if (saml::XML::isElementNamed(child,saml::XML::XMLSIG_NS,L(KeyInfo)))
175 cr_type="edu.internet2.middleware.shibboleth.creds.provider.KeyInfoResolver";
176 else if (saml::XML::isElementNamed(child,XML::SHIB_NS,SHIB_L(CustomCredResolver)))
178 auto_ptr_char c(child->getAttributeNS(NULL,SHIB_L(Class)));
182 if (!cr_type.empty())
186 ICredResolver* cr=ShibConfig::getConfig().newCredResolver(cr_type.c_str(),child);
187 m_resolverMap[id.get()]=cr;
189 catch (SAMLException& e)
191 log.error("failed to instantiate credential resolver (%s): %s", id.get(), e.what());
195 child=saml::XML::getNextSiblingElement(child);
198 // Now loop over the KeyUse elements.
199 while (child && saml::XML::isElementNamed(child,XML::SHIB_NS,SHIB_L(KeyUse)))
201 KeyUse* ku = new KeyUse(
203 child->getAttributeNS(NULL,SHIB_L(KeyRef)),
204 child->getAttributeNS(NULL,SHIB_L(CertificateRef))
206 m_keyuses.push_back(ku);
208 // Pull in the relying parties.
209 DOMNodeList* parties=child->getElementsByTagNameNS(XML::SHIB_NS,SHIB_L(RelyingParty));
211 while (parties && m<parties->getLength())
213 const XMLCh* name=parties->item(m)->getFirstChild()->getNodeValue();
216 static const XMLCh one[]={ chDigit_1, chNull };
217 static const XMLCh tru[]={ chLatin_t, chLatin_r, chLatin_u, chLatin_e, chNull };
219 static_cast<DOMElement*>(parties->item(m))->getAttributeNS(NULL,SHIB_L(regexp));
220 bool flag=(!XMLString::compareString(regexp,one) || !XMLString::compareString(regexp,tru));
221 ku->m_relying.push_back(pair<const XMLCh*,bool>(name,flag));
225 // If no RelyingParties, this is a catch-all binding.
227 ku->m_relying.push_back(pair<const XMLCh*,bool>(NULL,false));
229 // Now map the subjects to the credentials.
230 DOMNodeList* subs=child->getElementsByTagNameNS(XML::SHIB_NS,L(Subject));
232 while (subs && l<subs->getLength())
234 const XMLCh* name=subs->item(l)->getFirstChild()->getNodeValue();
237 static const XMLCh one[]={ chDigit_1, chNull };
238 static const XMLCh tru[]={ chLatin_t, chLatin_r, chLatin_u, chLatin_e, chNull };
240 static_cast<DOMElement*>(subs->item(l))->getAttributeNS(NULL,SHIB_L(regexp));
241 bool flag=(!XMLString::compareString(regexp,one) || !XMLString::compareString(regexp,tru));
242 m_bindings.insert(BindingMap::value_type(pair<const XMLCh*,bool>(name,flag),ku));
246 // If no Subjects, this is a catch-all binding.
248 m_bindings.insert(BindingMap::value_type(pair<const XMLCh*,bool>(NULL,false),ku));
250 child=saml::XML::getNextSiblingElement(child);
253 catch (SAMLException& e)
255 log.errorStream() << "Error while parsing creds configuration: " << e.what() << CategoryStream::ENDLINE;
256 for (vector<KeyUse*>::iterator i=m_keyuses.begin(); i!=m_keyuses.end(); i++)
258 for (resolvermap_t::iterator j=m_resolverMap.begin(); j!=m_resolverMap.end(); j++)
267 log.error("Unexpected error while parsing creds configuration");
268 for (vector<KeyUse*>::iterator i=m_keyuses.begin(); i!=m_keyuses.end(); i++)
270 for (resolvermap_t::iterator j=m_resolverMap.begin(); j!=m_resolverMap.end(); j++)
279 XMLCredentialsImpl::~XMLCredentialsImpl()
281 for (vector<KeyUse*>::iterator i=m_keyuses.begin(); i!=m_keyuses.end(); i++)
283 for (resolvermap_t::iterator j=m_resolverMap.begin(); j!=m_resolverMap.end(); j++)
287 bool XMLCredentials::attach(const XMLCh* subject, const ISite* relyingParty, SSL_CTX* ctx) const
291 // Use the matching bindings.
292 XMLCredentialsImpl* impl=dynamic_cast<XMLCredentialsImpl*>(getImplementation());
293 for (XMLCredentialsImpl::BindingMap::const_iterator i=impl->m_bindings.begin(); i!=impl->m_bindings.end(); i++)
297 if (i->first.first==NULL) // catch-all entry
301 else if (i->first.second) // regexp
305 RegularExpression re(i->first.first);
306 if (re.matches(subject))
309 catch (XMLException& ex)
311 auto_ptr<char> tmp(XMLString::transcode(ex.getMessage()));
312 Category& log=Category::getInstance(SHIB_LOGCAT".XMLCredentials");
313 log.errorStream() << "caught exception while parsing regular expression: " << tmp.get()
314 << CategoryStream::ENDLINE;
317 else if (!XMLString::compareString(subject,i->first.first))
324 // See if the relying party applies...
326 for (vector<pair<const XMLCh*,bool> >::const_iterator j=i->second->m_relying.begin(); j!=i->second->m_relying.end(); j++)
328 if (j->first==NULL) // catch-all entry
332 else if (j->second) // regexp
336 RegularExpression re(j->first);
337 if (re.matches(relyingParty->getName()))
341 Iterator<const XMLCh*> groups=relyingParty->getGroups();
342 while (!match && groups.hasNext())
343 if (re.matches(groups.next()))
347 catch (XMLException& ex)
349 auto_ptr<char> tmp(XMLString::transcode(ex.getMessage()));
350 Category& log=Category::getInstance(SHIB_LOGCAT".XMLCredentials");
351 log.errorStream() << "caught exception while parsing regular expression: " << tmp.get()
352 << CategoryStream::ENDLINE;
355 else if (!XMLString::compareString(relyingParty->getName(),j->first))
361 Iterator<const XMLCh*> groups=relyingParty->getGroups();
362 while (!match && groups.hasNext())
363 if (!XMLString::compareString(groups.next(),j->first))
373 i->second->m_key->resolveKey(ctx);
374 if (i->second->m_cert)
375 i->second->m_cert->resolveCert(ctx);
377 if (!SSL_CTX_check_private_key(ctx))
380 throw MetadataException("XMLCredentials::attach() found mismatch between the private key and certificate used");
385 catch (SAMLException& e)
387 Category& log=Category::getInstance(SHIB_LOGCAT".XMLCredentials");
388 log.error("caught a SAML exception while attaching credentials: %s", e.what());
392 Category& log=Category::getInstance(SHIB_LOGCAT".XMLCredentials");
393 log.error("caught an unknown exception while attaching credentials");