Raw signature trust support, Redirect binding, "simple" signing rule.
authorcantor <cantor@fb386ef7-a10c-0410-8ebf-fd3f8e989ab0>
Tue, 7 Nov 2006 04:58:24 +0000 (04:58 +0000)
committercantor <cantor@fb386ef7-a10c-0410-8ebf-fd3f8e989ab0>
Tue, 7 Nov 2006 04:58:24 +0000 (04:58 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-opensaml2/trunk@115 fb386ef7-a10c-0410-8ebf-fd3f8e989ab0

22 files changed:
saml/Makefile.am
saml/SAMLConfig.cpp
saml/binding/SecurityPolicyRule.h
saml/binding/SimpleSigningRule.h [new file with mode: 0644]
saml/binding/impl/MessageEncoder.cpp
saml/binding/impl/SecurityPolicy.cpp
saml/binding/impl/SimpleSigningRule.cpp [new file with mode: 0644]
saml/saml.vcproj
saml/saml2/binding/SAML2Redirect.h
saml/saml2/binding/SAML2RedirectEncoder.h [new file with mode: 0644]
saml/saml2/binding/impl/SAML2Redirect.cpp
saml/saml2/binding/impl/SAML2RedirectEncoder.cpp [new file with mode: 0644]
saml/security/AbstractPKIXTrustEngine.h
saml/security/ChainingTrustEngine.h
saml/security/TrustEngine.h
saml/security/impl/AbstractPKIXTrustEngine.cpp
saml/security/impl/ChainingTrustEngine.cpp
saml/security/impl/ExplicitKeyTrustEngine.cpp
samltest/Makefile.am
samltest/binding.h
samltest/saml2/binding/SAML2RedirectTest.h [new file with mode: 0644]
samltest/samltest.vcproj

index 8c3e2f2..5556709 100644 (file)
@@ -45,6 +45,7 @@ samlbindinclude_HEADERS = \
        binding/SAMLArtifact.h \
        binding/SecurityPolicy.h \
        binding/SecurityPolicyRule.h \
+       binding/SimpleSigningRule.h \
        binding/URLEncoder.h
 
 encinclude_HEADERS = \
@@ -87,7 +88,10 @@ saml2bindinclude_HEADERS = \
        saml2/binding/SAML2ArtifactDecoder.h \
        saml2/binding/SAML2ArtifactEncoder.h \
        saml2/binding/SAML2POSTDecoder.h \
-       saml2/binding/SAML2POSTEncoder.h
+       saml2/binding/SAML2POSTEncoder.h \
+       saml2/binding/SAML2Redirect.h \
+       saml2/binding/SAML2RedirectDecoder.h \
+       saml2/binding/SAML2RedirectEncoder.h
 
 saml2mdinclude_HEADERS = \
        saml2/metadata/AbstractMetadataProvider.h \
@@ -110,6 +114,7 @@ libsaml_la_SOURCES = \
        binding/impl/MessageSigningRule.cpp \
        binding/impl/SAMLArtifact.cpp \
        binding/impl/SecurityPolicy.cpp \
+       binding/impl/SimpleSigningRule.cpp \
        binding/impl/URLEncoder.cpp \
        saml1/core/impl/AssertionsImpl.cpp \
        saml1/core/impl/AssertionsSchemaValidators.cpp \
@@ -141,6 +146,9 @@ libsaml_la_SOURCES = \
        saml2/binding/impl/SAML2ArtifactEncoder.cpp \
        saml2/binding/impl/SAML2POSTDecoder.cpp \
        saml2/binding/impl/SAML2POSTEncoder.cpp \
+       saml2/binding/impl/SAML2Redirect.cpp \
+       saml2/binding/impl/SAML2RedirectDecoder.cpp \
+       saml2/binding/impl/SAML2RedirectEncoder.cpp \
        encryption/EncryptedKeyResolver.cpp \
        security/impl/TrustEngine.cpp \
        security/impl/AbstractPKIXTrustEngine.cpp \
index ef24319..5d34221 100644 (file)
@@ -199,9 +199,8 @@ string SAMLInternalConfig::hashSHA1(const char* s, bool toHex)
 
     auto_ptr<XSECCryptoHash> hasher(XSECPlatformUtils::g_cryptoProvider->hashSHA1());
     if (hasher.get()) {
-        auto_ptr<char> dup(strdup(s));
         unsigned char buf[21];
-        hasher->hash(reinterpret_cast<unsigned char*>(dup.get()),strlen(dup.get()));
+        hasher->hash(reinterpret_cast<unsigned char*>(const_cast<char*>(s)),strlen(s));
         if (hasher->finish(buf,20)==20) {
             string ret;
             if (toHex) {
index 749c571..668d097 100644 (file)
@@ -102,12 +102,20 @@ namespace opensaml {
     #define MESSAGEROUTING_POLICY_RULE  "org.opensaml.binding.MessageRoutingRule"
 
     /**
-     * SecurityPolicyRule for protocol message signing.
+     * SecurityPolicyRule for protocol message XML signing.
      * 
-     * Allows the message issuer to be authenticated using an XML or binding-specific
-     * digital signature over the message. The transport layer is not considered.
+     * Allows the message issuer to be authenticated using an XML digital signature
+     * over the message. The transport layer is not considered.
      */
     #define MESSAGESIGNING_POLICY_RULE  "org.opensaml.binding.MessageSigningRule"
+
+    /**
+     * SecurityPolicyRule for protocol message "blob" signing.
+     * 
+     * Allows the message issuer to be authenticated using a non-XML digital signature
+     * over the message body. The transport layer is not considered.
+     */
+    #define SIMPLESIGNING_POLICY_RULE  "org.opensaml.binding.SimpleSigningRule"
 };
 
 #endif /* __saml_secrule_h__ */
diff --git a/saml/binding/SimpleSigningRule.h b/saml/binding/SimpleSigningRule.h
new file mode 100644 (file)
index 0000000..5a2659c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  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 saml/binding/SimpleSigningRule.h
+ * 
+ * Blob-oriented signature checking SecurityPolicyRule
+ */
+
+#include <saml/binding/SecurityPolicyRule.h>
+
+
+namespace opensaml {
+    /**
+     * Blob-oriented signature checking SecurityPolicyRule for
+     * bindings that support non-XML signature techniques.
+     * 
+     * Subclasses can provide support for additional message types
+     * by overriding the issuer derivation method.
+     */
+    class SAML_API SimpleSigningRule : public SecurityPolicyRule
+    {
+    public:
+        SimpleSigningRule(const DOMElement* e) {}
+        virtual ~SimpleSigningRule() {}
+        
+        std::pair<saml2::Issuer*,const saml2md::RoleDescriptor*> evaluate(
+            const GenericRequest& request,
+            const xmltooling::XMLObject& message,
+            const saml2md::MetadataProvider* metadataProvider,
+            const xmltooling::QName* role,
+            const TrustEngine* trustEngine
+            ) const;
+    
+    protected:
+        /**
+         * Examines the message and/or its contents and extracts the issuer's claimed
+         * identity along with a protocol identifier. The two together can be used to
+         * locate metadata to use in validating the signature. Conventions may be needed
+         * to properly encode non-SAML2 issuer information into a compatible form. 
+         * 
+         * <p>The caller is responsible for freeing the Issuer object.
+         * 
+         * @param message       message to examine
+         * @return  a pair consisting of a SAML 2.0 Issuer object and a protocol constant.
+         */
+        virtual std::pair<saml2::Issuer*,const XMLCh*> getIssuerAndProtocol(const xmltooling::XMLObject& message) const;
+    };
+    
+};
index 16ca166..c12aa2e 100644 (file)
@@ -41,6 +41,7 @@ namespace opensaml {
     namespace saml2p {
         SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML2ArtifactEncoderFactory;
         SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML2POSTEncoderFactory;
+        SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML2RedirectEncoderFactory;
     };
 };
 
@@ -51,6 +52,7 @@ void SAML_API opensaml::registerMessageEncoders()
     conf.MessageEncoderManager.registerFactory(samlconstants::SAML1_PROFILE_BROWSER_POST, saml1p::SAML1POSTEncoderFactory);
     conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_ARTIFACT, saml2p::SAML2ArtifactEncoderFactory);
     conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_POST, saml2p::SAML2POSTEncoderFactory);
+    conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_REDIRECT, saml2p::SAML2RedirectEncoderFactory);
 }
 
 namespace {
index 6e9b6ae..882dfac 100644 (file)
@@ -35,6 +35,7 @@ namespace opensaml {
     SAML_DLLLOCAL PluginManager<SecurityPolicyRule,const DOMElement*>::Factory MessageFlowRuleFactory;
     SAML_DLLLOCAL PluginManager<SecurityPolicyRule,const DOMElement*>::Factory MessageRoutingRuleFactory;
     SAML_DLLLOCAL PluginManager<SecurityPolicyRule,const DOMElement*>::Factory MessageSigningRuleFactory;
+    SAML_DLLLOCAL PluginManager<SecurityPolicyRule,const DOMElement*>::Factory SimpleSigningRuleFactory;
 };
 
 void SAML_API opensaml::registerSecurityPolicyRules()
@@ -43,6 +44,7 @@ void SAML_API opensaml::registerSecurityPolicyRules()
     conf.SecurityPolicyRuleManager.registerFactory(MESSAGEFLOW_POLICY_RULE, MessageFlowRuleFactory);
     conf.SecurityPolicyRuleManager.registerFactory(MESSAGEROUTING_POLICY_RULE, MessageRoutingRuleFactory);
     conf.SecurityPolicyRuleManager.registerFactory(MESSAGESIGNING_POLICY_RULE, MessageSigningRuleFactory);
+    conf.SecurityPolicyRuleManager.registerFactory(SIMPLESIGNING_POLICY_RULE, SimpleSigningRuleFactory);
 }
 
 SecurityPolicy::~SecurityPolicy()
