Unified trust engines w/ KeyInfoSource interface, first cut at SOAP transport layer.
authorScott Cantor <cantor.2@osu.edu>
Sat, 18 Nov 2006 00:42:20 +0000 (00:42 +0000)
committerScott Cantor <cantor.2@osu.edu>
Sat, 18 Nov 2006 00:42:20 +0000 (00:42 +0000)
23 files changed:
xmltooling/Makefile.am
xmltooling/QName.h
xmltooling/XMLObject.h
xmltooling/XMLToolingConfig.cpp
xmltooling/base.h
xmltooling/security/AbstractPKIXTrustEngine.h [new file with mode: 0644]
xmltooling/security/ChainingTrustEngine.h [new file with mode: 0644]
xmltooling/security/KeyInfoSource.h [new file with mode: 0644]
xmltooling/security/OpenSSLTrustEngine.h [new file with mode: 0644]
xmltooling/security/TrustEngine.h
xmltooling/security/X509TrustEngine.h
xmltooling/security/impl/AbstractPKIXTrustEngine.cpp [new file with mode: 0644]
xmltooling/security/impl/ChainingTrustEngine.cpp [new file with mode: 0644]
xmltooling/security/impl/ExplicitKeyTrustEngine.cpp
xmltooling/security/impl/TrustEngine.cpp
xmltooling/soap/HTTPSOAPTransport.h [new file with mode: 0644]
xmltooling/soap/OpenSSLSOAPTransport.h [new file with mode: 0644]
xmltooling/soap/SOAPClient.h [new file with mode: 0644]
xmltooling/soap/SOAPTransport.h [new file with mode: 0644]
xmltooling/soap/impl/CURLSOAPTransport.cpp [new file with mode: 0644]
xmltooling/unicode.h
xmltooling/xmltooling.vcproj
xmltooling/xmltoolingconfig.h [new file with mode: 0644]

index 240f230..dba2c6e 100644 (file)
@@ -57,8 +57,12 @@ ioinclude_HEADERS = \
        io/AbstractXMLObjectUnmarshaller.h
 
 secinclude_HEADERS = \
+       security/AbstractPKIXTrustEngine.h \
+       security/ChainingTrustEngine.h \
+       security/KeyInfoSource.h \
        security/TrustEngine.h \
        security/X509TrustEngine.h \
+       security/OpenSSLTrustEngine.h \
        security/XSECCryptoX509CRL.h \
        security/OpenSSLCryptoX509CRL.h
 
@@ -73,7 +77,11 @@ siginclude_HEADERS = \
        signature/SignatureValidator.h
 
 soapinclude_HEADERS = \
-       soap/SOAP.h
+       soap/SOAP.h \
+       soap/SOAPClient.h \
+       soap/SOAPTransport.h \
+       soap/HTTPSOAPTransport.h \
+       soap/OpenSSLSOAPTransport.h
 
 utilinclude_HEADERS = \
        util/DateTime.h \
@@ -99,6 +107,8 @@ xmlsec_sources = \
        encryption/impl/Decrypter.cpp \
        encryption/impl/Encrypter.cpp \
        security/impl/TrustEngine.cpp \
+       security/impl/AbstractPKIXTrustEngine.cpp \
+       security/impl/ChainingTrustEngine.cpp \
        security/impl/ExplicitKeyTrustEngine.cpp \
        security/impl/XSECCryptoX509CRL.cpp \
        security/impl/OpenSSLCryptoX509CRL.cpp \
@@ -141,6 +151,7 @@ libxmltooling_la_SOURCES = \
        signature/impl/KeyInfoSchemaValidators.cpp \
        soap/impl/SOAPImpl.cpp \
        soap/impl/SOAPSchemaValidators.cpp \
+       soap/impl/CURLSOAPTransport.cpp \
        util/DateTime.cpp \
        util/NDC.cpp \
        util/ParserPool.cpp \
index ac17d27..296a58a 100644 (file)
  * Representing XML QNames 
  */
 
-#if !defined(__xmltooling_qname_h__)
+#ifndef __xmltooling_qname_h__
 #define __xmltooling_qname_h__
 
-#include <algorithm>
 #include <xmltooling/unicode.h>
