responseKey,responseCerts,assertionKey,assertionCerts);
}
-void ClubShibPOSTProfile::verifySignature(const SAMLSignedObject& obj, const XMLCh* signerName, XSECCryptoKey* knownKey)
+void ClubShibPOSTProfile::verifySignature(
+ const SAMLSignedObject& obj, const IOriginSite* originSite, const XMLCh* signerName, XSECCryptoKey* knownKey
+ )
{
- ShibPOSTProfile::verifySignature(obj,signerName,knownKey);
+ ShibPOSTProfile::verifySignature(obj,originSite,signerName,knownKey);
if (obj.getSignatureAlgorithm()!=SIGNATURE_RSA)
throw TrustException("ClubShibPOSTProfile::verifySignature() requires the RSA signature algorithm");
}
SimpleAttribute.cpp \
XML.cpp \
XMLMetadata.cpp \
+ XMLTrust.cpp \
shib-threads.cpp
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/* Metadata.h - a glue class that interfaces to metadata providers
+/* Metadata.h - glue classes that interface to metadata providers
Scott Cantor
9/27/02
OriginMetadata::OriginMetadata(const XMLCh* site) : m_mapper(NULL), m_site(NULL)
{
ShibInternalConfig& config=dynamic_cast<ShibInternalConfig&>(ShibConfig::getConfig());
- config.m_lock->lock();
for (vector<IMetadata*>::iterator i=config.m_providers.begin(); i!=config.m_providers.end(); i++)
{
(*i)->lock();
}
(*i)->unlock();
}
- config.m_lock->unlock();
}
OriginMetadata::~OriginMetadata()
if (m_mapper)
m_mapper->unlock();
}
+
+Iterator<XSECCryptoX509*> Trust::getCertificates(const XMLCh* subject)
+{
+ if (m_mapper)
+ {
+ m_mapper->unlock();
+ m_mapper=NULL;
+ }
+
+ ShibInternalConfig& config=dynamic_cast<ShibInternalConfig&>(ShibConfig::getConfig());
+ for (vector<ITrust*>::iterator i=config.m_trust_providers.begin(); i!=config.m_trust_providers.end(); i++)
+ {
+ (*i)->lock();
+ Iterator<XSECCryptoX509*> iter=(*i)->getCertificates(subject);
+ if (iter.size())
+ {
+ m_mapper=*i;
+ return iter;
+ }
+ (*i)->unlock();
+ }
+ return EMPTY(XSECCryptoX509*);
+}
+
+bool Trust::validate(const ISite* site, Iterator<XSECCryptoX509*> certs) const
+{
+ bool ret=false;
+ ShibInternalConfig& config=dynamic_cast<ShibInternalConfig&>(ShibConfig::getConfig());
+ for (vector<ITrust*>::iterator i=config.m_trust_providers.begin(); !ret && i!=config.m_trust_providers.end(); i++)
+ {
+ (*i)->lock();
+ ret=(*i)->validate(site,certs);
+ (*i)->unlock();
+ }
+ return ret;
+}
+
+bool Trust::validate(const ISite* site, Iterator<const XMLCh*> certs) const
+{
+ bool ret=false;
+ ShibInternalConfig& config=dynamic_cast<ShibInternalConfig&>(ShibConfig::getConfig());
+ for (vector<ITrust*>::iterator i=config.m_trust_providers.begin(); !ret && i!=config.m_trust_providers.end(); i++)
+ {
+ (*i)->lock();
+ ret=(*i)->validate(site,certs);
+ (*i)->unlock();
+ }
+ return ret;
+}
+
+Trust::~Trust()
+{
+ if (m_mapper)
+ m_mapper->unlock();
+}
#include "internal.h"
#include <log4cpp/Category.hh>
+#include <openssl/err.h>
using namespace saml;
using namespace shibboleth;
return new XMLMetadata(source);
}
+extern "C" ITrust* XMLTrustFactory(const char* source)
+{
+ return new XMLTrust(source);
+}
+
bool ShibInternalConfig::init()
{
saml::NDC ndc("init");
}
}
- m_lock=Mutex::create();
- if (!m_lock)
- {
- Category::getInstance(SHIB_LOGCAT".ShibConfig").fatal("init: failed to create provider lock");
- delete m_AAP;
- return false;
- }
-
regFactory("edu.internet2.middleware.shibboleth.metadata.XML",&XMLMetadataFactory);
+ regFactory("edu.internet2.middleware.shibboleth.trust.XML",&XMLTrustFactory);
return true;
}
{
for (vector<IMetadata*>::iterator i=m_providers.begin(); i!=m_providers.end(); i++)
delete *i;
- delete m_lock;
delete m_AAP;
}
m_metadataFactoryMap[type]=factory;
}
+void ShibInternalConfig::regFactory(const char* type, TrustFactory* factory)
+{
+ if (type && factory)
+ m_trustFactoryMap[type]=factory;
+}
+
void ShibInternalConfig::unregFactory(const char* type)
{
if (type)
+ {
m_metadataFactoryMap.erase(type);
+ m_trustFactoryMap.erase(type);
+ }
}
bool ShibInternalConfig::addMetadata(const char* type, const char* source)
saml::NDC ndc("addMetadata");
bool ret=false;
- m_lock->lock();
try
{
MetadataFactoryMap::const_iterator i=m_metadataFactoryMap.find(type);
ret=true;
}
else
- throw MetadataException("ShibConfig::addMetadata() unable to locate a metadata factory of the requested type");
-
+ {
+ TrustFactoryMap::const_iterator j=m_trustFactoryMap.find(type);
+ if (j!=m_trustFactoryMap.end())
+ {
+ m_trust_providers.push_back((j->second)(source));
+ ret=true;
+ }
+ else
+ throw MetadataException("ShibConfig::addMetadata() unable to locate a metadata factory of the requested type");
+ }
}
catch (SAMLException& e)
{
"failed to add %s provider to system using source '%s': unknown exception", type, source
);
}
- m_lock->unlock();
return ret;
}
{
return g_config;
}
+
+void shibboleth::log_openssl()
+{
+ const char* file;
+ const char* data;
+ int flags,line;
+
+ unsigned long code=ERR_get_error_line_data(&file,&line,&data,&flags);
+ while (code)
+ {
+ Category& log=Category::getInstance("OpenSSL");
+ log.errorStream() << "error code: " << code << " in " << file << ", line " << line << CategoryStream::ENDLINE;
+ if (data && (flags & ERR_TXT_STRING))
+ log.errorStream() << "error data: " << data << CategoryStream::ENDLINE;
+ code=ERR_get_error_line_data(&file,&line,&data,&flags);
+ }
+}
+
+X509* shibboleth::B64_to_X509(const char* buf)
+{
+ BIO* bmem = BIO_new_mem_buf((void*)buf,-1);
+ BIO* b64 = BIO_new(BIO_f_base64());
+ b64 = BIO_push(b64, bmem);
+ X509* x=NULL;
+ d2i_X509_bio(b64,&x);
+ if (!x)
+ log_openssl();
+ BIO_free_all(b64);
+ return x;
+}
#include <ctime>
+#include <openssl/x509v3.h>
+
using namespace shibboleth;
using namespace saml;
using namespace std;
while (certs.hasNext())
{
try {
- verifySignature(*assertion, handleService, certs.next()->clonePublicKey());
+ verifySignature(*assertion, mapper, handleService, certs.next()->clonePublicKey());
bVerified=true;
}
catch (InvalidCryptoException&) {
}
}
if (!bVerified)
- verifySignature(*assertion, handleService);
+ verifySignature(*assertion, mapper, handleService);
}
bVerified=false;
while (certs.hasNext())
{
try {
- verifySignature(*r, handleService, certs.next()->clonePublicKey());
+ verifySignature(*r, mapper, handleService, certs.next()->clonePublicKey());
bVerified=true;
}
catch (InvalidCryptoException&) {
}
}
if (!bVerified)
- verifySignature(*r, handleService);
+ verifySignature(*r, mapper, handleService);
return r.release();
}
return SAMLPOSTProfile::checkReplayCache(a);
}
-void ShibPOSTProfile::verifySignature(const SAMLSignedObject& obj, const XMLCh* signerName, XSECCryptoKey* knownKey)
+void ShibPOSTProfile::verifySignature(
+ const SAMLSignedObject& obj, const IOriginSite* originSite, const XMLCh* signerName, XSECCryptoKey* knownKey
+ )
{
obj.verify(knownKey);
+
+ // If not using a known key, perform additional trust checking on the certificate.
+ if (!knownKey)
+ {
+ vector<const XMLCh*> certs;
+ for (unsigned int i=0; i<obj.getX509CertificateCount(); i++)
+ certs.push_back(obj.getX509Certificate(i));
+
+ // Compare the name in the end entity certificate to the signer's name.
+ auto_ptr<char> temp(XMLString::transcode(certs[0]));
+ X509* x=B64_to_X509(temp.get());
+ if (!x)
+ throw TrustException("ShibPOSTProfile::verifySignature() unable to decode X.509 signing certificate");
+
+ bool match=false;
+ auto_ptr<char> sn(XMLString::transcode(signerName));
+ int extcount=X509_get_ext_count(x);
+ for (int c=0; c<extcount; c++)
+ {
+ X509_EXTENSION* ext=X509_get_ext(x,c);
+ const char* extstr=OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
+ if (!strcmp(extstr,"subjectAltName"))
+ {
+ X509V3_EXT_METHOD* meth=X509V3_EXT_get(ext);
+ if (!meth)
+ break;
+ unsigned char* data=ext->value->data;
+ STACK_OF(CONF_VALUE)* val=meth->i2v(meth,meth->d2i(NULL,&data,ext->value->length),NULL);
+ for (int j=0; j<sk_CONF_VALUE_num(val); j++)
+ {
+ CONF_VALUE* nval=sk_CONF_VALUE_value(val,j);
+ if (!strcmp(nval->name,"DNS") && !strcmp(nval->value,sn.get()))
+ {
+ match=true;
+ break;
+ }
+ }
+ }
+ if (match)
+ break;
+ }
+
+ char data[256];
+ X509_NAME* subj;
+ if (!match && (subj=X509_get_subject_name(x)) &&
+ X509_NAME_get_text_by_NID(subj,NID_commonName,data,256)>0)
+ {
+ data[255]=0;
+#ifdef HAVE_STRCASECMP
+ if (!strcasecmp(data,sn.get()))
+ match=true;
+#else
+ if (!stricmp(data,sn.get()))
+ match=true;
+#endif
+ }
+
+ X509_free(x);
+
+ if (!match)
+ throw TrustException("ShibPOSTProfile::verifySignature() cannot match CN or subjectAltName against signer");
+
+ // Ask the site to determine the trustworthiness of the certificate.
+ originSite->validate(certs);
+ }
}
const XMLCh XML::Literals::OriginSite[]=
{ chLatin_O, chLatin_r, chLatin_i, chLatin_g, chLatin_i, chLatin_n, chLatin_S, chLatin_i, chLatin_t, chLatin_e, chNull };
-const XMLCh XML::Literals::Sites[]=
-{ chLatin_S, chLatin_i, chLatin_t, chLatin_e, chLatin_s, chNull };
+const XMLCh XML::Literals::SiteGroup[]=
+{ chLatin_S, chLatin_i, chLatin_t, chLatin_e, chLatin_G, chLatin_r, chLatin_o, chLatin_u, chLatin_p, chNull };
+
+const XMLCh XML::Literals::KeyAuthority[] =
+{ chLatin_K, chLatin_e, chLatin_y,
+ chLatin_A, chLatin_u, chLatin_t, chLatin_h, chLatin_o, chLatin_r, chLatin_i, chLatin_t, chLatin_y, chNull };
+
+const XMLCh XML::Literals::Trust[] =
+{ chLatin_T, chLatin_r, chLatin_u, chLatin_s, chLatin_t, chNull };
const XMLCh XML::Literals::AnySite[]=
{ chLatin_A, chLatin_n, chLatin_y, chLatin_S, chLatin_i, chLatin_t, chLatin_e, chNull };
auto_ptr<char> m_url;
};
- struct OriginSite : public IOriginSite
+ class OriginSite : public IOriginSite
{
- OriginSite(const XMLCh* errorURL) : m_errorURL(XMLString::transcode(errorURL)) {}
+ public:
+ OriginSite(const XMLCh* name, const XMLCh* errorURL)
+ : m_name(name), m_errorURL(XMLString::transcode(errorURL)) {}
~OriginSite();
-
+
+ const XMLCh* getName() const {return m_name;}
+ Iterator<const XMLCh*> getGroups() const {return m_groups;}
Iterator<const IContactInfo*> getContacts() const {return m_contacts;}
const char* getErrorURL() const {return m_errorURL.get();}
- void validate(XSECCryptoX509* cert) const {}
- void validate(const XMLCh* cert) const {}
+ bool validate(Iterator<XSECCryptoX509*> certs) const {Trust t; return t.validate(this,certs);}
+ bool validate(Iterator<const XMLCh*> certs) const {Trust t; return t.validate(this,certs);}
Iterator<const IAuthority*> getHandleServices() const {return m_handleServices;}
Iterator<const IAuthority*> getAttributeAuthorities() const {return m_attributes;}
Iterator<std::pair<const XMLCh*,bool> > getSecurityDomains() const {return m_domains;}
+ private:
+ friend class XMLMetadataImpl;
+ const XMLCh* m_name;
auto_ptr<char> m_errorURL;
vector<const IContactInfo*> m_contacts;
vector<const IAuthority*> m_handleServices;
vector<const IAuthority*> m_attributes;
vector<pair<const XMLCh*,bool> > m_domains;
+ vector<const XMLCh*> m_groups;
};
std::map<saml::xstring,OriginSite*> m_sites;
DOMElement* e = m_doc->getDocumentElement();
if (XMLString::compareString(XML::SHIB_NS,e->getNamespaceURI()) ||
- XMLString::compareString(XML::Literals::Sites,e->getLocalName()))
+ XMLString::compareString(XML::Literals::SiteGroup,e->getLocalName()))
{
- log.error("Construction requires a valid site file: (shib:Sites as root element)");
- throw MetadataException("Construction requires a valid site file: (shib:Sites as root element)");
+ log.error("Construction requires a valid site file: (shib:SiteGroup as root element)");
+ throw MetadataException("Construction requires a valid site file: (shib:SiteGroup as root element)");
}
// Loop over the OriginSite elements.
continue;
OriginSite* os_obj =
- new OriginSite(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,XML::Literals::ErrorURL));
+ new OriginSite(os_name,static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,XML::Literals::ErrorURL));
m_sites[os_name]=os_obj;
+
+ // Record all the SiteGroups containing this site.
+ DOMNode* group=nlist->item(i)->getParentNode();
+ while (group && group->getNodeType()==DOMNode::ELEMENT_NODE)
+ {
+ os_obj->m_groups.push_back(static_cast<DOMElement*>(group)->getAttributeNS(NULL,XML::Literals::Name));
+ group=group->getParentNode();
+ }
DOMNode* os_child=nlist->item(i)->getFirstChild();
while (os_child)
--- /dev/null
+/*
+ * The Shibboleth License, Version 1.
+ * Copyright (c) 2002
+ * University Corporation for Advanced Internet Development, Inc.
+ * All rights reserved
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution, if any, must include
+ * the following acknowledgment: "This product includes software developed by
+ * the University Corporation for Advanced Internet Development
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
+ * may appear in the software itself, if and wherever such third-party
+ * acknowledgments normally appear.
+ *
+ * Neither the name of Shibboleth nor the names of its contributors, nor
+ * Internet2, nor the University Corporation for Advanced Internet Development,
+ * Inc., nor UCAID may be used to endorse or promote products derived from this
+ * software without specific prior written permission. For written permission,
+ * please contact shibboleth@shibboleth.org
+ *
+ * Products derived from this software may not be called Shibboleth, Internet2,
+ * UCAID, or the University Corporation for Advanced Internet Development, nor
+ * may Shibboleth appear in their name, without prior written permission of the
+ * University Corporation for Advanced Internet Development.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* XMLTrust.h - a trust implementation that uses an XML file
+
+ Scott Cantor
+ 9/27/02
+
+ $History:$
+*/
+
+#include "internal.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <openssl/err.h>
+#include <openssl/x509_vfy.h>
+
+#include <log4cpp/Category.hh>
+#include <xercesc/framework/URLInputSource.hpp>
+#include <xercesc/util/regx/RegularExpression.hpp>
+#include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
+
+using namespace shibboleth;
+using namespace saml;
+using namespace log4cpp;
+using namespace std;
+
+int verify_callback(int ok, X509_STORE_CTX* store)
+{
+ if (!ok)
+ Category::getInstance("OpenSSL").error(X509_verify_cert_error_string(store->error));
+ return ok;
+}
+
+class shibboleth::XMLTrustImpl
+{
+public:
+ XMLTrustImpl(const char* pathname);
+ ~XMLTrustImpl();
+
+ struct KeyAuthority
+ {
+ KeyAuthority() : m_store(NULL) {}
+ ~KeyAuthority();
+ X509_STORE* getX509Store();
+
+ vector<XSECCryptoX509*> m_certs;
+ X509_STORE* m_store;
+ };
+
+ vector<KeyAuthority*> m_keyauths;
+ typedef map<pair<const XMLCh*,bool>,KeyAuthority*> BindingMap;
+ BindingMap m_bindings;
+
+ DOMDocument* m_doc;
+};
+
+X509_STORE* XMLTrustImpl::KeyAuthority::getX509Store()
+{
+ // We cache them once they're built...
+ if (m_store)
+ return m_store;
+
+ NDC ndc("getX509Store");
+
+ // Load the cert vector into a store.
+
+ if (!(m_store=X509_STORE_new()))
+ {
+ log_openssl();
+ return NULL;
+ }
+
+ X509_STORE_set_verify_cb_func(m_store,verify_callback);
+
+ for (vector<XSECCryptoX509*>::const_iterator i=m_certs.begin(); i!=m_certs.end(); i++)
+ {
+ X509* x509=B64_to_X509((*i)->getDEREncodingSB().rawCharBuffer());
+ if (!x509)
+ {
+ X509_STORE_free(m_store);
+ return m_store=NULL;
+ }
+
+ if (!X509_STORE_add_cert(m_store,x509))
+ {
+ log_openssl();
+ X509_free(x509);
+ X509_STORE_free(m_store);
+ return m_store=NULL;
+ }
+ }
+
+ return m_store;
+}
+
+XMLTrustImpl::KeyAuthority::~KeyAuthority()
+{
+ for (vector<XSECCryptoX509*>::iterator i=m_certs.begin(); i!=m_certs.end(); i++)
+ delete (*i);
+ X509_STORE_free(m_store);
+}
+
+XMLTrustImpl::XMLTrustImpl(const char* pathname)
+{
+ NDC ndc("XMLTrustImpl");
+ Category& log=Category::getInstance(SHIB_LOGCAT".XMLTrustImpl");
+
+ saml::XML::Parser p;
+ try
+ {
+ static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull};
+ URLInputSource src(base,pathname);
+ Wrapper4InputSource dsrc(&src,false);
+ m_doc=p.parse(dsrc);
+
+ log.infoStream() << "Loaded and parsed trust file (" << pathname << ")" << CategoryStream::ENDLINE;
+
+ DOMElement* e = m_doc->getDocumentElement();
+ if (XMLString::compareString(XML::SHIB_NS,e->getNamespaceURI()) ||
+ XMLString::compareString(XML::Literals::Trust,e->getLocalName()))
+ {
+ log.error("Construction requires a valid trust file: (shib:Trust as root element)");
+ throw MetadataException("Construction requires a valid trust file: (shib:Trust as root element)");
+ }
+
+ // Loop over the KeyAuthority elements.
+ DOMNodeList* nlist=e->getElementsByTagNameNS(XML::SHIB_NS,XML::Literals::KeyAuthority);
+ for (int i=0; nlist && i<nlist->getLength(); i++)
+ {
+ KeyAuthority* ka=new KeyAuthority();
+ m_keyauths.push_back(ka);
+
+ // Very rudimentary, grab up all the X509Certificate elements, and flatten into one list.
+ DOMNodeList* certlist=static_cast<DOMElement*>(nlist->item(i))->getElementsByTagNameNS(
+ saml::XML::XMLSIG_NS,L(X509Certificate)
+ );
+ for (int j=0; certlist && j<certlist->getLength(); j++)
+ {
+ auto_ptr<char> blob(XMLString::transcode(certlist->item(j)->getFirstChild()->getNodeValue()));
+ XSECCryptoX509* cert=new OpenSSLCryptoX509();
+ cert->loadX509Base64Bin(blob.get(),strlen(blob.get()));
+ ka->m_certs.push_back(cert);
+ }
+
+ // Now map the subjects to the list of certs.
+ DOMNodeList* subs=static_cast<DOMElement*>(nlist->item(i))->getElementsByTagNameNS(XML::SHIB_NS,L(Subject));
+ for (int k=0; subs && k<subs->getLength(); k++)
+ {
+ const XMLCh* name=subs->item(k)->getFirstChild()->getNodeValue();
+ if (name && *name)
+ {
+ static const XMLCh one[]={ chDigit_1, chNull };
+ static const XMLCh tru[]={ chLatin_t, chLatin_r, chLatin_u, chLatin_e, chNull };
+ const XMLCh* regexp=
+ static_cast<DOMElement*>(subs->item(k))->getAttributeNS(NULL,XML::Literals::regexp);
+ bool flag=(!XMLString::compareString(regexp,one) || !XMLString::compareString(regexp,tru));
+ m_bindings[pair<const XMLCh*,bool>(name,flag)]=ka;
+ }
+ }
+ }
+ }
+ catch (SAMLException& e)
+ {
+ log.errorStream() << "XML error while parsing site configuration: " << e.what() << CategoryStream::ENDLINE;
+ for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
+ delete (*i);
+ if (m_doc)
+ m_doc->release();
+ throw;
+ }
+ catch (...)
+ {
+ log.error("Unexpected error while parsing site configuration");
+ for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
+ delete (*i);
+ if (m_doc)
+ m_doc->release();
+ throw;
+ }
+}
+
+XMLTrustImpl::~XMLTrustImpl()
+{
+ for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
+ delete (*i);
+ if (m_doc)
+ m_doc->release();
+}
+
+XMLTrust::XMLTrust(const char* pathname) : m_filestamp(0), m_source(pathname), m_impl(NULL)
+{
+#ifdef WIN32
+ struct _stat stat_buf;
+ if (_stat(pathname, &stat_buf) == 0)
+#else
+ struct stat stat_buf;
+ if (stat(pathname, &stat_buf) == 0)
+#endif
+ m_filestamp=stat_buf.st_mtime;
+ m_impl=new XMLTrustImpl(pathname);
+ m_lock=RWLock::create();
+}
+
+XMLTrust::~XMLTrust()
+{
+ delete m_lock;
+ delete m_impl;
+}
+
+void XMLTrust::lock()
+{
+ m_lock->rdlock();
+
+ // Check if we need to refresh.
+#ifdef WIN32
+ struct _stat stat_buf;
+ if (_stat(m_source.c_str(), &stat_buf) == 0)
+#else
+ struct stat stat_buf;
+ if (stat(m_source.c_str(), &stat_buf) == 0)
+#endif
+ {
+ if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime)
+ {
+ // Elevate lock and recheck.
+ m_lock->unlock();
+ m_lock->wrlock();
+ if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime)
+ {
+ try
+ {
+ XMLTrustImpl* new_mapper=new XMLTrustImpl(m_source.c_str());
+ delete m_impl;
+ m_impl=new_mapper;
+ m_lock->unlock();
+ }
+ catch(SAMLException& e)
+ {
+ m_lock->unlock();
+ saml::NDC ndc("lock");
+ Category::getInstance(SHIB_LOGCAT".XMLTrust").error("failed to reload trust metadata, sticking with what we have: %s", e.what());
+ }
+ catch(...)
+ {
+ m_lock->unlock();
+ saml::NDC ndc("lock");
+ Category::getInstance(SHIB_LOGCAT".XMLTrust").error("caught an unknown exception, sticking with what we have");
+ }
+ }
+ else
+ {
+ m_lock->unlock();
+ }
+ m_lock->rdlock();
+ }
+ }
+}
+
+void XMLTrust::unlock()
+{
+ m_lock->unlock();
+}
+
+Iterator<XSECCryptoX509*> XMLTrust::getCertificates(const XMLCh* subject) const
+{
+ // Find the first matching binding.
+ for (XMLTrustImpl::BindingMap::const_iterator i=m_impl->m_bindings.begin(); i!=m_impl->m_bindings.end(); i++)
+ {
+ if (i->first.second) // regexp
+ {
+ try
+ {
+ RegularExpression re(i->first.first);
+ if (re.matches(subject))
+ return i->second->m_certs;
+ }
+ catch (XMLException& ex)
+ {
+ auto_ptr<char> tmp(XMLString::transcode(ex.getMessage()));
+ NDC ndc("getCertificates");
+ Category& log=Category::getInstance(SHIB_LOGCAT".XMLTrust");
+ log.errorStream() << "caught exception while parsing regular expression: " << tmp.get()
+ << CategoryStream::ENDLINE;
+ }
+ }
+ else if (!XMLString::compareString(subject,i->first.first))
+ {
+ return i->second->m_certs;
+ }
+ }
+ return EMPTY(XSECCryptoX509*);
+}
+
+bool XMLTrust::validate(const ISite* site, Iterator<XSECCryptoX509*> certs) const
+{
+ vector<const XMLCh*> temp;
+ while (certs.hasNext())
+ temp.push_back(certs.next()->getDEREncodingSB().sbStrToXMLCh());
+ return validate(site,temp);
+}
+
+bool XMLTrust::validate(const ISite* site, Iterator<const XMLCh*> certs) const
+{
+ NDC ndc("validate");
+
+ STACK_OF(X509)* chain=sk_X509_new_null();
+ while (certs.hasNext())
+ {
+ auto_ptr<char> temp(XMLString::transcode(certs.next()));
+ X509* x=B64_to_X509(temp.get());
+ if (!x)
+ {
+ sk_X509_pop_free(chain,X509_free);
+ return false;
+ }
+ sk_X509_push(chain,x);
+ }
+
+ // Use the matching bindings.
+ for (XMLTrustImpl::BindingMap::const_iterator i=m_impl->m_bindings.begin(); i!=m_impl->m_bindings.end(); i++)
+ {
+ bool match=false;
+ if (i->first.second) // regexp
+ {
+ try
+ {
+ RegularExpression re(i->first.first);
+ if (re.matches(site->getName()))
+ match=true;
+ else
+ {
+ Iterator<const XMLCh*> groups=site->getGroups();
+ while (!match && groups.hasNext())
+ if (re.matches(groups.next()))
+ match=true;
+ }
+ }
+ catch (XMLException& ex)
+ {
+ auto_ptr<char> tmp(XMLString::transcode(ex.getMessage()));
+ NDC ndc("getCertificates");
+ Category& log=Category::getInstance(SHIB_LOGCAT".XMLTrust");
+ log.errorStream() << "caught exception while parsing regular expression: " << tmp.get()
+ << CategoryStream::ENDLINE;
+ }
+ }
+ else if (!XMLString::compareString(site->getName(),i->first.first))
+ {
+ match=true;
+ }
+ else
+ {
+ Iterator<const XMLCh*> groups=site->getGroups();
+ while (!match && groups.hasNext())
+ if (!XMLString::compareString(i->first.first,groups.next()))
+ match=true;
+ }
+
+ // If we have a match, use the associated keyauth and do a verify against the store.
+ if (match)
+ {
+ X509_STORE* store=i->second->getX509Store();
+ if (store)
+ {
+ X509_STORE_CTX* ctx=X509_STORE_CTX_new();
+ if (!ctx)
+ {
+ log_openssl();
+ return false;
+ }
+
+#if (OPENSSL_VERSION_NUMBER > 0x009070000L)
+ if (X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain)!=1)
+ {
+ log_openssl();
+ sk_X509_pop_free(chain,X509_free);
+ return false;
+ }
+#else
+ X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain);
+#endif
+ if (X509_verify_cert(ctx)==1)
+ {
+ sk_X509_pop_free(chain,X509_free);
+ X509_STORE_CTX_free(ctx);
+ return true;
+ }
+ X509_STORE_CTX_free(ctx);
+ }
+ }
+ }
+
+ return false;
+}
void lock();
void unlock();
const ISite* lookup(const XMLCh* site) const;
- time_t getTimestamp() const { return m_filestamp; }
private:
std::string m_source;
XMLMetadataImpl* m_impl;
};
+ class XMLTrustImpl;
+ class SHIB_EXPORTS XMLTrust : public ITrust
+ {
+ public:
+ XMLTrust(const char* pathname);
+ ~XMLTrust();
+
+ void lock();
+ void unlock();
+ saml::Iterator<XSECCryptoX509*> getCertificates(const XMLCh* subject) const;
+ bool validate(const ISite* site, saml::Iterator<XSECCryptoX509*> certs) const;
+ bool validate(const ISite* site, saml::Iterator<const XMLCh*> certs) const;
+
+ private:
+ std::string m_source;
+ time_t m_filestamp;
+ RWLock* m_lock;
+ XMLTrustImpl* m_impl;
+ };
+
class AAP
{
public:
class ShibInternalConfig : public ShibConfig
{
public:
- ShibInternalConfig() : m_AAP(NULL), m_lock(NULL) {}
+ ShibInternalConfig() : m_AAP(NULL) {}
bool init();
void term();
void regFactory(const char* type, MetadataFactory* factory);
+ void regFactory(const char* type, TrustFactory* factory);
void unregFactory(const char* type);
bool addMetadata(const char* type, const char* source);
private:
friend class OriginMetadata;
+ friend class Trust;
typedef std::map<std::string, MetadataFactory*> MetadataFactoryMap;
+ typedef std::map<std::string, TrustFactory*> TrustFactoryMap;
MetadataFactoryMap m_metadataFactoryMap;
+ TrustFactoryMap m_trustFactoryMap;
std::vector<IMetadata*> m_providers;
- Mutex* m_lock;
+ std::vector<ITrust*> m_trust_providers;
};
}
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SHIB_EXPORTS" /YX /FD /GZ /c
-# ADD CPP /nologo /MDd /W3 /Gm /GR /GX /ZI /Od /I "." /I "..\..\..\opensaml\c" /D "_WINDOWS" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "XSEC_NO_XALAN" /D "_AFXDLL" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GR /GX /ZI /Od /I "." /I "..\..\..\opensaml\c" /D "_WINDOWS" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_AFXDLL" /FR /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# End Source File
# Begin Source File
-SOURCE=.\OriginSiteMapper.cpp
+SOURCE=.\Metadata.cpp
# End Source File
# Begin Source File
# End Source File
# Begin Source File
-SOURCE=.\XMLOriginSiteMapper.cpp
+SOURCE=.\XMLMetadata.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\XMLTrust.cpp
# End Source File
# End Target
# End Project
#define __shib_h__
#include <saml/saml.h>
+#include <openssl/x509.h>
#ifdef WIN32
# ifndef SHIB_EXPORTS
struct SHIB_EXPORTS ISite
{
+ virtual const XMLCh* getName() const=0;
+ virtual saml::Iterator<const XMLCh*> getGroups() const=0;
virtual saml::Iterator<const IContactInfo*> getContacts() const=0;
virtual const char* getErrorURL() const=0;
- virtual void validate(XSECCryptoX509* cert) const=0;
- virtual void validate(const XMLCh* cert) const=0;
+ virtual bool validate(saml::Iterator<XSECCryptoX509*> certs) const=0;
+ virtual bool validate(saml::Iterator<const XMLCh*> certs) const=0;
virtual ~ISite() {}
};
virtual void lock()=0;
virtual void unlock()=0;
virtual const ISite* lookup(const XMLCh* site) const=0;
- virtual time_t getTimestamp() const=0;
virtual ~IMetadata() {}
};
+ struct SHIB_EXPORTS ITrust
+ {
+ virtual void lock()=0;
+ virtual void unlock()=0;
+ virtual saml::Iterator<XSECCryptoX509*> getCertificates(const XMLCh* subject) const=0;
+ virtual bool validate(const ISite* site, saml::Iterator<XSECCryptoX509*> certs) const=0;
+ virtual bool validate(const ISite* site, saml::Iterator<const XMLCh*> certs) const=0;
+ virtual ~ITrust() {}
+ };
+
#ifdef SHIB_INSTANTIATE
# ifdef NO_RTTI
const unsigned short RTTI_UnsupportedProtocolException= RTTI_EXTENSION_BASE;
virtual const XMLCh* getOriginSite(const saml::SAMLResponse& r);
protected:
- virtual void verifySignature(const saml::SAMLSignedObject& obj, const XMLCh* signerName, XSECCryptoKey* knownKey=NULL);
+ virtual void verifySignature(
+ const saml::SAMLSignedObject& obj,
+ const IOriginSite* originSite,
+ const XMLCh* signerName,
+ XSECCryptoKey* knownKey=NULL);
signatureMethod m_algorithm;
std::vector<const XMLCh*> m_policies;
);
protected:
- virtual void verifySignature(const saml::SAMLSignedObject& obj, const XMLCh* signerName, XSECCryptoKey* knownKey=NULL);
+ virtual void verifySignature(
+ const saml::SAMLSignedObject& obj,
+ const IOriginSite* originSite,
+ const XMLCh* signerName,
+ XSECCryptoKey* knownKey=NULL);
};
class SHIB_EXPORTS ShibPOSTProfileFactory
static ShibPOSTProfile* getInstance(const saml::Iterator<const XMLCh*>& policies, const XMLCh* issuer);
};
- // Glue class between abstract metadata and concrete providers
+ // Glue classes between abstract metadata and concrete providers
class SHIB_EXPORTS OriginMetadata
{
~OriginMetadata();
bool fail() const {return m_mapper==NULL;}
const IOriginSite* operator->() const {return m_site;}
+ operator const IOriginSite*() const {return m_site;}
private:
OriginMetadata(const OriginMetadata&);
const IOriginSite* m_site;
};
+ class SHIB_EXPORTS Trust
+ {
+ public:
+ Trust() : m_mapper(NULL) {}
+ ~Trust();
+ saml::Iterator<XSECCryptoX509*> getCertificates(const XMLCh* subject);
+ bool validate(const ISite* site, saml::Iterator<XSECCryptoX509*> certs) const;
+ bool validate(const ISite* site, saml::Iterator<const XMLCh*> certs) const;
+
+ private:
+ Trust(const Trust&);
+ void operator=(const Trust&);
+ ITrust* m_mapper;
+ };
+
extern "C" { typedef IMetadata* MetadataFactory(const char* source); }
+ extern "C" { typedef ITrust* TrustFactory(const char* source); }
class SHIB_EXPORTS ShibConfig
{
// allows pluggable implementations of metadata
virtual void regFactory(const char* type, MetadataFactory* factory)=0;
+ virtual void regFactory(const char* type, TrustFactory* factory)=0;
virtual void unregFactory(const char* type)=0;
// builds a specific metadata lookup object
static const XMLCh Location[];
static const XMLCh Name[];
static const XMLCh OriginSite[];
- static const XMLCh Sites[];
+ static const XMLCh SiteGroup[];
+
+ static const XMLCh KeyAuthority[];
+ static const XMLCh Trust[];
static const XMLCh AnySite[];
static const XMLCh AnyValue[];
public:
static saml::SAMLBinding* getInstance(const XMLCh* protocol=saml::SAMLBinding::SAML_SOAP_HTTPS);
};
+
+ // OpenSSL Utilities
+
+ // Log errors from OpenSSL error queue.
+ void log_openssl();
+
+ // build an OpenSSL cert out of a base-64 encoded DER buffer (XML style)
+ X509* B64_to_X509(const char* buf);
}
#endif
// Examine the root element to be sure we know what we have.
DOMElement* e=doc->getDocumentElement();
if (XMLString::compareString(shibboleth::XML::SHIB_NS,e->getNamespaceURI()) ||
- XMLString::compareString(shibboleth::XML::Literals::Sites,e->getLocalName()))
+ (XMLString::compareString(shibboleth::XML::Literals::SiteGroup,e->getLocalName())) &&
+ XMLString::compareString(shibboleth::XML::Literals::Trust,e->getLocalName()))
{
doc->release();
- log.error("requires a valid site file: (shib:Sites as root element)");
- throw OriginSiteMapperException("Construction requires a valid site file: (shib:Sites as root element)");
+ log.error("requires a valid site file: (shib:SiteGroup or shib:Trust as root element)");
+ throw OriginSiteMapperException("Construction requires a valid site file: (shib:SiteGroup or shib:Trust as root element)");
}
// If we're verifying, grab the embedded signature.