diff --git a/saml/binding/impl/SimpleSigningRule.cpp b/saml/binding/impl/SimpleSigningRule.cpp
new file mode 100644 (file)
index 0000000..9641ab5
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ *  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.
+ */
+
+/**
+ * SimpleSigningRule.cpp
+ * 
+ * Blob-oriented signature checking SecurityPolicyRule
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "RootObject.h"
+#include "binding/HTTPRequest.h"
+#include "binding/SimpleSigningRule.h"
+#include "saml2/core/Protocols.h"
+#include "saml2/metadata/Metadata.h"
+#include "saml2/metadata/MetadataProvider.h"
+#include "security/TrustEngine.h"
+
+#include <log4cpp/Category.hh>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/ReplayCache.h>
+#include <xsec/enc/XSECCryptoException.hpp>
+#include <xsec/enc/XSECCryptoProvider.hpp>
+#include <xsec/framework/XSECException.hpp>
+
+using namespace opensaml::saml2md;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace opensaml {
+    SecurityPolicyRule* SAML_DLLLOCAL SimpleSigningRuleFactory(const DOMElement* const & e)
+    {
+        return new SimpleSigningRule(e);
+    }
+
+    // Appends a raw parameter=value pair to the string.
+    static bool appendParameter(string& s, const char* data, const char* name)
+    {
+        const char* start = strstr(data,name);
+        if (!start)
+            return false;
+        if (!s.empty())
+            s += '&';
+        const char* end = strchr(start,'&');
+        if (end)
+            s.append(start, end-start);
+        else
+            s.append(start);
+        return true;
+    }
+};
+
+
+pair<saml2::Issuer*,const saml2md::RoleDescriptor*> SimpleSigningRule::evaluate(
+    const GenericRequest& request,
+    const XMLObject& message,
+    const MetadataProvider* metadataProvider,
+    const QName* role,
+    const opensaml::TrustEngine* trustEngine
+    ) const
+{
+    Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SimpleSigning");
+    log.debug("evaluating simple signing policy");
+    
+    pair<saml2::Issuer*,const RoleDescriptor*> ret = pair<saml2::Issuer*,const RoleDescriptor*>(NULL,NULL);  
+    
+    if (!metadataProvider || !role || !trustEngine) {
+        log.debug("ignoring message, no metadata supplied");
+        return ret;
+    }
+    
+    const char* signature = request.getParameter("Signature");
+    if (!signature) {
+        log.debug("ignoring unsigned message");
+        return ret;
+    }
+    
+    const char* sigAlgorithm = request.getParameter("SigAlg");
+    if (!sigAlgorithm) {
+        log.error("SigAlg parameter not found, no way to verify the signature");
+        return ret;
+    }
+
+    try {
+        log.debug("extracting issuer from message");
+        pair<saml2::Issuer*,const XMLCh*> issuerInfo = getIssuerAndProtocol(message);
+        
+        auto_ptr<saml2::Issuer> issuer(issuerInfo.first);
+        if (!issuerInfo.first || !issuerInfo.second ||
+                (issuer->getFormat() && !XMLString::equals(issuer->getFormat(), saml2::NameIDType::ENTITY))) {
+            log.warn("issuer identity not estabished, or was not an entityID");
+            return ret;
+        }
+        
+        log.debug("searching metadata for message issuer...");
+        const EntityDescriptor* entity = metadataProvider->getEntityDescriptor(issuer->getName());
+        if (!entity) {
+            auto_ptr_char temp(issuer->getName());
+            log.warn("no metadata found, can't establish identity of issuer (%s)", temp.get());
+            return ret;
+        }
+
+        log.debug("matched assertion issuer against metadata, searching for applicable role...");
+        const RoleDescriptor* roledesc=entity->getRoleDescriptor(*role, issuerInfo.second);
+        if (!roledesc) {
+            log.warn("unable to find compatible role (%s) in metadata", role->toString().c_str());
+            return ret;
+        }
+
+        // We have to construct a string containing the signature input by accessing the
+        // request directly. We can't use the decoded parameters because we need the raw
+        // data and URL-encoding isn't canonical.
+        string input;
+        const HTTPRequest& httpRequest = dynamic_cast<const HTTPRequest&>(request);
+        const char* raw =
+            (!strcmp(httpRequest.getMethod(), "GET")) ? httpRequest.getQueryString() : httpRequest.getRequestBody();
+        if (!appendParameter(input, raw, "SAMLRequest="))
+            appendParameter(input, raw, "SAMLResponse=");
+        appendParameter(input, raw, "RelayState=");
+        appendParameter(input, raw, "SigAlg=");
+
+        auto_ptr_XMLCh alg(sigAlgorithm);
+        if (!trustEngine->validate(alg.get(), signature, NULL, input.c_str(), input.length(), *roledesc, metadataProvider->getKeyResolver())) {
+            log.error("unable to verify signature on message with supplied trust engine");
+            return ret;
+        }
+
+        if (log.isDebugEnabled()) {
+            auto_ptr_char iname(entity->getEntityID());
+            log.debug("message from (%s), signature verified", iname.get());
+        }
+        
+        ret.first = issuer.release();
+        ret.second = roledesc;
+    }
+    catch (bad_cast&) {
+        // Just trap it.
+        log.warn("caught a bad_cast while extracting issuer");
+    }
+    return ret;
+}
+
+pair<saml2::Issuer*,const XMLCh*> SimpleSigningRule::getIssuerAndProtocol(const XMLObject& message) const
+{
+    // We just let any bad casts throw here.
+
+    // Shortcuts some of the casting.
+    const XMLCh* ns = message.getElementQName().getNamespaceURI();
+    if (ns) {
+        if (XMLString::equals(ns, samlconstants::SAML20P_NS) || XMLString::equals(ns, samlconstants::SAML20_NS)) {
+            // 2.0 namespace should be castable to a specialized 2.0 root.
+            const saml2::RootObject& root = dynamic_cast<const saml2::RootObject&>(message);
+            saml2::Issuer* issuer = root.getIssuer();
+            if (issuer && issuer->getName()) {
+                return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
+            }
+            
+            // No issuer in the message, so we have to try the Response approach. 
+            const vector<saml2::Assertion*>& assertions = dynamic_cast<const saml2p::Response&>(message).getAssertions();
+            if (!assertions.empty()) {
+                issuer = assertions.front()->getIssuer();
+                if (issuer && issuer->getName())
+                    return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
+            }
+        }
+    }
+    return pair<saml2::Issuer*,const XMLCh*>(NULL,NULL);
+}
index ed0106b..39548a4 100644 (file)
                        <Tool\r
                                Name="VCLinkerTool"\r
                                AdditionalDependencies="..\..\cpp-xmltooling\Debug\xmltooling_1D.lib xerces-c_2D.lib xsec_1D.lib log4cppD.lib libeay32_0_9_8D.lib"\r
-                               OutputFile="$(OutDir)\$(ProjectName)_6D.dll"\r
+                               OutputFile="$(OutDir)\$(ProjectName)2_0D.dll"\r
                                LinkIncremental="2"\r
                                GenerateDebugInformation="true"\r
                                SubSystem="2"\r
+                               ImportLibrary="$(TargetDir)$(ProjectName)2D.lib"\r
                                TargetMachine="1"\r
                        />\r
                        <Tool\r
                        <Tool\r
                                Name="VCLinkerTool"\r
                                AdditionalDependencies="..\..\cpp-xmltooling\Release\xmltooling_1.lib xerces-c_2.lib xsec_1.lib log4cpp.lib libeay32_0_9_8.lib"\r
-                               OutputFile="$(OutDir)\$(ProjectName)_6.dll"\r
+                               OutputFile="$(OutDir)\$(ProjectName)2_0.dll"\r
                                LinkIncremental="1"\r
                                GenerateDebugInformation="true"\r
                                SubSystem="2"\r
                                OptimizeReferences="2"\r
                                EnableCOMDATFolding="2"\r
+                               ImportLibrary="$(TargetDir)$(ProjectName)2.lib"\r
                                TargetMachine="1"\r
                        />\r
                        <Tool\r
                                                        RelativePath=".\saml2\binding\impl\SAML2RedirectDecoder.cpp"\r
                                                        >\r
                                                </File>\r
+                                               <File\r
+                                                       RelativePath=".\saml2\binding\impl\SAML2RedirectEncoder.cpp"\r
+                                                       >\r
+                                               </File>\r
                                        </Filter>\r
                                </Filter>\r
                        </Filter>\r
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\binding\impl\SimpleSigningRule.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\binding\impl\URLEncoder.cpp"\r
                                                >\r
                                        </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\zlib\compress.c"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\zlib\crc32.c"\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\zlib\deflate.c"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\zlib\inffast.c"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\zlib\trees.c"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\zlib\zutil.c"\r
                                        >\r
                                </File>\r
                                                RelativePath=".\saml2\binding\SAML2RedirectDecoder.h"\r
                                                >\r
                                        </File>\r
+                                       <File\r
+                                               RelativePath=".\saml2\binding\SAML2RedirectEncoder.h"\r
+                                               >\r
+                                       </File>\r
                                </Filter>\r
                        </Filter>\r
                        <Filter\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\binding\SimpleSigningRule.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\binding\URLEncoder.h"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\zlib\deflate.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\zlib\inffast.h"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\zlib\trees.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\zlib\zconf.h"\r
                                        >\r
                                </File>\r
index d6b8400..efca83a 100644 (file)
 
 namespace opensaml {
     namespace saml2p {
+        /**
+         * Deflates data in accordance with RFC1951. The caller must free the
+         * resulting buffer using delete[]
+         * 
+         * @param in        the data to compress
+         * @param in_len    length of input data
+         * @param out_len   will contain the length of the resulting data
+         * @return  allocated buffer of out_len bytes containing deflated data
+         */
+        char* deflate(char* in, unsigned int in_len, unsigned int* out_len);
         
