Updated to xsec 1.2, converted to new HTTP Hook.
authorScott Cantor <cantor.2@osu.edu>
Fri, 1 Jul 2005 01:42:53 +0000 (01:42 +0000)
committerScott Cantor <cantor.2@osu.edu>
Fri, 1 Jul 2005 01:42:53 +0000 (01:42 +0000)
shib-target/ArtifactMapper.cpp
shib-target/Makefile.am
shib-target/ShibHTTPHook.cpp [new file with mode: 0644]
shib-target/internal.h
shib-target/shib-ccache.cpp
shib-target/shib-target.h
shib-target/shibtarget.dsp

index 7312498..bffba88 100644 (file)
@@ -114,7 +114,7 @@ SAMLResponse* STArtifactMapper::resolve(SAMLRequest* request)
             request->getMinorVersion()==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
             );
         if (idp) {
-                   ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse ? credUse->getString("TLS").second : NULL,idp);
+                   ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
             const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
             Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
             while (!response && eps.hasNext()) {
@@ -150,7 +150,7 @@ SAMLResponse* STArtifactMapper::resolve(SAMLRequest* request)
                 request->getMinorVersion()==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
                 );
             if (idp) {
-                   ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse ? credUse->getString("TLS").second : NULL,idp);
+                   ShibHTTPHook::ShibHTTPHookCallContext callCtx(credUse,idp);
                 const IEndpointManager* mgr=idp->getArtifactResolutionServiceManager();
                 Iterator<const IEndpoint*> eps=mgr ? mgr->getEndpoints() : EMPTY(const IEndpoint*);
                 while (eps.hasNext()) {
index f5f23b1..56ebf8a 100644 (file)
@@ -21,6 +21,7 @@ libshib_target_la_SOURCES = \
        ArtifactMapper.cpp \
        MemoryListener.cpp \
        RPCListener.cpp \
+    ShibHTTPHook.cpp \
        shib-ccache.cpp \
        shib-config.cpp \
        shib-handlers.cpp \
diff --git a/shib-target/ShibHTTPHook.cpp b/shib-target/ShibHTTPHook.cpp
new file mode 100644 (file)
index 0000000..0fb01a0
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ *  Copyright 2001-2005 Internet2
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* ShibHTTPHook.cpp - Shibboleth hook for SAML Binding with SSL callback
+
+   Scott Cantor
+   2/13/05
+   
+   $History:$
+*/
+
+#include "internal.h"
+
+#include <saml/version.h>
+#include <openssl/ssl.h>
+#include <openssl/x509_vfy.h>
+
+using namespace std;
+using namespace log4cpp;
+using namespace shibtarget;
+using namespace shibboleth;
+using namespace saml;
+
+/*
+ * Our verifier callback is a front-end for invoking each trust plugin until
+ * success, or we run out of plugins.
+ */
+static int verify_callback(X509_STORE_CTX* x509_ctx, void* arg)
+{
+    Category::getInstance("OpenSSL").debug("invoking default X509 verify callback");
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+    ShibHTTPHook::ShibHTTPHookCallContext* ctx = reinterpret_cast<ShibHTTPHook::ShibHTTPHookCallContext*>(arg);
+#else
+    // Yes, this sucks. I'd use TLS, but there's no really obvious spot to put the thread key
+    // and global variables suck too. We can't access the X509_STORE_CTX depth directly because
+    // OpenSSL only copies it into the context if it's >=0, and the unsigned pointer may be
+    // negative in the SSL structure's int member.
+    SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(x509_ctx,SSL_get_ex_data_X509_STORE_CTX_idx()));
+    ShibHTTPHook::ShibHTTPHookCallContext* ctx =
+        reinterpret_cast<ShibHTTPHook::ShibHTTPHookCallContext*>(SSL_get_verify_depth(ssl));
+#endif
+
+    // Instead of using the supplied verifier, we let the plugins do whatever they want to do
+    // with the untrusted certificates we find in the object. We can save a bit of memory by
+    // just building a vector that points at them inside the supplied structure.
+    vector<void*> chain;
+    for (int i=0; i<sk_X509_num(x509_ctx->untrusted); i++)
+        chain.push_back(sk_X509_value(x509_ctx->untrusted,i));
+    
+    Trust t(ctx->getHook()->getTrustProviders());
+    if (!t.validate(x509_ctx->cert,chain,ctx->getRoleDescriptor(),false)) { // bypass name check (handled for us)
+        x509_ctx->error=X509_V_ERR_APPLICATION_VERIFICATION;     // generic error, check log for plugin specifics
+        return 0;
+    }
+    
+    // Signal success. Hopefully it doesn't matter what's actually in the structure now.
+    return 1;
+}
+
+/*
+ * OpenSAML callback is invoked during SSL context setup, before the handshake.
+ * We use it to attach credentials and our own certificate verifier callback above.
+ */
+static bool ssl_ctx_callback(void* ssl_ctx, void* userptr)
+{
+#ifdef _DEBUG
+    saml::NDC("ssl_ctx_callback");
+#endif
+    Category& log=Category::getInstance(SHIBT_LOGCAT".ShibHTTPHook");
+    
+    try {
+        log.debug("OpenSAML invoked SSL context callback");
+        ShibHTTPHook::ShibHTTPHookCallContext* ctx = reinterpret_cast<ShibHTTPHook::ShibHTTPHookCallContext*>(userptr);
+        const IPropertySet* credUse=ctx->getCredentialUse();
+        pair<bool,const char*> TLS=credUse ? credUse->getString("TLS") : pair<bool,const char*>(false,NULL);
+        if (TLS.first) {
+            Credentials c(ctx->getHook()->getCredentialProviders());
+            const ICredResolver* cr=c.lookup(TLS.second);
+            if (cr)
+                cr->attach(ssl_ctx);
+            else
+                log.error("unable to attach credentials to request using (%s), leaving anonymous",TLS.second);
+        }
+        else
+            log.warn("no TLS credentials supplied, leaving anonymous");
+        
+        SSL_CTX_set_verify(reinterpret_cast<SSL_CTX*>(ssl_ctx),SSL_VERIFY_PEER,NULL);
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+        // With 0.9.7, we can pass a callback argument directly.
+        SSL_CTX_set_cert_verify_callback(reinterpret_cast<SSL_CTX*>(ssl_ctx),verify_callback,userptr);
+#else
+        // With 0.9.6, there's no argument, so we're going to use a really embarrassing hack and
+        // stuff the argument in the depth property where it will get copied to the context object
+        // that's handed to the callback.
+        SSL_CTX_set_cert_verify_callback(
+            reinterpret_cast<SSL_CTX*>(ssl_ctx),
+            reinterpret_cast<int (*)()>(verify_callback),
+            NULL
+            );
+        SSL_CTX_set_verify_depth(reinterpret_cast<SSL_CTX*>(ssl_ctx),reinterpret_cast<int>(userptr));
+
+#endif
+        // The best we can do is assume authentication succeeds because when libcurl reuses
+        // SSL connections, no callback is made. Since we always authenticate SSL connections,
+        // the caller should check that the protocol is https.
+        ctx->setAuthenticated();
+    }
+    catch (SAMLException& e) {
+        log.error(string("caught a SAML exception while attaching credentials to request: ") + e.what());
+        return false;
+    }
+#ifndef _DEBUG
+    catch (...) {
+        log.error("caught an unknown exception while attaching credentials to request");
+        return false;
+    }
+#endif
+    
+    return true;
+}
+
+bool ShibHTTPHook::outgoing(HTTPClient* conn, void* globalCtx, void* callCtx)
+{
+    // Sanity check...
+    if (globalCtx != this)
+        return false;
+        
+    // The callCtx is our nested context class. Copy in the parent pointer.
+    reinterpret_cast<ShibHTTPHookCallContext*>(callCtx)->m_hook=this;
+    
+    // Clear authn status.
+    reinterpret_cast<ShibHTTPHookCallContext*>(callCtx)->m_authenticated=false;
+    // The hook function is called before connecting to the HTTP server. This
+    // gives us a chance to attach our own SSL callback, and set a version header.
+    if (!conn->setSSLCallback(ssl_ctx_callback,callCtx))
+        return false;
+    
+    if (!conn->setRequestHeader("Shibboleth", PACKAGE_VERSION))
+        return false;
+    if (!conn->setRequestHeader("Xerces-C", XERCES_FULLVERSIONDOT))
+        return false;
+    if (!conn->setRequestHeader("XML-Security-C", XSEC_VERSION))
+        return false;
+    if (!conn->setRequestHeader("OpenSAML-C", OPENSAML_FULLVERSIONDOT))
+        return false;
+
+    // Check for HTTP authentication...
+    const IPropertySet* credUse=reinterpret_cast<ShibHTTPHookCallContext*>(callCtx)->getCredentialUse();
+    pair<bool,const char*> authType=credUse ? credUse->getString("authType") : pair<bool,const char*>(false,NULL);
+    if (authType.first) {
+#ifdef _DEBUG
+        saml::NDC("outgoing");
+#endif
+        Category& log=Category::getInstance(SHIBT_LOGCAT".ShibHTTPHook");
+        HTTPClient::auth_t type=HTTPClient::auth_none;
+        pair<bool,const char*> username=credUse->getString("authUsername");
+        pair<bool,const char*> password=credUse->getString("authPassword");
+        if (!username.first || !password.first) {
+            log.error("HTTP authType (%s) specified but authUsername or authPassword was missing", authType.second);
+            return false;
+        }
+        else if (!strcmp(authType.second,"basic"))
+            type = HTTPClient::auth_basic;
+        else if (!strcmp(authType.second,"digest"))
+            type = HTTPClient::auth_digest;
+        else if (!strcmp(authType.second,"ntlm"))
+            type = HTTPClient::auth_ntlm;
+        else if (!strcmp(authType.second,"gss"))
+            type = HTTPClient::auth_gss;
+        else {
+            log.error("Unknown authType (%s) specified in CredentialUse element", authType.second);
+            return false;
+        }
+        log.debug("configured for HTTP authentication (method=%s, username=%s)", authType.second, username.second);
+        return conn->setAuth(type,username.second,password.second);
+    }
+    return true;
+}
index e5a0a5b..a5406d3 100644 (file)
@@ -1,53 +1,19 @@
 /*
- * The Shibboleth License, Version 1.
- * Copyright (c) 2002
- * University Corporation for Advanced Internet Development, Inc.
- * All rights reserved
+ *  Copyright 2001-2005 Internet2
  *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * 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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
-
 /* internal.h - internally visible declarations
 
    Scott Cantor
index 72e3082..98b89fa 100644 (file)
@@ -762,7 +762,7 @@ pair<SAMLResponse*,SAMLResponse*> InternalCCacheEntry::getNewResponse()
 
         // Call context object
         Trust t(application->getTrustProviders());
-        ShibHTTPHook::ShibHTTPHookCallContext ctx(credUse ? credUse->getString("TLS").second : NULL,AA);
+        ShibHTTPHook::ShibHTTPHookCallContext ctx(credUse,AA);
         
         // Use metadata to locate endpoints.
         Iterator<const IEndpoint*> endpoints=AA->getAttributeServiceManager()->getEndpoints();
index 10ea195..cc71d52 100644 (file)
@@ -136,6 +136,46 @@ namespace shibtarget {
         virtual ~IApplication() {}
     };
 
+    // Instead of wrapping the binding to deal with mutual authentication, we
+    // just use the HTTP hook functionality offered by OpenSAML. The hook will
+    // register "itself" as a globalCtx pointer with the SAML binding and the caller
+    // will declare and pass the embedded struct as callCtx for use by the hook.
+    class ShibHTTPHook : virtual public saml::SAMLSOAPHTTPBinding::HTTPHook
+    {
+    public:
+        ShibHTTPHook(const saml::Iterator<shibboleth::ITrust*>& trusts, const saml::Iterator<shibboleth::ICredentials*>& creds)
+            : m_trusts(trusts), m_creds(creds) {}
+        virtual ~ShibHTTPHook() {}
+        
+        // Only hook we need here is for outgoing connection to server.
+        virtual bool outgoing(saml::HTTPClient* conn, void* globalCtx=NULL, void* callCtx=NULL);
+
+        // Client declares a context object and pass as callCtx to send() method.
+        class ShibHTTPHookCallContext {
+        public:
+            ShibHTTPHookCallContext(const IPropertySet* credUse, const shibboleth::IRoleDescriptor* role)
+                : m_credUse(credUse), m_role(role), m_hook(NULL), m_authenticated(false) {}
+            const ShibHTTPHook* getHook() {return m_hook;}
+            const IPropertySet* getCredentialUse() {return m_credUse;}
+            const shibboleth::IRoleDescriptor* getRoleDescriptor() {return m_role;}
+            bool isAuthenticated() const {return m_authenticated;}
+            void setAuthenticated() {m_authenticated=true;}
+            
+        private:
+            const IPropertySet* m_credUse;
+            const shibboleth::IRoleDescriptor* m_role;
+            ShibHTTPHook* m_hook;
+            bool m_authenticated;
+            friend class ShibHTTPHook;
+        };
+        
+        const saml::Iterator<shibboleth::ITrust*>& getTrustProviders() const {return m_trusts;}
+        const saml::Iterator<shibboleth::ICredentials*>& getCredentialProviders() const {return m_creds;}
+    private:
+        saml::Iterator<shibboleth::ITrust*> m_trusts;
+        saml::Iterator<shibboleth::ICredentials*> m_creds;
+    };
+
     struct SHIBTARGET_EXPORTS ISessionCacheEntry : public virtual saml::ILockable
     {
         virtual bool isValid(time_t lifetime, time_t timeout) const=0;
index 74d80cb..1a23b2b 100644 (file)
@@ -53,7 +53,7 @@ BSC32=bscmake.exe
 # ADD BSC32 /nologo
 LINK32=link.exe
 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
-# ADD LINK32 log4cpp.lib xerces-c_2.lib wsock32.lib saml_5.lib /nologo /dll /machine:I386 /out:"Release/shibtarget_5.dll" /libpath:"..\..\..\opensaml\c\saml\Release"
+# ADD LINK32 log4cpp.lib xerces-c_2.lib saml_5.lib wsock32.lib libeay32.lib ssleay32.lib /nologo /dll /machine:I386 /out:"Release/shibtarget_5.dll" /libpath:"..\..\..\opensaml\c\saml\Release" /libpath:"\openssl-0.9.7e\out32dll"
 # SUBTRACT LINK32 /pdb:none
 
 !ELSEIF  "$(CFG)" == "shibtarget - Win32 Debug"
@@ -70,7 +70,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 "SHIBTARGET_EXPORTS" /YX /FD /GZ /c
-# ADD CPP /nologo /MDd /W3 /Gm /GR /GX /ZI /Od /I "." /I ".." /I "..\oncrpc" /I "..\..\..\opensaml\c" /D "_DEBUG" /D "_AFXDLL" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /D "WANT_TCP_SHAR" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GR /GX /ZI /Od /I "." /I ".." /I "..\oncrpc" /I "..\..\..\opensaml\c" /D "_WINDOWS" /D "WANT_TCP_SHAR" /D "WIN32" /D "_DEBUG" /D "_MBCS" /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"
@@ -80,7 +80,7 @@ BSC32=bscmake.exe
 # ADD BSC32 /nologo
 LINK32=link.exe
 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 log4cppD.lib xerces-c_2D.lib wsock32.lib saml_5D.lib /nologo /dll /debug /machine:I386 /out:"Debug/shibtarget_5D.dll" /pdbtype:sept /libpath:"..\..\..\opensaml\c\saml\Debug"
+# ADD LINK32 log4cppD.lib xerces-c_2D.lib saml_5D.lib wsock32.lib libeay32.lib ssleay32.lib /nologo /dll /debug /machine:I386 /out:"Debug/shibtarget_5D.dll" /pdbtype:sept /libpath:"..\..\..\opensaml\c\saml\Debug" /libpath:"\openssl-0.9.7e\out32dll.dbg"
 # SUBTRACT LINK32 /pdb:none
 
 !ENDIF 
@@ -151,6 +151,10 @@ SOURCE=".\shib-target.rc"
 # End Source File
 # Begin Source File
 
+SOURCE=.\ShibHTTPHook.cpp
+# End Source File
+# Begin Source File
+
 SOURCE=".\shibrpc-clnt.c"
 # End Source File
 # Begin Source File