diff options
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | common/Crypto.cpp | 141 | ||||
-rw-r--r-- | common/Crypto.hpp | 37 | ||||
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | tools/Config.cpp | 61 |
5 files changed, 242 insertions, 11 deletions
diff --git a/Makefile.am b/Makefile.am index 5e0d64b24..0d2d4884b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -130,7 +130,10 @@ loolstress_SOURCES = tools/Stress.cpp \ common/Log.cpp \ common/Util.cpp -loolconfig_SOURCES = tools/Config.cpp +loolconfig_SOURCES = tools/Config.cpp \ + common/Crypto.cpp \ + common/Log.cpp \ + common/Util.cpp wsd_headers = wsd/Admin.hpp \ wsd/AdminModel.hpp \ diff --git a/common/Crypto.cpp b/common/Crypto.cpp new file mode 100644 index 000000000..aa268ae3c --- /dev/null +++ b/common/Crypto.cpp @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "config.h" + +#if ENABLE_SUPPORT_KEY + +#include <Poco/DigestStream.h> +#include <Poco/Base64Decoder.h> +#include <Poco/DateTimeParser.h> +#include <Poco/Crypto/RSADigestEngine.h> + +#include <fstream> +#include <sstream> +#include <iostream> + +#include "Log.hpp" +#include "Crypto.hpp" + +using namespace Poco; +using namespace Poco::Crypto; + +struct SupportKeyImpl +{ + bool _invalid; + std::string _key; + std::string _data; + std::string _signature; + DateTime _expiry; + // Key format: iso-expiry-date:field1:field2:field:...:<signature> + SupportKeyImpl(const std::string &key) + : _invalid(true), _key(key) + { + LOG_INF("Support key '" << key << "' provided"); + size_t firstColon = key.find(':'); + if (firstColon != std::string::npos) + { + std::string expiry(key.substr(0, firstColon)); + LOG_INF("Support key with expiry '" << expiry << "'"); + + try { + int timeZoneDifferential = 0; + Poco::DateTimeParser::parse(expiry, _expiry, timeZoneDifferential); + + size_t lastColon = key.rfind(":"); + if (lastColon != std::string::npos) + { + _signature = key.substr(lastColon + 1, + key.length() - lastColon); + _data = key.substr(0, lastColon); + std::cout << "signature '" << _signature << "' data '" << _data << "'\n"; + + _invalid = false; + } + } catch (SyntaxException &e) { + LOG_ERR("Invalid support key expiry '" << expiry << "'"); + } + } + } +}; + +SupportKey::SupportKey(const std::string &key) : + _impl(new SupportKeyImpl(key)) +{ +} + +SupportKey::~SupportKey() +{ +} + +bool SupportKey::verify() +{ + if (_impl->_invalid) + { + LOG_ERR("Basic key structure is invalid."); + return false; + } + + std::ifstream pubStream; + try { + pubStream.open ("pubKey.pub", std::ifstream::in); + if (pubStream.fail()) + { + LOG_ERR("Failed to open support public key."); + return false; + } + } catch (...) { + LOG_ERR("Exception opening public key"); + return false; + } + try { + RSAKey keyPub(&pubStream); + RSADigestEngine rsaEngine(keyPub, RSADigestEngine::DigestType::DIGEST_SHA1); + rsaEngine.update(_impl->_data); + + std::istringstream sigStream(_impl->_signature); + Poco::Base64Decoder rawStream(sigStream); + + std::istreambuf_iterator<char> eos; + std::vector<unsigned char> rawSignature(std::istreambuf_iterator<char>(rawStream), eos); + LOG_INF("Signature of length " << rawSignature.size() + << " data size: " << _impl->_data.length()); + if (!rsaEngine.verify(rawSignature)) + { + LOG_ERR("Support key is not correctly signed."); + return false; + } + } catch (...) { + LOG_ERR("Exception validating support key."); + return false; + } + LOG_INF("Support key correctly signed."); + return true; +} + +int SupportKey::validDaysRemaining() +{ + if (!verify()) + { + LOG_ERR("Support key signature is invalid."); + return 0; + } + Timespan remaining = _impl->_expiry - DateTime(); + int days = remaining.days(); + if (days > 0) + LOG_INF("Support key has " << days << " remaining"); + else + LOG_ERR("Support key has expired for " << -days << " days"); + + return days; +} + +#endif // ENABLE_SUPPORT_KEY + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/Crypto.hpp b/common/Crypto.hpp new file mode 100644 index 000000000..94b194cc2 --- /dev/null +++ b/common/Crypto.hpp @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_CRYPTO_HPP +#define INCLUDED_CRYPTO_HPP + +#if ENABLE_SUPPORT_KEY + +#include <memory> + +struct SupportKeyImpl; + +class SupportKey { + std::unique_ptr<SupportKeyImpl> _impl; + +public: + SupportKey(const std::string &key); + virtual ~SupportKey(); + + /// Check the key is validly signed. + bool verify(); + + /// How many days until key expires + int validDaysRemaining(); +}; + +#endif + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/configure.ac b/configure.ac index 854d91526..f5b8a4a07 100644 --- a/configure.ac +++ b/configure.ac @@ -96,6 +96,10 @@ AC_ARG_ENABLE([ssl], AS_HELP_STRING([--disable-ssl], [Compile without SSL support])) +AC_ARG_ENABLE([support-key], + AS_HELP_STRING([--enable-support-key], + [Implements signed key with expiration required for support. Almost certainly you don't want to enable this.])) + AC_ARG_WITH([max-connections], AS_HELP_STRING([--max-connections], [Set the limit on the total number of client connections. Def: 20, Min: 3.])) @@ -291,6 +295,11 @@ fi AC_SUBST(ENABLE_SSL) +AS_IF([test "$enable_support_key" == "yes"], + [AC_DEFINE([ENABLE_SUPPORT_KEY],1,[Whether to enable support key])], + [AC_DEFINE([ENABLE_SUPPORT_KEY],0,[Whether to enable support key])]) +AC_SUBST(ENABLE_SUPPORT_KEY) + LIBS="$LIBS -lPocoNet${POCO_DEBUG_SUFFIX} -lPocoUtil${POCO_DEBUG_SUFFIX} -lPocoJSON${POCO_DEBUG_SUFFIX} -lPocoFoundation${POCO_DEBUG_SUFFIX} -lPocoXML${POCO_DEBUG_SUFFIX} -lPocoNetSSL${POCO_DEBUG_SUFFIX} -lPocoCrypto${POCO_DEBUG_SUFFIX}" AC_CHECK_HEADERS([LibreOfficeKit/LibreOfficeKit.h], diff --git a/tools/Config.cpp b/tools/Config.cpp index bf8c1e28a..986940c74 100644 --- a/tools/Config.cpp +++ b/tools/Config.cpp @@ -25,6 +25,7 @@ #include <Poco/Util/XMLConfiguration.h> #include "Util.hpp" +#include "Crypto.hpp" using Poco::Util::Application; using Poco::Util::HelpFormatter; @@ -80,7 +81,11 @@ void Config::displayHelp() helpFormatter.format(std::cout); std::cout << std::endl << "Commands:" << std::endl - << " set-admin-password" << std::endl; + << " set-admin-password" << std::endl +#if ENABLE_SUPPORT_KEY + << " set-support-key" << std::endl +#endif + ; } void Config::defineOptions(OptionSet& optionSet) @@ -165,12 +170,14 @@ int Config::main(const std::vector<std::string>& args) return Application::EXIT_NOINPUT; } -#if HAVE_PKCS5_PBKDF2_HMAC + bool changed = false; _loolConfig.load(ConfigFile); - for (unsigned i = 0; i < args.size(); i++) { + for (unsigned i = 0; i < args.size(); i++) + { if (args[i] == "set-admin-password") { +#if HAVE_PKCS5_PBKDF2_HMAC unsigned char pwdhash[_adminConfig.pwdHashLength]; unsigned char salt[_adminConfig.pwdSaltLength]; RAND_bytes(salt, _adminConfig.pwdSaltLength); @@ -226,18 +233,52 @@ int Config::main(const std::vector<std::string>& args) "Salt and password hash combination generated using PBKDF2 with SHA512 digest."); _loolConfig.setString("admin_console.secure_password", pwdConfigValue.str()); - std::cout << "Saving configuration to : " << ConfigFile << " ..." << std::endl; - _loolConfig.save(ConfigFile); - std::cout << "Saved" << std::endl; + changed = true; +#else + std::cerr << "This application was compiled with old OpenSSL. Operation not supported. You can use plain text password in /etc/loolwsd/loolwsd.xml." << std::endl; + return Application::EXIT_UNAVAILABLE; +#endif } +#if ENABLE_SUPPORT_KEY + else if (args[i] == "set-support-key") + { + std::string supportKeyString; + std::cout << "Enter support key: "; + std::cin >> supportKeyString; + if (supportKeyString.length() > 0) + { + SupportKey key(supportKeyString); + if (!key.verify()) + std::cerr << "Invalid key\n"; + else { + int validDays = key.validDaysRemaining(); + if (validDays <= 0) + std::cerr << "Valid but expired key\n"; + else + { + std::cerr << "Valid for " << validDays << " days - setting to config\n"; + _loolConfig.setString("support_key", supportKeyString); + } + } + } + else + { + std::cerr << "Removing empty support key\n"; + _loolConfig.remove("support_key"); + } + changed = true; + } +#endif + } + if (changed) + { + std::cout << "Saving configuration to : " << ConfigFile << " ..." << std::endl; + _loolConfig.save(ConfigFile); + std::cout << "Saved" << std::endl; } // This tool only handles options, nothing to do here return Application::EXIT_OK; -#else - std::cerr << "This application was compiled with old OpenSSL. Operation not supported. You can use plain text password in /etc/loolwsd/loolwsd.xml." << std::endl; - return Application::EXIT_UNAVAILABLE; -#endif } POCO_APP_MAIN(Config); |