[cpp-xmltooling] branch master updated: SSPCPP-775 - Client-side session storage

Scott Cantor cantor.2 at osu.edu
Mon Mar 19 16:12:22 EDT 2018


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository cpp-xmltooling.

View the commit online:
http://git.shibboleth.net/view/?p=cpp-xmltooling.git;a=commit;h=4e2c6a42b01c5d678ca729f1eda5835f8c973faf

The following commit(s) were added to refs/heads/master by this push:
       new  4e2c6a4    SSPCPP-775 - Client-side session storage
4e2c6a4 is described below

commit 4e2c6a42b01c5d678ca729f1eda5835f8c973faf
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Mar 19 16:11:28 2018 -0400

     SSPCPP-775 - Client-side session storage
    
    https://issues.shibboleth.net/jira/browse/SSPCPP-775
    
    Missed intended check-in of new sources.
---
 xmltooling/security/impl/DataSealer.cpp            |   2 +-
 xmltooling/security/impl/ManagedResource.h         | 125 +++++++++++
 .../impl/VersionedDataSealerKeyStrategy.cpp        | 237 +++++++++++++++++++++
 xmltoolingtest/data/VersionedDataSealer.xml        |   0
 xmltoolingtest/data/sealer.keys                    |   4 +
 5 files changed, 367 insertions(+), 1 deletion(-)

diff --git a/xmltooling/security/impl/DataSealer.cpp b/xmltooling/security/impl/DataSealer.cpp
index f81c496..fa97145 100644
--- a/xmltooling/security/impl/DataSealer.cpp
+++ b/xmltooling/security/impl/DataSealer.cpp
@@ -189,7 +189,7 @@ string DataSealer::unwrap(const char* s) const
 		requiredKey.second = m_strategy->getKey(requiredKey.first.c_str());
 	}
 	if (!requiredKey.second)
-		throw IOException("Required decryption key not available.");
+		throw IOException("Required decryption key ($1) not available.", params(1, requiredKey.first.c_str()));
 
 	m_log.debug("decrypting data with key (%s)", requiredKey.first.c_str());
 
