diff --git a/bin/bbackupd/bbackupd-config.in b/bin/bbackupd/bbackupd-config.in index 1fc224c2b..43f63b4e4 100755 --- a/bin/bbackupd/bbackupd-config.in +++ b/bin/bbackupd/bbackupd-config.in @@ -169,7 +169,7 @@ if(!-f $private_key) if(!-f $certificate_request) { die "Couldn't run openssl for CSR generation" unless - open(CSR,"|openssl req -new -key $private_key -sha1 -out $certificate_request"); + open(CSR,"|openssl req -new -key $private_key -sha256 -out $certificate_request"); print CSR <<__E; . . @@ -317,6 +317,21 @@ NotifyScript = $notify_script __E +if("@HAVE_SSL_CTX_SET_SECURITY_LEVEL@" eq "1") +{ + print CONFIG <<__E; +# Box Backup compiled with support for SSLSecurityLevel +SSLSecurityLevel = 2 +__E +} +else +{ + print CONFIG <<__E; +# Box Backup compiled without support for SSLSecurityLevel +# SSLSecurityLevel = 2 +__E +} + if($backup_mode eq 'lazy') { # lazy mode configuration diff --git a/bin/bbackupquery/bbackupquery.cpp b/bin/bbackupquery/bbackupquery.cpp index e10c48fe1..aef26ddc9 100644 --- a/bin/bbackupquery/bbackupquery.cpp +++ b/bin/bbackupquery/bbackupquery.cpp @@ -364,7 +364,9 @@ int main(int argc, const char *argv[]) std::string certFile(conf.GetKeyValue("CertificateFile")); std::string keyFile(conf.GetKeyValue("PrivateKeyFile")); std::string caFile(conf.GetKeyValue("TrustedCAsFile")); - tlsContext.Initialise(false /* as client */, certFile.c_str(), keyFile.c_str(), caFile.c_str()); + int ssl_security_level(conf.GetKeyValueInt("SSLSecurityLevel")); + tlsContext.Initialise(false /* as client */, certFile.c_str(), keyFile.c_str(), + caFile.c_str(), ssl_security_level); // Initialise keys BackupClientCryptoKeys_Setup(conf.GetKeyValue("KeysFile").c_str()); diff --git a/bin/bbstored/bbstored-certs.in b/bin/bbstored/bbstored-certs.in index 00085662d..10072a87c 100755 --- a/bin/bbstored/bbstored-certs.in +++ b/bin/bbstored/bbstored-certs.in @@ -122,7 +122,7 @@ sub cmd_init_create_root # make CSR die "Couldn't run openssl for CSR generation" unless - open(CSR,"|openssl req -new -key $key -sha1 -out $csr"); + open(CSR,"|openssl req -new -key $key -sha256 -out $csr"); print CSR <<__E; . . @@ -140,7 +140,7 @@ __E die "Certificate request wasn't created.\n" unless -f $csr; # sign it to make a self-signed root CA key - if(system("openssl x509 -req -in $csr -sha1 -extensions v3_ca -signkey $key -out $cert -days $root_sign_period") != 0) + if(system("openssl x509 -req -in $csr -sha256 -extensions v3_ca -signkey $key -out $cert -days $root_sign_period") != 0) { die "Couldn't generate root certificate." } @@ -201,7 +201,7 @@ __E my $out_cert = "$cert_dir/clients/$acc"."-cert.pem"; # sign it! - if(system("openssl x509 -req -in $csr -sha1 -extensions usr_crt -CA $cert_dir/roots/clientCA.pem -CAkey $cert_dir/keys/clientRootKey.pem -out $out_cert -days $sign_period") != 0) + if(system("openssl x509 -req -in $csr -sha256 -extensions usr_crt -CA $cert_dir/roots/clientCA.pem -CAkey $cert_dir/keys/clientRootKey.pem -out $out_cert -days $sign_period") != 0) { die "Signing failed" } @@ -257,7 +257,7 @@ __E my $out_cert = "$cert_dir/servers/$common_name"."-cert.pem"; # sign it! - if(system("openssl x509 -req -in $csr -sha1 -extensions usr_crt -CA $cert_dir/roots/serverCA.pem -CAkey $cert_dir/keys/serverRootKey.pem -out $out_cert -days $sign_period") != 0) + if(system("openssl x509 -req -in $csr -sha256 -extensions usr_crt -CA $cert_dir/roots/serverCA.pem -CAkey $cert_dir/keys/serverRootKey.pem -out $out_cert -days $sign_period") != 0) { die "Signing failed" } diff --git a/bin/bbstored/bbstored-config.in b/bin/bbstored/bbstored-config.in index 83305c4f5..1efaf6684 100755 --- a/bin/bbstored/bbstored-config.in +++ b/bin/bbstored/bbstored-config.in @@ -202,11 +202,24 @@ Server CertificateFile = $certificate PrivateKeyFile = $private_key TrustedCAsFile = $ca_root_cert -} - +__E +if("@HAVE_SSL_CTX_SET_SECURITY_LEVEL@" eq "1") +{ + print CONFIG <<__E; + # Box Backup compiled with support for SSLSecurityLevel + SSLSecurityLevel = 2 __E +} +else +{ + print CONFIG <<__E; + # Box Backup compiled without support for SSLSecurityLevel + # SSLSecurityLevel = 2 +__E +} +print CONFIG "}\n"; close CONFIG; # explain to the user what they need to do next diff --git a/infrastructure/cmake/CMakeLists.txt b/infrastructure/cmake/CMakeLists.txt index 5116e7ffa..eadd280e9 100644 --- a/infrastructure/cmake/CMakeLists.txt +++ b/infrastructure/cmake/CMakeLists.txt @@ -430,6 +430,7 @@ else() endif() include_directories(${OPENSSL_INCLUDE_DIR}) target_link_libraries(lib_crypto PUBLIC ${OPENSSL_LIBRARIES}) +list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) # Link to PCRE if (WIN32) @@ -613,6 +614,9 @@ foreach(function_name ${detect_functions}) file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_${platform_var_name}\n") endforeach() +check_function_exists(SSL_CTX_set_security_level HAVE_SSL_CTX_SET_SECURITY_LEVEL) +file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_SSL_CTX_SET_SECURITY_LEVEL\n") + check_symbol_exists(dirfd "dirent.h" HAVE_DECL_DIRFD) file(APPEND "${boxconfig_h_file}" "#cmakedefine01 HAVE_DECL_DIRFD\n") diff --git a/infrastructure/m4/boxbackup_tests.m4 b/infrastructure/m4/boxbackup_tests.m4 index 86aa560a2..f9fc2a805 100644 --- a/infrastructure/m4/boxbackup_tests.m4 +++ b/infrastructure/m4/boxbackup_tests.m4 @@ -128,6 +128,7 @@ AC_CHECK_FUNCS([dlsym dladdr]) AC_SEARCH_LIBS([gethostbyname], [nsl socket resolv]) AC_SEARCH_LIBS([shutdown], [nsl socket resolv]) AX_CHECK_SSL(, [AC_MSG_ERROR([[OpenSSL is not installed but is required]])]) +AC_CHECK_DECLS([SSL_R_EE_KEY_TOO_SMALL],,, [[#include ]]) AC_ARG_ENABLE( [old-ssl], [AC_HELP_STRING([--enable-old-ssl], @@ -142,7 +143,8 @@ AC_SEARCH_LIBS( Upgrade or read the documentation for alternatives]]) fi ]) - +AC_CHECK_FUNCS([SSL_CTX_set_security_level], [HAVE_SSL_CTX_SET_SECURITY_LEVEL=1]) +AC_SUBST([HAVE_SSL_CTX_SET_SECURITY_LEVEL]) ### Checks for header files. diff --git a/lib/backupclient/BackupDaemonConfigVerify.cpp b/lib/backupclient/BackupDaemonConfigVerify.cpp index 865ee4132..e28e26ff5 100644 --- a/lib/backupclient/BackupDaemonConfigVerify.cpp +++ b/lib/backupclient/BackupDaemonConfigVerify.cpp @@ -8,10 +8,11 @@ // -------------------------------------------------------------------------- #include "Box.h" + +#include "BackupConstants.h" #include "BackupDaemonConfigVerify.h" -#include "Daemon.h" #include "BoxPortsAndFiles.h" -#include "BackupConstants.h" +#include "Daemon.h" #include "MemLeakFindOn.h" @@ -148,7 +149,9 @@ static const ConfigurationVerifyKey verifyrootkeys[] = ConfigTest_IsUint32), ConfigurationVerifyKey("CertificateFile", 0), ConfigurationVerifyKey("PrivateKeyFile", 0), - ConfigurationVerifyKey("TrustedCAsFile", ConfigTest_LastEntry), + ConfigurationVerifyKey("TrustedCAsFile", 0), + ConfigurationVerifyKey("SSLSecurityLevel", ConfigTest_IsInt | ConfigTest_LastEntry, + BOX_DEFAULT_SSL_SECURITY_LEVEL), }; const ConfigurationVerify BackupDaemonConfigVerify = diff --git a/lib/bbackupd/BackupDaemon.cpp b/lib/bbackupd/BackupDaemon.cpp index 996c1919c..d75aa3812 100644 --- a/lib/bbackupd/BackupDaemon.cpp +++ b/lib/bbackupd/BackupDaemon.cpp @@ -579,8 +579,9 @@ void BackupDaemon::InitCrypto() std::string certFile(conf.GetKeyValue("CertificateFile")); std::string keyFile(conf.GetKeyValue("PrivateKeyFile")); std::string caFile(conf.GetKeyValue("TrustedCAsFile")); + int ssl_security_level(conf.GetKeyValueInt("SSLSecurityLevel")); mTlsContext.Initialise(false /* as client */, certFile.c_str(), - keyFile.c_str(), caFile.c_str()); + keyFile.c_str(), caFile.c_str(), ssl_security_level); // Set up the keys for various things BackupClientCryptoKeys_Setup(conf.GetKeyValue("KeysFile")); diff --git a/lib/common/BoxPortsAndFiles.h.in b/lib/common/BoxPortsAndFiles.h.in index 047a828f6..1983212de 100644 --- a/lib/common/BoxPortsAndFiles.h.in +++ b/lib/common/BoxPortsAndFiles.h.in @@ -20,6 +20,10 @@ // directory within the RAIDFILE root for the backup store daemon #define BOX_RAIDFILE_ROOT_BBSTORED "backup" +// default security level if SSLSecurityLevel is not specified: see +// https://www.boxbackup.org/wiki/ReplacingCertificates +const int BOX_DEFAULT_SSL_SECURITY_LEVEL = -1; + // configuration file paths #ifdef WIN32 // no default config file path, use these macros to call diff --git a/lib/common/Configuration.cpp b/lib/common/Configuration.cpp index 8ce8d389b..9860864ab 100644 --- a/lib/common/Configuration.cpp +++ b/lib/common/Configuration.cpp @@ -470,6 +470,16 @@ int Configuration::GetKeyValueInt(const std::string& rKeyName) const } +int Configuration::GetKeyValueInt(const std::string& rKeyName, int default_value) const +{ + if(!KeyExists(rKeyName)) + { + return default_value; + } + return GetKeyValueInt(rKeyName); +} + + // -------------------------------------------------------------------------- // // Function @@ -778,8 +788,7 @@ bool Configuration::Verify(const ConfigurationVerify &rVerify, } else if(pvkey->HasDefaultValue()) { - mKeys[pvkey->Name()] = - pvkey->DefaultValue(); + mKeys[pvkey->Name()] = pvkey->DefaultValue(); } } @@ -922,5 +931,3 @@ bool Configuration::Verify(const ConfigurationVerify &rVerify, return ok; } - - diff --git a/lib/common/Configuration.h b/lib/common/Configuration.h index e6498e801..f9b5eb773 100644 --- a/lib/common/Configuration.h +++ b/lib/common/Configuration.h @@ -122,6 +122,7 @@ class Configuration bool KeyExists(const std::string& rKeyName) const; const std::string &GetKeyValue(const std::string& rKeyName) const; int GetKeyValueInt(const std::string& rKeyName) const; + int GetKeyValueInt(const std::string& rKeyName, int default_value) const; uint32_t GetKeyValueUint32(const std::string& rKeyName) const; bool GetKeyValueBool(const std::string& rKeyName) const; std::vector GetKeyNames() const; diff --git a/lib/common/Test.cpp b/lib/common/Test.cpp index cd6edb6fc..32d13e125 100644 --- a/lib/common/Test.cpp +++ b/lib/common/Test.cpp @@ -28,7 +28,7 @@ int num_tests_selected = 0; int num_failures = 0; -int old_failure_count = 0; +static int old_failure_count = 0; // do not expose! int first_fail_line; std::string original_working_dir; std::string first_fail_file; @@ -98,6 +98,8 @@ bool setUp(const char* function_name) StartsWith("0_", filename) || filename == "accounts.txt" || filename == "bbackupd-data" || + filename == "bbackupquery.log" || + filename == "bbstored.log" || filename == "ca" || StartsWith("file", filename) || StartsWith("notifyran", filename) || diff --git a/lib/common/Test.h b/lib/common/Test.h index 1f6479d89..d70197139 100644 --- a/lib/common/Test.h +++ b/lib/common/Test.h @@ -39,7 +39,6 @@ extern int num_failures; extern int first_fail_line; extern int num_tests_selected; -extern int old_failure_count; extern std::string first_fail_file; extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; extern std::list run_only_named_tests; diff --git a/lib/server/ConnectionException.txt b/lib/server/ConnectionException.txt index 7dcaadeb0..d7f59e41e 100644 --- a/lib/server/ConnectionException.txt +++ b/lib/server/ConnectionException.txt @@ -15,6 +15,7 @@ TLSNoPeerCertificate 36 TLSPeerCertificateInvalid 37 Check certification process TLSClosedWhenWriting 38 TLSHandshakeTimedOut 39 +TLSPeerWeakCertificate 40 The peer's RSA key is too short and no longer considered secure, see https://www.boxbackup.org/wiki/ReplacingCertificates for details Protocol_Timeout 41 Probably a network issue between client and server. Protocol_ObjTooBig 42 Protocol_BadCommandRecieved 44 diff --git a/lib/server/Daemon.cpp b/lib/server/Daemon.cpp index d3c8441f9..76141b6fe 100644 --- a/lib/server/Daemon.cpp +++ b/lib/server/Daemon.cpp @@ -42,6 +42,7 @@ #include "autogen_ConnectionException.h" #include "autogen_ServerException.h" +#include "BoxPortsAndFiles.h" #include "Configuration.h" #include "Daemon.h" #include "FileModificationTime.h" @@ -52,6 +53,9 @@ #include "MemLeakFindOn.h" +const ConfigurationVerifyKey ssl_security_level_key("SSLSecurityLevel", + ConfigTest_IsInt | ConfigTest_LastEntry, BOX_DEFAULT_SSL_SECURITY_LEVEL); + Daemon *Daemon::spDaemon = 0; diff --git a/lib/server/Daemon.h b/lib/server/Daemon.h index b53849187..60ce4507f 100644 --- a/lib/server/Daemon.h +++ b/lib/server/Daemon.h @@ -121,5 +121,7 @@ class Daemon ConfigurationVerifyKey("LogFacility", 0), \ ConfigurationVerifyKey("User", ConfigTest_LastEntry) +extern const ConfigurationVerifyKey ssl_security_level_key; + #endif // DAEMON__H diff --git a/lib/server/ServerException.txt b/lib/server/ServerException.txt index 474b4067f..a128ca76d 100644 --- a/lib/server/ServerException.txt +++ b/lib/server/ServerException.txt @@ -29,6 +29,7 @@ TLSSetCiphersFailed 28 SSLLibraryInitialisationError 29 TLSNoSSLObject 31 TLSAlreadyHandshaked 35 +TLSServerWeakCertificate 36 Our RSA key is too short and no longer considered secure, see https://www.boxbackup.org/wiki/ReplacingCertificates for details SocketSetNonBlockingFailed 40 Protocol_BadUsage 43 Protocol_UnsuitableStreamTypeForSending 51 diff --git a/lib/server/ServerTLS.h b/lib/server/ServerTLS.h index f748f4b23..054f0c75e 100644 --- a/lib/server/ServerTLS.h +++ b/lib/server/ServerTLS.h @@ -10,6 +10,7 @@ #ifndef SERVERTLS__H #define SERVERTLS__H +#include "BoxPortsAndFiles.h" #include "ServerStream.h" #include "SocketStreamTLS.h" #include "SSLLib.h" @@ -52,8 +53,14 @@ class ServerTLS : public ServerStream -#include + #include #include @@ -19,6 +18,10 @@ #include #endif +#include +#include +#include + #include "autogen_ConnectionException.h" #include "autogen_ServerException.h" #include "BoxTime.h" @@ -126,8 +129,8 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer) mpBIO = ::BIO_new(::BIO_s_socket()); if(mpBIO == 0) { - CryptoUtils::LogError("creating socket bio"); - THROW_EXCEPTION(ServerException, TLSAllocationFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSAllocationFailed, + "Failed to create SSL BIO: " << CryptoUtils::LogError("creating socket bio")); } tOSSocketHandle socket = GetSocketHandle(); @@ -137,8 +140,8 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer) mpSSL = ::SSL_new(rContext.GetRawContext()); if(mpSSL == 0) { - CryptoUtils::LogError("creating SSL object"); - THROW_EXCEPTION(ServerException, TLSAllocationFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSAllocationFailed, + "Failed to create SSL object: " << CryptoUtils::LogError("creating SSL object")); } // Make the socket non-blocking so timeouts on Read work @@ -203,15 +206,43 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer) default: // (and SSL_ERROR_ZERO_RETURN) // Error occured +#if HAVE_DECL_SSL_R_EE_KEY_TOO_SMALL + int err_reason = ERR_GET_REASON(ERR_peek_error()); + const char *file, *data; + int line, flags; + ERR_peek_error_line_data(&file, &line, &data, &flags); + long verify_result = SSL_get_verify_result(mpSSL); + + if(se == SSL_ERROR_SSL && verify_result == X509_V_ERR_CA_KEY_TOO_SMALL) + { + // Would be nice to use GetPeerCommonName() in these error messages, + // but since the certificate isn't trusted, that might be misleading, + // and it's not available to us anyway :( + + THROW_EXCEPTION_MESSAGE(ConnectionException, TLSPeerWeakCertificate, + (IsServer ? "Failed to accept connection from" : + "Failed to connect to") << " " << mPeerSocketDesc << + ": key too short for current security level"); + } + else if(se == SSL_ERROR_SSL && verify_result == X509_V_ERR_CA_MD_TOO_WEAK) + { + THROW_EXCEPTION_MESSAGE(ConnectionException, TLSPeerWeakCertificate, + (IsServer ? "Failed to accept connection from" : + "Failed to connect to") << " " << mPeerSocketDesc << + ": hash too weak for current security level"); + } + else +#endif // HAVE_DECL_SSL_R_EE_KEY_TOO_SMALL if(IsServer) { - CryptoUtils::LogError("accepting connection"); - THROW_EXCEPTION(ConnectionException, TLSHandshakeFailed) + THROW_EXCEPTION_MESSAGE(ConnectionException, TLSHandshakeFailed, + "Failed to accept connection: " << + CryptoUtils::LogError("accepting connection")); } else { - CryptoUtils::LogError("connecting"); - THROW_EXCEPTION(ConnectionException, TLSHandshakeFailed) + THROW_EXCEPTION_MESSAGE(ConnectionException, TLSHandshakeFailed, + "Failed to connect: " << CryptoUtils::LogError("connecting")); } } } diff --git a/lib/server/SocketStreamTLS.h b/lib/server/SocketStreamTLS.h index 3fda98c14..8a7939667 100644 --- a/lib/server/SocketStreamTLS.h +++ b/lib/server/SocketStreamTLS.h @@ -34,10 +34,11 @@ class SocketStreamTLS : public SocketStream SocketStreamTLS(); SocketStreamTLS(int socket); ~SocketStreamTLS(); + private: SocketStreamTLS(const SocketStreamTLS &rToCopy); -public: +public: void Open(const TLSContext &rContext, Socket::Type Type, const std::string& rName, int Port = 0); void Handshake(const TLSContext &rContext, bool IsServer = false); diff --git a/lib/server/TLSContext.cpp b/lib/server/TLSContext.cpp index 1a6d4a534..1c2c41a26 100644 --- a/lib/server/TLSContext.cpp +++ b/lib/server/TLSContext.cpp @@ -10,10 +10,12 @@ #include "Box.h" #define TLS_CLASS_IMPLEMENTATION_CPP +#include #include #include "autogen_ConnectionException.h" #include "autogen_ServerException.h" +#include "BoxPortsAndFiles.h" #include "CryptoUtils.h" #include "SSLLib.h" #include "TLSContext.h" @@ -21,7 +23,7 @@ #include "MemLeakFindOn.h" #define MAX_VERIFICATION_DEPTH 2 -#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" +#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" // Macros to allow compatibility with OpenSSL 1.0 and 1.1 APIs. See // https://github.com/charybdis-ircd/charybdis/blob/release/3.5/libratbox/src/openssl_ratbox.h @@ -71,7 +73,8 @@ TLSContext::~TLSContext() // Created: 2003/08/06 // // -------------------------------------------------------------------------- -void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, const char *TrustedCAsFile) +void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, + const char *TrustedCAsFile, int SSLSecurityLevel) { if(mpContext != 0) { @@ -84,29 +87,64 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c THROW_EXCEPTION(ServerException, TLSAllocationFailed) } +#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL + if(SSLSecurityLevel == -1) + { + BOX_WARNING("SSLSecurityLevel not set. Your connection may not be secure. " + "Please see https://www.boxbackup.org/wiki/ReplacingCertificates for details"); + SSLSecurityLevel = 1; // Default for now. Unsafe, but we warned the user. + // TODO: upgrade to level 2 soon. + } + + SSL_CTX_set_security_level(mpContext, SSLSecurityLevel); +#else + if(SSLSecurityLevel != BOX_DEFAULT_SSL_SECURITY_LEVEL) + { + BOX_WARNING("SSLSecurityLevel is set, but this Box Backup is not compiled with " + "OpenSSL 1.1 or higher, so will be ignored (compiled with " + OPENSSL_VERSION_TEXT ")"); + } +#endif + // Setup our identity if(::SSL_CTX_use_certificate_chain_file(mpContext, CertificatesFile) != 1) { - std::string msg = "loading certificates from "; - msg += CertificatesFile; - CryptoUtils::LogError(msg); - THROW_EXCEPTION(ServerException, TLSLoadCertificatesFailed) +#if HAVE_DECL_SSL_R_EE_KEY_TOO_SMALL + int err_reason = ERR_GET_REASON(ERR_peek_error()); + if(err_reason == SSL_R_EE_KEY_TOO_SMALL) + { + THROW_EXCEPTION_MESSAGE(ServerException, TLSServerWeakCertificate, + "Failed to load certificates from " << CertificatesFile << ": " + "key too short for current security level"); + } + else if(err_reason == SSL_R_CA_MD_TOO_WEAK) + { + THROW_EXCEPTION_MESSAGE(ServerException, TLSServerWeakCertificate, + "Failed to load certificates from " << CertificatesFile << ": " + "hash too weak for current security level"); + } + else +#endif // HAVE_DECL_SSL_R_EE_KEY_TOO_SMALL + { + THROW_EXCEPTION_MESSAGE(ServerException, TLSLoadCertificatesFailed, + "Failed to load certificates from " << CertificatesFile << ": " << + CryptoUtils::LogError("loading certificates")); + } } + if(::SSL_CTX_use_PrivateKey_file(mpContext, PrivateKeyFile, SSL_FILETYPE_PEM) != 1) { - std::string msg = "loading private key from "; - msg += PrivateKeyFile; - CryptoUtils::LogError(msg); - THROW_EXCEPTION(ServerException, TLSLoadPrivateKeyFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSLoadPrivateKeyFailed, + "Failed to load private key from " << PrivateKeyFile << ": " << + CryptoUtils::LogError("loading private key")); } // Setup the identify of CAs we trust if(::SSL_CTX_load_verify_locations(mpContext, TrustedCAsFile, NULL) != 1) { - std::string msg = "loading CA cert from "; - msg += TrustedCAsFile; - CryptoUtils::LogError(msg); - THROW_EXCEPTION(ServerException, TLSLoadTrustedCAsFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSLoadTrustedCAsFailed, + "Failed to load CA certificate from " << TrustedCAsFile << ": " << + CryptoUtils::LogError("loading CA cert")); } // Setup options to require these certificates @@ -117,8 +155,9 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c // Setup allowed ciphers if(::SSL_CTX_set_cipher_list(mpContext, CIPHER_LIST) != 1) { - CryptoUtils::LogError("setting cipher list to " CIPHER_LIST); - THROW_EXCEPTION(ServerException, TLSSetCiphersFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSSetCiphersFailed, + "Failed to set cipher list to " << CIPHER_LIST << ": " << + CryptoUtils::LogError("setting cipher list")); } } diff --git a/lib/server/TLSContext.h b/lib/server/TLSContext.h index f52f5457f..6239d136e 100644 --- a/lib/server/TLSContext.h +++ b/lib/server/TLSContext.h @@ -30,7 +30,8 @@ class TLSContext private: TLSContext(const TLSContext &); public: - void Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, const char *TrustedCAsFile); + void Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, + const char *TrustedCAsFile, int SSLSecurityLevel = -1); SSL_CTX *GetRawContext() const; private: diff --git a/test/backupstorefix/testbackupstorefix.cpp b/test/backupstorefix/testbackupstorefix.cpp index 38492bd15..ac1d0c14c 100644 --- a/test/backupstorefix/testbackupstorefix.cpp +++ b/test/backupstorefix/testbackupstorefix.cpp @@ -671,8 +671,12 @@ int test(int argc, const char *argv[]) char name[256]; while(::fgets(line, sizeof(line), f) != 0) { - TEST_THAT(::sscanf(line, "%x %s %s", &id, - flags, name) == 3); + if(StartsWith("WARNING: SSLSecurityLevel not set.", line)) + { + continue; + } + TEST_EQUAL_LINE(3, ::sscanf(line, "%x %s %s", &id, flags, name), + "Unexpected format in initial-listing.txt: <" << line << ">"); bool isDir = (::strcmp(flags, "-d---") == 0); //TRACE3("%x,%d,%s\n", id, isDir, name); MEMLEAKFINDER_NO_LEAKS; diff --git a/test/basicserver/testbasicserver.cpp b/test/basicserver/testbasicserver.cpp index afb1132db..fe313de96 100644 --- a/test/basicserver/testbasicserver.cpp +++ b/test/basicserver/testbasicserver.cpp @@ -97,7 +97,7 @@ void testservers_connection(SocketStream &rStream) while(!getline.IsEOF()) { std::string line; - while(!getline.GetLine(line)) + while(!getline.GetLine(line, false, SHORT_TIMEOUT)) ; if(line == "QUIT") { @@ -207,11 +207,16 @@ const ConfigurationVerify *testserver::GetConfigVerify() const } }; + static ConfigurationVerifyKey root_keys[] = + { + ssl_security_level_key, + }; + static ConfigurationVerify verify = { "root", /* mName */ verifyserver, /* mpSubConfigurations */ - 0, /* mpKeys */ + root_keys, // mpKeys ConfigTest_Exists | ConfigTest_LastEntry, 0 }; @@ -448,6 +453,151 @@ void TestStreamReceive(TestProtocolClient &protocol, int value, bool uncertainst TEST_THAT(count == (24273*3)); // over 64 k of data, definately } +bool test_security_level(int cert_level, int test_level, bool expect_failure_on_connect = false) +{ + int old_num_failures = num_failures; + + // Context first + TLSContext context; + if(cert_level == 0) + { + context.Initialise(false /* client */, + "testfiles/clientCerts.pem", + "testfiles/clientPrivKey.pem", + "testfiles/clientTrustedCAs.pem", + test_level); // SecurityLevel + } + else if(cert_level == 1) + { + context.Initialise(false /* client */, + "testfiles/seclevel2-sha1/ca/clients/1234567-cert.pem", + "testfiles/seclevel2-sha1/bbackupd/1234567-key.pem", + "testfiles/seclevel2-sha1/ca/roots/serverCA.pem", + test_level); // SecurityLevel + } + else if(cert_level == 2) + { + context.Initialise(false /* client */, + "testfiles/seclevel2-sha256/ca/clients/1234567-cert.pem", + "testfiles/seclevel2-sha256/bbackupd/1234567-key.pem", + "testfiles/seclevel2-sha256/ca/roots/serverCA.pem", + test_level); // SecurityLevel + } + else + { + TEST_FAIL_WITH_MESSAGE("No certificates generated for level " << cert_level); + return false; + } + + SocketStreamTLS conn; + + if(expect_failure_on_connect) + { + TEST_CHECK_THROWS( + conn.Open(context, Socket::TypeINET, "localhost", 2003), + ConnectionException, TLSPeerWeakCertificate); + } + else + { + conn.Open(context, Socket::TypeINET, "localhost", 2003); + } + + return (num_failures == old_num_failures); // no new failures -> good +} + +// Test the certificates that were distributed with the Box Backup source since ancient times, +// which have only 1024-bit keys, and thus fail with "ee key too small". +bool test_ancient_certificates() +{ + int old_num_failures = num_failures; + + // Level -1 (allow weaker, with warning) should pass with any certificates: + TEST_THAT(test_security_level(0, -1)); // cert_level, test_level + + // We do not test level 0 (system-wide default) because the system + // may have it set high, and our old certificate will not be usable + // in that case, and the user has no way to fix that, so it's not a + // useful test. + + // Level 1 (allow weaker, without a warning) should pass with any certificates: + TEST_THAT(test_security_level(0, 1)); // cert_level, test_level + +#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL + // Level 2 (disallow weaker, without a warning) should NOT pass with old certificates: + TEST_CHECK_THROWS( + test_security_level(0, 2), // cert_level, test_level + ServerException, TLSServerWeakCertificate); +#else + // We have no way to increase the security level, so it should still pass: + test_security_level(0, 2); // cert_level, test_level +#endif + + return (num_failures == old_num_failures); // no new failures -> good +} + +// Test a set of more recent certificates, which have a longer key but are signed using the SHA1 +// algorithm instead of SHA256, which fail with "ca md too weak" instead. +bool test_old_certificates() +{ + int old_num_failures = num_failures; + + // Level -1 (allow weaker, with warning) should pass with any certificates: + TEST_THAT(test_security_level(1, -1)); // cert_level, test_level + + // We do not test level 0 (system-wide default) because the system + // may have it set high, and our old certificate will not be usable + // in that case, and the user has no way to fix that, so it's not a + // useful test. + + // Level 1 (allow weaker, without a warning) should pass with any certificates: + TEST_THAT(test_security_level(1, 1)); // cert_level, test_level + +#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL + // Level 2 (disallow weaker, without a warning) should NOT pass with old certificates: + TEST_CHECK_THROWS( + test_security_level(1, 2), // cert_level, test_level + ServerException, TLSServerWeakCertificate); +#else + // We have no way to increase the security level, so it should still pass: + test_security_level(1, 2); // cert_level, test_level +#endif + + return (num_failures == old_num_failures); // no new failures -> good +} + + +bool test_new_certificates(bool expect_failure_level_2) +{ + int old_num_failures = num_failures; + + // Level -1 (allow weaker, with warning) should pass with any certificates: + TEST_THAT(test_security_level(2, -1)); // cert_level, test_level + + // Level 0 (system dependent). This will fail if the user (or their + // distro) sets the system-wide security level very high. We check + // this because *we* may need to update Box Backup if this happens + // again, as it did when Debian increased the default level. + // Newly generated certificates may need to be strengthened. + // And we may need to update the documentation. + TEST_THAT(test_security_level(2, 0)); // cert_level, test_level + + // Level 1 (allow weaker, without a warning) should pass with any certificates: + TEST_THAT(test_security_level(2, 1)); // cert_level, test_level + +#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL + // Level 2 (disallow weaker, without a warning) should pass with new certificates, + // but might fail to connect to a peer with weak (insecure) certificates: + TEST_THAT(test_security_level(2, 2, expect_failure_level_2)); + // cert_level, test_level, expect_failure +#else + // We have no way to increase the security level, so it should not fail to connect to a + // daemon with weak certificates: + test_security_level(2, 2, false); // cert_level, test_level, expect_failure +#endif + + return (num_failures == old_num_failures); // no new failures -> good +} + int test(int argc, const char *argv[]) { @@ -643,6 +793,7 @@ int test(int argc, const char *argv[]) "testfiles/clientCerts.pem", "testfiles/clientPrivKey.pem", "testfiles/clientTrustedCAs.pem"); + // SecurityLevel == -1 by default (old security + warnings) SocketStreamTLS conn1; conn1.Open(context, Socket::TypeINET, "localhost", 2003); @@ -681,6 +832,11 @@ int test(int argc, const char *argv[]) TEST_THAT(ServerIsAlive(pid)); #endif + // Try testing with different security levels, check that the behaviour is + // as documented at: + // https://www.boxbackup.org/wiki/ReplacingCertificates#SupportinBoxBackup + TEST_THAT(test_ancient_certificates()); + // Kill it TEST_THAT(KillServer(pid)); ::sleep(1); @@ -690,6 +846,36 @@ int test(int argc, const char *argv[]) TestRemoteProcessMemLeaks("test-srv3.memleaks"); #endif } + + cmd = TEST_EXECUTABLE " --test-daemon-args="; + cmd += test_args; + cmd += " srv3 testfiles/srv3-seclevel2-sha1.conf"; + pid = LaunchServer(cmd, "testfiles/srv3.pid"); + + TEST_THAT(pid != -1 && pid != 0); + TEST_THAT(test_old_certificates()); + TEST_THAT(KillServer(pid)); + + cmd = TEST_EXECUTABLE " --test-daemon-args="; + cmd += test_args; + cmd += " srv3 testfiles/srv3-seclevel2-sha256.conf"; + pid = LaunchServer(cmd, "testfiles/srv3.pid"); + + TEST_THAT(pid != -1 && pid != 0); + TEST_THAT(test_new_certificates(false)); // !expect_failure_level_2 + TEST_THAT(KillServer(pid)); + + // Start a daemon using old, insecure certificates. We should get an error when we + // try to connect to it: + + cmd = TEST_EXECUTABLE " --test-daemon-args="; + cmd += test_args; + cmd += " srv3 testfiles/srv3-insecure-daemon.conf"; + pid = LaunchServer(cmd, "testfiles/srv3.pid"); + + TEST_THAT(pid != -1 && pid != 0); + TEST_THAT(test_new_certificates(true)); // expect_failure_level_2 + TEST_THAT(KillServer(pid)); } //protocolserver: @@ -697,7 +883,7 @@ int test(int argc, const char *argv[]) { std::string cmd = TEST_EXECUTABLE " --test-daemon-args="; cmd += test_args; - cmd += " srv4 testfiles/srv4.conf"; + cmd += " srv4 testfiles/srv4-seclevel1.conf"; int pid = LaunchServer(cmd, "testfiles/srv4.pid"); TEST_THAT(pid != -1 && pid != 0); diff --git a/test/basicserver/testfiles/seclevel2-sha1/bbackupd.conf b/test/basicserver/testfiles/seclevel2-sha1/bbackupd.conf new file mode 100644 index 000000000..eb37d9ab5 --- /dev/null +++ b/test/basicserver/testfiles/seclevel2-sha1/bbackupd.conf @@ -0,0 +1,196 @@ + +StoreHostname = localhost +AccountNumber = 0x1234567 +KeysFile = ./bbackupd/1234567-FileEncKeys.raw + +CertificateFile = ./bbackupd/1234567-cert.pem +PrivateKeyFile = ./bbackupd/1234567-key.pem +TrustedCAsFile = ./bbackupd/serverCA.pem +SSLSecurityLevel = 2 + +DataDirectory = /home/chris/boxbackup/test/basicserver/testfiles/seclevel2-sha1 + + +# This script is run whenever bbackupd changes state or encounters a +# problem which requires the system administrator to assist: +# +# 1) The store is full, and no more data can be uploaded. +# 2) Some files or directories were not readable. +# 3) A backup run starts or finishes. +# +# The default script emails the system administrator, except for backups +# starting and stopping, where it does nothing. + +NotifyScript = ./bbackupd/NotifySysadmin.sh + + +# The number of seconds between backup runs under normal conditions. To avoid +# cycles of load on the server, this time is randomly adjusted by a small +# percentage as the daemon runs. + +UpdateStoreInterval = 3600 + + +# The minimum age of a file, in seconds, that will be uploaded. Avoids +# repeated uploads of a file which is constantly being modified. + +MinimumFileAge = 21600 + + +# If a file is modified repeated, it won't be uploaded immediately in case +# it's modified again, due to the MinimumFileAge specified above. However, it +# should be uploaded eventually even if it is being modified repeatedly. This +# is how long we should wait, in seconds, after first noticing a change. +# (86400 seconds = 1 day) + +MaxUploadWait = 86400 + +# If the connection is idle for some time (e.g. over 10 minutes or 600 +# seconds, not sure exactly how long) then the server will give up and +# disconnect the client, resulting in Connection Protocol_Timeout errors +# on the server and TLSReadFailed or TLSWriteFailed errors on the client. +# Also, some firewalls and NAT gateways will kill idle connections after +# similar lengths of time. +# +# This can happen for example when most files are backed up already and +# don't need to be sent to the store again, while scanning a large +# directory, or while calculating diffs of a large file. To avoid this, +# KeepAliveTime specifies that special keep-alive messages should be sent +# when the connection is otherwise idle for a certain length of time, +# specified here in seconds. +# +# The default is that these messages are never sent, equivalent to setting +# this option to zero, but we recommend that all users enable this. + +KeepAliveTime = 120 + + +# Files above this size (in bytes) are tracked, and if they are renamed they will simply be +# renamed on the server, rather than being uploaded again. (64k - 1) + +FileTrackingSizeThreshold = 65535 + + +# The daemon does "changes only" uploads for files above this size (in bytes). +# Files less than it are uploaded whole without this extra processing. + +DiffingUploadSizeThreshold = 8192 + + +# The limit on how much time is spent diffing files, in seconds. Most files +# shouldn't take very long, but if you have really big files you can use this +# to limit the time spent diffing them. +# +# * Reduce if you are having problems with processor usage. +# +# * Increase if you have large files, and think the upload of changes is too +# large and you want bbackupd to spend more time searching for unchanged +# blocks. + +MaximumDiffingTime = 120 + + +# Uncomment this line to see exactly what the daemon is going when it's connected to the server. + +# ExtendedLogging = yes + + +# This specifies a program or script script which is run just before each +# sync, and ideally the full path to the interpreter. It will be run as the +# same user bbackupd is running as, usually root. +# +# The script must output (print) either "now" or a number to STDOUT (and a +# terminating newline, no quotes). +# +# If the result was "now", then the sync will happen. If it's a number, then +# no backup will happen for that number of seconds (bbackupd will pause) and +# then the script will be run again. +# +# Use this to temporarily stop bbackupd from syncronising or connecting to the +# store. For example, you could use this on a laptop to only backup when on a +# specific network, or when it has a working Internet connection. + +# SyncAllowScript = /path/to/intepreter/or/exe script-name parameters etc + + +# Where the command socket is created in the filesystem. + +CommandSocket = /home/chris/boxbackup/test/basicserver/testfiles/seclevel2-sha1/bbackupd.sock + +# Uncomment the StoreObjectInfoFile to enable the experimental archiving +# of the daemon's state (including client store marker and configuration) +# between backup runs. This saves time and increases efficiency when +# bbackupd is frequently stopped and started, since it removes the need +# to rescan all directories on the remote server. However, it is new and +# not yet heavily tested, so use with caution. + +# StoreObjectInfoFile = /home/chris/boxbackup/test/basicserver/testfiles/seclevel2-sha1/bbackupd.state + +Server +{ + PidFile = /home/chris/boxbackup/test/basicserver/testfiles/seclevel2-sha1/bbackupd.pid +} + + +# BackupLocations specifies which locations on disc should be backed up. Each +# directory is in the format +# +# name +# { +# Path = /path/of/directory +# (optional exclude directives) +# } +# +# 'name' is derived from the Path by the config script, but should merely be +# unique. +# +# The exclude directives are of the form +# +# [Exclude|AlwaysInclude][File|Dir][|sRegex] = regex or full pathname +# +# (The regex suffix is shown as 'sRegex' to make File or Dir plural) +# +# For example: +# +# ExcludeDir = /home/guest-user +# ExcludeFilesRegex = .(mp3|MP3)$ +# AlwaysIncludeFile = /home/username/veryimportant.mp3 +# +# This excludes the directory /home/guest-user from the backup along with all mp3 +# files, except one MP3 file in particular. +# +# In general, Exclude excludes a file or directory, unless the directory is +# explicitly mentioned in a AlwaysInclude directive. However, Box Backup +# does NOT scan inside excluded directories and will never back up an +# AlwaysIncluded file or directory inside an excluded directory or any +# subdirectory thereof. +# +# To back up a directory inside an excluded directory, use a configuration +# like this, to ensure that each directory in the path to the important +# files is included, but none of their contents will be backed up except +# the directories further down that path to the important one. +# +# ExcludeDirsRegex = ^/home/user/bigfiles/ +# ExcludeFilesRegex = ^/home/user/bigfiles/ +# AlwaysIncludeDir = /home/user/bigfiles/path +# AlwaysIncludeDir = /home/user/bigfiles/path/to +# AlwaysIncludeDir = /home/user/bigfiles/path/important +# AlwaysIncludeDir = /home/user/bigfiles/path/important/files +# AlwaysIncludeDirsRegex = ^/home/user/bigfiles/path/important/files/ +# AlwaysIncludeFilesRegex = ^/home/user/bigfiles/path/important/files/ +# +# If a directive ends in Regex, then it is a regular expression rather than a +# explicit full pathname. See +# +# man 7 re_format +# +# for the regex syntax on your platform. + +BackupLocations +{ + home-chris-boxbackup-test-basicserver-testfiles-seclevel2-sha1 + { + Path = /home/chris/boxbackup/test/basicserver/testfiles/seclevel2-sha1 + } +} + diff --git a/test/basicserver/testfiles/seclevel2-sha1/bbackupd/1234567-FileEncKeys.raw b/test/basicserver/testfiles/seclevel2-sha1/bbackupd/1234567-FileEncKeys.raw new file mode 100644 index 000000000..0145d3985 Binary files /dev/null and b/test/basicserver/testfiles/seclevel2-sha1/bbackupd/1234567-FileEncKeys.raw differ diff --git a/test/basicserver/testfiles/seclevel2-sha1/bbackupd/1234567-csr.pem b/test/basicserver/testfiles/seclevel2-sha1/bbackupd/1234567-csr.pem new file mode 100644 index 000000000..340116ed0 --- /dev/null +++ b/test/basicserver/testfiles/seclevel2-sha1/bbackupd/1234567-csr.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICXjCCAUYCAQAwGTEXMBUGA1UEAwwOQkFDS1VQLTEyMzQ1NjcwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrSHZEUGZxLnDFr0B02Utd5rF6YwYmhzLG +WNBnC0FBrCN0qJgjEHpQ0jqMGA9vIvBuesYhBmk8hOyJFHNtJB8MJyeHvKSwdwlF +Isz+gr60RGAKj290nSdFgMvMgkdqz6Vg4R9t94fzhxjk/BJyNjr8r+64hffIOQmM +YlmADLX38BLRLAfbVVkq/bRgqBFtmvFYTZKl6of1jVSWQLcXGShWE45lc5Hpd+qv +DRjzsQukb3gJmKU4DMW1BCaS8W6v7R0MG/5CooiwMRrct8puH4IeIDrByBz/0mRP +fMPjR2qpjx4EmLRcC39lGVBTnXLYM1XGIYsX7f1ssYZZXSSajUp9AgMBAAGgADAN +BgkqhkiG9w0BAQUFAAOCAQEAbDRc2PW9WnUu7F1g/mWQW8aGhyzMcYTp28kVEtMC +dvvbNLFWtWPXktM9PjR6F+3QRQktdXwYXsTctmGL4vvSKFd66gw4HklGe+Opiiw/ +o9F6E2PAFzRYbMio7UYevs/RhktaJRkVyd81e8LtFHuUD3vqBY84NVeKwmxnbdoK +jzBj3x3COkLLiPTWjb+RgxnPWcNtXhLAcATZeCKBo4U0gvRL1NTMCslIumdhtD8h +BQOdEaSP2sB8o3mMEh8W5tgja4zWv1GszJK9sJNL/EZag331/++5H06yf8vPhQW7 +rqRHA33CUe7XiqAkXp+Rgq5W97qcKIlo4uKRzCsTYC/QUg== +-----END CERTIFICATE REQUEST----- diff --git a/test/basicserver/testfiles/seclevel2-sha1/bbackupd/1234567-key.pem b/test/basicserver/testfiles/seclevel2-sha1/bbackupd/1234567-key.pem new file mode 100644 index 000000000..9ee696ee6 --- /dev/null +++ b/test/basicserver/testfiles/seclevel2-sha1/bbackupd/1234567-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAq0h2RFBmcS5wxa9AdNlLXeaxemMGJocyxljQZwtBQawjdKiY +IxB6UNI6jBgPbyLwbnrGIQZpPITsiRRzbSQfDCcnh7yksHcJRSLM/oK+tERgCo9v +dJ0nRYDLzIJHas+lYOEfbfeH84cY5PwScjY6/K/uuIX3yDkJjGJZgAy19/AS0SwH +21VZKv20YKgRbZrxWE2SpeqH9Y1UlkC3FxkoVhOOZXOR6Xfqrw0Y87ELpG94CZil +OAzFtQQmkvFur+0dDBv+QqKIsDEa3LfKbh+CHiA6wcgc/9JkT3zD40dqqY8eBJi0 +XAt/ZRlQU51y2DNVxiGLF+39bLGGWV0kmo1KfQIDAQABAoIBAHLetfI6uXlOW/M4 +BVJYKGNhQ8WAg69zHGpJRfrVYX5Zo62pI97gPifV1c3+lNtD41s0m4uqcQlVXAzS +2lZn0yqjV6+ApDJ0opLrM++8X4kmEgMDDwx2GNBUAFm3RY4slAzU7e8iAtsfz2JC +a1yNYiH1G3RE5FgzaGPt0Xg/DgqorT6uR5/jIzlSpqRse9sXG4/uGEmfkogMwvU+ +gmcMOs+Jm7HbLMIGxzBydNTFoup1YUVSCuIjdJBpWRCbBaeYeTSoQgdAPALtwJgz +v8quFaJOkJMKIaXOF+1VN8w5rPTJJdfHtYITz6i0V5A7qSHR5jckbm0UWcXnEdaZ +YvkKRUECgYEA4krb4xrXLuSbUv70dKXybyNxEFK+IVG6NZG4+iaW8B8oU8+q8FzM +HPYAdppYKkYrjslKWIOwZdTsYa4Z8U/uhmMv4OpcCq7nYv6W/g1N/AMd8pEJvV9m +EQ5hY1uMg3rgorYWGDyh3HcYl2q4/9EJiPKUVoZb9IPeO3Po3TgK8A8CgYEAwcTf +EHJVs5F2mnetRhETpC5IGUB9OKbPm+JR6+BNFsh1vaPosobfYOzO6PJm0H/z4jMN +n29oc1SAphUXegE6gbVO8/hd9S4OhTq8egFO9Y/BN3/lHUYe/RPs2BZ+Foh70PH7 +9l6K/UDrwJ458hBrFM/DCcjRRcw12GBPUZ8xkLMCgYEAlND8GDc/igQnLYajhs7X +R0V8hmqTxN+1YKNLjZ6xJoqm/68TUG0Ggok5NsY78tkgrg8sSFeaOu2y4m4Xe33A +dDpoczZMf24UlyKsc7iWL4RxPmMpj5NcUR0u6KN9Hb5CWl762seM/qqHzpQNw5ZZ ++ejlqp1DfeL21Axe+JRxhPsCgYEAiYEWtoocbRhd7RHeYWl+4bSCL4FHG2usyjdZ +4SREMFXCz/fACuiRHiwOTNqvwWf7ftqx4SFjIuylerZe+ZJjnWY3iAQJURME9OCQ +nZfOG46PE75rrVF0bi20lken5H+oNcdzAQtoYH2wjvj5r+CczKD/DDN45qoaz9jQ +kOCCgOcCgYBDeOUq+6UoZMmx1c/H4MnRWMpHu0hNfivDEeJMYkxLMA98clstohc7 +T4B4gaoCewJ5XVR72k+Oqgvy++d4g5EpRjFE8hVNjw7Vo3WP0+X5iI+TmBuLKh/c +Wl10t7jLE25vyLJs4nmQd4hav9gWMbP5l99sVq61DM6bMuRcQnyeIA== +-----END RSA PRIVATE KEY----- diff --git a/test/basicserver/testfiles/seclevel2-sha1/bbackupd/NotifySysadmin.sh b/test/basicserver/testfiles/seclevel2-sha1/bbackupd/NotifySysadmin.sh new file mode 100755 index 000000000..48e8cf8e6 --- /dev/null +++ b/test/basicserver/testfiles/seclevel2-sha1/bbackupd/NotifySysadmin.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +# This script is run whenever bbackupd changes state or encounters a +# problem which requires the system administrator to assist: +# +# 1) The store is full, and no more data can be uploaded. +# 2) Some files or directories were not readable. +# 3) A backup run starts or finishes. +# +# The default script emails the system administrator, except for backups +# starting and stopping, where it does nothing. + +SUBJECT="BACKUP PROBLEM on host debian-unstable" +SENDTO="chris" + +if [ "$1" = "" ]; then + echo "Usage: $0 " >&2 + exit 2 +elif [ "$1" = store-full ]; then + sendmail: $SENDTO <" >&2 + exit 2 +elif [ "$1" = store-full ]; then + sendmail: $SENDTO <= 30); + if(line.size() < 30) + { + continue; + } + + if(StartsWith("SSLSecurityLevel not set. Your connection may not " + "be secure.", line.substr(30))) + { + found_not_set = true; + } + else if(StartsWith("SSLSecurityLevel is set, but this Box Backup " + "is not compiled with OpenSSL 1.1 or higher", line.substr(30))) + { + found_not_supported = true; + } + else if(StartsWith(sentinel_value, line.substr(30))) + { + found_sentinel = true; + break; + } + } + +#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL + TEST_THAT(!found_not_set); // We should set it in bbackupd-config + TEST_THAT(!found_not_supported); // And this message should never be logged +#else + TEST_THAT(!found_not_supported); // We should not set it in bbackupd-config + TEST_THAT(!found_not_set); // And this message should never be logged +#endif + + TEST_THAT(found_sentinel); // Otherwise we're looking for the wrong thing! + + return (num_failures == old_num_failures); // no new failures -> good +} + + bool test_bbackupd_config_script() { SETUP_TEST_BBACKUPD(); @@ -4115,24 +4165,63 @@ bool test_bbackupd_config_script() TEST_RETURN(system(cmd.c_str()), 0) bbstored_pid = StartDaemon(bbstored_pid, BBSTORED " " + bbstored_args + - " testfiles/tmp/bbstored.conf", "testfiles/bbstored.pid", 22011); + " -o testfiles/bbstored.log testfiles/tmp/bbstored.conf", "testfiles/bbstored.pid", + 22011); - BackupDaemon bbackupd; - TEST_THAT( - prepare_test_with_client_daemon( - bbackupd, - true, // do_unpack_files - false, // !do_start_bbstored - "testfiles/tmp/bbackupd.conf") - ); + { + Capture capture; + Logging::TempLoggerGuard guard(&capture); - bbackupd.RunSyncNow(); + BackupDaemon bbackupd; + TEST_THAT( + prepare_test_with_client_daemon( + bbackupd, + true, // do_unpack_files + false, // !do_start_bbstored + "testfiles/tmp/bbackupd.conf") + ); + + bbackupd.RunSyncNow(); + + std::vector messages = capture.GetMessages(); + TEST_THAT(!messages.empty()); + if (!messages.empty()) + { + bool found_not_set = false, found_not_supported = false; + for(std::vector::iterator i = messages.begin(); + i != messages.end(); i++) + { + if(StartsWith("SSLSecurityLevel not set. Your connection may not " + "be secure.", i->message)) + { + found_not_set = true; + } + else if(StartsWith("SSLSecurityLevel is set, but this Box Backup " + "is not compiled with OpenSSL 1.1 or higher", i->message)) + { + found_not_supported = true; + } + } + +#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL + TEST_THAT(!found_not_set); // We should set it in bbackupd-config + TEST_THAT(!found_not_supported); // And this message should never be logged +#else + TEST_THAT(!found_not_supported); // We should not set it in bbackupd-config + TEST_THAT(!found_not_set); // And this message should never be logged +#endif + } + } TEST_THAT(compare_external(BackupQueries::ReturnCode::Compare_Same, - "", "-acQ", "testfiles/tmp/bbackupd.conf")); + "-otestfiles/bbackupquery.log", "-acQ", "testfiles/tmp/bbackupd.conf")); + TEST_THAT(check_output_log_file_for_ssl_security_level_warnings("testfiles/bbackupquery.log", + "Connecting to store")); + TEST_THAT(check_output_log_file_for_ssl_security_level_warnings("testfiles/bbstored.log", + "Forked child process")); TEST_THAT(StopServer()); -#endif +#endif // !WIN32 TEARDOWN_TEST_BBACKUPD(); }