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 /* XMLRevocation.cpp - a revocation implementation that uses an XML file
60 #include <sys/types.h>
63 #include <openssl/err.h>
65 #include <log4cpp/Category.hh>
67 using namespace shibboleth;
69 using namespace log4cpp;
74 class XMLRevocationImpl : public ReloadableXMLFileImpl
77 XMLRevocationImpl(const char* pathname) : ReloadableXMLFileImpl(pathname), m_wildcard(NULL) { init(); }
78 XMLRevocationImpl(const DOMElement* e) : ReloadableXMLFileImpl(e), m_wildcard(NULL) { init(); }
88 vector<const XMLCh*> m_subjects;
93 vector<KeyAuthority*> m_keyauths;
94 KeyAuthority* m_wildcard;
96 typedef map<xstring,KeyAuthority*> AuthMap;
101 class XMLRevocation : public IRevocation, public ReloadableXMLFile
104 XMLRevocation(const DOMElement* e) : ReloadableXMLFile(e) {}
107 Iterator<void*> getRevocationLists(const IProvider* provider, const IProviderRole* role=NULL) const;
110 virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const;
111 virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e) const;
116 extern "C" IRevocation* XMLRevocationFactory(const DOMElement* e)
118 XMLRevocation* r=new XMLRevocation(e);
120 r->getImplementation();
130 ReloadableXMLFileImpl* XMLRevocation::newImplementation(const char* pathname) const
132 return new XMLRevocationImpl(pathname);
135 ReloadableXMLFileImpl* XMLRevocation::newImplementation(const DOMElement* e) const
137 return new XMLRevocationImpl(e);
140 XMLRevocationImpl::KeyAuthority::~KeyAuthority()
142 for (vector<void*>::iterator i=m_crls.begin(); i!=m_crls.end(); i++)
143 X509_CRL_free(reinterpret_cast<X509_CRL*>(*i));
146 void XMLRevocationImpl::init()
148 NDC ndc("XMLRevocationImpl");
149 Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLRevocationImpl");
152 if (!saml::XML::isElementNamed(m_root,::XML::TRUST_NS,SHIB_L(Trust))) {
153 log.error("Construction requires a valid trust file: (trust:Trust as root element)");
154 throw TrustException("Construction requires a valid trust file: (trust:Trust as root element)");
157 // Loop over the KeyAuthority elements.
158 DOMNodeList* nlist=m_root->getElementsByTagNameNS(::XML::TRUST_NS,SHIB_L(KeyAuthority));
159 for (int i=0; nlist && i<nlist->getLength(); i++) {
160 auto_ptr<KeyAuthority> ka(new KeyAuthority());
162 // Very rudimentary, grab up all the in-band X509CRL elements, and flatten into one list.
163 DOMNodeList* crllist=static_cast<DOMElement*>(nlist->item(i))->getElementsByTagNameNS(
164 saml::XML::XMLSIG_NS,SHIB_L(X509CRL)
166 for (int j=0; crllist && j<crllist->getLength(); j++) {
167 auto_ptr_char blob(crllist->item(j)->getFirstChild()->getNodeValue());
168 X509_CRL* x=B64_to_CRL(blob.get());
170 ka->m_crls.push_back(x);
172 log.warn("unable to create CRL from inline X509CRL data");
175 // Now look for externally referenced objects.
176 crllist=static_cast<DOMElement*>(nlist->item(i))->getElementsByTagNameNS(
177 saml::XML::XMLSIG_NS,SHIB_L(RetrievalMethod)
179 for (int k=0; crllist && k<crllist->getLength(); k++) {
180 DOMElement* crl=static_cast<DOMElement*>(crllist->item(k));
181 if (!XMLString::compareString(crl->getAttributeNS(NULL,SHIB_L(Type)),::XML::XMLSIG_RETMETHOD_RAWX509CRL)) {
183 auto_ptr_char fname(crl->getAttributeNS(NULL,SHIB_L(URI)));
184 FILE* f=fopen(fname.get(),"r");
187 d2i_X509_CRL_fp(f,&x);
189 ka->m_crls.push_back(x);
195 log.warn("unable to create CRL from externally referenced X509CRL file");
197 else if (!XMLString::compareString(crl->getAttributeNS(NULL,SHIB_L(Type)),::XML::SHIB_RETMETHOD_PEMX509CRL)) {
200 auto_ptr_char fname(crl->getAttributeNS(NULL,SHIB_L(URI)));
201 FILE* f=fopen(fname.get(),"r");
204 while (x=PEM_read_X509_CRL(f,NULL,NULL,NULL)) {
205 ka->m_crls.push_back(x);
210 log.warn("unable to create CRL from externally referenced X509CRL file");
214 if (ka->m_crls.empty())
216 m_keyauths.push_back(ka.get());
218 // Now map the ds:KeyName values to the list of certs.
220 DOMElement* sub=saml::XML::getFirstChildElement(static_cast<DOMElement*>(nlist->item(i)),saml::XML::XMLSIG_NS,SHIB_L(KeyName));
222 const XMLCh* name=sub->getFirstChild()->getNodeValue();
226 m_map[name]=ka.get();
228 ka->m_subjects.push_back(name);
231 sub=saml::XML::getNextSiblingElement(sub,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
234 // If no Subjects, this is a catch-all binding.
239 log.warn("found multiple wildcard KeyAuthority elements, ignoring all but the first");
244 catch (SAMLException& e) {
245 log.errorStream() << "Error while parsing revocation configuration: " << e.what() << CategoryStream::ENDLINE;
246 for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
251 log.error("Unexpected error while parsing revocation configuration");
252 for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
258 XMLRevocationImpl::~XMLRevocationImpl()
260 for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
264 Iterator<void*> XMLRevocation::getRevocationLists(const IProvider* provider, const IProviderRole* role) const
266 saml::NDC ndc("getRevocationLists");
267 Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLRevocation");
268 XMLRevocationImpl* impl=dynamic_cast<XMLRevocationImpl*>(getImplementation());
270 // Build a list of the names to match. We include any named KeyDescriptors, and the provider ID and its groups.
271 vector<const XMLCh*> names;
273 Iterator<const IKeyDescriptor*> kdlist=role->getKeyDescriptors();
274 while (kdlist.hasNext()) {
275 const IKeyDescriptor* kd=kdlist.next();
276 if (kd->getUse()!=IKeyDescriptor::signing)
278 DSIGKeyInfoList* kilist=kd->getKeyInfo();
279 for (size_t s=0; kilist && s<kilist->getSize(); s++) {
280 const XMLCh* n=kilist->item(s)->getKeyName();
286 names.push_back(provider->getId());
287 Iterator<const XMLCh*> groups=provider->getGroups();
288 while (groups.hasNext())
289 names.push_back(groups.next());
291 // Now check each name.
292 for (vector<const XMLCh*>::const_iterator name=names.begin(); name!=names.end(); name++) {
293 const XMLRevocationImpl::KeyAuthority* kauth=NULL;
295 XMLRevocationImpl::AuthMap::const_iterator c=impl->m_map.find(*name);
296 if (c!=impl->m_map.end()) {
298 if (log.isDebugEnabled()) {
299 auto_ptr_char temp(*name);
300 log.debug("revocation list match on %s",temp.get());
304 // Without a decent STL, we trade-off the transcoding by doing a linear search.
305 for (vector<XMLRevocationImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); keyauths!=impl->m_keyauths.end(); keyauths++) {
306 for (vector<const XMLCh*>::const_iterator subs=keyauths->m_subjects.begin(); subs!=keyauths->m_subjects.end(); subs++) {
307 if (!XMLString::compareString(*name,*subs)) {
309 if (log.isDebugEnabled()) {
310 auto_ptr_char temp(*name);
311 log.debug("revocation list match on %s",temp.get());
318 return kauth->m_crls;
319 else if (impl->m_wildcard)
320 return impl->m_wildcard->m_crls;
323 log.debug("no matching revocation list");