From 8aa39b27d8fd6ada556b51c4547a504956474078 Mon Sep 17 00:00:00 2001 From: TJ Saunders Date: Mon, 9 May 2022 17:49:08 -0700 Subject: [PATCH] Issue #1448: Backporting the mod_sftp/OpenSSL 3.x fixes to the 1.3.7 branch. --- NEWS | 4 ++ RELEASE_NOTES | 6 ++ contrib/mod_sftp/cipher.c | 79 ++++++++++++++++++--- contrib/mod_sftp/configure | 123 +++++++++++++++++++++++++++++++++ contrib/mod_sftp/configure.in | 74 +++++++++++++++++++- contrib/mod_sftp/crypto.c | 14 +++- contrib/mod_sftp/keys.c | 4 +- contrib/mod_sftp/mod_sftp.c | 20 +++++- contrib/mod_sftp/mod_sftp.h.in | 14 +++- 9 files changed, 324 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index 52d4b58c94..6cf27d97ff 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,10 @@ where `N' is the issue number. ----------------------------------------------------------------------------- +1.3.7e +-------------------------------- +- Issue 1448 - Ensure that mod_sftp algorithms work properly with OpenSSL 3.x. + 1.3.7d - Released 23-Apr-2022 -------------------------------- - Issue 1321 - Crash with long lines in AuthGroupFile due to large realloc(3). diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 7c274a01f6..ea583ca079 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -6,6 +6,12 @@ This file contains a description of the major changes to ProFTPD for the releases. More information on these changes can be found in the NEWS and ChangeLog files. +1.3.7e +--------- + + + Ensure that mod_sftp algorithms work properly when OpenSSL 3.x is used. + + 1.3.7d --------- diff --git a/contrib/mod_sftp/cipher.c b/contrib/mod_sftp/cipher.c index 77c79e0672..57d4c2f996 100644 --- a/contrib/mod_sftp/cipher.c +++ b/contrib/mod_sftp/cipher.c @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp ciphers - * Copyright (c) 2008-2020 TJ Saunders + * Copyright (c) 2008-2022 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -192,14 +192,40 @@ static int set_cipher_iv(struct sftp_cipher *cipher, const EVP_MD *hash, } ctx = EVP_MD_CTX_create(); - EVP_DigestInit(ctx, hash); + if (EVP_DigestInit(ctx, hash) != 1) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "unable to initialize MD context for '%s': %s", EVP_MD_name(hash), + sftp_crypto_get_errors()); + free(iv); + errno = EINVAL; + return -1; + } + if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_CIPHER_USE_K)) { EVP_DigestUpdate(ctx, k, klen); } - EVP_DigestUpdate(ctx, h, hlen); + + if (EVP_DigestUpdate(ctx, h, hlen) != 1) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "unable to update MD context for '%s': %s", EVP_MD_name(hash), + sftp_crypto_get_errors()); + free(iv); + errno = EINVAL; + return -1; + } + EVP_DigestUpdate(ctx, letter, sizeof(char)); EVP_DigestUpdate(ctx, (char *) id, id_len); - EVP_DigestFinal(ctx, iv, &iv_len); + + if (EVP_DigestFinal(ctx, iv, &iv_len) != 1) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "unable to finish MD context for '%s': %s", EVP_MD_name(hash), + sftp_crypto_get_errors()); + free(iv); + errno = EINVAL; + return -1; + } + EVP_MD_CTX_destroy(ctx); /* If we need more, keep hashing, as per RFC, until we have enough @@ -264,12 +290,37 @@ static int set_cipher_key(struct sftp_cipher *cipher, const EVP_MD *hash, } ctx = EVP_MD_CTX_create(); - EVP_DigestInit(ctx, hash); - EVP_DigestUpdate(ctx, k, klen); + if (EVP_DigestInit(ctx, hash) != 1) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "unable to initialize MD context for '%s': %s", EVP_MD_name(hash), + sftp_crypto_get_errors()); + free(key); + errno = EINVAL; + return -1; + } + + if (EVP_DigestUpdate(ctx, k, klen) != 1) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "unable to update MD context for '%s': %s", EVP_MD_name(hash), + sftp_crypto_get_errors()); + free(key); + errno = EINVAL; + return -1; + } + EVP_DigestUpdate(ctx, h, hlen); EVP_DigestUpdate(ctx, letter, sizeof(char)); EVP_DigestUpdate(ctx, (char *) id, id_len); - EVP_DigestFinal(ctx, key, &key_len); + + if (EVP_DigestFinal(ctx, key, &key_len) != 1) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "unable to finish MD context for '%s': %s", EVP_MD_name(hash), + sftp_crypto_get_errors()); + free(key); + errno = EINVAL; + return -1; + } + EVP_MD_CTX_destroy(ctx); pr_trace_msg(trace_channel, 19, "hashed data to produce key (%lu bytes)", @@ -507,7 +558,19 @@ int sftp_cipher_set_read_key(pool *p, const EVP_MD *hash, const BIGNUM *k, } pr_memscrub(ptr, bufsz); - sftp_cipher_set_block_size(EVP_CIPHER_block_size(cipher->cipher)); + + if (strcmp(cipher->algo, "aes128-ctr") == 0 || + strcmp(cipher->algo, "aes192-ctr") == 0 || + strcmp(cipher->algo, "aes256-ctr") == 0) { + /* For some reason, OpenSSL returns 8 for the AES block size (even + * though the AES block size is 16, per RFC 5647), but OpenSSH wants 16. + */ + sftp_cipher_set_block_size(16); + + } else { + sftp_cipher_set_block_size(EVP_CIPHER_block_size(cipher->cipher)); + } + return 0; } diff --git a/contrib/mod_sftp/configure b/contrib/mod_sftp/configure index f0cd88d445..0907fdbd14 100755 --- a/contrib/mod_sftp/configure +++ b/contrib/mod_sftp/configure @@ -3865,6 +3865,88 @@ $as_echo "no" >&6; } LIBS="$saved_libs" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +LIBS=`echo "$LIBS" | sed -e 's/-lsupp//g'`; +LIBS="-lcrypto $LIBS" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL supports EVP_aes_128_ctr" >&5 +$as_echo_n "checking whether OpenSSL supports EVP_aes_128_ctr... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + EVP_CIPHER *cipher; + cipher = EVP_aes_128_ctr(); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAVE_EVP_AES_128_CTR_OPENSSL 1" >>confdefs.h + + LIBS="$saved_libs" + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + LIBS="$saved_libs" + + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +LIBS=`echo "$LIBS" | sed -e 's/-lsupp//g'`; +LIBS="-lcrypto $LIBS" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL supports EVP_aes_192_ctr" >&5 +$as_echo_n "checking whether OpenSSL supports EVP_aes_192_ctr... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + EVP_CIPHER *cipher; + cipher = EVP_aes_192_ctr(); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAVE_EVP_AES_192_CTR_OPENSSL 1" >>confdefs.h + + LIBS="$saved_libs" + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + LIBS="$saved_libs" + + fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext @@ -3906,6 +3988,47 @@ $as_echo "no" >&6; } LIBS="$saved_libs" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +LIBS=`echo "$LIBS" | sed -e 's/-lsupp//g'`; +LIBS="-lcrypto $LIBS" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL supports OSSL_PROVIDER_load" >&5 +$as_echo_n "checking whether OpenSSL supports OSSL_PROVIDER_load... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + OSSL_PROVIDER *provider; + provider = OSSL_PROVIDER_load(NULL, "default"); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAVE_OSSL_PROVIDER_LOAD_OPENSSL 1" >>confdefs.h + + LIBS="$saved_libs" + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + LIBS="$saved_libs" + + fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext diff --git a/contrib/mod_sftp/configure.in b/contrib/mod_sftp/configure.in index b45d434fec..690efef3da 100644 --- a/contrib/mod_sftp/configure.in +++ b/contrib/mod_sftp/configure.in @@ -1,5 +1,5 @@ dnl ProFTPD - mod_sftp -dnl Copyright (c) 2012-2019 TJ Saunders +dnl Copyright (c) 2012-2022 TJ Saunders dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by @@ -206,6 +206,54 @@ dnl Splice out -lsupp, since that library hasn't been built yet LIBS=`echo "$LIBS" | sed -e 's/-lsupp//g'`; LIBS="-lcrypto $LIBS" +AC_MSG_CHECKING([whether OpenSSL supports EVP_aes_128_ctr]) +AC_TRY_LINK( + [ + #include + ], + [ + EVP_CIPHER *cipher; + cipher = EVP_aes_128_ctr(); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_EVP_AES_128_CTR_OPENSSL, 1, [OpenSSL supports EVP_aes_128_ctr]) + LIBS="$saved_libs" + ], + [ + AC_MSG_RESULT(no) + LIBS="$saved_libs" + ] +) + +dnl Splice out -lsupp, since that library hasn't been built yet +LIBS=`echo "$LIBS" | sed -e 's/-lsupp//g'`; +LIBS="-lcrypto $LIBS" + +AC_MSG_CHECKING([whether OpenSSL supports EVP_aes_192_ctr]) +AC_TRY_LINK( + [ + #include + ], + [ + EVP_CIPHER *cipher; + cipher = EVP_aes_192_ctr(); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_EVP_AES_192_CTR_OPENSSL, 1, [OpenSSL supports EVP_aes_192_ctr]) + LIBS="$saved_libs" + ], + [ + AC_MSG_RESULT(no) + LIBS="$saved_libs" + ] +) + +dnl Splice out -lsupp, since that library hasn't been built yet +LIBS=`echo "$LIBS" | sed -e 's/-lsupp//g'`; +LIBS="-lcrypto $LIBS" + AC_MSG_CHECKING([whether OpenSSL supports EVP_aes_256_ctr]) AC_TRY_LINK( [ @@ -226,6 +274,30 @@ AC_TRY_LINK( ] ) +dnl Splice out -lsupp, since that library hasn't been built yet +LIBS=`echo "$LIBS" | sed -e 's/-lsupp//g'`; +LIBS="-lcrypto $LIBS" + +AC_MSG_CHECKING([whether OpenSSL supports OSSL_PROVIDER_load]) +AC_TRY_LINK( + [ + #include + ], + [ + OSSL_PROVIDER *provider; + provider = OSSL_PROVIDER_load(NULL, "default"); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_OSSL_PROVIDER_LOAD_OPENSSL, 1, [OpenSSL supports OSSL_PROVIDER_load]) + LIBS="$saved_libs" + ], + [ + AC_MSG_RESULT(no) + LIBS="$saved_libs" + ] +) + LIBS="$saved_libs" INCLUDES="$ac_build_addl_includes" diff --git a/contrib/mod_sftp/crypto.c b/contrib/mod_sftp/crypto.c index e9d776bcde..3ff94d4b3e 100644 --- a/contrib/mod_sftp/crypto.c +++ b/contrib/mod_sftp/crypto.c @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp OpenSSL interface - * Copyright (c) 2008-2017 TJ Saunders + * Copyright (c) 2008-2022 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -983,13 +983,25 @@ const EVP_CIPHER *sftp_crypto_get_cipher(const char *name, size_t *key_len, # endif /* !OPENSSL_NO_DES */ } else if (strncmp(name, "aes256-ctr", 11) == 0) { +# if defined(HAVE_EVP_AES_256_CTR_OPENSSL) + cipher = EVP_aes_256_ctr(); +# else cipher = get_aes_ctr_cipher(32); +# endif /* HAVE_EVP_AES_256_CTR_OPENSSL */ } else if (strncmp(name, "aes192-ctr", 11) == 0) { +# if defined(HAVE_EVP_AES_192_CTR_OPENSSL) + cipher = EVP_aes_192_ctr(); +# else cipher = get_aes_ctr_cipher(24); +# endif /* HAVE_EVP_AES_192_CTR_OPENSSL */ } else if (strncmp(name, "aes128-ctr", 11) == 0) { +# if defined(HAVE_EVP_AES_128_CTR_OPENSSL) + cipher = EVP_aes_128_ctr(); +# else cipher = get_aes_ctr_cipher(16); +# endif /* HAVE_EVP_AES_128_CTR_OPENSSL */ #endif /* OpenSSL older than 0.9.7 */ } else { diff --git a/contrib/mod_sftp/keys.c b/contrib/mod_sftp/keys.c index c6b88cf4de..54f19321db 100644 --- a/contrib/mod_sftp/keys.c +++ b/contrib/mod_sftp/keys.c @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp key mgmt (keys) - * Copyright (c) 2008-2021 TJ Saunders + * Copyright (c) 2008-2022 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -2903,7 +2903,7 @@ static int decrypt_openssh_data(pool *p, const char *path, * trailing AEAD bytes. Need to fix that in the future. */ - if (EVP_Cipher(cipher_ctx, buf, encrypted_data, encrypted_len) != 1) { + if (EVP_Cipher(cipher_ctx, buf, encrypted_data, encrypted_len) < 0) { /* This might happen due to a wrong/bad passphrase. */ pr_trace_msg(trace_channel, 3, "error decrypting %s data for key: %s", cipher->algo, diff --git a/contrib/mod_sftp/mod_sftp.c b/contrib/mod_sftp/mod_sftp.c index b84b1a77b6..f53407e88e 100644 --- a/contrib/mod_sftp/mod_sftp.c +++ b/contrib/mod_sftp/mod_sftp.c @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp - * Copyright (c) 2008-2020 TJ Saunders + * Copyright (c) 2008-2022 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,6 +72,10 @@ static const char *sftp_server_version = SFTP_ID_DEFAULT_STRING; #define SFTP_HOSTKEY_FL_CLEAR_ECDSA_KEY 0x004 #define SFTP_HOSTKEY_FL_CLEAR_ED25519_KEY 0x008 +#if defined(HAVE_OSSL_PROVIDER_LOAD_OPENSSL) +static OSSL_PROVIDER *legacy_provider = NULL; +#endif /* HAVE_OSSL_PROVIDER_LOAD_OPENSSL */ + static const char *trace_channel = "ssh2"; static int sftp_have_authenticated(cmd_rec *cmd) { @@ -1881,6 +1885,13 @@ static void sftp_shutdown_ev(const void *event_data, void *user_data) { /* Clean up the OpenSSL stuff. */ sftp_crypto_free(0); +#if defined(HAVE_OSSL_PROVIDER_LOAD_OPENSSL) + if (legacy_provider != NULL) { + OSSL_PROVIDER_unload(legacy_provider); + legacy_provider = NULL; + } +#endif /* HAVE_OSSL_PROVIDER_LOAD_OPENSSL */ + destroy_pool(sftp_pool); sftp_pool = NULL; @@ -2035,6 +2046,13 @@ static int sftp_init(void) { } #endif /* HAVE_SODIUM_H */ +#if defined(HAVE_OSSL_PROVIDER_LOAD_OPENSSL) + /* Load the "legacy" OpenSSL algorithm provider, for those SSH algorithms + * that require support of algorithms that OpenSSL deemed "legacy". + */ + legacy_provider = OSSL_PROVIDER_load(NULL, "legacy"); +#endif /* HAVE_OSSL_PROVIDER_LOAD_OPENSSL */ + sftp_keystore_init(); sftp_cipher_init(); sftp_mac_init(); diff --git a/contrib/mod_sftp/mod_sftp.h.in b/contrib/mod_sftp/mod_sftp.h.in index 065981c313..9af590e8e3 100644 --- a/contrib/mod_sftp/mod_sftp.h.in +++ b/contrib/mod_sftp/mod_sftp.h.in @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp - * Copyright (c) 2008-2020 TJ Saunders + * Copyright (c) 2008-2022 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -49,9 +49,18 @@ /* Define if you have OpenSSL with crippled AES support. */ #undef HAVE_AES_CRIPPLED_OPENSSL +/* Define if you have OpenSSL with EVP_aes_128_ctr support. */ +#undef HAVE_EVP_AES_128_CTR_OPENSSL + +/* Define if you have OpenSSL with EVP_aes_192_ctr support. */ +#undef HAVE_EVP_AES_192_CTR_OPENSSL + /* Define if you have OpenSSL with EVP_aes_256_ctr support. */ #undef HAVE_EVP_AES_256_CTR_OPENSSL +/* Define if you have OpenSSL with OSSL_PROVIDER_load support. */ +#undef HAVE_OSSL_PROVIDER_LOAD_OPENSSL + /* Define if you have OpenSSL with SHA256 support. */ #undef HAVE_SHA256_OPENSSL @@ -93,6 +102,9 @@ # include # include #endif /* PR_USE_OPENSSL_ECC */ +#if defined(HAVE_OSSL_PROVIDER_LOAD_OPENSSL) +# include +#endif /* HAVE_OSSL_PROVIDER_LOAD_OPENSSL */ /* Define if you have the LibreSSL library. */ #if defined(LIBRESSL_VERSION_NUMBER)