-        unsigned int inflate(char* in, unsigned int inlen, std::ostream& out);
-        
+        /**
+         * Inflates data compressed in accordance with RFC1951 and sends the
+         * results to an output stream.
+         * 
+         * @param in        the data to inflate
+         * @param in_len    length of input data
+         * @param out       reference to output stream to receive data
+         * @return  number of bytes written to stream
+         */
+        unsigned int inflate(char* in, unsigned int in_len, std::ostream& out);
     };
 };
diff --git a/saml/saml2/binding/SAML2RedirectEncoder.h b/saml/saml2/binding/SAML2RedirectEncoder.h
new file mode 100644 (file)
index 0000000..36b7367
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ *  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 saml/saml2/binding/SAML2RedirectEncoder.h
+ * 
+ * SAML 2.0 HTTP-Redirect binding message encoder
+ */
+
+#include <saml/binding/MessageEncoder.h>
+
+
+namespace opensaml {
+    namespace saml2p {
+
+        /**
+         * SAML 2.0 HTTP-Redirect binding message encoder
+         */
+        class SAML_API SAML2RedirectEncoder : public MessageEncoder
+        {
+        public:
+            SAML2RedirectEncoder(const DOMElement* e) {}
+            virtual ~SAML2RedirectEncoder() {}
+            
+            long encode(
+                GenericResponse& genericResponse,
+                xmltooling::XMLObject* xmlObject,
+                const char* destination,
+                const char* recipientID=NULL,
+                const char* relayState=NULL,
+                const xmlsignature::CredentialResolver* credResolver=NULL,
+                const XMLCh* sigAlgorithm=NULL
+                ) const;
+        };
+
+    };
+};
index d221bf3..5da0c6c 100644 (file)
@@ -44,6 +44,46 @@ namespace {
     };
 };
 