diff --git a/xmltooling/security/impl/ManagedResource.h b/xmltooling/security/impl/ManagedResource.h
new file mode 100644
index 0000000..60be7ee
--- /dev/null
+++ b/xmltooling/security/impl/ManagedResource.h
@@ -0,0 +1,125 @@
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you 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.
+ */
+
+/**
+ * ManagedResource.h
+ *
+ * Internal helper for managing local/remote sources of information.
+ */
+
+#include "internal.h"
+#include "logging.h"
+#include "soap/SOAPTransport.h"
+#include "util/Threads.h"
+
+#include <memory>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+namespace xmltooling {
+
+    // The ManagedResource classes handle memory management, loading of the files
+    // and staleness detection. A copy of the active objects is always stored in
+    // these instances.
+
+    class XMLTOOL_DLLLOCAL ManagedResource {
+    protected:
+        ManagedResource() : local(true), reloadChanges(true), filestamp(0), reloadInterval(0) {}
+        ~ManagedResource() {}
+
+        SOAPTransport* getTransport() {
+            SOAPTransport::Address addr("ManagedResource", source.c_str(), source.c_str());
+            std::string scheme(addr.m_endpoint, strchr(addr.m_endpoint,':') - addr.m_endpoint);
+            SOAPTransport* ret = XMLToolingConfig::getConfig().SOAPTransportManager.newPlugin(scheme.c_str(), addr);
+            if (ret)
+                ret->setCacheTag(&cacheTag);
+            return ret;
+        }
+
+    public:
+        bool stale(logging::Category& log, RWLock* lock=nullptr) {
+            if (local) {
+#ifdef WIN32
+                struct _stat stat_buf;
+				if (_stat(source.c_str(), &stat_buf) != 0) {
+					log.error("unable to stat local resource (%s)", source.c_str());
+					return false;
+				}
+#else
+				struct stat stat_buf;
+				if (stat(source.c_str(), &stat_buf) != 0) {
+					log.error("unable to stat local resource (%s)", source.c_str());	
+					return false;
+				}
+#endif
+                if (filestamp >= stat_buf.st_mtime)
+                    return false;
+
+                // If necessary, elevate lock and recheck.
+                if (lock) {
+                    log.debug("timestamp of local resource changed, elevating to a write lock");
+                    lock->unlock();
+                    lock->wrlock();
+                    if (filestamp >= stat_buf.st_mtime) {
+                        // Somebody else handled it, just downgrade.
+                        log.debug("update of local resource handled by another thread, downgrading lock");
+                        lock->unlock();
+                        lock->rdlock();
+                        return false;
+                    }
+                }
+
+                // Update the timestamp regardless. No point in repeatedly trying.
+                filestamp = stat_buf.st_mtime;
+                log.info("change detected, reloading local resource...");
+            }
+            else {
+                time_t now = time(nullptr);
+
+                // Time to reload?
+                if (now - filestamp < reloadInterval)
+                    return false;
+
+                // If necessary, elevate lock and recheck.
+                if (lock) {
+                    log.debug("reload interval for remote resource elapsed, elevating to a write lock");
+                    lock->unlock();
+                    lock->wrlock();
+                    if (now - filestamp < reloadInterval) {
+                        // Somebody else handled it, just downgrade.
+                        log.debug("update of remote resource handled by another thread, downgrading lock");
+                        lock->unlock();
+                        lock->rdlock();
+                        return false;
+                    }
+                }
+
+                filestamp = now;
+                log.info("reloading remote resource...");
+            }
+            return true;
+        }
+
+        bool local,reloadChanges;
+        std::string source,backing,cacheTag;
+        time_t filestamp,reloadInterval;
+    };
+
+};
diff --git a/xmltooling/security/impl/VersionedDataSealerKeyStrategy.cpp b/xmltooling/security/impl/VersionedDataSealerKeyStrategy.cpp
new file mode 100644
index 0000000..3ff8de1
--- /dev/null
+++ b/xmltooling/security/impl/VersionedDataSealerKeyStrategy.cpp
@@ -0,0 +1,237 @@
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you 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.
+ */
+
+/**
+ * URLEncoder.cpp
+ * 
+ * Interface to a URL-encoding mechanism along with a
+ * default implementation. 
+ */
+
+#include "internal.h"
+#include "io/HTTPResponse.h"
+#include "security/DataSealer.h"
+#include "security/impl/ManagedResource.h"
+#include "soap/HTTPSOAPTransport.h"
+#include "util/PathResolver.h"
+#include "util/NDC.h"
+#include "util/XMLHelper.h"
+
+#include <map>
+#include <fstream>
+#include <boost/smart_ptr/shared_ptr.hpp>
+#include <xercesc/util/Base64.hpp>
+
+using namespace xmltooling::logging;
+using namespace xmltooling;
+using xercesc::Base64;
+using xercesc::DOMElement;
+using namespace std;
+
+namespace xmltooling {
+
+    class XMLTOOL_DLLLOCAL VersionedDataSealerKeyStrategy : public DataSealerKeyStrategy, public ManagedResource {
+    public:
+		VersionedDataSealerKeyStrategy(const DOMElement* e);
+        virtual ~VersionedDataSealerKeyStrategy();
+
+		Lockable* lock();
+		void unlock() {
+			m_lock->unlock();
+		}
+
+        pair<string,const XSECCryptoSymmetricKey*> getDefaultKey() const;
+        const XSECCryptoSymmetricKey* getKey(const char* name) const;
+
+    private:
+		void load();
+		void load(ifstream& in);
+
+    		Category& m_log;
+		auto_ptr<RWLock> m_lock;
+		mutable map< string, boost::shared_ptr<XSECCryptoSymmetricKey> > m_keyMap;
+		string m_default;
+	};
+
+    DataSealerKeyStrategy* XMLTOOL_DLLLOCAL VersionedDataSealerKeyStrategyFactory(const DOMElement* const & e)
+    {
+        return new VersionedDataSealerKeyStrategy(e);
+    }
+
+};
+
+VersionedDataSealerKeyStrategy::VersionedDataSealerKeyStrategy(const DOMElement* e)
+	: m_log(Category::getInstance(XMLTOOLING_LOGCAT".DataSealer")), m_lock(RWLock::create())
+{
+	static const XMLCh backingFilePath[] = UNICODE_LITERAL_15(b,a,c,k,i,n,g,F,i,l,e,P,a,t,h);
+	static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h);
+	static const XMLCh _reloadChanges[] = UNICODE_LITERAL_13(r,e,l,o,a,d,C,h,a,n,g,e,s);
+	static const XMLCh _reloadInterval[] = UNICODE_LITERAL_14(r,e,l,o,a,d,I,n,t,e,r,v,a,l);
+	static const XMLCh url[] = UNICODE_LITERAL_3(u,r,l);
+
+	if (e->hasAttributeNS(nullptr, path)) {
+		source = XMLHelper::getAttrString(e, nullptr, path);
+		XMLToolingConfig::getConfig().getPathResolver()->resolve(source, PathResolver::XMLTOOLING_CFG_FILE);
+		local = true;
+		reloadChanges = XMLHelper::getAttrBool(e, true, _reloadChanges);
+	}
+	else if (e->hasAttributeNS(nullptr, url)) {
+		source = XMLHelper::getAttrString(e, nullptr, url);
+		local = false;
+		backing = XMLHelper::getAttrString(e, nullptr, backingFilePath);
+		if (backing.empty())
+			throw XMLSecurityException("DataSealer can't support remote resource, backingFilePath missing.");
+		XMLToolingConfig::getConfig().getPathResolver()->resolve(backing, PathResolver::XMLTOOLING_CACHE_FILE);
+		reloadInterval = XMLHelper::getAttrInt(e, 0, _reloadInterval);
+	}
+	else {
+		throw XMLSecurityException("DataSealer requires path or url XML attribute.");
+	}
+}
+
+VersionedDataSealerKeyStrategy::~VersionedDataSealerKeyStrategy()
+{
+}
+
+void VersionedDataSealerKeyStrategy::load()
+{
+	if (source.empty())
+		return;
+	m_log.info("loading secret keys from %s (%s)", local ? "local file" : "URL", source.c_str());
+	if (local) {
+		ifstream in(source);
+		load(in);
+	}
+	else {
+		auto_ptr<SOAPTransport> t(getTransport());
+
+		// Fetch the data.
+		t->send();
+		istream& msg = t->receive();
+
+		// Check for "not modified" status.
+		if (dynamic_cast<HTTPSOAPTransport*>(t.get()) && t->getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
+			throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
+
+		// Dump to output file.
+		ofstream out(backing, fstream::trunc | fstream::binary);
+		out << msg.rdbuf();
+		out.close();
+
+		ifstream in(backing);
+		load(in);
+	}
+}
+
+void VersionedDataSealerKeyStrategy::load(ifstream& in)
+{
+	m_default.clear();
+	m_keyMap.clear();
+	
+	string line;
+	while (getline(in, line).good()) {
+		size_t delim = line.find(':');
+		if (delim != string::npos && delim > 0) {
+			string name = line.substr(0, delim);
+
+			XMLSize_t x;
+			XMLByte* decoded = Base64::decode((const XMLByte*) line.c_str() + delim + 1, &x);
+			if (!decoded) {
+				m_log.warn("failed to base64-decode key (%s)", name.c_str());
+				continue;
+			}
+			boost::shared_ptr<XSECCryptoSymmetricKey> key;
+			if (x >= 32) {
+				key.reset(XSECPlatformUtils::g_cryptoProvider->keySymmetric(XSECCryptoSymmetricKey::KEY_AES_256));
+			}
+			else if (x >= 24) {
+				key.reset(XSECPlatformUtils::g_cryptoProvider->keySymmetric(XSECCryptoSymmetricKey::KEY_AES_192));
+			}
+			else if (x >= 16) {
+				key.reset(XSECPlatformUtils::g_cryptoProvider->keySymmetric(XSECCryptoSymmetricKey::KEY_AES_128));
+			}
+			else {
+				XMLString::release((char**)&decoded);
+				m_log.warn("insufficient data to create 128-bit AES key (%s)", name.c_str());
+				continue;
+			}
+			key->setKey(decoded, x);
+			XMLString::release((char**)&decoded);
+
+			m_default = name;
+			m_keyMap[name] = key;
+			m_log.debug("loaded secret key (%s)", name.c_str());
+		}
+	}
+}
+
+Lockable* VersionedDataSealerKeyStrategy::lock()
+{
+#ifdef _DEBUG
+	NDC ndc("lock");
+#endif
+	m_lock->rdlock();
+
+	// Check our managed resource while holding a read lock for staleness.
+	// If it comes back false, the lock is left as is, and the resource was stable.
+	// If it comes back true, the lock was elevated to a write lock, and the resource
+	// needs to be reloaded, and the keys updated.
+
+	bool writelock = false;
+
+	if (stale(m_log, m_lock.get())) {
+		writelock = true;
+		try {
+			load();
+		}
+		catch (long& ex) {
+			if (ex == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED) {
+				m_log.info("remote key source (%s) unchanged from cached version", source.c_str());
+			}
+			else {
+				// Shouldn't happen, we should only get codes intended to be gracefully handled.
+				m_log.crit("maintaining existing keys, remote fetch returned atypical status code (%d)", ex);
+			}
+		}
+		catch (exception& ex) {
+			m_log.crit("maintaining existing keys: %s", ex.what());
+		}
+	}
+
+	if (writelock) {
+		m_lock->unlock();
+		m_lock->rdlock();
+	}
+	return this;
+
+}
+
+pair<string,const XSECCryptoSymmetricKey*> VersionedDataSealerKeyStrategy::getDefaultKey() const
+{
+	const XSECCryptoSymmetricKey* key = m_keyMap[m_default].get();
+	if (!key)
+		throw XMLSecurityException("Unable to find default key.");
+	return make_pair(m_default, key);
+}
+
+const XSECCryptoSymmetricKey* VersionedDataSealerKeyStrategy::getKey(const char* name) const
+{
+	return m_keyMap[name].get();
+}
diff --git a/xmltoolingtest/data/VersionedDataSealer.xml b/xmltoolingtest/data/VersionedDataSealer.xml
new file mode 100644
index 0000000..e69de29
diff --git a/xmltoolingtest/data/sealer.keys b/xmltoolingtest/data/sealer.keys
new file mode 100644
index 0000000..acbf84e
--- /dev/null
+++ b/xmltoolingtest/data/sealer.keys
@@ -0,0 +1,4 @@
+1:d5STfTpiMuM/AYSDHBkcPQ==    
+2:C2Ln/WFZ104/jk+1ND841w==   
+3:EtE0dGosiYaUYsfYXiYdiA==  
+4:21xMxNnD8FtrMp3UtgOeCg== 

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list