First cut of new trust interfaces
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 19 May 2003 14:42:42 +0000 (14:42 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 19 May 2003 14:42:42 +0000 (14:42 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@451 cb58f699-b61c-0410-a6fe-9272a202ed29

12 files changed:
shib/ClubShibPOSTProfile.cpp
shib/Makefile.am
shib/Metadata.cpp
shib/ShibConfig.cpp
shib/ShibPOSTProfile.cpp
shib/XML.cpp
shib/XMLMetadata.cpp
shib/XMLTrust.cpp [new file with mode: 0644]
shib/internal.h
shib/shib.dsp
shib/shib.h
siterefresh/siterefresh.cpp

index 5ed207c..ce8d150 100644 (file)
@@ -112,9 +112,11 @@ SAMLResponse* ClubShibPOSTProfile::prepare(
                                     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");
 }
index c92e603..5365cd7 100644 (file)
@@ -21,6 +21,7 @@ libshib_la_SOURCES = \
                     SimpleAttribute.cpp \
                     XML.cpp \
                     XMLMetadata.cpp \
+                    XMLTrust.cpp \
                     shib-threads.cpp
 
 
index d45679b..06340bf 100644 (file)
@@ -47,7 +47,7 @@
  * 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
@@ -64,7 +64,6 @@ using namespace std;
 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();
@@ -75,7 +74,6 @@ OriginMetadata::OriginMetadata(const XMLCh* site) : m_mapper(NULL), m_site(NULL)
         }
         (*i)->unlock();
     }
-    config.m_lock->unlock();
 }
 
 OriginMetadata::~OriginMetadata()
@@ -83,3 +81,58 @@ 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();
+}
index 32afa68..d0ae7a8 100644 (file)
@@ -64,6 +64,7 @@
 
 #include "internal.h"
 #include <log4cpp/Category.hh>
+#include <openssl/err.h>
 
 using namespace saml;
 using namespace shibboleth;
@@ -84,6 +85,11 @@ extern "C" IMetadata* XMLMetadataFactory(const char* source)
     return new XMLMetadata(source);
 }
 
+extern "C" ITrust* XMLTrustFactory(const char* source)
+{
+    return new XMLTrust(source);
+}
+
 bool ShibInternalConfig::init()
 {
     saml::NDC ndc("init");
@@ -107,15 +113,8 @@ bool ShibInternalConfig::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;
 }
@@ -124,7 +123,6 @@ void ShibInternalConfig::term()
 {
     for (vector<IMetadata*>::iterator i=m_providers.begin(); i!=m_providers.end(); i++)
         delete *i;
-    delete m_lock;
     delete m_AAP;
 }
 
@@ -134,10 +132,19 @@ void ShibInternalConfig::regFactory(const char* type, MetadataFactory* factory)
         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)
@@ -145,7 +152,6 @@ 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);
@@ -155,8 +161,16 @@ bool ShibInternalConfig::addMetadata(const char* type, const char* source)
             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)
     {
@@ -170,7 +184,6 @@ bool ShibInternalConfig::addMetadata(const char* type, const char* source)
             "failed to add %s provider to system using source '%s': unknown exception", type, source
             );
     }
-    m_lock->unlock();
     return ret;
 }
 
@@ -178,3 +191,33 @@ ShibConfig& ShibConfig::getConfig()
 {
     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;
+}
index 927995f..c46f19a 100644 (file)
@@ -59,6 +59,8 @@
 
 #include <ctime>
 
+#include <openssl/x509v3.h>
+
 using namespace shibboleth;
 using namespace saml;
 using namespace std;
@@ -182,7 +184,7 @@ SAMLResponse* ShibPOSTProfile::accept(const XMLByte* buf, XMLCh** originSitePtr)
         while (certs.hasNext())
         {
             try {
-                verifySignature(*assertion, handleService, certs.next()->clonePublicKey());
+                verifySignature(*assertion, mapper, handleService, certs.next()->clonePublicKey());
                 bVerified=true;
             }
             catch (InvalidCryptoException&) {
@@ -190,14 +192,14 @@ SAMLResponse* ShibPOSTProfile::accept(const XMLByte* buf, XMLCh** originSitePtr)
             }
         }
         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&) {
@@ -205,7 +207,7 @@ SAMLResponse* ShibPOSTProfile::accept(const XMLByte* buf, XMLCh** originSitePtr)
         }
     }
     if (!bVerified)
-        verifySignature(*r, handleService);
+        verifySignature(*r, mapper, handleService);
 
     return r.release();
 }
@@ -251,7 +253,74 @@ bool ShibPOSTProfile::checkReplayCache(const SAMLAssertion& a)
     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);
+    }
 }
index 2037f78..e118fe5 100644 (file)
@@ -116,8 +116,15 @@ const XMLCh XML::Literals::Name[]=
 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 };
index 3ba716d..f883fed 100644 (file)
@@ -104,24 +104,32 @@ public:
         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;
@@ -155,10 +163,10 @@ XMLMetadataImpl::XMLMetadataImpl(const char* pathname)
 
         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.
@@ -170,8 +178,16 @@ XMLMetadataImpl::XMLMetadataImpl(const char* pathname)
                 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)
diff --git a/shib/XMLTrust.cpp b/shib/XMLTrust.cpp
new file mode 100644 (file)
index 0000000..956261d
--- /dev/null
@@ -0,0 +1,443 @@
+/* 
+ * 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;
+}
index cf55bdb..204ed32 100644 (file)
@@ -87,7 +87,6 @@ namespace shibboleth
         void lock();
         void unlock();
         const ISite* lookup(const XMLCh* site) const;
-        time_t getTimestamp() const { return m_filestamp; }
 
     private:
         std::string m_source;
@@ -96,6 +95,26 @@ namespace shibboleth
         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:
@@ -123,12 +142,13 @@ namespace shibboleth
     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);
@@ -137,11 +157,14 @@ namespace shibboleth
         
     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;
     };
 }
 
index b8efcdd..006382d 100644 (file)
@@ -69,7 +69,7 @@ LINK32=link.exe
 # 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"
@@ -109,7 +109,7 @@ SOURCE=.\internal.h
 # End Source File
 # Begin Source File
 
-SOURCE=.\OriginSiteMapper.cpp
+SOURCE=.\Metadata.cpp
 # End Source File
 # Begin Source File
 
@@ -153,7 +153,11 @@ SOURCE=.\XML.cpp
 # 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
index d002a28..edf6a50 100644 (file)
@@ -60,6 +60,7 @@
 #define __shib_h__
 
 #include <saml/saml.h>
+#include <openssl/x509.h>
 
 #ifdef WIN32
 # ifndef SHIB_EXPORTS
@@ -107,10 +108,12 @@ namespace shibboleth
 
     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() {}
     };
     
@@ -135,10 +138,19 @@ namespace shibboleth
         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;
@@ -220,7 +232,11 @@ namespace shibboleth
         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;
@@ -255,7 +271,11 @@ namespace shibboleth
             );
 
     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
@@ -265,7 +285,7 @@ namespace shibboleth
         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
     {
@@ -274,6 +294,7 @@ namespace shibboleth
         ~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&);
@@ -282,7 +303,23 @@ namespace shibboleth
         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
     {
@@ -299,6 +336,7 @@ namespace shibboleth
 
         // 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
@@ -339,7 +377,10 @@ namespace shibboleth
             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[];
@@ -369,6 +410,14 @@ namespace shibboleth
     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
index 49840fa..f402574 100644 (file)
@@ -200,11 +200,12 @@ int main(int argc,char* argv[])
         // 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.