+char* opensaml::saml2p::deflate(char* in, unsigned int in_len, unsigned int* out_len)
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("deflate");
+#endif
+    Category& log = Category::getInstance(SAML_LOGCAT".MessageDecoder.SAML2Redirect.zlib");
+
+    z_stream z;
+    memset(&z, 0, sizeof(z_stream));
+    
+    z.zalloc = saml_zalloc;
+    z.zfree = saml_zfree;
+    z.opaque = NULL;
+    z.next_in = (Bytef*)in;
+    z.avail_in = in_len;
+    *out_len = 0;
+
+    int ret = deflateInit2(&z, 9, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
+    if (ret != Z_OK) {
+        log.error("zlib deflateInit2 failed with error code (%d)", ret);
+        return NULL;
+    }
+  
+    int dlen = in_len + (in_len >> 8) + 12;  /* orig_size * 1.001 + 12 */
+    char* out = new char[dlen];
+    z.next_out = (Bytef*)out;
+    z.avail_out = dlen;
+  
+    ret = deflate(&z, Z_FINISH);
+    if (ret != Z_STREAM_END) {
+    deflateEnd(&z);
+        log.error("zlib deflateInit2 failed with error code (%d)", ret);
+        delete[] out;
+    }
+  
+    *out_len = z.total_out;
+    deflateEnd(&z);
+    return out;
+}
+
 unsigned int opensaml::saml2p::inflate(char* in, unsigned int in_len, ostream& out)
 {
 #ifdef _DEBUG
@@ -68,7 +108,7 @@ unsigned int opensaml::saml2p::inflate(char* in, unsigned int in_len, ostream& o
   
     int ret = inflateInit2(&z, -15);
     if (ret != Z_OK) {
-        log.error("zlib inflateInit failed with error code (%d)", ret);
+        log.error("zlib inflateInit2 failed with error code (%d)", ret);
         delete[] buf;
         return 0;
     }
diff --git a/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp b/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp
new file mode 100644 (file)
index 0000000..f345866
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ *  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.
+ */
+
+/**
+ * SAML2RedirectEncoder.cpp
+ * 
+ * SAML 2.0 HTTP-POST binding message encoder
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "binding/HTTPResponse.h"
+#include "binding/URLEncoder.h"
+#include "saml2/binding/SAML2Redirect.h"
+#include "saml2/binding/SAML2RedirectEncoder.h"
+#include "saml2/core/Protocols.h"
+
+#include <fstream>
+#include <sstream>
+#include <log4cpp/Category.hh>
+#include <xercesc/util/Base64.hpp>
+#include <xsec/enc/XSECCryptoException.hpp>
+#include <xsec/enc/XSECCryptoProvider.hpp>
+#include <xsec/framework/XSECException.hpp>
+#include <xmltooling/util/NDC.h>
+
+using namespace opensaml::saml2p;
+using namespace opensaml;
+using namespace xmlsignature;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace opensaml {
+    namespace saml2p {              
+        MessageEncoder* SAML_DLLLOCAL SAML2RedirectEncoderFactory(const DOMElement* const & e)
+        {
+            return new SAML2RedirectEncoder(e);
+        }
+    };
+};
+
+long SAML2RedirectEncoder::encode(
+    GenericResponse& genericResponse,
+    XMLObject* xmlObject,
+    const char* destination,
+    const char* recipientID,
+    const char* relayState,
+    const CredentialResolver* credResolver,
+    const XMLCh* sigAlgorithm
+    ) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("encode");
+#endif
+    Category& log = Category::getInstance(SAML_LOGCAT".MessageEncoder.SAML2POST");
+
+    log.debug("validating input");
+    HTTPResponse* httpResponse=dynamic_cast<HTTPResponse*>(&genericResponse);
+    if (!httpResponse)
+        throw BindingException("Unable to cast response interface to HTTPResponse type.");
+    if (xmlObject->getParent())
+        throw BindingException("Cannot encode XML content with parent.");
+    
+    StatusResponseType* response = NULL;
+    RequestAbstractType* request = dynamic_cast<RequestAbstractType*>(xmlObject);
+    if (!request) {
+        response = dynamic_cast<StatusResponseType*>(xmlObject);
+        if (!response)
+            throw BindingException("XML content for SAML 2.0 HTTP-POST Encoder must be a SAML 2.0 protocol message.");
+    }
+    
+    // Check for XML signature.
+    if (request ? request->getSignature() : response->getSignature()) {
+        log.debug("message already signed, removing native signature due to size considerations");
+        request ? request->setSignature(NULL) : response->setSignature(NULL);
+    }
+    
+    log.debug("marshalling, deflating, base64-encoding the message");
+    DOMElement* rootElement = xmlObject->marshall();
+    string xmlbuf;
+    XMLHelper::serialize(rootElement, xmlbuf);
+    
+    unsigned int len;
+    char* deflated = deflate(const_cast<char*>(xmlbuf.c_str()), xmlbuf.length(), &len);
+    if (!deflated)
+        throw BindingException("Failed to deflate message.");
+    
+    XMLByte* encoded=Base64::encode(reinterpret_cast<XMLByte*>(deflated),len,&len);
+    delete[] deflated;
+    if (!encoded)
+        throw BindingException("Base64 encoding of XML failed.");
+    
+    // Create beginnings of redirect query string.
+    URLEncoder* escaper = SAMLConfig::getConfig().getURLEncoder();
+    xmlbuf.erase();
+    xmlbuf.append(reinterpret_cast<char*>(encoded),len);
+    xmlbuf = (request ? "SAMLRequest=" : "SAMLResponse=") + escaper->encode(xmlbuf.c_str()); 
+    if (relayState)
+        xmlbuf = xmlbuf + "&RelayState=" + escaper->encode(relayState);
+  
+    if (credResolver) {
+        // Sign the query string after adding the algorithm.
+        if (!sigAlgorithm)
+            sigAlgorithm = DSIGConstants::s_unicodeStrURIRSA_SHA1;
+        auto_ptr_char alg(sigAlgorithm);
+        xmlbuf = xmlbuf + "&SigAlg=" + escaper->encode(alg.get());
+
+        try {
+            char sigbuf[1024];
+            memset(sigbuf,0,sizeof(sigbuf));
+            auto_ptr<XSECCryptoKey> key(credResolver->getKey());
+            Signature::createRawSignature(key.get(), sigAlgorithm, xmlbuf.c_str(), xmlbuf.length(), sigbuf, sizeof(sigbuf)-1);
+            xmlbuf = xmlbuf + "&Signature=" + escaper->encode(sigbuf);
+        }
+        catch(XSECException& e) {
+            auto_ptr_char temp(e.getMsg());
+            throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());
+        }
+        catch(XSECCryptoException& e) {
+            throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());
+        }
+    }
+    
+    // Generate redirect.
+    log.debug("message encoded, sending redirect to client");
+    xmlbuf.insert(0,1,(strchr(destination,'?') ? '&' : '?'));
+    xmlbuf.insert(0,destination);
+    long ret = httpResponse->sendRedirect(xmlbuf.c_str());
+
+    // Cleanup by destroying XML.
+    delete xmlObject;
+    
+    return ret;
+}
index b73895e..4df5422 100644 (file)
@@ -76,6 +76,16 @@ namespace opensaml {
             ) const;
 
         virtual bool validate(
+            const XMLCh* sigAlgorithm,
+            const char* sig,
+            xmlsignature::KeyInfo* keyInfo,
+            const char* in,
+            unsigned int in_len,
+            const saml2md::RoleDescriptor& role,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const;
+
+        virtual bool validate(
             XSECCryptoX509* certEE,
             const std::vector<XSECCryptoX509*>& certChain,
             const saml2md::RoleDescriptor& role,
index 4551841..b4f9c78 100644 (file)
@@ -83,6 +83,15 @@ namespace opensaml {
             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 saml2md::RoleDescriptor& role,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const;
+        virtual bool validate(
             XSECCryptoX509* certEE,
             const std::vector<XSECCryptoX509*>& certChain,
             const saml2md::RoleDescriptor& role,
index 3ca02b4..aeb76d9 100644 (file)
@@ -65,12 +65,40 @@ namespace opensaml {
          * @param sig           reference to a signature object to validate
          * @param role          metadata role supplying key information
          * @param keyResolver   optional externally supplied KeyResolver, or NULL
+         * @return  true iff the signature validates
          */
         virtual bool validate(
             xmlsignature::Signature& sig,
             const saml2md::RoleDescriptor& role,
             const xmlsignature::KeyResolver* keyResolver=NULL
             ) const=0;
+
+        /**
+         * Determines whether a raw signature is correct and valid with respect to
+         * the information known about the signer.
+         * 
+         * <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.
+         * 
+         * @param sigAlgorithm  XML Signature identifier for the algorithm used
+         * @param sig           null-terminated base64-encoded signature value
+         * @param keyInfo       KeyInfo object accompanying the signature, if any
+         * @param in            the input data over which the signature was created
+         * @param in_len        size of input data in bytes
+         * @param role          metadata role supplying key information
+         * @param keyResolver   optional externally supplied KeyResolver, or NULL
+         * @return  true iff the signature validates
+         */
+        virtual bool validate(
+            const XMLCh* sigAlgorithm,
+            const char* sig,
+            xmlsignature::KeyInfo* keyInfo,
+            const char* in,
+            unsigned int in_len,
+            const saml2md::RoleDescriptor& role,
+            const xmlsignature::KeyResolver* keyResolver=NULL
+            ) const=0;
     };
     
 
index f7f1fbc..717c438 100644 (file)
@@ -370,3 +370,51 @@ bool AbstractPKIXTrustEngine::validate(Signature& sig, const RoleDescriptor& rol
     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 RoleDescriptor& role,
+    const KeyResolver* keyResolver
+    ) const
+{
+#ifdef _DEBUG
+    NDC ndc("validate");
+#endif
+    Category& log=Category::getInstance(SAML_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(),role,true,keyResolver);
+        
+    log.error("failed to verify signature with embedded certificates");
+    return false;
+}
index f6b95cb..4d61107 100644 (file)
@@ -84,6 +84,23 @@ bool ChainingTrustEngine::validate(
 }
 
 bool ChainingTrustEngine::validate(
+    const XMLCh* sigAlgorithm,
+    const char* sig,
+    KeyInfo* keyInfo,
+    const char* in,
+    unsigned int in_len,
+    const RoleDescriptor& role,
+    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, role, keyResolver))
+            return true;
+    }
+    return false;
+}
+
+bool ChainingTrustEngine::validate(
     XSECCryptoX509* certEE,
     const vector<XSECCryptoX509*>& certChain,
     const RoleDescriptor& role,
index 7249ee9..34219a6 100644 (file)
@@ -60,6 +60,15 @@ namespace opensaml {
             const KeyResolver* keyResolver=NULL
             ) const;
         virtual bool validate(
+            const XMLCh* sigAlgorithm,
+            const char* sig,
+            KeyInfo* keyInfo,
+            const char* in,
+            unsigned int in_len,
+            const RoleDescriptor& role,
+            const KeyResolver* keyResolver=NULL
+            ) const;
+        virtual bool validate(
             XSECCryptoX509* certEE,
             const vector<XSECCryptoX509*>& certChain,
             const RoleDescriptor& role,
@@ -106,6 +115,20 @@ bool ExplicitKeyTrustEngine::validate(
 }
 
 bool ExplicitKeyTrustEngine::validate(
+    const XMLCh* sigAlgorithm,
+    const char* sig,
+    KeyInfo* keyInfo,
+    const char* in,
+    unsigned int in_len,
+    const RoleDescriptor& role,
+    const KeyResolver* keyResolver
+    ) const
+{
+    MetadataKeyInfoIterator keys(role);
+    return static_cast<xmltooling::TrustEngine*>(m_engine)->validate(sigAlgorithm,sig,keyInfo,in,in_len,keys,keyResolver);
+}
+
+bool ExplicitKeyTrustEngine::validate(
     XSECCryptoX509* certEE,
     const vector<XSECCryptoX509*>& certChain,
     const RoleDescriptor& role,
index cb82f02..681cb7a 100644 (file)
@@ -91,6 +91,7 @@ samltest_h = \
     saml2/core/impl/Terminate20Test.h \
     saml2/binding/SAML2ArtifactTest.h \
     saml2/binding/SAML2POSTTest.h \
+    saml2/binding/SAML2RedirectTest.h \
     saml2/metadata/FilesystemMetadataProviderTest.h
 
 noinst_HEADERS = \
index fdd0ce7..4e7117e 100644 (file)
@@ -36,7 +36,7 @@ protected:
     opensaml::TrustEngine* m_trust;\r
     map<string,string> m_fields;\r
     map<string,string> m_headers;\r
-    string m_method,m_url;\r
+    string m_method,m_url,m_query;\r
     vector<XSECCryptoX509*> m_clientCerts;\r
     vector<const SecurityPolicyRule*> m_rules;\r
 \r
@@ -49,6 +49,7 @@ public:
         m_headers.clear();\r
         m_method.erase();\r
         m_url.erase();\r
+        m_query.erase();\r
 \r
         try {\r
             string config = data_path + "binding/ExampleMetadataProvider.xml";\r
@@ -77,7 +78,9 @@ public:
             m_trust = SAMLConfig::getConfig().TrustEngineManager.newPlugin(EXPLICIT_KEY_SAMLTRUSTENGINE, NULL);\r
 \r
             m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(MESSAGEFLOW_POLICY_RULE,NULL));\r
+            m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(MESSAGEROUTING_POLICY_RULE,NULL));\r
             m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(MESSAGESIGNING_POLICY_RULE,NULL));\r
+            m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(SIMPLESIGNING_POLICY_RULE,NULL));\r
         }\r
         catch (XMLToolingException& ex) {\r
             TS_TRACE(ex.what());\r
@@ -99,6 +102,7 @@ public:
         m_headers.clear();\r
         m_method.erase();\r
         m_url.erase();\r
+        m_query.erase();\r
     }\r
 \r
     // HTTPRequest methods\r
