From 971fbbce13186199b3ac67f35a7fb5acfc777295 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 1 Jan 2018 11:12:16 +0000 Subject: [PATCH 1/6] Speed up superbuild rebuild by not extracting Boost again (cherry picked from commit 8976906a563ab04eb3d1310c59d4c13cdc3b525e) --- infrastructure/cmake/windows/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infrastructure/cmake/windows/CMakeLists.txt b/infrastructure/cmake/windows/CMakeLists.txt index 80d1369b2..d78faeccd 100644 --- a/infrastructure/cmake/windows/CMakeLists.txt +++ b/infrastructure/cmake/windows/CMakeLists.txt @@ -100,6 +100,8 @@ ExternalProject_Add(boost URL "http://downloads.sourceforge.net/project/boost/boost/${BOOST_VERSION}/boost_${BOOST_VERSION_UNDERSCORES}.tar.bz2" URL_HASH ${BOOST_HASH} # DOWNLOAD_NO_PROGRESS 1 + # Disable automatic updating (untarring) as it's slow and not necessary + UPDATE_DISCONNECTED 1 CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "No configure step needed" BUILD_COMMAND ${CMAKE_COMMAND} -E echo "No build step needed" INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "No install step needed" From d432128c6f9390a3749af1430c8a76fbc52badf7 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 5 Aug 2017 11:37:51 +0100 Subject: [PATCH 2/6] Remove BoxBackup install step from Windows unibuild We don't need to install anything in C:\Program Files\BoxBackup, and often don't have permission to do that anyway. (cherry picked from commit 2d31a3178ba5a39af594c1a0188748f5c8045b18) (cherry picked from commit dab8ccc4b0b9eef06c9e063fc510f9952a0757f7) --- infrastructure/cmake/windows/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infrastructure/cmake/windows/CMakeLists.txt b/infrastructure/cmake/windows/CMakeLists.txt index d78faeccd..0fbe35e34 100644 --- a/infrastructure/cmake/windows/CMakeLists.txt +++ b/infrastructure/cmake/windows/CMakeLists.txt @@ -126,5 +126,6 @@ ExternalProject_Add(boxbackup -DDEBUG=${DEBUG} ${boxbackup_cmake_args} ${SUB_CMAKE_EXTRA_ARGS} - STEP_TARGETS configure build install + INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "No install step needed" + STEP_TARGETS configure build ) From d22ea1891f8930b105665d896ad6c7d5034769cf Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 2 Dec 2016 22:25:15 +0000 Subject: [PATCH 3/6] Detect which debugger to use in t-gdb scripts: lldb or gdb Should make debugging on Mac and BSD less painful. Make t-gdb fail with an error when no debugger was detected. (cherry picked from commit da98a8183737fb1fcb658cebd9612791a4750c4c) (cherry picked from commit ea6bc722131222d1fad08521e61f552f16941b01) (cherry picked from commit 38ba7510f2cad9accf445d5633c64e98a637a139) (cherry picked from commit 02bfd7e5bdd2abc185b12f37e1f536269db6817a) --- infrastructure/m4/boxbackup_tests.m4 | 8 +++++++- infrastructure/makebuildenv.pl.in | 16 +++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/infrastructure/m4/boxbackup_tests.m4 b/infrastructure/m4/boxbackup_tests.m4 index 7410159a5..59467e66a 100644 --- a/infrastructure/m4/boxbackup_tests.m4 +++ b/infrastructure/m4/boxbackup_tests.m4 @@ -357,4 +357,10 @@ fi ;; esac - +AC_CHECK_PROGS(default_debugger, [lldb gdb]) +AC_ARG_WITH([debugger], + [AS_HELP_STRING([--with-debugger=], + [use this debugger in t-gdb scripts to debug tests @<:@default=lldb if present, otherwise gdb@:>@])], + [], + [with_debugger=$default_debugger]) +AC_SUBST([with_debugger]) diff --git a/infrastructure/makebuildenv.pl.in b/infrastructure/makebuildenv.pl.in index 597b6f82c..e43798247 100755 --- a/infrastructure/makebuildenv.pl.in +++ b/infrastructure/makebuildenv.pl.in @@ -379,6 +379,7 @@ $default_cflags =~ s/ -O2//g; $default_cxxflags =~ s/ -O2//g; my $debug_base_dir = 'debug'; my $release_base_dir = 'release'; +my $debugger = '@with_debugger@'; my $release_flags = "-O2"; if ($target_windows) @@ -614,9 +615,18 @@ __E writetestfile("$mod/t", "GLIBCXX_FORCE_NEW=1 ". './_test' . $platform_exe_ext . ' "$@"', $mod); - writetestfile("$mod/t-gdb", "GLIBCXX_FORCE_NEW=1 ". - 'gdb ./_test' . $platform_exe_ext . ' "$@"', $mod); - + + if($debugger) + { + writetestfile("$mod/t-gdb", "GLIBCXX_FORCE_NEW=1 ". + $debugger . ' ./_test' . $platform_exe_ext . ' "$@"', $mod); + } + else + { + writetestfile("$mod/t-gdb", + "echo 'No debugger was detected by configure script'\n". + "exit 2"); + } } my @all_deps_for_module; From 01755b62f6542d25a885a4fe26ce9fc1ac6decaf Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 2 Jun 2017 21:59:21 +0100 Subject: [PATCH 4/6] Fix quoting of CXXFLAGS and LDFLAGS in Makefiles Previously, it was impossible to include an @ sign in these flags (for example, to link to support --with-ssl-headers=/usr/local/opt/openssl@1.1/include/) because it was interpreted as a list variable name by Perl. Now the variables are evaluated single-quoted before being substituted into the Makefile output. (cherry picked from commit 2e9c90d3f32c69cc89d01fb64ced9fca80aae985) --- infrastructure/makebuildenv.pl.in | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/infrastructure/makebuildenv.pl.in b/infrastructure/makebuildenv.pl.in index e43798247..d5ac9f2f4 100755 --- a/infrastructure/makebuildenv.pl.in +++ b/infrastructure/makebuildenv.pl.in @@ -391,6 +391,9 @@ if ($target_windows) print "done\n\nGenerating Makefiles...\n"; my $makefile_ifdef_prefix = $bsd_make ? "." : ""; +my $autoconf_cppflags = '@CPPFLAGS@'; +my $autoconf_cxxflags = '@CXXFLAGS_STRICT@'; +my $autoconf_ldflags = '@LDFLAGS@'; open MASTER_MAKEFILE, ">Makefile" or die "Makefile: $!"; print MASTER_MAKEFILE <<__E; @@ -412,13 +415,13 @@ WINDRES = @WINDRES@ # Work around a mistake in QDBM (using includes for a file not in the # system path) by adding it to the include path with -I. -DEFAULT_CFLAGS = \@CPPFLAGS@ $default_cflags \@CXXFLAGS_STRICT@ \\ +DEFAULT_CFLAGS = $autoconf_cppflags $default_cflags $autoconf_cxxflags \\ $extra_platform_defines $platform_compile_line_extra \\ -DBOX_VERSION="\\"$product_version\\"" -Iqdbm -DEFAULT_CXXFLAGS = \@CPPFLAGS@ $default_cxxflags \@CXXFLAGS_STRICT@ \\ +DEFAULT_CXXFLAGS = $autoconf_cppflags $default_cxxflags $autoconf_cxxflags \\ $extra_platform_defines $platform_compile_line_extra \\ -DBOX_VERSION="\\"$product_version\\"" -LDFLAGS += \@LDFLAGS@ \@LDADD_RDYNAMIC@ $platform_link_line_extra +LDFLAGS += $autoconf_ldflags \@LDADD_RDYNAMIC@ $platform_link_line_extra RELEASE_CFLAGS = \$(DEFAULT_CFLAGS) -DBOX_RELEASE_BUILD $release_flags RELEASE_CXXFLAGS = \$(DEFAULT_CXXFLAGS) -DBOX_RELEASE_BUILD $release_flags From 3db0438644b10370293d9b2f3bd8457561e2c9f8 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 3 Jun 2017 12:06:35 +0100 Subject: [PATCH 5/6] Replace RAND_pseudo_bytes with RAND_bytes RAND_pseudo_bytes is less secure and is now deprecated. (cherry picked from commit f449986b68bd12c18d742c91b625779ae75b11a2) --- lib/crypto/Random.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crypto/Random.cpp b/lib/crypto/Random.cpp index 1d6a07f01..c34a6eea5 100644 --- a/lib/crypto/Random.cpp +++ b/lib/crypto/Random.cpp @@ -50,7 +50,7 @@ void Random::Initialise() // -------------------------------------------------------------------------- void Random::Generate(void *pOutput, int Length) { - if(RAND_pseudo_bytes((uint8_t*)pOutput, Length) == -1) + if(RAND_bytes((uint8_t*)pOutput, Length) == -1) { THROW_EXCEPTION(CipherException, PseudoRandNotAvailable) } From 85e7efc3fa0477f60318d2cd2144503a9ea8feb9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 3 Jun 2017 12:13:42 +0100 Subject: [PATCH 6/6] Add support for OpenSSL 1.1 and replace deprecated function calls See https://github.com/boxbackup/boxbackup/issues/16 for details. Thanks to Chris West @FauxFaux for the initial patch! Use OpenSSL 1.1.0g for Windows superbuild. Fix Windows build by removing calls to obsolete do_ms.bat . Workaround for incorrect library suffixes searched by FindOpenSSL . Link OpenSSL statically and fix missing dependency on crypt32.lib. (cherry picked from commit edd3687f067c68b131822e0064cdeff5bf7a3835) (cherry picked from commit b003e009d1fccadf55a0f59a01c66f7b80f9b3e5) (cherry picked from commit cc6e20466b857f7e20d816a2c20cb36f112398ab) (cherry picked from commit 303c6405e3bc1e673dbc691792e0633742c01f4e) (cherry picked from commit 4e24006f75142a254f1a7db76cf9da1e752d9283) (cherry picked from commit 448ac48a89538f67f508f8d21931c5e6e90472c0) (cherry picked from commit 00b3bb0697219ef8c742698dd43d53b538e362b1) --- appveyor.yml | 2 +- infrastructure/cmake/CMakeLists.txt | 21 ++- infrastructure/cmake/windows/CMakeLists.txt | 18 ++- lib/crypto/CipherBlowfish.cpp | 2 +- lib/crypto/CipherContext.cpp | 157 +++++++++----------- lib/crypto/CipherContext.h | 24 ++- lib/crypto/CipherException.txt | 1 + lib/server/TLSContext.cpp | 13 +- test/crypto/testcrypto.cpp | 2 +- 9 files changed, 141 insertions(+), 99 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 093113f73..d75eff77e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ configuration: environment: VisualStudioVersion: 11.0 Generator_Base: Visual Studio 11 2012 - OPENSSL_VERSION: 1.0.2f + OPENSSL_VERSION: 1.1.0f PCRE_VERSION: 8.38 CMAKE_UNIBUILD_DIR: '%APPVEYOR_BUILD_FOLDER%\..\cmake' BOXBACKUP_VERSION_BASE: 0.12 diff --git a/infrastructure/cmake/CMakeLists.txt b/infrastructure/cmake/CMakeLists.txt index 250154525..65f59eb86 100644 --- a/infrastructure/cmake/CMakeLists.txt +++ b/infrastructure/cmake/CMakeLists.txt @@ -407,7 +407,26 @@ else() endif() # Link to OpenSSL -find_package(OpenSSL REQUIRED) +# Workaround for incorrect library suffixes searched by FindOpenSSL: +# https://gitlab.kitware.com/cmake/cmake/issues/17604 +if(WIN32 AND MSVC) + find_package(OpenSSL) + set(OPENSSL_SSL_LIBRARY ${SSL_EAY_RELEASE}) + set(OPENSSL_CRYPTO_LIBRARY ${LIB_EAY_RELEASE}) + set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} crypt32) + find_package_handle_standard_args(OpenSSL + REQUIRED_VARS + OPENSSL_SSL_LIBRARY + OPENSSL_CRYPTO_LIBRARY + OPENSSL_INCLUDE_DIR + VERSION_VAR + OPENSSL_VERSION + FAIL_MESSAGE + "Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR" + ) +else() + find_package(OpenSSL REQUIRED) +endif() include_directories(${OPENSSL_INCLUDE_DIR}) target_link_libraries(lib_crypto PUBLIC ${OPENSSL_LIBRARIES}) diff --git a/infrastructure/cmake/windows/CMakeLists.txt b/infrastructure/cmake/windows/CMakeLists.txt index 0fbe35e34..49a1ea4d4 100644 --- a/infrastructure/cmake/windows/CMakeLists.txt +++ b/infrastructure/cmake/windows/CMakeLists.txt @@ -15,9 +15,9 @@ set(ZLIB_VERSION 1.2.11) set(ZLIB_HASH SHA256=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1) # Version of OpenSSL to download, build, and compile Box Backup against: -set(OPENSSL_VERSION 1.0.2j) +set(OPENSSL_VERSION 1.1.0g) # Hash of openssl-${OPENSSL_VERSION}.tar.gz, to be verified after download: -set(OPENSSL_HASH SHA256=e7aff292be21c259c6af26469c7a9b3ba26e9abaaffd325e3dccc9785256c431) +set(OPENSSL_HASH SHA256=de4d501267da39310905cb6dc8c6121f7a2cad45a7707f76df828fe1b85073af) # Version of PCRE to download, build, and compile Box Backup against: set(PCRE_VERSION 8.39) @@ -49,15 +49,21 @@ if(WIN32) URL "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz" URL_HASH ${OPENSSL_HASH} DOWNLOAD_NO_PROGRESS 1 - CONFIGURE_COMMAND perl Configure debug-VC-WIN32 no-asm --prefix=${install_dir} - COMMAND cmd /c ms\\do_ms.bat + CONFIGURE_COMMAND perl Configure debug-VC-WIN32 no-asm no-shared + --prefix=${install_dir} + --openssldir=etc + # Run tests before install, but don't make the main target depend on them, so that + # we don't have to run them whenever we build manually on Windows. + TEST_BEFORE_INSTALL 1 + TEST_EXCLUDE_FROM_MAIN 1 # You would expect us to use nt.mak to compile a static library here, but mk1mf.pl uses the /MT[d] # CRT in that case, which is incompatible with our dynamic runtime, /MD[d]. It seems that the libs # built by ntdll.mak, which are compiled with /MD[d], are full libraries and not import libs, # so we can link statically against them and still get a dynamic runtime. BUILD_IN_SOURCE 1 - BUILD_COMMAND nmake /s /f ms\\nt.mak - INSTALL_COMMAND nmake /s /f ms\\nt.mak install + BUILD_COMMAND nmake /s + TEST_COMMAND nmake /s test + INSTALL_COMMAND nmake /s install ) elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") ExternalProject_Add(openssl diff --git a/lib/crypto/CipherBlowfish.cpp b/lib/crypto/CipherBlowfish.cpp index e16cc6ed2..4c75b1de7 100644 --- a/lib/crypto/CipherBlowfish.cpp +++ b/lib/crypto/CipherBlowfish.cpp @@ -206,7 +206,7 @@ void CipherBlowfish::SetupParameters(EVP_CIPHER_CTX *pCipherContext) const } // Set key #ifndef HAVE_OLD_SSL - if(EVP_CipherInit_ex(pCipherContext, NULL, NULL, (unsigned char*)mpKey, (unsigned char*)mpInitialisationVector, -1) != 1) + if(EVP_CipherInit_ex(pCipherContext, GetCipher(), NULL, (unsigned char*)mpKey, (unsigned char*)mpInitialisationVector, -1) != 1) #else if(EVP_CipherInit(pCipherContext, NULL, (unsigned char*)mKey.c_str(), (unsigned char*)mInitialisationVector, -1) != 1) #endif diff --git a/lib/crypto/CipherContext.cpp b/lib/crypto/CipherContext.cpp index fd1493951..3de88c642 100644 --- a/lib/crypto/CipherContext.cpp +++ b/lib/crypto/CipherContext.cpp @@ -2,7 +2,7 @@ // // File // Name: CipherContext.cpp -// Purpose: Context for symmetric encryption / descryption +// Purpose: Context for symmetric encryption / decryption // Created: 1/12/03 // // -------------------------------------------------------------------------- @@ -50,7 +50,7 @@ CipherContext::~CipherContext() if(mInitialised) { // Clean up - EVP_CIPHER_CTX_cleanup(&ctx); + BOX_OPENSSL_CLEANUP_CTX(ctx); mInitialised = false; } #ifdef HAVE_OLD_SSL @@ -98,7 +98,7 @@ void CipherContext::Init(CipherContext::CipherFunction Function, const CipherDes // Check for bad usage if(mInitialised) { - THROW_EXCEPTION(CipherException, AlreadyInitialised) + THROW_EXCEPTION(CipherException, AlreadyInitialised); } if(Function != Decrypt && Function != Encrypt) { @@ -109,43 +109,45 @@ void CipherContext::Init(CipherContext::CipherFunction Function, const CipherDes mFunction = Function; // Initialise the cipher -#ifndef HAVE_OLD_SSL - EVP_CIPHER_CTX_init(&ctx); // no error return code, even though the docs says it does - - if(EVP_CipherInit_ex(&ctx, rDescription.GetCipher(), NULL, NULL, NULL, - (mFunction == Encrypt) ? 1 : 0) != 1) -#else +#ifdef HAVE_OLD_SSL // Use old version of init call if(EVP_CipherInit(&ctx, rDescription.GetCipher(), NULL, NULL, (mFunction == Encrypt) ? 1 : 0) != 1) +#else + BOX_OPENSSL_INIT_CTX(ctx); + + // Don't set key or IV yet, because we will modify the parameters: + if(EVP_CipherInit_ex(BOX_OPENSSL_CTX(ctx), rDescription.GetCipher(), NULL, NULL, NULL, + (mFunction == Encrypt) ? 1 : 0) != 1) #endif { THROW_EXCEPTION_MESSAGE(CipherException, EVPInitFailure, "Failed to initialise " << rDescription.GetFullName() - << "cipher: " << LogError("initialising cipher")); + << ": " << LogError("initialising cipher")); } + UsePadding(mPaddingOn); try { mCipherName = rDescription.GetFullName(); #ifndef HAVE_OLD_SSL // Let the description set up everything else - rDescription.SetupParameters(&ctx); + mpDescription = &rDescription; #else // With the old version, a copy needs to be taken first. mpDescription = rDescription.Clone(); // Mark it as not a leak, otherwise static cipher contexts // cause spurious memory leaks to be reported MEMLEAKFINDER_NOT_A_LEAK(mpDescription); - mpDescription->SetupParameters(&ctx); #endif + mpDescription->SetupParameters(BOX_OPENSSL_CTX(ctx)); } catch(...) { THROW_EXCEPTION_MESSAGE(CipherException, EVPInitFailure, - "Failed to configure " << mCipherName << " cipher: " << + "Failed to configure " << mCipherName << ": " << LogError("configuring cipher")); - EVP_CIPHER_CTX_cleanup(&ctx); + BOX_OPENSSL_CLEANUP_CTX(ctx); throw; } @@ -166,7 +168,7 @@ void CipherContext::Reset() if(mInitialised) { // Clean up - EVP_CIPHER_CTX_cleanup(&ctx); + EVP_CIPHER_CTX_cleanup(BOX_OPENSSL_CTX(ctx)); mInitialised = false; } #ifdef HAVE_OLD_SSL @@ -177,6 +179,7 @@ void CipherContext::Reset() } #endif mWithinTransform = false; + mIV.clear(); } @@ -192,24 +195,22 @@ void CipherContext::Begin() { if(!mInitialised) { - THROW_EXCEPTION(CipherException, NotInitialised) + THROW_EXCEPTION(CipherException, NotInitialised); } - // Warn if in a transformation (not an error, because a context might not have been finalised if an exception occured) if(mWithinTransform) { - BOX_WARNING("CipherContext::Begin called when context " - "flagged as within a transform"); + THROW_EXCEPTION(CipherException, AlreadyInTransform); } - // Initialise the cipher context again - if(EVP_CipherInit(&ctx, NULL, NULL, NULL, -1) != 1) + if(EVP_CipherInit_ex(BOX_OPENSSL_CTX(ctx), NULL, NULL, NULL, + (const unsigned char *)(mIV.size() > 0 ? mIV.c_str() : NULL), + -1) != 1) { THROW_EXCEPTION_MESSAGE(CipherException, EVPInitFailure, - "Failed to reset " << mCipherName << " cipher: " << - LogError("resetting cipher")); + "Failed to set IV for " << mCipherName << ": " << LogError(GetFunction())); } - + // Mark as being within a transform mWithinTransform = true; } @@ -251,18 +252,18 @@ int CipherContext::Transform(void *pOutBuffer, int OutLength, const void *pInBuf } // Check output buffer size - if(OutLength < (InLength + EVP_CIPHER_CTX_block_size(&ctx))) + if(OutLength < (InLength + EVP_CIPHER_CTX_block_size(BOX_OPENSSL_CTX(ctx)))) { THROW_EXCEPTION(CipherException, OutputBufferTooSmall); } // Do the transform int outLength = OutLength; - if(EVP_CipherUpdate(&ctx, (unsigned char*)pOutBuffer, &outLength, (unsigned char*)pInBuffer, InLength) != 1) + if(EVP_CipherUpdate(BOX_OPENSSL_CTX(ctx), (unsigned char*)pOutBuffer, &outLength, + (unsigned char*)pInBuffer, InLength) != 1) { THROW_EXCEPTION_MESSAGE(CipherException, EVPUpdateFailure, - "Failed to " << GetFunction() << " (update) " << - mCipherName << " cipher: " << LogError(GetFunction())); + "Failed to update " << mCipherName << ": " << LogError(GetFunction())); } return outLength; @@ -300,7 +301,7 @@ int CipherContext::Final(void *pOutBuffer, int OutLength) } // Check output buffer size - if(OutLength < (2 * EVP_CIPHER_CTX_block_size(&ctx))) + if(OutLength < (2 * EVP_CIPHER_CTX_block_size(BOX_OPENSSL_CTX(ctx)))) { THROW_EXCEPTION(CipherException, OutputBufferTooSmall); } @@ -308,12 +309,11 @@ int CipherContext::Final(void *pOutBuffer, int OutLength) // Do the transform int outLength = OutLength; #ifndef HAVE_OLD_SSL - if(EVP_CipherFinal(&ctx, (unsigned char*)pOutBuffer, &outLength) != 1) + if(EVP_CipherFinal(BOX_OPENSSL_CTX(ctx), (unsigned char*)pOutBuffer, &outLength) != 1) { mWithinTransform = false; THROW_EXCEPTION_MESSAGE(CipherException, EVPFinalFailure, - "Failed to " << GetFunction() << " (final) " << - mCipherName << " cipher: " << LogError(GetFunction())); + "Failed to finalise " << mCipherName << ": " << LogError(GetFunction())); } #else OldOpenSSLFinal((unsigned char*)pOutBuffer, outLength); @@ -340,11 +340,11 @@ void CipherContext::OldOpenSSLFinal(unsigned char *Buffer, int &rOutLengthOut) // Old version needs to use a different form, and then set up the cipher again for next time around int outLength = rOutLengthOut; // Have to emulate padding off... - int blockSize = EVP_CIPHER_CTX_block_size(&ctx); + int blockSize = EVP_CIPHER_CTX_block_size(ctx); if(mPaddingOn) { // Just use normal final call - if(EVP_CipherFinal(&ctx, Buffer, &outLength) != 1) + if(EVP_CipherFinal(ctx, Buffer, &outLength) != 1) { THROW_EXCEPTION(CipherException, EVPFinalFailure) } @@ -357,13 +357,13 @@ void CipherContext::OldOpenSSLFinal(unsigned char *Buffer, int &rOutLengthOut) { // NASTY -- fiddling around with internals like this is bad. // But only way to get this working on old versions of OpenSSL. - if(!EVP_EncryptUpdate(&ctx,Buffer,&outLength,ctx.buf,0) + if(!EVP_EncryptUpdate(ctx,Buffer,&outLength,ctx.buf,0) || outLength != blockSize) { THROW_EXCEPTION(CipherException, EVPFinalFailure) } // Clean up - EVP_CIPHER_CTX_cleanup(&ctx); + EVP_CIPHER_CTX_free(ctx); } else { @@ -391,12 +391,14 @@ void CipherContext::OldOpenSSLFinal(unsigned char *Buffer, int &rOutLengthOut) } } // Reinitialise the cipher for the next time around - if(EVP_CipherInit(&ctx, mpDescription->GetCipher(), NULL, NULL, + if(EVP_CipherInit_ex(&ctx, mpDescription->GetCipher(), NULL, NULL, + (const unsigned char *)(mIV.size() > 0 ? mIV.c_str() : NULL), (mFunction == Encrypt) ? 1 : 0) != 1) { THROW_EXCEPTION(CipherException, EVPInitFailure) } mpDescription->SetupParameters(&ctx); + UsePadding(mPaddingOn); // Update length for caller rOutLengthOut = outLength; @@ -421,7 +423,7 @@ int CipherContext::InSizeForOutBufferSize(int OutLength) // Strictly speaking, the *2 is unnecessary. However... // Final() is paranoid, and requires two input blocks of space to work. - return OutLength - (EVP_CIPHER_CTX_block_size(&ctx) * 2); + return OutLength - (EVP_CIPHER_CTX_block_size(BOX_OPENSSL_CTX(ctx)) * 2); } // -------------------------------------------------------------------------- @@ -442,7 +444,7 @@ int CipherContext::MaxOutSizeForInBufferSize(int InLength) // Final() is paranoid, and requires two input blocks of space to work, and so we need to add // three blocks on to be absolutely sure. - return InLength + (EVP_CIPHER_CTX_block_size(&ctx) * 3); + return InLength + (EVP_CIPHER_CTX_block_size(BOX_OPENSSL_CTX(ctx)) * 3); } @@ -456,20 +458,8 @@ int CipherContext::MaxOutSizeForInBufferSize(int InLength) // -------------------------------------------------------------------------- int CipherContext::TransformBlock(void *pOutBuffer, int OutLength, const void *pInBuffer, int InLength) { - if(!mInitialised) - { - THROW_EXCEPTION(CipherException, NotInitialised) - } - - // Warn if in a transformation - if(mWithinTransform) - { - BOX_WARNING("CipherContext::TransformBlock called when " - "context flagged as within a transform"); - } - // Check output buffer size - if(OutLength < (InLength + EVP_CIPHER_CTX_block_size(&ctx))) + if(OutLength < (InLength + EVP_CIPHER_CTX_block_size(BOX_OPENSSL_CTX(ctx)))) { // Check if padding is off, in which case the buffer can be smaller if(!mPaddingOn && OutLength <= InLength) @@ -481,40 +471,36 @@ int CipherContext::TransformBlock(void *pOutBuffer, int OutLength, const void *p THROW_EXCEPTION(CipherException, OutputBufferTooSmall); } } - - // Initialise the cipher context again - if(EVP_CipherInit(&ctx, NULL, NULL, NULL, -1) != 1) - { - THROW_EXCEPTION(CipherException, EVPInitFailure) - } + + Begin(); // Do the entire block - int outLength = 0; + int output_space_used = OutLength; // Update - outLength = OutLength; - if(EVP_CipherUpdate(&ctx, (unsigned char*)pOutBuffer, &outLength, (unsigned char*)pInBuffer, InLength) != 1) + if(EVP_CipherUpdate(BOX_OPENSSL_CTX(ctx), (unsigned char*)pOutBuffer, &output_space_used, + (unsigned char*)pInBuffer, InLength) != 1) { THROW_EXCEPTION_MESSAGE(CipherException, EVPUpdateFailure, - "Failed to " << GetFunction() << " (update) " << - mCipherName << " cipher: " << LogError(GetFunction())); + "Failed to update " << mCipherName << ": " << LogError(GetFunction())); } // Finalise - int outLength2 = OutLength - outLength; -#ifndef HAVE_OLD_SSL - if(EVP_CipherFinal(&ctx, ((unsigned char*)pOutBuffer) + outLength, &outLength2) != 1) + int output_space_remain = OutLength - output_space_used; + +#ifdef HAVE_OLD_SSL + OldOpenSSLFinal(((unsigned char*)pOutBuffer) + output_space_used, output_space_remain); +#else + if(EVP_CipherFinal(BOX_OPENSSL_CTX(ctx), ((unsigned char*)pOutBuffer) + output_space_used, + &output_space_remain) != 1) { THROW_EXCEPTION_MESSAGE(CipherException, EVPFinalFailure, - "Failed to " << GetFunction() << " (final) " << - mCipherName << " cipher: " << LogError(GetFunction())); + "Failed to finalise " << mCipherName << ": " << LogError(GetFunction())); } -#else - OldOpenSSLFinal(((unsigned char*)pOutBuffer) + outLength, outLength2); #endif - outLength += outLength2; - return outLength; + mWithinTransform = false; + return output_space_used + output_space_remain; } @@ -533,7 +519,7 @@ int CipherContext::GetIVLength() THROW_EXCEPTION(CipherException, NotInitialised) } - return EVP_CIPHER_CTX_iv_length(&ctx); + return EVP_CIPHER_CTX_iv_length(BOX_OPENSSL_CTX(ctx)); } @@ -559,12 +545,14 @@ void CipherContext::SetIV(const void *pIV) "flagged as within a transform"); } + mIV = std::string((const char *)pIV, GetIVLength()); + // Set IV - if(EVP_CipherInit(&ctx, NULL, NULL, (unsigned char *)pIV, -1) != 1) + if(EVP_CipherInit_ex(BOX_OPENSSL_CTX(ctx), NULL, NULL, NULL, + (const unsigned char *)mIV.c_str(), -1) != 1) { THROW_EXCEPTION_MESSAGE(CipherException, EVPInitFailure, - "Failed to " << GetFunction() << " (set IV) " << - mCipherName << " cipher: " << LogError(GetFunction())); + "Failed to set IV for " << mCipherName << ": " << LogError(GetFunction())); } #ifdef HAVE_OLD_SSL @@ -601,19 +589,20 @@ const void *CipherContext::SetRandomIV(int &rLengthOut) } // Get length of IV - unsigned int ivLen = EVP_CIPHER_CTX_iv_length(&ctx); - if(ivLen > sizeof(mGeneratedIV)) + uint8_t generated_iv[CIPHERCONTEXT_MAX_GENERATED_IV_LENGTH]; + unsigned int ivLen = EVP_CIPHER_CTX_iv_length(BOX_OPENSSL_CTX(ctx)); + if(ivLen > sizeof(generated_iv)) { THROW_EXCEPTION(CipherException, IVSizeImplementationLimitExceeded) } // Generate some random data - Random::Generate(mGeneratedIV, ivLen); - SetIV(mGeneratedIV); + Random::Generate(generated_iv, ivLen); + SetIV(generated_iv); // Return the IV and it's length rLengthOut = ivLen; - return mGeneratedIV; + return mIV.c_str(); } @@ -628,9 +617,11 @@ const void *CipherContext::SetRandomIV(int &rLengthOut) void CipherContext::UsePadding(bool Padding) { #ifndef HAVE_OLD_SSL - if(EVP_CIPHER_CTX_set_padding(&ctx, Padding) != 1) + if(EVP_CIPHER_CTX_set_padding(BOX_OPENSSL_CTX(ctx), Padding) != 1) { - THROW_EXCEPTION(CipherException, EVPSetPaddingFailure) + THROW_EXCEPTION_MESSAGE(CipherException, EVPSetPaddingFailure, + "Failed to set padding for " << mCipherName << ": " << + LogError(GetFunction())); } #endif mPaddingOn = Padding; diff --git a/lib/crypto/CipherContext.h b/lib/crypto/CipherContext.h index 93c889d6c..b6e97b4e3 100644 --- a/lib/crypto/CipherContext.h +++ b/lib/crypto/CipherContext.h @@ -19,6 +19,22 @@ class CipherDescription; #define CIPHERCONTEXT_MAX_GENERATED_IV_LENGTH 32 +// 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 +// for the gory details. +#if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER >= 0x10100000L) // OpenSSL >= 1.1 +# define BOX_OPENSSL_INIT_CTX(ctx) ctx = EVP_CIPHER_CTX_new(); +# define BOX_OPENSSL_CTX(ctx) ctx +# define BOX_OPENSSL_CLEANUP_CTX(ctx) EVP_CIPHER_CTX_free(ctx) +typedef EVP_CIPHER_CTX* BOX_EVP_CIPHER_CTX; +#else // OpenSSL < 1.1 +# define BOX_OPENSSL_INIT_CTX(ctx) EVP_CIPHER_CTX_init(&ctx); // no error return code, even though the docs says it does +# define BOX_OPENSSL_CTX(ctx) &ctx +# define BOX_OPENSSL_CLEANUP_CTX(ctx) EVP_CIPHER_CTX_cleanup(&ctx) +typedef EVP_CIPHER_CTX BOX_EVP_CIPHER_CTX; +#endif + + // -------------------------------------------------------------------------- // // Class @@ -74,16 +90,14 @@ class CipherContext #endif private: - EVP_CIPHER_CTX ctx; + BOX_EVP_CIPHER_CTX ctx; bool mInitialised; bool mWithinTransform; bool mPaddingOn; - uint8_t mGeneratedIV[CIPHERCONTEXT_MAX_GENERATED_IV_LENGTH]; CipherFunction mFunction; std::string mCipherName; -#ifdef HAVE_OLD_SSL - CipherDescription *mpDescription; -#endif + const CipherDescription *mpDescription; + std::string mIV; }; diff --git a/lib/crypto/CipherException.txt b/lib/crypto/CipherException.txt index abdbac872..494ed3cc3 100644 --- a/lib/crypto/CipherException.txt +++ b/lib/crypto/CipherException.txt @@ -16,3 +16,4 @@ PseudoRandNotAvailable 12 EVPSetPaddingFailure 13 RandomInitFailed 14 Failed to read from random device LengthRequestedTooLongForRandomHex 15 +AlreadyInTransform 16 Tried to initialise crypto when already in a transform diff --git a/lib/server/TLSContext.cpp b/lib/server/TLSContext.cpp index 35e254fdb..1a6d4a534 100644 --- a/lib/server/TLSContext.cpp +++ b/lib/server/TLSContext.cpp @@ -23,6 +23,17 @@ #define MAX_VERIFICATION_DEPTH 2 #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 +// for the gory details. +#if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER >= 0x10100000L) // OpenSSL >= 1.1 +# define BOX_TLS_SERVER_METHOD TLS_server_method +# define BOX_TLS_CLIENT_METHOD TLS_client_method +#else // OpenSSL < 1.1 +# define BOX_TLS_SERVER_METHOD TLSv1_server_method +# define BOX_TLS_CLIENT_METHOD TLSv1_client_method +#endif + // -------------------------------------------------------------------------- // // Function @@ -67,7 +78,7 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c ::SSL_CTX_free(mpContext); } - mpContext = ::SSL_CTX_new(AsServer?TLSv1_server_method():TLSv1_client_method()); + mpContext = ::SSL_CTX_new(AsServer ? BOX_TLS_SERVER_METHOD() : BOX_TLS_CLIENT_METHOD()); if(mpContext == NULL) { THROW_EXCEPTION(ServerException, TLSAllocationFailed) diff --git a/test/crypto/testcrypto.cpp b/test/crypto/testcrypto.cpp index 4e623cc28..32d2efb8e 100644 --- a/test/crypto/testcrypto.cpp +++ b/test/crypto/testcrypto.cpp @@ -266,7 +266,7 @@ int test(int argc, const char *argv[]) // Check rolling checksums uint8_t *checkdata_blk = (uint8_t *)malloc(CHECKSUM_DATA_SIZE); uint8_t *checkdata = checkdata_blk; - RAND_pseudo_bytes(checkdata, CHECKSUM_DATA_SIZE); + RAND_bytes(checkdata, CHECKSUM_DATA_SIZE); for(int size = CHECKSUM_BLOCK_SIZE_BASE; size <= CHECKSUM_BLOCK_SIZE_LAST; ++size) { // Test skip-roll code