+#include <algorithm>
 
 namespace xmltooling {
     
index 43b30fa..353f586 100644 (file)
 #ifndef __xmltooling_xmlobj_h__
 #define __xmltooling_xmlobj_h__
 
+#include <xmltooling/QName.h>
+#include <xmltooling/Namespace.h>
+
 #include <set>
 #include <list>
 #include <vector>
 #include <xercesc/dom/DOM.hpp>
-#include <xmltooling/QName.h>
-#include <xmltooling/Namespace.h>
 
 using namespace xercesc;
 
index 009b724..45168c5 100644 (file)
@@ -29,6 +29,7 @@
 #include "security/OpenSSLCryptoX509CRL.h"
 #include "signature/CredentialResolver.h"
 #include "soap/SOAP.h"
+#include "soap/SOAPTransport.h"
 #include "util/NDC.h"
 #include "util/ReplayCache.h"
 #include "util/StorageService.h"
@@ -219,6 +220,8 @@ bool XMLToolingInternalConfig::init()
         registerCredentialResolvers();
         registerTrustEngines();
 #endif
+        registerSOAPTransports();
+        initSOAPTransports();
         registerStorageServices();
 
         // Register xml:id as an ID attribute.        
@@ -242,6 +245,8 @@ void XMLToolingInternalConfig::term()
     AttributeExtensibleXMLObject::deregisterIDAttributes();
 
     StorageServiceManager.deregisterFactories();
+    termSOAPTransports();
+    SOAPTransportManager.deregisterFactories();
 #ifndef XMLTOOLING_NO_XMLSEC
     TrustEngineManager.deregisterFactories();
     CredentialResolverManager.deregisterFactories();
index f40c07d..8d5a370 100644 (file)
         static const XMLCh LOCAL_NAME[]
 
 /**
+ * Begins the declaration of an XMLObject specialization with five base classes.
+ * Basic boilerplate includes a protected constructor, empty virtual destructor,
+ * and Unicode constants for the default associated element's name and prefix.
+ * 
+ * @param linkage   linkage specifier for the class
+ * @param cname     the name of the class to declare
+ * @param base      the first base class to derive from using public virtual inheritance
+ * @param base2     the second base class to derive from using public virtual inheritance
+ * @param base3     the third base class to derive from using public virtual inheritance
+ * @param base4     the fourth base class to derive from using public virtual inheritance
+ * @param base5     the fifth base class to derive from using public virtual inheritance
+ * @param desc      documentation comment for class
+ */
+#define BEGIN_XMLOBJECT5(linkage,cname,base,base2,base3,base4,base5,desc) \
+    XMLTOOLING_DOXYGEN(desc) \
+    class linkage cname : public virtual base, public virtual base2, public virtual base3, public virtual base4, public virtual base5 { \
+    protected: \
+        cname() {} \
+    public: \
+        virtual ~cname() {} \
+        XMLTOOLING_DOXYGEN(Type-specific clone method.) \
+        virtual cname* clone##cname() const=0; \
+        XMLTOOLING_DOXYGEN(Element local name) \
+        static const XMLCh LOCAL_NAME[]
+
+/**
  * Ends the declaration of an XMLObject specialization.
  */
 #define END_XMLOBJECT }
diff --git a/xmltooling/security/AbstractPKIXTrustEngine.h b/xmltooling/security/AbstractPKIXTrustEngine.h
new file mode 100644 (file)
index 0000000..7295ed2
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file xmltooling/security/AbstractPKIXTrustEngine.h
+ * 
+ * A trust engine that uses X.509 trust anchors and CRLs associated with a KeyInfoSource
+ * to perform PKIX validation of signatures and certificates.
+ */
+
+#if !defined(__xmltooling_pkixtrust_h__) && !defined(XMLTOOLING_NO_XMLSEC)
+#define __xmltooling_pkixtrust_h__
+
+#include <xmltooling/security/OpenSSLTrustEngine.h>
+#include <xmltooling/security/XSECCryptoX509CRL.h>
+
+namespace xmltooling {
+
+    /**
+     * A trust engine that uses X.509 trust anchors and CRLs associated with a KeyInfoSource
+     * to perform PKIX validation of signatures and certificates.
+     */
+    class XMLTOOL_API AbstractPKIXTrustEngine : public OpenSSLTrustEngine
+    {
+    protected:
+        /**
+         * Constructor.
+         * 
+         * If a DOM is supplied, the following XML content is supported:
+         * 
+         * <ul>
+         *  <li>&lt;KeyResolver&gt; elements with a type attribute
+         * </ul>
+         * 
+         * XML namespaces are ignored in the processing of this content.
+         * 
+         * @param e DOM to supply configuration for provider
+         */
+        AbstractPKIXTrustEngine(const DOMElement* e=NULL);
+        
+        /**
+         * Checks that either the ID for the entity with the given role or the key names
+         * for the given role match the subject or subject alternate names
+         * of the entity's certificate.
+         * 
+         * @param certEE        the credential for the entity to validate
+         * @param keyInfoSource supplies KeyInfo objects to the TrustEngine
+         * 
+         * @return true the name check succeeds, false if not
+         */
+        bool checkEntityNames(X509* certEE, const KeyInfoSource& keyInfoSource) const;
+        
+        /** An inline KeyResolver for extracting certificates out of a signature. */
+        xmlsignature::KeyResolver* m_inlineResolver;
+        
+    public:
+        virtual ~AbstractPKIXTrustEngine();
+
+        virtual bool validate(
+            xmlsignature::Signature& sig,
+            const KeyInfoSource& keyInfoSource,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const;
+
+        virtual bool validate(
+            const XMLCh* sigAlgorithm,
+            const char* sig,
+            xmlsignature::KeyInfo* keyInfo,
+            const char* in,
+            unsigned int in_len,
+            const KeyInfoSource& keyInfoSource,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const;
+
+        virtual bool validate(
+            XSECCryptoX509* certEE,
+            const std::vector<XSECCryptoX509*>& certChain,
+            const KeyInfoSource& keyInfoSource,
+            bool checkName=true,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const;
+
+        virtual bool validate(
+            X509* certEE,
+            STACK_OF(X509)* certChain,
+            const KeyInfoSource& keyInfoSource,
+            bool checkName=true,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const;
+
+        /**
+         * Stateful interface that supplies PKIX validation data to the trust engine.
+         * Applications can adapt this TrustEngine to their environment by returning
+         * implementations of this interface from the getPKIXValidationInfoIterator
+         * method.
+         */
+        class XMLTOOL_API PKIXValidationInfoIterator {
+            MAKE_NONCOPYABLE(PKIXValidationInfoIterator);
+        protected:
+            PKIXValidationInfoIterator() {}
+        public:
+            virtual ~PKIXValidationInfoIterator() {}
+            
+            /**
+             * Advances to the next set of information, if any.
+             * 
+             * @return true iff another set of information is available
+             */
+            virtual bool next()=0;
+            
+            /**
+             * Returns the allowable trust chain verification depth for the
+             * validation data in the current position.
+             * 
+             * @return  allowable trust chain verification depth
+             */
+            virtual int getVerificationDepth() const=0;
+            
+            /**
+             * Returns the set of trust anchors for the validation data in the
+             * current position. Keeping the certificates beyond the lifetime
+             * of the iterator or after advancing to the next position requires
+             * copying them.
+             * 
+             * @return  set of trust anchors
+             */
+            virtual const std::vector<XSECCryptoX509*>& getTrustAnchors() const=0;
+
+            /**
+             * Returns the set of CRLs for the validation data in the
+             * current position. Keeping the CRLs beyond the lifetime
+             * of the iterator or after advancing to the next position requires
+             * copying them.
+             * 
+             * @return  set of CRLs
+             */
+            virtual const std::vector<XSECCryptoX509CRL*>& getCRLs() const=0;
+        };
+        
+        /**
+         * Provides access to the information necessary, for the given key source, for
+         * PKIX validation of credentials. Each set of validation information returned
+         * will be tried, in turn, until one succeeds or no more remain.
+         * The caller must free the returned interface when finished with it.
+         * 
+         * @return interface for obtaining validation data  
+         */
+        virtual PKIXValidationInfoIterator* getPKIXValidationInfoIterator(const KeyInfoSource& pkixSource) const=0;
+    };
+};
+
+#endif /* __xmltooling_pkixtrust_h__ */
diff --git a/xmltooling/security/ChainingTrustEngine.h b/xmltooling/security/ChainingTrustEngine.h
new file mode 100644 (file)
index 0000000..c8b2d31
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file xmltooling/security/ChainingTrustEngine.h
+ * 
+ * X509TrustEngine that uses multiple engines in sequence.
+ */
+
+#if !defined(__xmltooling_chaintrust_h__) && !defined(XMLTOOLING_NO_XMLSEC)
+#define __xmltooling_chaintrust_h__
+
+#include <xmltooling/security/X509TrustEngine.h>
+
+namespace xmltooling {
+
+    /**
+     * X509TrustEngine that uses multiple engines in sequence.
+     */
+    class XMLTOOL_API ChainingTrustEngine : public X509TrustEngine {
+    public:
+        /**
+         * Constructor.
+         * 
+         * If a DOM is supplied, the following XML content is supported:
+         * 
+         * <ul>
+         *  <li>&lt;TrustEngine&gt; elements with a type attribute
+         * </ul>
+         * 
+         * XML namespaces are ignored in the processing of this content.
+         * 
+         * @param e DOM to supply configuration for provider
+         */
+        ChainingTrustEngine(const DOMElement* e=NULL);
+        
+        /**
+         * Destructor will delete any embedded engines.
+         */
+        virtual ~ChainingTrustEngine();
+
+        /**
+         * Adds a trust engine for future calls.
+         * 
+         * @param newEngine trust engine to add
+         */
+        void addTrustEngine(X509TrustEngine* newEngine) {
+            m_engines.push_back(newEngine);
+        }
+
+        /**
+         * Removes a trust engine. The caller must delete the engine if necessary.
+         * 
+         * @param oldEngine trust engine to remove
+         * @return  the old engine
+         */
+        X509TrustEngine* removeTrustEngine(X509TrustEngine* oldEngine) {
+            for (std::vector<X509TrustEngine*>::iterator i=m_engines.begin(); i!=m_engines.end(); i++) {
+                if (oldEngine==(*i)) {
+                    m_engines.erase(i);
+                    return oldEngine;
+                }
+            }
+            return NULL;
+        }
+
+        virtual bool validate(
+            xmlsignature::Signature& sig,
+            const KeyInfoSource& keyInfoSource,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const;
+        virtual bool validate(
+            const XMLCh* sigAlgorithm,
+            const char* sig,
+            xmlsignature::KeyInfo* keyInfo,
+            const char* in,
+            unsigned int in_len,
+            const KeyInfoSource& keyInfoSource,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const;
+        virtual bool validate(
+            XSECCryptoX509* certEE,
+            const std::vector<XSECCryptoX509*>& certChain,
+            const KeyInfoSource& keyInfoSource,
+            bool checkName=true,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const;
+
+    private:
+        std::vector<X509TrustEngine*> m_engines;
+    };
+    
+};
+
+#endif /* __xmltooling_chaintrust_h__ */
diff --git a/xmltooling/security/KeyInfoSource.h b/xmltooling/security/KeyInfoSource.h
new file mode 100644 (file)
index 0000000..6d5bc78
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file xmltooling/security/KeyInfoSource.h
+ * 
+ * Interface for objects that can supply KeyInfo objects to a TrustEngine
+ * via the KeyInfoIterator interface.
+ */
+
+#if !defined(__xmltooling_keysource_h__) && !defined(XMLTOOLING_NO_XMLSEC)
+#define __xmltooling_keysource_h__
+
+#include <xmltooling/base.h>
+#include <string>
+
+namespace xmlsignature {
+    class XMLTOOL_API KeyInfo;
+};
+
+namespace xmltooling {
+
+    /**
+     * Callback interface to supply KeyInfo objects to a TrustEngine.
+     * Applications can adapt TrustEngines to their environment by supplying
+     * implementations of this interface. 
+     */
+    class XMLTOOL_API KeyInfoIterator {
+        MAKE_NONCOPYABLE(KeyInfoIterator);
+    protected:
+        KeyInfoIterator() {}
+    public:
+        virtual ~KeyInfoIterator() {}
+        
+        /**
+         * Indicates whether additional KeyInfo objects are available.
+         * 
+         * @return true iff another KeyInfo object can be fetched
+         */
+        virtual bool hasNext() const=0;
+        
+        /**
+         * Returns the next KeyInfo object available.
+         * 
+         * @return the next KeyInfo object, or NULL if none are left
+         */
+        virtual const xmlsignature::KeyInfo* next()=0;
+    };
+
+    /**
+     * Interface for objects that can supply KeyInfo objects to a TrustEngine
+     * via the KeyInfoIterator interface.
+     */
+    class XMLTOOL_API KeyInfoSource {
+    protected:
+        KeyInfoSource() {}
+    public:
+        virtual ~KeyInfoSource() {}
+
+        /**
+         * Returns the name of this source of keys, for example a peer entity name
+         * or a principal's name.
+         * 
+         * @return  name of key source, or empty string
+         */
+        virtual std::string getName() const=0;
+        
+        /**
+         * Provides access to the KeyInfo information associated with the source.
+         * The caller must free the returned interface when finished with it.
+         * 
+         * @return interface for obtaining KeyInfo data  
+         */
+        virtual KeyInfoIterator* getKeyInfoIterator() const=0;
+    };
+
+};
+
+#endif /* __xmltooling_keysource_h__ */
diff --git a/xmltooling/security/OpenSSLTrustEngine.h b/xmltooling/security/OpenSSLTrustEngine.h
new file mode 100644 (file)
index 0000000..039e29b
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file xmltooling/security/OpenSSLTrustEngine.h
+ * 
+ * Extended TrustEngine interface that adds validation of X.509 credentials
+ * using OpenSSL data types directly for efficiency.
+ */
+
+#if !defined(__xmltooling_openssltrust_h__) && !defined(XMLTOOLING_NO_XMLSEC)
+#define __xmltooling_openssltrust_h__
+
+#include <xmltooling/security/X509TrustEngine.h>
+
+#include <openssl/x509.h>
+
+namespace xmltooling {
+
+    /**
+     * Extended TrustEngine interface that adds validation of X.509 credentials
+     * using OpenSSL data types directly for efficiency.
+     */
+    class XMLTOOL_API OpenSSLTrustEngine : public X509TrustEngine {
+    protected:
+        /**
+         * Constructor.
+         * 
+         * If a DOM is supplied, the following XML content is supported:
+         * 
+         * <ul>
+         *  <li>&lt;KeyResolver&gt; elements with a type attribute
+         * </ul>
+         * 
+         * XML namespaces are ignored in the processing of this content.
+         * 
+         * @param e DOM to supply configuration for provider
+         */
+        OpenSSLTrustEngine(const DOMElement* e=NULL) : X509TrustEngine(e) {}
+        
+    public:
+        virtual ~OpenSSLTrustEngine() {}
+        
+        /**
+         * Determines whether an X.509 credential is valid with respect to the
+         * source of KeyInfo data supplied. It is the responsibility of the
+         * application to ensure that the KeyInfo information supplied is in fact
+         * associated with the peer who presented the credential. 
+         * 
+         * A custom KeyResolver can be supplied from outside the TrustEngine.
+         * Alternatively, one may be specified to the plugin constructor.
+         * A non-caching, inline resolver will be used as a fallback.
+         * 
+         * @param certEE        end-entity certificate to validate
+         * @param certChain     stack of certificates presented for validation (includes certEE)
+         * @param keyInfoSource supplies KeyInfo objects to the TrustEngine
+         * @param checkName     true iff certificate subject/name checking has <b>NOT</b> already occurred
+         * @param keyResolver   optional externally supplied KeyResolver, or NULL
+         */
+        virtual bool validate(
+            X509* certEE,
+            STACK_OF(X509)* certChain,
+            const KeyInfoSource& keyInfoSource,
+            bool checkName=true,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const=0;
+    };
+    
+};
+
+#endif /* __xmltooling_openssltrust_h__ */
index 1d836d9..74bd09b 100644 (file)
@@ -24,6 +24,7 @@
 #if !defined(__xmltooling_trust_h__) && !defined(XMLTOOLING_NO_XMLSEC)
 #define __xmltooling_trust_h__
 
+#include <xmltooling/security/KeyInfoSource.h>
 #include <xmltooling/signature/KeyResolver.h>
 #include <xmltooling/signature/Signature.h>
 
@@ -58,38 +59,10 @@ namespace xmltooling {
         virtual ~TrustEngine();
         
         /**
-         * Callback interface to supply KeyInfo objects to a TrustEngine.
-         * Applications can adapt TrustEngines to their environment by supplying
-         * implementations of this interface, or create specialized TrustEngine APIs
-         * by combining a KeyInfoIterator with a delegated TrustEngine. 
-         */
-        class XMLTOOL_API KeyInfoIterator {
-            MAKE_NONCOPYABLE(KeyInfoIterator);
-        protected:
-            KeyInfoIterator() {}
-        public:
-            virtual ~KeyInfoIterator() {}
-            
-            /**
-             * Indicates whether additional KeyInfo objects are available.
-             * 
-             * @return true iff another KeyInfo object can be fetched
-             */
-            virtual bool hasNext() const=0;
-            
-            /**
-             * Returns the next KeyInfo object available.
-             * 
-             * @return the next KeyInfo object, or NULL if none are left
-             */
-            virtual const xmlsignature::KeyInfo* next()=0;
-        };
-        
-        /**
          * Determines whether an XML signature is correct and valid with respect to
-         * the KeyInfo data supplied. It is the responsibility of the application to
-         * ensure that the KeyInfo information supplied is in fact associated with
-         * the peer who created the signature. 
+         * the source of KeyInfo data supplied. It is the responsibility of the
+         * application to ensure that the KeyInfo information supplied is in fact
+         * associated with the peer who created the signature. 
          * 
          * <p>A custom KeyResolver can be supplied from outside the TrustEngine.
          * Alternatively, one may be specified to the plugin constructor.
@@ -102,23 +75,23 @@ namespace xmltooling {
          */
         virtual bool validate(
             xmlsignature::Signature& sig,
-            KeyInfoIterator& keyInfoSource,
+            const KeyInfoSource& keyInfoSource,
             const xmlsignature::KeyResolver* keyResolver=NULL
             ) const=0;
 
         /**
          * Determines whether a raw signature is correct and valid with respect to
-         * the KeyInfo data supplied. It is the responsibility of the application to
-         * ensure that the KeyInfo information supplied is in fact associated with
-         * the peer who created the signature.
+         * the source of KeyInfo data supplied. It is the responsibility of the
+         * application to ensure that the KeyInfo information supplied is in fact
+         * associated with the peer who created the signature.
          * 
          * <p>A custom KeyResolver can be supplied from outside the TrustEngine.
          * Alternatively, one may be specified to the plugin constructor.
          * A non-caching, inline resolver will be used as a fallback.
          * 
          * <p>Note that the keyInfo parameter is not part of the implicitly trusted
-         * set of key information supplied via the iterator, but rather advisory data
-         * that may have accompanied the signature itself.
+         * set of key information supplied via the KeyInfoSource, but rather advisory
+         * data that may have accompanied the signature itself.
          * 
          * @param sigAlgorithm  XML Signature identifier for the algorithm used
          * @param sig           null-terminated base64-encoded signature value
@@ -135,7 +108,7 @@ namespace xmltooling {
             xmlsignature::KeyInfo* keyInfo,
             const char* in,
             unsigned int in_len,
-            KeyInfoIterator& keyInfoSource,
+            const KeyInfoSource& keyInfoSource,
             const xmlsignature::KeyResolver* keyResolver=NULL
             ) const=0;
     };
@@ -146,7 +119,11 @@ namespace xmltooling {
     void XMLTOOL_API registerTrustEngines();
 
     /** TrustEngine based on explicit knowledge of peer key information. */
-    #define EXPLICIT_KEY_TRUSTENGINE  "org.opensaml.xmlooling.security.ExplicitKeyTrustEngine"
+    #define EXPLICIT_KEY_TRUSTENGINE  "org.opensaml.xmltooling.security.ExplicitKeyTrustEngine"
+    
+    /** TrustEngine that tries multiple engines in sequence. */
+    #define CHAINING_TRUSTENGINE  "org.opensaml.xmltooling.security.ChainingTrustEngine"
+    
 };
 
 #endif /* __xmltooling_trust_h__ */
index 3aabe05..bd76771 100644 (file)
@@ -52,9 +52,9 @@ namespace xmltooling {
         
         /**
          * Determines whether an X.509 credential is valid with respect to the
-         * KeyInfo data supplied. It is the responsibility of the application to
-         * ensure that the KeyInfo information supplied is in fact associated with
-         * the peer who presented the signature
+         * source of KeyInfo data supplied. It is the responsibility of the
+         * application to ensure that the KeyInfo information supplied is in fact
+         * associated with the peer who presented the credential
          * 
          * A custom KeyResolver can be supplied from outside the TrustEngine.
          * Alternatively, one may be specified to the plugin constructor.
@@ -69,7 +69,7 @@ namespace xmltooling {
         virtual bool validate(
             XSECCryptoX509* certEE,
             const std::vector<XSECCryptoX509*>& certChain,
-            TrustEngine::KeyInfoIterator& keyInfoSource,
+            const KeyInfoSource& keyInfoSource,
             bool checkName=true,
             const xmlsignature::KeyResolver* keyResolver=NULL
             ) const=0;
diff --git a/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp b/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp
new file mode 100644 (file)
index 0000000..bd4d350
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ *  Copyright 2006 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.
+ */
+
+/**
+ * AbstractPKIXTrustEngine.cpp
+ * 
+ * A trust engine that uses X.509 trust anchors and CRLs associated with a KeyInfoSource
+ * to perform PKIX validation of signatures and certificates.
+ */
+
+#include "internal.h"
+#include "security/AbstractPKIXTrustEngine.h"
+#include "signature/KeyInfo.h"
+
+#include <log4cpp/Category.hh>
+#include <openssl/x509_vfy.h>
+#include <openssl/x509v3.h>
+#include <xmltooling/security/OpenSSLCryptoX509CRL.h>
+#include <xmltooling/signature/SignatureValidator.h>
+#include <xmltooling/util/NDC.h>
+#include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
+
+using namespace xmlsignature;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+AbstractPKIXTrustEngine::AbstractPKIXTrustEngine(const DOMElement* e) : OpenSSLTrustEngine(e), m_inlineResolver(NULL)
+{
+    m_inlineResolver = XMLToolingConfig::getConfig().KeyResolverManager.newPlugin(INLINE_KEY_RESOLVER,NULL);
+}
+
+AbstractPKIXTrustEngine::~AbstractPKIXTrustEngine()
+{
+    delete m_inlineResolver;
+}
+
+namespace {
+    static int XMLTOOL_DLLLOCAL error_callback(int ok, X509_STORE_CTX* ctx)
+    {
+        if (!ok)
+            Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
+        return ok;
+    }
+
+    static bool XMLTOOL_DLLLOCAL validate(
+        X509* EE, STACK_OF(X509)* untrusted, AbstractPKIXTrustEngine::PKIXValidationInfoIterator* pkixInfo
+        )
+    {
+        Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
+    
+        // First we build a stack of CA certs. These objects are all referenced in place.
+        log.debug("building CA list from PKIX Validation information");
+    
+        // We need this for CRL support.
+        X509_STORE* store=X509_STORE_new();
+        if (!store) {
+            log_openssl();
+            return false;
+        }
+    #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+        X509_STORE_set_flags(store,X509_V_FLAG_CRL_CHECK_ALL);
+    #endif
+    
+        STACK_OF(X509)* CAstack = sk_X509_new_null();
+        
+        // This contains the state of the validate operation.
+        X509_STORE_CTX ctx;
+        
+        const vector<XSECCryptoX509*>& CAcerts = pkixInfo->getTrustAnchors();
+        for (vector<XSECCryptoX509*>::const_iterator i=CAcerts.begin(); i!=CAcerts.end(); ++i) {
+            if ((*i)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) {
+                sk_X509_push(CAstack,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
+            }
+        }
+
+        const vector<XSECCryptoX509CRL*>& crls = pkixInfo->getCRLs();
+        for (vector<XSECCryptoX509CRL*>::const_iterator j=crls.begin(); j!=crls.end(); ++j) {
+            if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) {
+                // owned by store
+                X509_STORE_add_crl(
+                    store,
+                    X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL())
+                    );
+            }
+        }
+     
+        // AFAICT, EE and untrusted are passed in but not owned by the ctx.
+    #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+        if (X509_STORE_CTX_init(&ctx,store,EE,untrusted)!=1) {
+            log_openssl();
+            log.error("unable to initialize X509_STORE_CTX");
+            sk_X509_free(CAstack);
+            X509_STORE_free(store);
+            return false;
+        }
+    #else
+        X509_STORE_CTX_init(&ctx,store,EE,untrusted);
+    #endif
+    
+        // Seems to be most efficient to just pass in the CA stack.
+        X509_STORE_CTX_trusted_stack(&ctx,CAstack);
+        X509_STORE_CTX_set_depth(&ctx,100);    // we check the depth down below
+        X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
+        
+        int ret=X509_verify_cert(&ctx);
+        if (ret==1) {
+            // Now see if the depth was acceptable by counting the number of intermediates.
+            int depth=sk_X509_num(ctx.chain)-2;
+            if (pkixInfo->getVerificationDepth() < depth) {
+                log.error(
+                    "certificate chain was too long (%d intermediates, only %d allowed)",
+                    (depth==-1) ? 0 : depth,
+                    pkixInfo->getVerificationDepth()
+                    );
+                ret=0;
+            }
+        }
+        
+        // Clean up...
+        X509_STORE_CTX_cleanup(&ctx);
+        X509_STORE_free(store);
+        sk_X509_free(CAstack);
+    
+        if (ret==1) {
+            log.info("successfully validated certificate chain");
+            return true;
+        }
+        
+        return false;
+    }
+};
+
+bool AbstractPKIXTrustEngine::checkEntityNames(X509* certEE, const KeyInfoSource& keyInfoSource) const
+{
+    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
+    
+    // Build a list of acceptable names. Transcode the possible key "names" to UTF-8.
+    // For some simple cases, this should handle UTF-8 encoded DNs in certificates.
+    vector<string> keynames;
+    auto_ptr<KeyInfoIterator> keyInfoIter(keyInfoSource.getKeyInfoIterator());
+    while (keyInfoIter->hasNext()) {
+        const KeyInfo* keyInfo = keyInfoIter->next();
+        const vector<KeyName*>& knames=keyInfo->getKeyNames();
+        for (vector<KeyName*>::const_iterator kn_i=knames.begin(); kn_i!=knames.end(); ++kn_i) {
+            const XMLCh* n=(*kn_i)->getName();
+            if (n && *n) {
+                char* kn=toUTF8(n);
+                keynames.push_back(kn);
+                delete[] kn;
+            }
+        }
+    }
+
+    string peername = keyInfoSource.getName();
+    if (!peername.empty())
+        keynames.push_back(peername);
+    
+    char buf[256];
+    X509_NAME* subject=X509_get_subject_name(certEE);
+    if (subject) {
+        // One way is a direct match to the subject DN.
+        // Seems that the way to do the compare is to write the X509_NAME into a BIO.
+        BIO* b = BIO_new(BIO_s_mem());
+        BIO* b2 = BIO_new(BIO_s_mem());
+        BIO_set_mem_eof_return(b, 0);
+        BIO_set_mem_eof_return(b2, 0);
+        // The flags give us LDAP order instead of X.500, with a comma separator.
+        int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
+        string subjectstr,subjectstr2;
+        BIO_flush(b);
+        while ((len = BIO_read(b, buf, 255)) > 0) {
+            buf[len] = '\0';
+            subjectstr+=buf;
+        }
+        log.infoStream() << "certificate subject: " << subjectstr << CategoryStream::ENDLINE;
+        // The flags give us LDAP order instead of X.500, with a comma plus space separator.
+        len=X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
+        BIO_flush(b2);
+        while ((len = BIO_read(b2, buf, 255)) > 0) {
+            buf[len] = '\0';
+            subjectstr2+=buf;
+        }
+        
+        // Check each keyname.
+        for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
+#ifdef HAVE_STRCASECMP
+            if (!strcasecmp(n->c_str(),subjectstr.c_str()) || !strcasecmp(n->c_str(),subjectstr2.c_str())) {
+#else
+            if (!stricmp(n->c_str(),subjectstr.c_str()) || !stricmp(n->c_str(),subjectstr2.c_str())) {
+#endif
+                log.info("matched full subject DN to a key name (%s)", n->c_str());
+                BIO_free(b);
+                BIO_free(b2);
+                return true;
+            }
+        }
+        BIO_free(b);
+        BIO_free(b2);
+
+        log.debug("unable to match DN, trying TLS subjectAltName match");
+        STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(certEE, NID_subject_alt_name, NULL, NULL);
+        if (altnames) {
+            int numalts = sk_GENERAL_NAME_num(altnames);
+            for (int an=0; an<numalts; an++) {
+                const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
+                if (check->type==GEN_DNS || check->type==GEN_URI) {
+                    const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
+                    const int altlen = ASN1_STRING_length(check->d.ia5);
+                    
+                    for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
+#ifdef HAVE_STRCASECMP
+                        if ((check->type==GEN_DNS && !strncasecmp(altptr,n->c_str(),altlen))
+#else
+                        if ((check->type==GEN_DNS && !strnicmp(altptr,n->c_str(),altlen))
+#endif
+                                || (check->type==GEN_URI && !strncmp(altptr,n->c_str(),altlen))) {
+                            log.info("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
+                            GENERAL_NAMES_free(altnames);
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        GENERAL_NAMES_free(altnames);
+            
+        log.debug("unable to match subjectAltName, trying TLS CN match");
+        memset(buf,0,sizeof(buf));
+        if (X509_NAME_get_text_by_NID(subject,NID_commonName,buf,255)>0) {
+            for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
+#ifdef HAVE_STRCASECMP
+                if (!strcasecmp(buf,n->c_str())) {
+#else
+                if (!stricmp(buf,n->c_str())) {
+#endif
+                    log.info("matched subject CN to a key name (%s)", n->c_str());
+                    return true;
+                }
+            }
+        }
+        else
+            log.warn("no common name in certificate subject");
+    }
+    else
+        log.error("certificate has no subject?!");
+    
+    return false;
+}
+
+bool AbstractPKIXTrustEngine::validate(
+    X509* certEE,
+    STACK_OF(X509)* certChain,
+    const KeyInfoSource& keyInfoSource,
+    bool checkName,
+    const KeyResolver* keyResolver
+    ) const
+{
+#ifdef _DEBUG
+    NDC ndc("validate");
+#endif
+    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
+
+    if (!certEE) {
+        log.error("X.509 credential was NULL, unable to perform validation");
+        return false;
+    }
+
+    if (checkName) {
+        log.debug("checking that the certificate name is acceptable");
+        if (!checkEntityNames(certEE,keyInfoSource)) {
+            log.error("certificate name was not acceptable");
+            return false;
+        }
+    }
+    
+    log.debug("performing certificate path validation...");
+
+    auto_ptr<PKIXValidationInfoIterator> pkix(getPKIXValidationInfoIterator(keyInfoSource));
+    while (pkix->next()) {
+        if (::validate(certEE,certChain,pkix.get())) {
+            return true;
+        }
+    }
+
+    log.error("failed to validate certificate chain using supplied PKIX information");
+    return false;
+}
+
+bool AbstractPKIXTrustEngine::validate(
+    XSECCryptoX509* certEE,
+    const vector<XSECCryptoX509*>& certChain,
+    const KeyInfoSource& keyInfoSource,
+    bool checkName,
+    const KeyResolver* keyResolver
+    ) const
+{
+    if (!certEE) {
+#ifdef _DEBUG
+        NDC ndc("validate");
+#endif
+        Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine").error("X.509 credential was NULL, unable to perform validation");
+        return false;
+    }
+    else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
+#ifdef _DEBUG
+        NDC ndc("validate");
+#endif
+        Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine").error("only the OpenSSL XSEC provider is supported");
+        return false;
+    }
+
+    STACK_OF(X509)* untrusted=sk_X509_new_null();
+    for (vector<XSECCryptoX509*>::const_iterator i=certChain.begin(); i!=certChain.end(); ++i) {
+        sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
+    }
+
+    bool ret = validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(),untrusted,keyInfoSource,checkName,keyResolver);
+    sk_X509_free(untrusted);
+    return ret;
+}
+
+bool AbstractPKIXTrustEngine::validate(
+    Signature& sig,
+    const KeyInfoSource& keyInfoSource,
+    const KeyResolver* keyResolver
+    ) const
+{
+#ifdef _DEBUG
+    NDC ndc("validate");
+#endif
+    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
+
+    // Pull the certificate chain out of the signature using an inline KeyResolver.
+    KeyResolver::ResolvedCertificates certs;
+    if (0==m_inlineResolver->resolveCertificates(&sig, certs)) {
+        log.error("unable to perform PKIX validation, signature does not contain any certificates");
+        return false;
+    }
+
+    log.debug("validating signature using certificate from within the signature");
+
+    // Find and save off a pointer to the certificate that unlocks the object.
+    // Most of the time, this will be the first one anyway.
+    XSECCryptoX509* certEE=NULL;
+    SignatureValidator keyValidator;
+    for (vector<XSECCryptoX509*>::const_iterator i=certs.v().begin(); !certEE && i!=certs.v().end(); ++i) {
+        try {
+            keyValidator.setKey((*i)->clonePublicKey());
+            keyValidator.validate(&sig);
+            log.info("signature verified with key inside signature, attempting certificate validation...");
+            certEE=(*i);
+        }
+        catch (ValidationException&) {
+            // trap failures
+        }
+    }
+    
+    if (certEE)
+        return validate(certEE,certs.v(),keyInfoSource,true,keyResolver);
+        
+    log.error("failed to verify signature with embedded certificates");
+    return false;
+}
+
+bool AbstractPKIXTrustEngine::validate(
+    const XMLCh* sigAlgorithm,
+    const char* sig,
+    KeyInfo* keyInfo,
+    const char* in,
+    unsigned int in_len,
+    const KeyInfoSource& keyInfoSource,
+    const KeyResolver* keyResolver
+    ) const
+{
+#ifdef _DEBUG
+    NDC ndc("validate");
+#endif
+    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
+
+    // Pull the certificate chain out of the KeyInfo using an inline KeyResolver.
+    KeyResolver::ResolvedCertificates certs;
+    if (!keyInfo || 0==m_inlineResolver->resolveCertificates(keyInfo, certs)) {
+        log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
+        return false;
+    }
+
+    log.debug("validating signature using certificate from within KeyInfo");
+
+    // Find and save off a pointer to the certificate that unlocks the object.
+    // Most of the time, this will be the first one anyway.
+    XSECCryptoX509* certEE=NULL;
+    SignatureValidator keyValidator;
+    for (vector<XSECCryptoX509*>::const_iterator i=certs.v().begin(); !certEE && i!=certs.v().end(); ++i) {
+        try {
+            auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
+            if (Signature::verifyRawSignature(key.get(), sigAlgorithm, sig, in, in_len)) {
+                log.info("signature verified with key inside signature, attempting certificate validation...");
+                certEE=(*i);
+            }
+        }
+        catch (SignatureException&) {
+            // trap failures
+        }
+    }
+    
+    if (certEE)
+        return validate(certEE,certs.v(),keyInfoSource,true,keyResolver);
+        
+    log.error("failed to verify signature with embedded certificates");
+    return false;
+}
diff --git a/xmltooling/security/impl/ChainingTrustEngine.cpp b/xmltooling/security/impl/ChainingTrustEngine.cpp
new file mode 100644 (file)
index 0000000..361d95c
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  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.
+ */
+
+/**
+ * ChainingTrustEngine.cpp
+ * 
+ * TrustEngine that uses multiple engines in sequence.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "security/ChainingTrustEngine.h"
+
+#include <xercesc/util/XMLUniDefs.hpp>
+
+using namespace xmlsignature;
+using namespace xmltooling;
+using namespace std;
+
+namespace xmltooling {
+    TrustEngine* XMLTOOL_DLLLOCAL ChainingTrustEngineFactory(const DOMElement* const & e)
+    {
+        return new ChainingTrustEngine(e);
+    }
+};
+
+static const XMLCh GenericTrustEngine[] =           UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e);
+static const XMLCh type[] =                         UNICODE_LITERAL_4(t,y,p,e);
+
+ChainingTrustEngine::ChainingTrustEngine(const DOMElement* e) : X509TrustEngine(e) {
+    try {
+        e = e ? xmltooling::XMLHelper::getFirstChildElement(e, GenericTrustEngine) : NULL;
+        while (e) {
+            xmltooling::auto_ptr_char temp(e->getAttributeNS(NULL,type));
+            if (temp.get()) {
+                auto_ptr<TrustEngine> engine(
+                    XMLToolingConfig::getConfig().TrustEngineManager.newPlugin(temp.get(), e)
+                    );
+                X509TrustEngine* x509 = dynamic_cast<X509TrustEngine*>(engine.get());
+                if (x509) {
+                    m_engines.push_back(x509);
+                    engine.release();
+                }
+                else {
+                    throw xmltooling::UnknownExtensionException("Embedded trust engine does not support required interface.");
+                }
+            }
+            e = xmltooling::XMLHelper::getNextSiblingElement(e, GenericTrustEngine);
+        }
+    }
+    catch (xmltooling::XMLToolingException&) {
+        for_each(m_engines.begin(), m_engines.end(), xmltooling::cleanup<X509TrustEngine>());
+        throw;
+    }
+}
+
+ChainingTrustEngine::~ChainingTrustEngine() {
+    for_each(m_engines.begin(), m_engines.end(), xmltooling::cleanup<X509TrustEngine>());
+}
+
+bool ChainingTrustEngine::validate(
+    Signature& sig,
+    const KeyInfoSource& keyInfoSource,
+    const KeyResolver* keyResolver
+    ) const
+{
+    for (vector<X509TrustEngine*>::const_iterator i=m_engines.begin(); i!=m_engines.end(); ++i) {
+        if (static_cast<TrustEngine*>(*i)->validate(sig,keyInfoSource,keyResolver))
+            return true;
+    }
+    return false;
+}
+
+bool ChainingTrustEngine::validate(
+    const XMLCh* sigAlgorithm,
+    const char* sig,
+    KeyInfo* keyInfo,
+    const char* in,
+    unsigned int in_len,
+    const KeyInfoSource& keyInfoSource,
+    const KeyResolver* keyResolver
+    ) const
+{
+    for (vector<X509TrustEngine*>::const_iterator i=m_engines.begin(); i!=m_engines.end(); ++i) {
+        if (static_cast<TrustEngine*>(*i)->validate(sigAlgorithm, sig, keyInfo, in, in_len, keyInfoSource, keyResolver))
+            return true;
+    }
+    return false;
+}
+
+bool ChainingTrustEngine::validate(
+    XSECCryptoX509* certEE,
+    const vector<XSECCryptoX509*>& certChain,
+    const KeyInfoSource& keyInfoSource,
+    bool checkName,
+    const KeyResolver* keyResolver
+    ) const
+{
+    for (vector<X509TrustEngine*>::const_iterator i=m_engines.begin(); i!=m_engines.end(); ++i) {
+        if ((*i)->validate(certEE,certChain,keyInfoSource,checkName,keyResolver))
+            return true;
+    }
+    return false;
+}
index db63081..f33a7cc 100644 (file)
@@ -21,7 +21,7 @@
  */
 
 #include "internal.h"
-#include "security/X509TrustEngine.h"
+#include "security/OpenSSLTrustEngine.h"
 #include "signature/SignatureValidator.h"
 #include "util/NDC.h"
 
@@ -35,31 +35,38 @@ using namespace log4cpp;
 using namespace std;
 
 namespace xmltooling {
-    class XMLTOOL_DLLLOCAL ExplicitKeyTrustEngine : public X509TrustEngine
+    class XMLTOOL_DLLLOCAL ExplicitKeyTrustEngine : public OpenSSLTrustEngine
     {
     public:
-        ExplicitKeyTrustEngine(const DOMElement* e) : X509TrustEngine(e) {}
+        ExplicitKeyTrustEngine(const DOMElement* e) : OpenSSLTrustEngine(e) {}
         virtual ~ExplicitKeyTrustEngine() {}
 
         virtual bool validate(
             Signature& sig,
-            TrustEngine::KeyInfoIterator& keyInfoSource,
+            const KeyInfoSource& keyInfoSource,
+            const KeyResolver* keyResolver=NULL
+            ) const;
+        virtual bool validate(
+            const XMLCh* sigAlgorithm,
+            const char* sig,
+            KeyInfo* keyInfo,
+            const char* in,
+            unsigned int in_len,
+            const KeyInfoSource& keyInfoSource,
             const KeyResolver* keyResolver=NULL
             ) const;
         virtual bool validate(
             XSECCryptoX509* certEE,
             const vector<XSECCryptoX509*>& certChain,
-            TrustEngine::KeyInfoIterator& keyInfoSource,
+            const KeyInfoSource& keyInfoSource,
             bool checkName=true,
             const KeyResolver* keyResolver=NULL
             ) const;
         virtual bool validate(
-            const XMLCh* sigAlgorithm,
-            const char* sig,
-            KeyInfo* keyInfo,
-            const char* in,
-            unsigned int in_len,
-            KeyInfoIterator& keyInfoSource,
+            X509* certEE,
+            STACK_OF(X509)* certChain,
+            const KeyInfoSource& keyInfoSource,
+            bool checkName=true,
             const KeyResolver* keyResolver=NULL
             ) const;
     };
@@ -72,7 +79,7 @@ namespace xmltooling {
 
 bool ExplicitKeyTrustEngine::validate(
     Signature& sig,
-    TrustEngine::KeyInfoIterator& keyInfoSource,
+    const KeyInfoSource& keyInfoSource,
     const KeyResolver* keyResolver
     ) const
 {
@@ -81,15 +88,16 @@ bool ExplicitKeyTrustEngine::validate(
 #endif
     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
     
-    if (!keyInfoSource.hasNext()) {
+    auto_ptr<KeyInfoIterator> keyInfoIter(keyInfoSource.getKeyInfoIterator());
+    if (!keyInfoIter->hasNext()) {
         log.warn("unable to validate signature, no key information available for peer");
         return false;
     }
     
     log.debug("attempting to validate signature with the key information for peer");
     SignatureValidator sigValidator;
-    while (keyInfoSource.hasNext()) {
-        XSECCryptoKey* key = (keyResolver ? keyResolver : m_keyResolver)->resolveKey(keyInfoSource.next());
+    while (keyInfoIter->hasNext()) {
+        XSECCryptoKey* key = (keyResolver ? keyResolver : m_keyResolver)->resolveKey(keyInfoIter->next());
         if (key) {
             log.debug("attempting to validate signature with public key...");
             try {
@@ -119,7 +127,7 @@ bool ExplicitKeyTrustEngine::validate(
     KeyInfo* keyInfo,
     const char* in,
     unsigned int in_len,
-    KeyInfoIterator& keyInfoSource,
+    const KeyInfoSource& keyInfoSource,
     const KeyResolver* keyResolver
     ) const
 {
@@ -128,14 +136,15 @@ bool ExplicitKeyTrustEngine::validate(
 #endif
     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
     
-    if (!keyInfoSource.hasNext()) {
+    auto_ptr<KeyInfoIterator> keyInfoIter(keyInfoSource.getKeyInfoIterator());
+    if (!keyInfoIter->hasNext()) {
         log.warn("unable to validate signature, no key information available for peer");
         return false;
     }
     
     log.debug("attempting to validate signature with the key information for peer");
-    while (keyInfoSource.hasNext()) {
-        auto_ptr<XSECCryptoKey> key((keyResolver ? keyResolver : m_keyResolver)->resolveKey(keyInfoSource.next()));
+    while (keyInfoIter->hasNext()) {
+        auto_ptr<XSECCryptoKey> key((keyResolver ? keyResolver : m_keyResolver)->resolveKey(keyInfoIter->next()));
         if (key.get()) {
             log.debug("attempting to validate signature with public key...");
             try {
@@ -162,7 +171,33 @@ bool ExplicitKeyTrustEngine::validate(
 bool ExplicitKeyTrustEngine::validate(
     XSECCryptoX509* certEE,
     const vector<XSECCryptoX509*>& certChain,
-    TrustEngine::KeyInfoIterator& keyInfoSource,
+    const KeyInfoSource& keyInfoSource,
+    bool checkName,
+    const KeyResolver* keyResolver
+    ) const
+{
+    if (!certEE) {
+#ifdef _DEBUG
+        NDC ndc("validate");
+#endif
+        Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine").error("unable to validate, end-entity certificate was null");
+        return false;
+    }
+    else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
+#ifdef _DEBUG
+        NDC ndc("validate");
+#endif
+        Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine").error("only the OpenSSL XSEC provider is supported");
+        return false;
+    }
+
+    return validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), NULL, keyInfoSource, checkName, keyResolver);
+}
+
+bool ExplicitKeyTrustEngine::validate(
+    X509* certEE,
+    STACK_OF(X509)* certChain,
+    const KeyInfoSource& keyInfoSource,
     bool checkName,
     const KeyResolver* keyResolver
     ) const
@@ -176,22 +211,20 @@ bool ExplicitKeyTrustEngine::validate(
         log.error("unable to validate, end-entity certificate was null");
         return false;
     }
-    else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
-        log.error("only the OpenSSL XSEC provider is supported");
-        return false;
-    }
-    else if (!keyInfoSource.hasNext()) {
+
+    auto_ptr<KeyInfoIterator> keyInfoIter(keyInfoSource.getKeyInfoIterator());
+    if (!keyInfoIter->hasNext()) {
         log.warn("unable to validate, no key information available for peer");
         return false;
     }
 
-    // The new "basic" trust implementation relies solely on certificates living within the
-    // role interface to verify the EE certificate.
+    // The "basic" trust implementation relies solely on certificates living within the
+    // peer interface to verify the EE certificate.
 
     log.debug("attempting to match key information from peer with end-entity certificate");
-    while (keyInfoSource.hasNext()) {
+    while (keyInfoIter->hasNext()) {
         KeyResolver::ResolvedCertificates resolvedCerts;
-        if (0 == (keyResolver ? keyResolver : m_keyResolver)->resolveCertificates(keyInfoSource.next(),resolvedCerts)) {
+        if (0 == (keyResolver ? keyResolver : m_keyResolver)->resolveCertificates(keyInfoIter->next(),resolvedCerts)) {
             log.debug("key information does not resolve to a certificate, skipping it");
             continue;
         }
@@ -201,7 +234,7 @@ bool ExplicitKeyTrustEngine::validate(
             log.error("only the OpenSSL XSEC provider is supported");
             continue;
         }
-        else if (!X509_cmp(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(),static_cast<OpenSSLCryptoX509*>(resolvedCerts.v().front())->getOpenSSLX509())) {
+        else if (!X509_cmp(certEE,static_cast<OpenSSLCryptoX509*>(resolvedCerts.v().front())->getOpenSSLX509())) {
             log.info("end-entity certificate matches certificate from peer key information");
             return true;
         }
index 3158fb8..1f7d246 100644 (file)
@@ -29,13 +29,15 @@ using namespace xmltooling;
 using namespace std;
 
 namespace xmltooling {
-    XMLTOOL_DLLLOCAL PluginManager<TrustEngine,const DOMElement*>::Factory ExplicitKeyTrustEngineFactory; 
+    XMLTOOL_DLLLOCAL PluginManager<TrustEngine,const DOMElement*>::Factory ExplicitKeyTrustEngineFactory;
+    XMLTOOL_DLLLOCAL PluginManager<TrustEngine,const DOMElement*>::Factory ChainingTrustEngineFactory;
 };
 
 void XMLTOOL_API xmltooling::registerTrustEngines()
 {
     XMLToolingConfig& conf=XMLToolingConfig::getConfig();
     conf.TrustEngineManager.registerFactory(EXPLICIT_KEY_TRUSTENGINE, ExplicitKeyTrustEngineFactory);
+    conf.TrustEngineManager.registerFactory(CHAINING_TRUSTENGINE, ChainingTrustEngineFactory);
 }
 
 static const XMLCh GenericKeyResolver[] =           UNICODE_LITERAL_11(K,e,y,R,e,s,o,l,v,e,r);
diff --git a/xmltooling/soap/HTTPSOAPTransport.h b/xmltooling/soap/HTTPSOAPTransport.h
new file mode 100644 (file)
index 0000000..0bc5c35
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file xmltooling/soap/HTTPSOAPTransport.h
+ * 
+ * Encapsulates HTTP SOAP transport layer.
+ */
+
+#ifndef __xmltooling_httpsoaptrans_h__
+#define __xmltooling_httpsoaptrans_h__
+
+#include <xmltooling/soap/SOAPTransport.h>
+#include <string>
+#include <vector>
+
+namespace xmltooling {
+    
+    /**
+     * Encapsulates HTTP SOAP transport layer.
+     */
+    class XMLTOOL_API HTTPSOAPTransport : public virtual SOAPTransport 
+    {
+    protected:
+        HTTPSOAPTransport() {}
+    public:
+        virtual ~HTTPSOAPTransport() {}
+        
+        /**
+         * Sets an outgoing HTTP request header.
+         * 
+         * @param name   name of header, without the colon separator
+         * @param value  header value to send
+         * @return  true iff the header is successfully set
+         */
+        virtual bool setRequestHeader(const char* name, const char* val) const=0;
+        
+        /**
+         * Returns the values of an HTTP response header.
+         * 
+         * @param name  name of header, without the colon separator
+         * @return  reference to array of header values
+         */
+        virtual const std::vector<std::string>& getResponseHeader(const char* name) const=0;
+    };
+
+};
+
+#endif /* __xmltooling_httpsoaptrans_h__ */
diff --git a/xmltooling/soap/OpenSSLSOAPTransport.h b/xmltooling/soap/OpenSSLSOAPTransport.h
new file mode 100644 (file)
index 0000000..5620d78
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file xmltooling/soap/OpenSSLSOAPTransport.h
+ * 
+ * Encapsulates OpenSSL-capable SOAP transport layer.
+ */
+
+#if !defined(__xmltooling_opensslsoaptrans_h__) && !defined(XMLTOOLING_NO_XMLSEC)
+#define __xmltooling_opensslsoaptrans_h__
+
+#include <xmltooling/signature/CredentialResolver.h>
+#include <xmltooling/soap/SOAPTransport.h>
+
+#include <openssl/ssl.h>
+
+namespace xmltooling {
+    
+    /**
+     * Encapsulates OpenSSL-capable SOAP transport layer.
+     */
+    class XMLTOOL_API OpenSSLSOAPTransport : public virtual SOAPTransport 
+    {
+    protected:
+        OpenSSLSOAPTransport() {}
+    public:
+        virtual ~OpenSSLSOAPTransport() {}
+        
+        /** OpenSSL context callback for manipulating credentials and validation behavior. */
+        typedef bool (*ssl_ctx_callback_fn)(SSL_CTX* ssl_ctx, void* userptr);
+
+        /**
+         * Sets a callback function to invoke against the SSL_CTX before the handshake.
+         * 
+         * @param fn        callback function
+         * @param userptr   a caller-supplied value to pass to the callback function
+         * @return true iff the callback was set
+         */
+        virtual bool setSSLCallback(ssl_ctx_callback_fn fn, void* userptr=NULL) const=0;
+    };
+
+};
+
+#endif /* __xmltooling_opensslsoaptrans_h__ */
diff --git a/xmltooling/soap/SOAPClient.h b/xmltooling/soap/SOAPClient.h
new file mode 100644 (file)
index 0000000..3e11436
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file xmltooling/soap/SOAPClient.h
+ * 
+ * Implements SOAP 1.1 messaging over a transport.
+ */
+
+#ifndef __xmltooling_soap11client_h__
+#define __xmltooling_soap11client_h__
+
+#include <xmltooling/soap/SOAP.h>
+#include <xmltooling/soap/SOAPTransport.h>
+
+namespace soap11 {
+    
+    /**
+     * Implements SOAP 1.1 messaging over a transport.
+     * 
+     * In the abstract, this can be a one-way exchange, or use asynchronous
+     * transports, but this is mostly theoretical.
+     */
+    class XMLTOOL_API SOAPClient
+    {
+        MAKE_NONCOPYABLE(SOAPClient);
+    public:
+        SOAPClient() : m_response(NULL) {}
+        virtual ~SOAPClient();
+        
+        /**
+         * Sends the supplied envelope to the identified recipient/endpoint.
+         * 
+         * <p>The caller is responsible for freeing the outgoing envelope.
+         * 
+         * <p>The client object will instantiate a transport layer object
+         * appropriate for the endpoint URL provided and supply it to the
+         * prepareTransport() method below.
+         * 
+         * @param env       SOAP envelope to send
+         * @param to        identifier/name of party to send message to
+         * @param endpoint  URL of endpoint to recieve message
+         */
+        virtual void send(const Envelope* env, const char* to, const char* endpoint);
+        
+        /**
+         * Returns the response message, if any. As long as a response is
+         * "expected" but not available, NULL will be returned. If no response
+         * will be forthcoming, an exception is raised.
+         * 
+         * <p>The caller is responsible for freeing the incoming envelope.
+         */
+        virtual Envelope* receive();
+
+    protected:
+        /**
+         * Allows client to supply transport-layer settings prior to sending message.
+         * 
+         * @param transport reference to transport layer
+         * @return true iff transport preparation was successful 
+         */
+        virtual bool prepareTransport(const xmltooling::SOAPTransport& transport) {}
+        
+        /** Holds response until retrieved by caller. */
+        Envelope* m_response;
+    };
+
+};
+
+#endif /* __xmltooling_soap11client_h__ */
diff --git a/xmltooling/soap/SOAPTransport.h b/xmltooling/soap/SOAPTransport.h
new file mode 100644 (file)
index 0000000..c25f43d
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file xmltooling/soap/SOAPTransport.h
+ * 
+ * Encapsulates a transport layer protocol for sending/receiving messages.
+ */
+
+#ifndef __xmltooling_soaptrans_h__
+#define __xmltooling_soaptrans_h__
+
+#include <xmltooling/base.h>
+#include <iostream>
+
+namespace xmlsignature {
+    class XMLTOOL_API CredentialResolver;
+    class XMLTOOL_API KeyResolver;
+};
+
+namespace xmltooling {
+    
+    class XMLTOOL_API X509TrustEngine;
+    
+    /**
+     * Encapsulates a transport layer protocol for sending/receiving messages.
+     * 
+     * Most of the methods are const, meaning they don't affect the transport
+     * layer until the data is sent.
+     */
+    class XMLTOOL_API SOAPTransport
+    {
+        MAKE_NONCOPYABLE(SOAPTransport);
+    protected:
+        SOAPTransport() {}
+    public:
+        virtual ~SOAPTransport() {}
+        
+        /**
+         * Sets the connection timeout.
+         * 
+         * @param timeout  time to wait for connection to server in seconds, or -1 for no timeout
+         * @return  true iff the transport supports connection timeouts
+         */
+        virtual bool setConnectTimeout(long timeout) const=0;
+        
+        /**
+         * Sets the request timeout.
+         * 
+         * @param timeout  time to wait for a response in seconds, or -1 for no timeout
+         * @return  true iff the transport supports request/response timeouts
+         */
+        virtual bool setTimeout(long timeout) const=0;
+        
+        /**
+         * Common types of transport authentication that may be supported.
+         */
+        enum transport_auth_t {
+            transport_auth_none = 0,
+            transport_auth_basic = 1,
+            transport_auth_digest = 2,
+            transport_auth_ntlm = 3,
+            transport_auth_gss = 4
+        };
+        
+        /**
+         * Sets a particular form of transport authentication and credentials.
+         * 
+         * @param authType  type of transport authentication to use
+         * @param username  username for transport authentication
+         * @param password  simple password/credential for transport authentication
+         * @return  true iff the transport supports the indicated form of authentication
+         */
+        virtual bool setAuth(transport_auth_t authType, const char* username=NULL, const char* password=NULL) const=0;
+
+#ifndef XMLTOOLING_NO_XMLSEC 
+        /**
+         * Provides a CredentialResolver to the transport to supply transport credentials.
+         * The lifetime of the resolver must be longer than the lifetime of this object.
+         * 
+         * <p>The CredentialResolver <strong>MUST</strong> be locked by the caller. 
+         * 
+         * @param credResolver  a locked CredentialResolver instance, or NULL
+         * @return true iff the transport supports the use of a CredentialResolver
+         */
+        virtual bool setCredentialResolver(const xmlsignature::CredentialResolver* credResolver) const=0;
+
+        /**
+         * Provides a TrustEngine to the transport to authenticate the transport peer.
+         * The lifetime of the engine must be longer than the lifetime of this object.
+         * 
+         * @param trustEngine   a TrustEngine instance, or NULL
+         * @param keyResolver   optional externally supplied KeyResolver, or NULL
+         * @return true iff the transport supports the use of a TrustEngine
+         */
+        virtual bool setTrustEngine(const X509TrustEngine* trustEngine, const xmlsignature::KeyResolver* keyResolver=NULL) const=0;
+#endif
+
+        /**
+         * Sends a stream of data over the transport, and writes the results into another.
+         * 
+         * @param in    input stream to send
+         * @param out   output stream to write result into 
+         */        
+        virtual size_t send(std::istream& in, std::ostream& out)=0;
+        
+        /**
+         * Returns the MIME type of the response, if any.
+         * 
+         * @return  MIME type of response, or an empty string
+         */
+        virtual std::string getContentType() const=0;
+    };
+
+    /**
+     * Registers SOAPTransport classes into the runtime.
+     */
+    void XMLTOOL_API registerSOAPTransports();
+    
+    /**
+     * Notifies transport infrastructure to initialize. 
+     */
+    void XMLTOOL_API initSOAPTransports();
+    
+    /**
+     * Notifies transport infrastructure to shutdown. 
+     */
+    void XMLTOOL_API termSOAPTransports();
+
+};
+
+#endif /* __xmltooling_soaptrans_h__ */
diff --git a/xmltooling/soap/impl/CURLSOAPTransport.cpp b/xmltooling/soap/impl/CURLSOAPTransport.cpp
new file mode 100644 (file)
index 0000000..8b2aad3
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * CURLSOAPTransport.cpp
+ * 
+ * libcurl-based SOAPTransport implementation
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "security/OpenSSLTrustEngine.h"
+#include "signature/OpenSSLCredentialResolver.h"
+#include "soap/HTTPSOAPTransport.h"
+#include "soap/OpenSSLSOAPTransport.h"
+#include "util/NDC.h"
+#include "util/Threads.h"
+
+#include <list>
+#include <curl/curl.h>
+#include <log4cpp/Category.hh>
+#include <openssl/x509_vfy.h>
+
+using namespace xmlsignature;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace xmltooling {
+
+    // Manages cache of socket connections via CURL handles.
+    class XMLTOOL_DLLLOCAL CURLPool
+    {
+    public:
+        CURLPool() : m_size(256), m_lock(Mutex::create()),
+            m_log(Category::getInstance(XMLTOOLING_LOGCAT".SOAPTransport.CURLPool")) {}
+        ~CURLPool();
+        
+        CURL* get(const string& to, const char* endpoint);
+        void put(const string& to, const char* endpoint, CURL* handle);
+    
+    private:    
+        typedef map<string,vector<CURL*> > poolmap_t;
+        poolmap_t m_bindingMap;
+        list< vector<CURL*>* > m_pools;
+        long m_size;
+        Mutex* m_lock;
+        Category& m_log;
+    };
+    
+    static XMLTOOL_DLLLOCAL CURLPool* g_CURLPool = NULL;
+    
+    class XMLTOOL_DLLLOCAL CURLSOAPTransport : public HTTPSOAPTransport, public OpenSSLSOAPTransport
+    {
+    public:
+        CURLSOAPTransport(const KeyInfoSource& peer, const char* endpoint)
+                : m_peer(peer), m_endpoint(endpoint), m_handle(NULL), m_headers(NULL),
+                    m_credResolver(NULL), m_trustEngine(NULL), m_keyResolver(NULL),
+                    m_ssl_callback(NULL), m_ssl_userptr(NULL) {
+            m_handle = g_CURLPool->get(peer.getName(), endpoint);
+            curl_easy_setopt(m_handle,CURLOPT_URL,endpoint);
+            curl_easy_setopt(m_handle,CURLOPT_CONNECTTIMEOUT,15);
+            curl_easy_setopt(m_handle,CURLOPT_TIMEOUT,30);
+            curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,0);
+            curl_easy_setopt(m_handle,CURLOPT_USERPWD,NULL);
+            curl_easy_setopt(m_handle,CURLOPT_HEADERDATA,this);
+        }
+        
+        virtual ~CURLSOAPTransport() {
+            curl_slist_free_all(m_headers);
+            curl_easy_setopt(m_handle,CURLOPT_ERRORBUFFER,NULL);
+            g_CURLPool->put(m_peer.getName(), m_endpoint.c_str(), m_handle);
+        }
+
+        bool setConnectTimeout(long timeout) const {
+            return (curl_easy_setopt(m_handle,CURLOPT_CONNECTTIMEOUT,timeout)==CURLE_OK);
+        }
+        
+        bool setTimeout(long timeout) const {
+            return (curl_easy_setopt(m_handle,CURLOPT_TIMEOUT,timeout)==CURLE_OK);
+        }
+        
+        bool setAuth(transport_auth_t authType, const char* username=NULL, const char* password=NULL) const;
+        
+        bool setCredentialResolver(const CredentialResolver* credResolver) const {
+            const OpenSSLCredentialResolver* down = dynamic_cast<const OpenSSLCredentialResolver*>(credResolver);
+            if (!down) {
+                m_credResolver = NULL;
+                return (credResolver==NULL);
+            }
+            m_credResolver = down;
+            return true;
+        }
+        
+        bool setTrustEngine(const X509TrustEngine* trustEngine, const KeyResolver* keyResolver=NULL) const {
+            const OpenSSLTrustEngine* down = dynamic_cast<const OpenSSLTrustEngine*>(trustEngine);
+            if (!down) {
+                m_trustEngine = NULL;
+                m_keyResolver = NULL;
+                return (trustEngine==NULL);
+            }
+            m_trustEngine = down;
+            m_keyResolver = keyResolver;
+            return true;
+        }
+        
+        size_t send(istream& in, ostream& out);
+        
+        string getContentType() const;
+        
+        bool setRequestHeader(const char* name, const char* val) const {
+            string temp(name);
+            temp=temp + ": " + val;
+            m_headers=curl_slist_append(m_headers,temp.c_str());
+            return true;
+        }
+        
+        const vector<string>& getResponseHeader(const char* val) const;
+        
+        bool setSSLCallback(ssl_ctx_callback_fn fn, void* userptr=NULL) const {
+            m_ssl_callback=fn;
+            m_ssl_userptr=userptr;
+            return true;
+        }
+
+    private:        
+        // per-call state
+        const KeyInfoSource& m_peer;
+        string m_endpoint;
+        CURL* m_handle;
+        mutable struct curl_slist* m_headers;
+        map<string,vector<string> > m_response_headers;
+        mutable const OpenSSLCredentialResolver* m_credResolver;
+        mutable const OpenSSLTrustEngine* m_trustEngine;
+        mutable const KeyResolver* m_keyResolver;
+        mutable ssl_ctx_callback_fn m_ssl_callback;
+        mutable void* m_ssl_userptr;
+        
+        friend size_t XMLTOOL_DLLLOCAL curl_header_hook(void* ptr, size_t size, size_t nmemb, void* stream);
+        friend CURLcode XMLTOOL_DLLLOCAL xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* userptr);
+        friend int XMLTOOL_DLLLOCAL verify_callback(X509_STORE_CTX* x509_ctx, void* arg);
+    };
+
+    // libcurl callback functions
+    size_t XMLTOOL_DLLLOCAL curl_header_hook(void* ptr, size_t size, size_t nmemb, void* stream);
+    size_t XMLTOOL_DLLLOCAL curl_write_hook(void* ptr, size_t size, size_t nmemb, void* stream);
+    size_t XMLTOOL_DLLLOCAL curl_read_hook( void *ptr, size_t size, size_t nmemb, void *stream);
+    int XMLTOOL_DLLLOCAL curl_debug_hook(CURL* handle, curl_infotype type, char* data, size_t len, void* ptr);
+    CURLcode XMLTOOL_DLLLOCAL xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* userptr);
+    int XMLTOOL_DLLLOCAL verify_callback(X509_STORE_CTX* x509_ctx, void* arg);
+
+    SOAPTransport* CURLSOAPTransportFactory(const pair<const KeyInfoSource*,const char*>& dest)
+    {
+        return new CURLSOAPTransport(*dest.first, dest.second);
+    }
+};
+
+void xmltooling::registerSOAPTransports()
+{
+    XMLToolingConfig& conf=XMLToolingConfig::getConfig();
+    conf.SOAPTransportManager.registerFactory("http", CURLSOAPTransportFactory);
+    conf.SOAPTransportManager.registerFactory("https", CURLSOAPTransportFactory);
+}
+
+void xmltooling::initSOAPTransports()
+{
+    g_CURLPool=new CURLPool();
+}
+
+void xmltooling::termSOAPTransports()
+{
+    delete g_CURLPool;
+    g_CURLPool = NULL;
+}
+
+CURLPool::~CURLPool()
+{
+    for (poolmap_t::iterator i=m_bindingMap.begin(); i!=m_bindingMap.end(); i++) {
+        for (vector<CURL*>::iterator j=i->second.begin(); j!=i->second.end(); j++)
+            curl_easy_cleanup(*j);
+    }
+    delete m_lock;
+}
+
+CURL* CURLPool::get(const string& to, const char* endpoint)
+{
+#ifdef _DEBUG
+    xmltooling::NDC("get");
+#endif
+    m_log.debug("getting connection handle to %s", endpoint);
+    m_lock->lock();
+    poolmap_t::iterator i=m_bindingMap.find(to + "|" + endpoint);
+    
+    if (i!=m_bindingMap.end()) {
+        // Move this pool to the front of the list.
+        m_pools.remove(&(i->second));
+        m_pools.push_front(&(i->second));
+        
+        // If a free connection exists, return it.
+        if (!(i->second.empty())) {
+            CURL* handle=i->second.back();
+            i->second.pop_back();
+            m_size--;
+            m_lock->unlock();
+            m_log.debug("returning existing connection handle from pool");
+            return handle;
+        }
+    }
+    
+    m_lock->unlock();
+    m_log.debug("nothing free in pool, returning new connection handle");
+    
+    // Create a new connection and set non-varying options.
+    CURL* handle=curl_easy_init();
+    if (!handle)
+        return NULL;
+    curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
+    curl_easy_setopt(handle,CURLOPT_NOSIGNAL,1);
+    curl_easy_setopt(handle,CURLOPT_FAILONERROR,1);
+    curl_easy_setopt(handle,CURLOPT_SSLVERSION,3);
+    curl_easy_setopt(handle,CURLOPT_SSL_VERIFYHOST,2);
+    curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,&curl_header_hook);
+    curl_easy_setopt(handle,CURLOPT_READFUNCTION,&curl_read_hook);
+    curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,&curl_write_hook);
+    curl_easy_setopt(handle,CURLOPT_DEBUGFUNCTION,&curl_debug_hook);
+
+    return handle;
+}
+
+void CURLPool::put(const string& to, const char* endpoint, CURL* handle)
+{
+    string key = to + "|" + endpoint;
+    m_lock->lock();
+    poolmap_t::iterator i=m_bindingMap.find(key);
+    if (i==m_bindingMap.end())
+        m_pools.push_front(&(m_bindingMap.insert(poolmap_t::value_type(key,vector<CURL*>(1,handle))).first->second));
+    else
+        i->second.push_back(handle);
+    
+    CURL* killit=NULL;
+    if (++m_size > 256) {
+        // Kick a handle out from the back of the bus.
+        while (true) {
+            vector<CURL*>* corpse=m_pools.back();
+            if (!corpse->empty()) {
+                killit=corpse->back();
+                corpse->pop_back();
+                m_size--;
+                break;
+            }
+            
+            // Move an empty pool up to the front so we don't keep hitting it.
+            m_pools.pop_back();
+            m_pools.push_front(corpse);
+        }
+    }
+    m_lock->unlock();
+    if (killit) {
+        curl_easy_cleanup(killit);
+#ifdef _DEBUG
+        xmltooling::NDC("put");
+#endif
+        m_log.info("conn_pool_max limit reached, dropping an old connection");
+    }
+}
+
+bool CURLSOAPTransport::setAuth(transport_auth_t authType, const char* username, const char* password) const
+{
+    if (authType==transport_auth_none) {
+        if (curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,0)!=CURLE_OK)
+            return false;
+        return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,NULL)==CURLE_OK);
+    }
+    long flag=0;
+    switch (authType) {
+        case transport_auth_basic:    flag = CURLAUTH_BASIC; break;
+        case transport_auth_digest:   flag = CURLAUTH_DIGEST; break;
+        case transport_auth_ntlm:     flag = CURLAUTH_NTLM; break;
+        case transport_auth_gss:      flag = CURLAUTH_GSSNEGOTIATE; break;
+        default:            return false;
+    }
+    if (curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,flag)!=CURLE_OK)
+        return false;
+    string creds = string(username ? username : "") + ':' + (password ? password : "");
+    return (curl_easy_setopt(m_handle,CURLOPT_USERPWD,creds.c_str())==CURLE_OK);
+}
+
+const vector<string>& CURLSOAPTransport::getResponseHeader(const char* name) const
+{
+    static vector<string> emptyVector;
+
+    map<string,vector<string> >::const_iterator i=m_response_headers.find(name);
+    if (i!=m_response_headers.end())
+        return i->second;
+    
+    for (map<string,vector<string> >::const_iterator j=m_response_headers.begin(); j!=m_response_headers.end(); j++) {
+#ifdef HAVE_STRCASECMP
+        if (!strcasecmp(j->first.c_str(), name))
+#else
+        if (!stricmp(j->first.c_str(), name))
+#endif
+            return j->second;
+    }
+    
+    return emptyVector;
+}
+
+string CURLSOAPTransport::getContentType() const
+{
+    char* content_type=NULL;
+    curl_easy_getinfo(m_handle,CURLINFO_CONTENT_TYPE,&content_type);
+    return content_type ? content_type : "";
+}
+
+size_t CURLSOAPTransport::send(istream& in, ostream& out)
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("send");
+#endif
+    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".SOAPTransport");
+    Category& log_curl=Category::getInstance(XMLTOOLING_LOGCAT".libcurl");
+
+    // By this time, the handle has been prepared with the URL to use and the
+    // caller should have executed any set functions to manipulate it.
+
+    // Setup standard per-call curl properties.
+    size_t content_length=0;
+    pair<ostream*,size_t*> output = make_pair(&out,&content_length); 
+    curl_easy_setopt(m_handle,CURLOPT_POST,1);
+    curl_easy_setopt(m_handle,CURLOPT_READDATA,&in);
+    curl_easy_setopt(m_handle,CURLOPT_FILE,&output);
+    curl_easy_setopt(m_handle,CURLOPT_DEBUGDATA,&log_curl);
+
+    char curl_errorbuf[CURL_ERROR_SIZE];
+    curl_errorbuf[0]=0;
+    curl_easy_setopt(m_handle,CURLOPT_ERRORBUFFER,curl_errorbuf);
+    if (log_curl.isDebugEnabled())
+        curl_easy_setopt(m_handle,CURLOPT_VERBOSE,1);
+
+    // Set request headers (possibly appended by hooks).
+    curl_easy_setopt(m_handle,CURLOPT_HTTPHEADER,m_headers);
+
+    if (m_ssl_callback || m_credResolver || m_trustEngine) {
+        curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,xml_ssl_ctx_callback);
+        curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_DATA,this);
+    }
+    else {
+        curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,NULL);
+        curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_DATA,NULL);
+    }
+    
+    // Verification of the peer is via TrustEngine only.
+    curl_easy_setopt(m_handle,CURLOPT_SSL_VERIFYPEER,0);
+
+    // Make the call.
+    log.info("sending SOAP message to %s", m_endpoint.c_str());
+    if (curl_easy_perform(m_handle) != CURLE_OK) {
+        log.error("failed communicating with SOAP endpoint: %s",
+            (curl_errorbuf[0] ? curl_errorbuf : "no further information available"));
+        throw IOException(
+            string("CURLSOAPTransport::send() failed while contacting SOAP responder: ") +
+                (curl_errorbuf[0] ? curl_errorbuf : "no further information available"));
+    }
+    
+    return content_length;
+}
+
+// callback to buffer headers from server
+size_t xmltooling::curl_header_hook(void* ptr, size_t size, size_t nmemb, void* stream)
+{
+    // only handle single-byte data
+    if (size!=1)
+        return 0;
+    CURLSOAPTransport* ctx = reinterpret_cast<CURLSOAPTransport*>(stream);
+    char* buf = (char*)malloc(nmemb + 1);
+    if (buf) {
+        memset(buf,0,nmemb + 1);
+        memcpy(buf,ptr,nmemb);
+        char* sep=(char*)strchr(buf,':');
+        if (sep) {
+            *(sep++)=0;
+            while (*sep==' ')
+                *(sep++)=0;
+            char* white=buf+nmemb-1;
+            while (isspace(*white))
+                *(white--)=0;
+            ctx->m_response_headers[buf].push_back(sep);
+        }
+        free(buf);
+        return nmemb;
+    }
+    return 0;
+}
+
+// callback to send data to server
+size_t xmltooling::curl_read_hook(void* ptr, size_t size, size_t nmemb, void* stream)
+{
+    // *stream is actually an istream object
+    istream& buf=*(reinterpret_cast<istream*>(stream));
+    buf.read(reinterpret_cast<char*>(ptr),size*nmemb);
+    return buf.gcount();
+}
+
+// callback to buffer data from server
+size_t xmltooling::curl_write_hook(void* ptr, size_t size, size_t nmemb, void* stream)
+{
+    pair<ostream*,size_t*>* output = reinterpret_cast<pair<ostream*,size_t*>*>(stream); 
+    size_t len = size*nmemb;
+    output->first->write(reinterpret_cast<const char*>(ptr),len);
+    *(output->second) += len;
+    return len;
+}
+
+// callback for curl debug data
+int xmltooling::curl_debug_hook(CURL* handle, curl_infotype type, char* data, size_t len, void* ptr)
+{
+    // *ptr is actually a logging object
+    if (!ptr) return 0;
+    CategoryStream log=reinterpret_cast<Category*>(ptr)->debugStream();
+    for (char* ch=data; len && (isprint(*ch) || isspace(*ch)); len--)
+        log << *ch++;
+    log << CategoryStream::ENDLINE;
+    return 0;
+}
+
+int xmltooling::verify_callback(X509_STORE_CTX* x509_ctx, void* arg)
+{
+    Category::getInstance("OpenSSL").debug("invoking X509 verify callback");
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+    CURLSOAPTransport* ctx = reinterpret_cast<CURLSOAPTransport*>(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.
+    CURLSOAPTransport* ctx = reinterpret_cast<CURLSOAPTransport*>(
+        SSL_get_verify_depth(
+            reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(x509_ctx,SSL_get_ex_data_X509_STORE_CTX_idx()))
+            )
+        );
+#endif
+
+     // Bypass name check (handled for us by curl).
+    if (!ctx->m_trustEngine->validate(x509_ctx->cert,x509_ctx->untrusted,ctx->m_peer,false,ctx->m_keyResolver)) {
+        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;
+}
+
+// callback to invoke a caller-defined SSL callback
+CURLcode xmltooling::xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* userptr)
+{
+    CURLSOAPTransport* conf = reinterpret_cast<CURLSOAPTransport*>(userptr);
+    if (conf->m_credResolver)
+        conf->m_credResolver->attach(ssl_ctx);
+
+    if (conf->m_trustEngine) {
+        SSL_CTX_set_verify(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(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(ssl_ctx,reinterpret_cast<int (*)()>(verify_callback),NULL);
+        SSL_CTX_set_verify_depth(ssl_ctx,reinterpret_cast<int>(userptr));
+#endif
+    }
+        
+    if (!conf->m_ssl_callback(ssl_ctx,conf->m_ssl_userptr))
+        return CURLE_SSL_CERTPROBLEM;
+        
+    return CURLE_OK;
+}
index b4bd111..f8888d3 100644 (file)
  * Helper classes and types for manipulating Unicode
  */
  
-#if !defined(__xmltooling_unicode_h__)
+#ifndef __xmltooling_unicode_h__
 #define __xmltooling_unicode_h__
 
+#include <xmltooling/base.h>
+
 #include <string>
 #include <xercesc/util/XMLString.hpp>
-#include <xmltooling/base.h>
 
 using namespace xercesc;
 
index 76dc203..d662bf2 100644 (file)
@@ -62,7 +62,7 @@
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="log4cppD.lib xerces-c_2D.lib xsec_1D.lib libeay32_0_9_8D.lib ssleay32_0_9_8D.lib"\r
+                               AdditionalDependencies="log4cppD.lib xerces-c_2D.lib xsec_1D.lib libeay32_0_9_8D.lib ssleay32_0_9_8D.lib libcurld_imp.lib"\r
                                OutputFile="$(OutDir)\$(ProjectName)_1D.dll"\r
                                LinkIncremental="2"\r
                                GenerateDebugInformation="true"\r
                        />\r
                        <Tool\r
                                Name="VCLinkerTool"\r
-                               AdditionalDependencies="log4cpp.lib xerces-c_2.lib xsec_1.lib libeay32_0_9_8.lib ssleay32_0_9_8.lib"\r
+                               AdditionalDependencies="log4cpp.lib xerces-c_2.lib xsec_1.lib libeay32_0_9_8.lib ssleay32_0_9_8.lib libcurl_imp.lib"\r
                                OutputFile="$(OutDir)\$(ProjectName)_1.dll"\r
                                LinkIncremental="2"\r
                                GenerateDebugInformation="true"\r
                                        Name="impl"\r
                                        >\r
                                        <File\r
+                                               RelativePath=".\security\impl\AbstractPKIXTrustEngine.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
+                                               RelativePath=".\security\impl\ChainingTrustEngine.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\security\impl\ExplicitKeyTrustEngine.cpp"\r
                                                >\r
                                        </File>\r
                                        Name="impl"\r
                                        >\r
                                        <File\r
+                                               RelativePath=".\soap\impl\CURLSOAPTransport.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\soap\impl\SOAPImpl.cpp"\r
                                                >\r
                                        </File>\r
                                Name="security"\r
                                >\r
                                <File\r
+                                       RelativePath=".\security\AbstractPKIXTrustEngine.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath=".\security\ChainingTrustEngine.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath=".\security\KeyInfoSource.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\security\OpenSSLCryptoX509CRL.h"\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\security\OpenSSLTrustEngine.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\security\TrustEngine.h"\r
                                        >\r
                                </File>\r
                                Name="soap"\r
                                >\r
                                <File\r
+                                       RelativePath=".\soap\HTTPSOAPTransport.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath=".\soap\OpenSSLSOAPTransport.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\soap\SOAP.h"\r
                                        >\r
                                </File>\r
+                               <File\r
+                                       RelativePath=".\soap\SOAPClient.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath=".\soap\SOAPTransport.h"\r
+                                       >\r
+                               </File>\r
                        </Filter>\r
                </Filter>\r
                <Filter\r
diff --git a/xmltooling/xmltoolingconfig.h b/xmltooling/xmltoolingconfig.h
new file mode 100644 (file)
index 0000000..a815dd4
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file XMLToolingConfig.h
+ * 
+ * Library configuration 
+ */
+
+#ifndef __xmltooling_config_h__
+#define __xmltooling_config_h__
+
+#include <xmltooling/Lockable.h>
+#include <xmltooling/PluginManager.h>
+#include <xmltooling/util/ParserPool.h>
+
+#ifndef XMLTOOLING_NO_XMLSEC
+namespace xmlsignature {
+    class XMLTOOL_API CredentialResolver;
+    class XMLTOOL_API KeyResolver;
+};
+#endif
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4251 )
+#endif
+
+namespace xmltooling {
+    
+    class XMLTOOL_API ReplayCache;
+    class XMLTOOL_API SOAPTransport;
+    class XMLTOOL_API StorageService;
+    class XMLTOOL_API TemplateEngine;
+    class XMLTOOL_API TrustEngine;
+    class XMLTOOL_API KeyInfoSource;
+    class XMLTOOL_API XSECCryptoX509CRL;
+
+    /**
+     * Singleton object that manages library startup/shutdown.configuration.
+     * 
+     * A locking interface is supplied as a convenience for code that wants to
+     * obtain a global system lock, but the actual configuration itself is not
+     * synchronized.
+     */
+    class XMLTOOL_API XMLToolingConfig : public Lockable
+    {
+        MAKE_NONCOPYABLE(XMLToolingConfig);
+    protected:
+        XMLToolingConfig() : m_replayCache(NULL), clock_skew_secs(180) {}
+        
+        /** Global ReplayCache instance. */
+        ReplayCache* m_replayCache;
+        
+        /** Global TemplateEngine instance. */
+        TemplateEngine* m_templateEngine;
+    public:
+        virtual ~XMLToolingConfig() {}
+
+        /**
+         * Returns the global configuration object for the library.
+         * 
+         * @return reference to the global library configuration object
+         */
+        static XMLToolingConfig& getConfig();
+        
+        /**
+         * Initializes library
+         * 
+         * Each process using the library MUST call this function exactly once
+         * before using any library classes except for the LogConfig method.
+         * 
+         * @return true iff initialization was successful 
+         */
+        virtual bool init()=0;
+        
+        /**
+         * Shuts down library
+         * 
+         * Each process using the library SHOULD call this function exactly once
+         * before terminating itself
+         */
+        virtual void term()=0;
+
+        /**
+         * Loads a shared/dynamic library extension.
+         * 
+         * Extension libraries are managed using a pair of "C" linkage functions:<br>
+         *      extern "C" int xmltooling_extension_init(void* context);<br>
+         *      extern "C" void xmltooling_extension_term();
+         * 
+         * This method is internally synchronized.
+         * 
+         * @param path      pathname of shared library to load into process
+         * @param context   arbitrary data to pass to library initialization hook
+         * @return true iff library was loaded successfully
+         */
+        virtual bool load_library(const char* path, void* context=NULL)=0;
+        
+        /**
+         * Configure logging system.
+         * 
+         * May be called first, before initializing the library. Other calls to it
+         * must be externally synchronized. 
+         * 
+         * @param config    either a logging configuration file, or a level from the set
+         *                  (DEBUG, INFO, NOTICE, WARN, ERROR, CRIT, ALERT, FATAL, EMERG)
+         * @return true iff configuration was successful
+         */
+        virtual bool log_config(const char* config=NULL)=0;
+
+        /**
+         * Obtains a non-validating parser pool.
+         * Library must be initialized first.
+         *
+         * @return reference to a non-validating parser pool.
+         */
+        virtual ParserPool& getParser() const=0;
+
+        /**
+         * Obtains a validating parser pool.
+         * Library must be initialized first. Schema/catalog registration must be
+         * externally synchronized.
+         *
+         * @return reference to a validating parser pool.
+         */
+        virtual ParserPool& getValidatingParser() const=0;
+
+        /**
+         * Sets the global ReplayCache instance.
+         * This method must be externally synchronized with any code that uses the object.
+         * Any previously set object is destroyed.
+         * 
+         * @param replayCache   new ReplayCache instance to store
+         */
+        void setReplayCache(ReplayCache* replayCache);
+
+        /**
+         * Returns the global ReplayCache instance.
+         * 
+         * @return  global ReplayCache or NULL
+         */
+        ReplayCache* getReplayCache() const {
+            return m_replayCache;
+        }
+
+        /**
+         * Sets the global TemplateEngine instance.
+         * This method must be externally synchronized with any code that uses the object.
+         * Any previously set object is destroyed.
+         * 
+         * @param templateEngine   new TemplateEngine instance to store
+         */
+        void setTemplateEngine(TemplateEngine* templateEngine);
+
+        /**
+         * Returns the global TemplateEngine instance.
+         * 
+         * @return  global TemplateEngine or NULL
+         */
+        TemplateEngine* getTemplateEngine() const {
+            return m_templateEngine;
+        }
+                
+        /**
+         * List of catalog files to load into validating parser pool at initialization time.
+         * Like other path settings, the separator depends on the platform
+         * (semicolon on Windows, colon otherwise). 
+         */
+        std::string catalog_path;
+        
+        /**
+         * Adjusts any clock comparisons to be more liberal/permissive by the
+         * indicated number of seconds.
+         */
+        unsigned int clock_skew_secs;
+
+#ifndef XMLTOOLING_NO_XMLSEC
+        /**
+         * Returns an X.509 CRL implementation object.
+         */
+        virtual XSECCryptoX509CRL* X509CRL() const=0;
+
+        /**
+         * Manages factories for KeyResolver plugins.
+         */
+        PluginManager<xmlsignature::KeyResolver,const DOMElement*> KeyResolverManager;
+
+        /**
+         * Manages factories for CredentialResolver plugins.
+         */
+        PluginManager<xmlsignature::CredentialResolver,const DOMElement*> CredentialResolverManager;
+
+        /**
+         * Manages factories for TrustEngine plugins.
+         */
+        PluginManager<TrustEngine,const DOMElement*> TrustEngineManager;
+#endif
+
+        /**
+         * Manages factories for SOAPTransport plugins.
+         */
+        PluginManager<SOAPTransport,std::pair<const KeyInfoSource*,const char*>> SOAPTransportManager;
+
+        /**
+         * Manages factories for StorageService plugins.
+         */
+        PluginManager<StorageService,const DOMElement*> StorageServiceManager;
+    };
+
+};
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+#endif /* __xmltooling_config_h__ */