@@ -132,7 +136,7 @@ public:
     }\r
     \r
     const char* getQueryString() const {\r
-        return NULL;\r
+        return m_query.c_str();\r
     }\r
     \r
     string getRemoteUser() const {\r
@@ -188,6 +192,7 @@ public:
         char* pch = strchr(dup,'?');\r
         if (pch) {\r
             *pch++=0;\r
+            m_query = pch;\r
             char* name=pch;\r
             while (name && *name) {\r
                 pch=strchr(pch,'=');\r
diff --git a/samltest/saml2/binding/SAML2RedirectTest.h b/samltest/saml2/binding/SAML2RedirectTest.h
new file mode 100644 (file)
index 0000000..92d9741
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *  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.
+ */
+
+#include "binding.h"
+
+#include <saml/saml2/core/Protocols.h>
+
+using namespace opensaml::saml2p;
+using namespace opensaml::saml2;
+
+class SAML2RedirectTest : public CxxTest::TestSuite, public SAMLBindingBaseTestCase {
+public:
+    void setUp() {
+        SAMLBindingBaseTestCase::setUp();
+    }
+
+    void tearDown() {
+        SAMLBindingBaseTestCase::tearDown();
+    }
+
+    void testSAML2Redirect() {
+        try {
+            QName idprole(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);
+            SecurityPolicy policy(m_rules, m_metadata, &idprole, m_trust);
+
+            // Read message to use from file.
+            string path = data_path + "saml2/binding/SAML2Response.xml";
+            ifstream in(path.c_str());
+            DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in);
+            XercesJanitor<DOMDocument> janitor(doc);
+            auto_ptr<Response> toSend(
+                dynamic_cast<Response*>(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(),true))
+                );
+            janitor.release();
+
+            // Freshen timestamp and ID.
+            toSend->setIssueInstant(time(NULL));
+            toSend->setID(NULL);
+    
+            // Encode message.
+            auto_ptr<MessageEncoder> encoder(
+                SAMLConfig::getConfig().MessageEncoderManager.newPlugin(samlconstants::SAML20_BINDING_HTTP_REDIRECT, NULL)
+                );
+            encoder->encode(*this,toSend.get(),"https://sp.example.org/SAML/SSO","https://sp.example.org/","state",m_creds);
+            toSend.release();
+            
+            // Decode message.
+            string relayState;
+            auto_ptr<MessageDecoder> decoder(
+                SAMLConfig::getConfig().MessageDecoderManager.newPlugin(samlconstants::SAML20_BINDING_HTTP_REDIRECT, NULL)
+                );
+            Locker locker(m_metadata);
+            auto_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState,*this,policy)));
+            
+            // Test the results.
+            TSM_ASSERT_EQUALS("RelayState was not the expected result.", relayState, "state");
+            TSM_ASSERT("SAML Response not decoded successfully.", response.get());
+            TSM_ASSERT("Message was not verified.", policy.getIssuer()!=NULL);
+            auto_ptr_char entityID(policy.getIssuer()->getName());
+            TSM_ASSERT("Issuer was not expected.", !strcmp(entityID.get(),"https://idp.example.org/"));
+            TSM_ASSERT_EQUALS("Assertion count was not correct.", response->getAssertions().size(), 1);
+
+            // Trigger a replay.
+            TSM_ASSERT_THROWS("Did not catch the replay.", decoder->decode(relayState,*this,policy), BindingException);
+        }
+        catch (XMLToolingException& ex) {
+            TS_TRACE(ex.what());
+            throw;
+        }
+    }
+};
index cd29fdf..71952f7 100644 (file)
                                                RelativePath=".\saml2\binding\SAML2POSTTest.cpp"\r
                                                >\r
                                        </File>\r
