[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