+                                       <File\r
+                                               RelativePath=".\saml2\binding\SAML2RedirectTest.cpp"\r
+                                               >\r
+                                       </File>\r
                                </Filter>\r
                        </Filter>\r
                        <Filter\r
                                                        />\r
                                                </FileConfiguration>\r
                                        </File>\r
+                                       <File\r
+                                               RelativePath=".\saml2\binding\SAML2RedirectTest.h"\r
+                                               >\r
+                                               <FileConfiguration\r
+                                                       Name="Debug|Win32"\r
+                                                       >\r
+                                                       <Tool\r
+                                                               Name="VCCustomBuildTool"\r
+                                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputDir)$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                                               Outputs="&quot;$(InputDir)$(InputName)&quot;.cpp"\r
+                                                       />\r
+                                               </FileConfiguration>\r
+                                               <FileConfiguration\r
+                                                       Name="Release|Win32"\r
+                                                       >\r
+                                                       <Tool\r
+                                                               Name="VCCustomBuildTool"\r
+                                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputDir)$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                                               Outputs="&quot;$(InputDir)$(InputName)&quot;.cpp"\r
+                                                       />\r
+                                               </FileConfiguration>\r
+                                       </File>\r
                                </Filter>\r
                        </Filter>\r
                        <Filter\r