[Git][debian-proftpd-team/proftpd][upstream] New upstream version 1.3.8.b+dfsg

Hilmar Preuße (@hilmar-guest) gitlab at salsa.debian.org
Wed Dec 20 13:38:37 GMT 2023



Hilmar Preuße pushed to branch upstream at Debian ProFTPD Team / proftpd


Commits:
209e4d5c by Hilmar Preusse at 2023-12-20T10:10:20+00:00
New upstream version 1.3.8.b+dfsg
- - - - -


24 changed files:

- .github/workflows/ci.yml
- .github/workflows/rpm.yml
- NEWS
- README.md
- RELEASE_NOTES
- configure
- configure.in
- contrib/dist/rpm/proftpd.spec
- contrib/mod_digest.c
- contrib/mod_sftp/crypto.c
- contrib/mod_sftp/fxp.c
- contrib/mod_sftp/kex.c
- contrib/mod_sftp/keys.c
- contrib/mod_sftp/mac.c
- contrib/mod_sftp/mod_sftp.c
- contrib/mod_sftp/mod_sftp.h.in
- contrib/mod_sftp/packet.c
- contrib/mod_sftp/packet.h
- contrib/mod_sftp/tap.c
- contrib/mod_sftp/tap.h
- contrib/mod_tls.c
- doc/contrib/mod_sftp.html
- include/version.h
- tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm


Changes:

=====================================
.github/workflows/ci.yml
=====================================
@@ -4,9 +4,19 @@ on:
   push:
     branches:
       - 1.3.8
+    paths-ignore:
+      - NEWS
+      - RELEASE_NOTES
+      - '**/*.html'
+      - '**/*.md'
   pull_request:
     branches:
       - 1.3.8
+    paths-ignore:
+      - NEWS
+      - RELEASE_NOTES
+      - '**/*.html'
+      - '**/*.md'
 
 jobs:
   build:


=====================================
.github/workflows/rpm.yml
=====================================
@@ -4,9 +4,19 @@ on:
   push:
     branches:
       - 1.3.8
+    paths-ignore:
+      - NEWS
+      - RELEASE_NOTES
+      - '**/*.html'
+      - '**/*.md'
   pull_request:
     branches:
       - 1.3.8
+    paths-ignore:
+      - NEWS
+      - RELEASE_NOTES
+      - '**/*.html'
+      - '**/*.md'
 
 jobs:
   build:


=====================================
NEWS
=====================================
@@ -15,6 +15,14 @@
   where `N' is the issue number.
 -----------------------------------------------------------------------------
 
+1.3.8b - Released 19-Dec-2023
+--------------------------------
+- Issue 1735 - Compiling ProFTPD 1.3.8a mod_sftp, mod_tls using libressl 3.7.3
+  fails.
+- Issue 1756 - Build system fails for specific module names.
+- Issue 1760 - mod_sftp is affected by "Terrapin" Prefix Truncation Attacks in
+  SSH Specification (CVE-2023-48795).
+
 1.3.8a - Released 08-Oct-2023
 --------------------------------
 - Issue 1581 - mod_sftp fails to handle SFTP requests to truncate files to


=====================================
README.md
=====================================
@@ -8,7 +8,7 @@
 [![Coverage Status](https://coveralls.io/repos/github/proftpd/proftpd/badge.svg?branch=master)](https://coveralls.io/github/proftpd/proftpd?branch=master)
 [![Coverity Scan Status](https://scan.coverity.com/projects/198/badge.svg)](https://scan.coverity.com/projects/198)
 [![C/C++ Language Grade](https://img.shields.io/lgtm/grade/cpp/g/proftpd/proftpd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/proftpd/proftpd/context:cpp)
-[![Release](https://img.shields.io/badge/release-1.3.8a-brightgreen)](https://github.com/proftpd/proftpd/releases/latest)
+[![Release](https://img.shields.io/badge/release-1.3.8b-brightgreen)](https://github.com/proftpd/proftpd/releases/latest)
 [![License](https://img.shields.io/badge/license-GPL-brightgreen.svg)](https://img.shields.io/badge/license-GPL-brightgreen.svg)
 
 ## Introduction


=====================================
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.8b
+------
+
+  + Implemented mitigations for "Terrapin" SSH attack (CVE-2023-48795).
+
+
 1.3.8a
 ------
 


=====================================
configure
=====================================
@@ -24090,12 +24090,13 @@ ac_static_module_dirs=
 
 for amodule in $ac_shared_modules; do
   module=`echo "$amodule" | sed -e 's/\.la//g'`.o;
+  modulename=`echo "$amodule" | sed -e 's/\.la$//'`;
 
-  ac_core_modules=`echo "$ac_core_modules" | sed -e "s/$module//g"`;
-  ac_build_core_modules=`echo "$ac_build_core_modules" | sed -e "s/modules\/$module//g"`;
+  ac_core_modules=`echo "$ac_core_modules" | sed -e "s/$modulename\.o//g"`;
+  ac_build_core_modules=`echo "$ac_build_core_modules" | sed -e "s/modules\/$modulename\.o//g"`;
 
-  ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$module//g"`;
-  ac_build_static_modules=`echo "$ac_build_static_modules" | sed -e "s/modules\/$module//g"`;
+  ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$modulename\.o//g"`;
+  ac_build_static_modules=`echo "$ac_build_static_modules" | sed -e "s/modules\/$modulename\.o//g"`;
 done
 
 GLUE_MODULE_OBJS="$ac_core_modules $ac_static_modules"
@@ -25418,33 +25419,39 @@ fi
 
 for module in $ac_shared_modules ; do
   moduledir=`echo "$module" | sed -e 's/\.la$//'`;
+  modulename=`echo "$module" | sed -e 's/\.la$//'`;
+  src=`echo "$module" | sed -e 's/\.o$//'`.c;
+  srcinc=`echo "$module" | sed -e 's/\.o$//'`.h;
 
   if test -f $srcdir/modules/$src -o -f $srcdir/contrib/$src; then
     continue
 
   elif test -d $srcdir/modules/$moduledir; then
     ac_shared_module_dirs="$ac_shared_module_dirs modules/$moduledir";
-    ac_shared_modules=`echo "$ac_shared_modules" | sed -e "s/$module//"`
+    ac_shared_modules=`echo "$ac_shared_modules" | sed -e "s/$modulename\.la//"`
 
   elif test -d $srcdir/contrib/$moduledir; then
     ac_shared_module_dirs="$ac_shared_module_dirs contrib/$moduledir";
-    ac_shared_modules=`echo "$ac_shared_modules" | sed -e "s/$module//"`
+    ac_shared_modules=`echo "$ac_shared_modules" | sed -e "s/$modulename\.la//"`
   fi
 done
 
 for module in $ac_static_modules ; do
   moduledir=`echo "$module" | sed -e 's/\.o$//'`;
+  modulename=`echo "$module" | sed -e 's/\.o$//'`;
+  src=`echo "$module" | sed -e 's/\.o$//'`.c;
+  srcinc=`echo "$module" | sed -e 's/\.o$//'`.h;
 
   if test -f $srcdir/modules/$src -o -f $srcdir/contrib/$src; then
     continue
 
   elif test -d $srcdir/modules/$moduledir; then
     ac_static_module_dirs="$ac_static_module_dirs modules/$moduledir";
-    ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$module//"`
+    ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$modulename\.o//"`
 
   elif test -d $srcdir/contrib/$moduledir; then
     ac_static_module_dirs="$ac_static_module_dirs contrib/$moduledir";
-    ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$module//"`
+    ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$modulename\.o//"`
 
     addonlibs=""
     src=`echo "$module" | sed -e 's/\.o$//'`.c;
@@ -25478,8 +25485,8 @@ for module in $ac_static_modules ; do
         as_fn_error $? "specified archive '$thearch' does not match expected module archive name '$moduledir.a' -- aborting" "$LINENO" 5
       fi
 
-      ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$module//g"`
-      ac_build_static_modules=`echo "$ac_build_static_modules" | sed -e "s/modules\/$module//g"`
+      ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$modulename\.o//g"`
+      ac_build_static_modules=`echo "$ac_build_static_modules" | sed -e "s/modules\/$modulename\.o//g"`
       ac_build_static_module_archives="$ac_build_static_module_archives contrib/$moduledir/$thearch"
     done
 


=====================================
configure.in
=====================================
@@ -3136,12 +3136,13 @@ dnl Remove any modules in the shared module list from the core and static
 dnl module lists
 for amodule in $ac_shared_modules; do
   module=`echo "$amodule" | sed -e 's/\.la//g'`.o;
+  modulename=`echo "$amodule" | sed -e 's/\.la$//'`;
 
-  ac_core_modules=`echo "$ac_core_modules" | sed -e "s/$module//g"`;
-  ac_build_core_modules=`echo "$ac_build_core_modules" | sed -e "s/modules\/$module//g"`;
+  ac_core_modules=`echo "$ac_core_modules" | sed -e "s/$modulename\.o//g"`;
+  ac_build_core_modules=`echo "$ac_build_core_modules" | sed -e "s/modules\/$modulename\.o//g"`;
 
-  ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$module//g"`;
-  ac_build_static_modules=`echo "$ac_build_static_modules" | sed -e "s/modules\/$module//g"`;
+  ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$modulename\.o//g"`;
+  ac_build_static_modules=`echo "$ac_build_static_modules" | sed -e "s/modules\/$modulename\.o//g"`;
 done
 
 GLUE_MODULE_OBJS="$ac_core_modules $ac_static_modules"
@@ -3991,33 +3992,39 @@ fi
 
 for module in $ac_shared_modules ; do
   moduledir=`echo "$module" | sed -e 's/\.la$//'`;
+  modulename=`echo "$module" | sed -e 's/\.la$//'`;
+  src=`echo "$module" | sed -e 's/\.o$//'`.c;
+  srcinc=`echo "$module" | sed -e 's/\.o$//'`.h;
 
   if test -f $srcdir/modules/$src -o -f $srcdir/contrib/$src; then
     continue
 
   elif test -d $srcdir/modules/$moduledir; then
     ac_shared_module_dirs="$ac_shared_module_dirs modules/$moduledir";
-    ac_shared_modules=`echo "$ac_shared_modules" | sed -e "s/$module//"`
+    ac_shared_modules=`echo "$ac_shared_modules" | sed -e "s/$modulename\.la//"`
 
   elif test -d $srcdir/contrib/$moduledir; then
     ac_shared_module_dirs="$ac_shared_module_dirs contrib/$moduledir";
-    ac_shared_modules=`echo "$ac_shared_modules" | sed -e "s/$module//"`
+    ac_shared_modules=`echo "$ac_shared_modules" | sed -e "s/$modulename\.la//"`
   fi
 done
 
 for module in $ac_static_modules ; do
   moduledir=`echo "$module" | sed -e 's/\.o$//'`;
+  modulename=`echo "$module" | sed -e 's/\.o$//'`;
+  src=`echo "$module" | sed -e 's/\.o$//'`.c;
+  srcinc=`echo "$module" | sed -e 's/\.o$//'`.h;
 
   if test -f $srcdir/modules/$src -o -f $srcdir/contrib/$src; then
     continue
 
   elif test -d $srcdir/modules/$moduledir; then
     ac_static_module_dirs="$ac_static_module_dirs modules/$moduledir";
-    ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$module//"`
+    ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$modulename\.o//"`
 
   elif test -d $srcdir/contrib/$moduledir; then
     ac_static_module_dirs="$ac_static_module_dirs contrib/$moduledir";
-    ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$module//"`
+    ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$modulename\.o//"`
 
     addonlibs=""
     src=`echo "$module" | sed -e 's/\.o$//'`.c;
@@ -4053,8 +4060,8 @@ for module in $ac_static_modules ; do
         AC_MSG_ERROR([specified archive '$thearch' does not match expected module archive name '$moduledir.a' -- aborting])
       fi
 
-      ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$module//g"`
-      ac_build_static_modules=`echo "$ac_build_static_modules" | sed -e "s/modules\/$module//g"`
+      ac_static_modules=`echo "$ac_static_modules" | sed -e "s/$modulename\.o//g"`
+      ac_build_static_modules=`echo "$ac_build_static_modules" | sed -e "s/modules\/$modulename\.o//g"`
       ac_build_static_module_archives="$ac_build_static_module_archives contrib/$moduledir/$thearch"
     done
 


=====================================
contrib/dist/rpm/proftpd.spec
=====================================
@@ -53,7 +53,7 @@
 # RHEL5 and clones don't have suitably recent versions of pcre/libmemcached
 # so use --with rhel5 to inhibit those features when using --with everything
 
-%global proftpd_version			1.3.8a
+%global proftpd_version			1.3.8b
 
 # rc_version should be incremented for each RC release, and reset back to 1
 # AFTER each stable release.
@@ -61,7 +61,7 @@
 
 # release_version should be incremented for each maint release, and reset back
 # to 1 BEFORE starting new release cycle.
-%global release_version			2
+%global release_version			3
 
 %if %(echo %{proftpd_version} | grep rc >/dev/null 2>&1 && echo 1 || echo 0)
 %global rpm_version %(echo %{proftpd_version} | sed -e 's/rc.*//')


=====================================
contrib/mod_digest.c
=====================================
@@ -1,7 +1,7 @@
 /*
  * ProFTPD: mod_digest - File hashing/checksumming module
  * Copyright (c) Mathias Berchtold <mb at smartftp.com>
- * Copyright (c) 2016-2022 TJ Saunders <tj at castaglia.org>
+ * Copyright (c) 2016-2023 TJ Saunders <tj at castaglia.org>
  * 
  * 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
@@ -321,12 +321,12 @@ static int CRC32_Free(CRC32_CTX *ctx) {
 static int crc32_init(EVP_MD_CTX *ctx) {
   void *md_data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   return CRC32_Init(md_data);
 }
@@ -334,12 +334,12 @@ static int crc32_init(EVP_MD_CTX *ctx) {
 static int crc32_update(EVP_MD_CTX *ctx, const void *data, size_t datasz) {
   void *md_data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   return CRC32_Update(md_data, data, datasz);
 }
@@ -347,12 +347,12 @@ static int crc32_update(EVP_MD_CTX *ctx, const void *data, size_t datasz) {
 static int crc32_final(EVP_MD_CTX *ctx, unsigned char *md) {
   void *md_data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   return CRC32_Final(md, md_data);
 }
@@ -360,18 +360,18 @@ static int crc32_final(EVP_MD_CTX *ctx, unsigned char *md) {
 static int crc32_free(EVP_MD_CTX *ctx) {
   void *md_data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   return CRC32_Free(md_data);
 }
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
 static const EVP_MD crc32_md = {
   NID_undef,
   NID_undef,
@@ -386,13 +386,13 @@ static const EVP_MD crc32_md = {
   CRC32_BLOCK,
   sizeof(EVP_MD *) + sizeof(CRC32_CTX)
 };
-#endif /* Older OpenSSLs */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
 static const EVP_MD *EVP_crc32(void) {
   EVP_MD *md;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   /* XXX TODO: At some point, we also need to call EVP_MD_meth_free() on
    * this, to avoid a resource leak.
    */
@@ -407,7 +407,7 @@ static const EVP_MD *EVP_crc32(void) {
   EVP_MD_meth_set_flags(md, 0);
 #else
   md = &crc32_md;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   return md;
 }
@@ -1039,9 +1039,9 @@ static int compute_digest(pool *p, const char *path, off_t start, off_t len,
   unsigned char *buf;
   size_t bufsz, readsz, iter_count;
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
 
   fh = pr_fsio_open(path, O_RDONLY);
@@ -1097,21 +1097,21 @@ static int compute_digest(pool *p, const char *path, off_t start, off_t len,
   }
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   pctx = &ctx;
 #else
   pctx = EVP_MD_CTX_new();
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   EVP_MD_CTX_init(pctx);
   if (EVP_DigestInit_ex(pctx, md, NULL) != 1) {
     pr_log_debug(DEBUG1, MOD_DIGEST_VERSION
       ": error preparing digest context: %s", get_errors());
     (void) pr_fsio_close(fh);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     errno = EPERM;
     return -1;
   }
@@ -1169,10 +1169,10 @@ static int compute_digest(pool *p, const char *path, off_t start, off_t len,
   (void) pr_fsio_close(fh);
 
   if (len != 0) {
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     pr_log_debug(DEBUG3, MOD_DIGEST_VERSION
       ": failed to read all %" PR_LU " bytes of '%s' (premature EOF?)",
       (pr_off_t) len, path);
@@ -1183,18 +1183,18 @@ static int compute_digest(pool *p, const char *path, off_t start, off_t len,
   if (EVP_DigestFinal_ex(pctx, digest, digest_len) != 1) {
     pr_log_debug(DEBUG1, MOD_DIGEST_VERSION
       ": error finishing digest: %s", get_errors());
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     errno = EPERM;
     return -1;
   }
 
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   return 0;
 }
@@ -1207,25 +1207,25 @@ static const EVP_MD *get_algo_md(unsigned long algo) {
       md = EVP_crc32();
       break;
 
-#ifndef OPENSSL_NO_MD5
+#if !defined(OPENSSL_NO_MD5)
     case DIGEST_ALGO_MD5:
       md = EVP_md5();
       break;
 #endif /* OPENSSL_NO_MD5 */
 
-#ifndef OPENSSL_NO_SHA1
+#if !defined(OPENSSL_NO_SHA1)
     case DIGEST_ALGO_SHA1:
       md = EVP_sha1();
       break;
 #endif /* OPENSSL_NO_SHA1 */
 
-#ifndef OPENSSL_NO_SHA256
+#if !defined(OPENSSL_NO_SHA256)
     case DIGEST_ALGO_SHA256:
       md = EVP_sha256();
       break;
 #endif /* OPENSSL_NO_SHA256 */
 
-#ifndef OPENSSL_NO_SHA512
+#if !defined(OPENSSL_NO_SHA512)
     case DIGEST_ALGO_SHA512:
       md = EVP_sha512();
       break;


=====================================
contrib/mod_sftp/crypto.c
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp OpenSSL interface
- * Copyright (c) 2008-2022 TJ Saunders
+ * Copyright (c) 2008-2023 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
@@ -104,10 +104,12 @@ static struct sftp_cipher ciphers[] = {
   { "aes128-cbc",	"aes-128-cbc",	0, 0,	EVP_aes_128_cbc, TRUE, TRUE },
 #endif
 
-#if !defined(OPENSSL_NO_BF)
+#if !defined(OPENSSL_NO_BF) && \
+    (!defined(HAVE_LIBRESSL) || \
+      (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L))
 # if OPENSSL_VERSION_NUMBER < 0x30000000L
   { "blowfish-ctr",	NULL,		0, 0,	NULL,	FALSE, FALSE },
-# endif /* Prior to OpenSSL 3.x */
+# endif /* Prior to OpenSSL 3.x/LibreSSL-3.5.0 */
   { "blowfish-cbc",	"bf-cbc",	0, 0,	EVP_bf_cbc, FALSE, FALSE },
 #endif /* !OPENSSL_NO_BF */
 
@@ -134,10 +136,12 @@ static struct sftp_cipher ciphers[] = {
   { "arcfour",		"rc4",		0, 0,	EVP_rc4, FALSE, FALSE },
 #endif
 
-#if !defined(OPENSSL_NO_DES)
+#if !defined(OPENSSL_NO_DES) && \
+    (!defined(HAVE_LIBRESSL) || \
+      (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L))
 # if OPENSSL_VERSION_NUMBER < 0x30000000L
   { "3des-ctr",		NULL,		0, 0,	NULL, TRUE, TRUE },
-# endif /* Prior to OpenSSL 3.x */
+# endif /* Prior to OpenSSL 3.x/LibreSSL-3.5.0 */
   { "3des-cbc",		"des-ede3-cbc",	0, 0,	EVP_des_ede3_cbc, TRUE, TRUE },
 #endif /* !OPENSSL_NO_DES */
 
@@ -253,7 +257,9 @@ static const char *key_exchanges[] = {
 
 static const char *trace_channel = "ssh2";
 
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#if OPENSSL_VERSION_NUMBER < 0x30000000L && \
+    (!defined(HAVE_LIBRESSL) || \
+      (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L))
 static void ctr_incr(unsigned char *ctr, size_t len) {
   register int i;
 
@@ -271,6 +277,8 @@ static void ctr_incr(unsigned char *ctr, size_t len) {
 #endif /* Prior to OpenSSL 3.x */
 
 #if !defined(OPENSSL_NO_BF) && \
+    (!defined(HAVE_LIBRESSL) || \
+      (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)) && \
     OPENSSL_VERSION_NUMBER < 0x30000000L
 /* Blowfish CTR mode implementation */
 
@@ -396,8 +404,8 @@ static int do_bf_ctr(EVP_CIPHER_CTX *ctx, unsigned char *dst,
 static const EVP_CIPHER *get_bf_ctr_cipher(void) {
   EVP_CIPHER *cipher;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   /* XXX TODO: At some point, we also need to call EVP_CIPHER_meth_free() on
    * this, to avoid a resource leak.
    */
@@ -407,7 +415,6 @@ static const EVP_CIPHER *get_bf_ctr_cipher(void) {
   EVP_CIPHER_meth_set_cleanup(cipher, cleanup_bf_ctr);
   EVP_CIPHER_meth_set_do_cipher(cipher, do_bf_ctr);
   EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CBC_MODE|EVP_CIPH_VARIABLE_LENGTH|EVP_CIPH_ALWAYS_CALL_INIT|EVP_CIPH_CUSTOM_IV);
-
 #else
   static EVP_CIPHER bf_ctr_cipher;
 
@@ -424,7 +431,7 @@ static const EVP_CIPHER *get_bf_ctr_cipher(void) {
   bf_ctr_cipher.flags = EVP_CIPH_CBC_MODE|EVP_CIPH_VARIABLE_LENGTH|EVP_CIPH_ALWAYS_CALL_INIT|EVP_CIPH_CUSTOM_IV;
 
   cipher = &bf_ctr_cipher;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   return cipher;
 }
@@ -433,6 +440,8 @@ static const EVP_CIPHER *get_bf_ctr_cipher(void) {
 #if OPENSSL_VERSION_NUMBER > 0x000907000L
 
 # if !defined(OPENSSL_NO_DES) && \
+     (!defined(HAVE_LIBRESSL) || \
+       (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)) && \
      OPENSSL_VERSION_NUMBER < 0x30000000L
 /* 3DES CTR mode implementation */
 
@@ -568,8 +577,8 @@ static int do_des3_ctr(EVP_CIPHER_CTX *ctx, unsigned char *dst,
 static const EVP_CIPHER *get_des3_ctr_cipher(void) {
   EVP_CIPHER *cipher;
 
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   unsigned long flags;
 
   /* XXX TODO: At some point, we also need to call EVP_CIPHER_meth_free() on
@@ -607,7 +616,7 @@ static const EVP_CIPHER *get_des3_ctr_cipher(void) {
 #  endif /* OPENSSL_FIPS */
 
   cipher = &des3_ctr_cipher;
-# endif /* prior to OpenSSL-1.1.0 */
+# endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   return cipher;
 }
@@ -615,7 +624,9 @@ static const EVP_CIPHER *get_des3_ctr_cipher(void) {
 
 #if !defined(HAVE_EVP_AES_256_CTR_OPENSSL) && \
     !defined(HAVE_EVP_AES_192_CTR_OPENSSL) && \
-    !defined(HAVE_EVP_AES_128_CTR_OPENSSL)
+    !defined(HAVE_EVP_AES_128_CTR_OPENSSL) && \
+    (!defined(HAVE_LIBRESSL) || \
+      (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L))
 
 /* AES CTR mode implementation */
 struct aes_ctr_ex {
@@ -869,12 +880,12 @@ static int update_umac64(EVP_MD_CTX *ctx, const void *data, size_t len) {
   int res;
   void *md_data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   if (md_data == NULL) {
     struct umac_ctx *umac;
     void **ptr;
@@ -897,12 +908,12 @@ static int update_umac128(EVP_MD_CTX *ctx, const void *data, size_t len) {
   int res;
   void *md_data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   if (md_data == NULL) {
     struct umac_ctx *umac;
@@ -927,12 +938,12 @@ static int final_umac64(EVP_MD_CTX *ctx, unsigned char *md) {
   int res;
   void *md_data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   res = umac_final(md_data, md, nonce);
   return res;
@@ -943,12 +954,12 @@ static int final_umac128(EVP_MD_CTX *ctx, unsigned char *md) {
   int res;
   void *md_data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   res = umac128_final(md_data, md, nonce);
   return res;
@@ -958,12 +969,12 @@ static int delete_umac64(EVP_MD_CTX *ctx) {
   struct umac_ctx *umac;
   void *md_data, **ptr;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   umac = md_data;
   umac_delete(umac);
@@ -978,12 +989,12 @@ static int delete_umac128(EVP_MD_CTX *ctx) {
   struct umac_ctx *umac;
   void *md_data, **ptr;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   md_data = EVP_MD_CTX_md_data(ctx);
 #else
   md_data = ctx->md_data;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   umac = md_data;
   umac128_delete(umac);
@@ -997,8 +1008,8 @@ static int delete_umac128(EVP_MD_CTX *ctx) {
 static const EVP_MD *get_umac64_digest(void) {
   EVP_MD *md;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   /* XXX TODO: At some point, we also need to call EVP_MD_meth_free() on
    * this, to avoid a resource leak.
    */
@@ -1024,7 +1035,7 @@ static const EVP_MD *get_umac64_digest(void) {
   umac64_digest.block_size = 32;
 
   md = &umac64_digest;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   return md;
 }
@@ -1032,8 +1043,8 @@ static const EVP_MD *get_umac64_digest(void) {
 static const EVP_MD *get_umac128_digest(void) {
   EVP_MD *md;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   /* XXX TODO: At some point, we also need to call EVP_MD_meth_free() on
    * this, to avoid a resource leak.
    */
@@ -1044,7 +1055,6 @@ static const EVP_MD *get_umac128_digest(void) {
   EVP_MD_meth_set_update(md, update_umac128);
   EVP_MD_meth_set_final(md, final_umac128);
   EVP_MD_meth_set_cleanup(md, delete_umac128);
-
 #else
   static EVP_MD umac128_digest;
 
@@ -1060,7 +1070,7 @@ static const EVP_MD *get_umac128_digest(void) {
   umac128_digest.block_size = 64;
 
   md = &umac128_digest;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   return md;
 }
@@ -1081,6 +1091,8 @@ const EVP_CIPHER *sftp_crypto_get_cipher(const char *name, size_t *key_len,
 
       if (strcmp(name, "blowfish-ctr") == 0) {
 #if !defined(OPENSSL_NO_BF) && \
+    (!defined(HAVE_LIBRESSL) || \
+      (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)) && \
     OPENSSL_VERSION_NUMBER < 0x30000000L
         cipher = get_bf_ctr_cipher();
 #else
@@ -1093,6 +1105,8 @@ const EVP_CIPHER *sftp_crypto_get_cipher(const char *name, size_t *key_len,
 #if OPENSSL_VERSION_NUMBER > 0x000907000L
       } else if (strcmp(name, "3des-ctr") == 0) {
 # if !defined(OPENSSL_NO_DES) && \
+     (!defined(HAVE_LIBRESSL) || \
+       (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)) && \
      OPENSSL_VERSION_NUMBER < 0x30000000L
         cipher = get_des3_ctr_cipher();
 # else


=====================================
contrib/mod_sftp/fxp.c
=====================================
@@ -4106,9 +4106,9 @@ static int fxp_handle_ext_check_file(struct fxp_packet *fxp, char *digest_list,
   void *data;
   size_t datasz;
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX md_ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   const EVP_MD *md;
 
@@ -4437,12 +4437,12 @@ static int fxp_handle_ext_check_file(struct fxp_packet *fxp, char *digest_list,
   }
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   pctx = &md_ctx;
   EVP_MD_CTX_init(pctx);
 #else
   pctx = EVP_MD_CTX_new();
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_FXP_EXTENDED_REPLY);
   sftp_msg_write_int(&buf, &buflen, fxp->request_id);
@@ -4461,12 +4461,12 @@ static int fxp_handle_ext_check_file(struct fxp_packet *fxp, char *digest_list,
 
     pr_signals_handle();
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
     EVP_MD_CTX_cleanup(pctx);
     EVP_MD_CTX_init(pctx);
 #else
     EVP_MD_CTX_reset(pctx);
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     EVP_DigestInit(pctx, md);
 
     pr_trace_msg(trace_channel, 19,


=====================================
contrib/mod_sftp/kex.c
=====================================
@@ -166,6 +166,13 @@ static struct sftp_kex *kex_first_kex = NULL;
 static struct sftp_kex *kex_rekey_kex = NULL;
 static int kex_sent_kexinit = FALSE;
 
+/* Using strict kex?  Note that we maintain this value here, rather than
+ * in the sftp_kex struct, so that any "use strict KEX" flag set via the
+ * first KEXINIT is used through any subsequent KEXINITs.
+ */
+static int use_strict_kex = FALSE;
+static int kex_done_first_kex = FALSE;
+
 /* Diffie-Hellman group moduli */
 
 static const char *dh_group1_str =
@@ -310,9 +317,9 @@ static const unsigned char *calculate_h(struct sftp_kex *kex,
     const unsigned char *hostkey_data, uint32_t hostkey_datalen,
     const BIGNUM *k, uint32_t *hlen) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   const BIGNUM *dh_pub_key = NULL;
   unsigned char *buf, *ptr;
@@ -348,23 +355,23 @@ static const unsigned char *calculate_h(struct sftp_kex *kex,
   sftp_msg_write_mpint(&buf, &buflen, kex->e);
 
   /* Server's key */
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DH_get0_key(kex->dh, &dh_pub_key, NULL);
 #else
   dh_pub_key = kex->dh->pub_key;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(&buf, &buflen, dh_pub_key);
 
   /* Shared secret */
   sftp_msg_write_mpint(&buf, &buflen, k);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   /* In OpenSSL 0.9.6, many of the EVP_Digest* functions returned void, not
    * int.  Without these ugly OpenSSL version preprocessor checks, the
@@ -378,10 +385,10 @@ static const unsigned char *calculate_h(struct sftp_kex *kex,
     BN_clear_free((BIGNUM *) kex->e);
     kex->e = NULL;
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
@@ -395,10 +402,10 @@ static const unsigned char *calculate_h(struct sftp_kex *kex,
     BN_clear_free((BIGNUM *) kex->e);
     kex->e = NULL;
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
@@ -412,20 +419,20 @@ static const unsigned char *calculate_h(struct sftp_kex *kex,
     BN_clear_free((BIGNUM *) kex->e);
     kex->e = NULL;
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
   EVP_DigestFinal(pctx, kex_digest_buf, hlen);
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   BN_clear_free((BIGNUM *) kex->e);
   kex->e = NULL;
@@ -439,9 +446,9 @@ static const unsigned char *calculate_gex_h(struct sftp_kex *kex,
     const BIGNUM *k, uint32_t min, uint32_t pref, uint32_t max,
     uint32_t *hlen) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   const BIGNUM *dh_p = NULL, *dh_g = NULL, *dh_pub_key = NULL;
   unsigned char *buf, *ptr;
@@ -485,13 +492,13 @@ static const unsigned char *calculate_gex_h(struct sftp_kex *kex,
     sftp_msg_write_int(&buf, &buflen, max);
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
 #else
   dh_p = kex->dh->p;
   dh_g = kex->dh->g;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(&buf, &buflen, dh_p);
   sftp_msg_write_mpint(&buf, &buflen, dh_g);
 
@@ -499,23 +506,23 @@ static const unsigned char *calculate_gex_h(struct sftp_kex *kex,
   sftp_msg_write_mpint(&buf, &buflen, kex->e);
 
   /* Server's key */
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DH_get0_key(kex->dh, &dh_pub_key, NULL);
 #else
   dh_pub_key = kex->dh->pub_key;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(&buf, &buflen, dh_pub_key);
 
   /* Shared secret */
   sftp_msg_write_mpint(&buf, &buflen, k);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   /* In OpenSSL 0.9.6, many of the EVP_Digest* functions returned void, not
    * int.  Without these ugly OpenSSL version preprocessor checks, the
@@ -529,10 +536,10 @@ static const unsigned char *calculate_gex_h(struct sftp_kex *kex,
     BN_clear_free((BIGNUM *) kex->e);
     kex->e = NULL;
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
@@ -546,10 +553,10 @@ static const unsigned char *calculate_gex_h(struct sftp_kex *kex,
     BN_clear_free((BIGNUM *) kex->e);
     kex->e = NULL;
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
@@ -563,20 +570,20 @@ static const unsigned char *calculate_gex_h(struct sftp_kex *kex,
     BN_clear_free((BIGNUM *) kex->e);
     kex->e = NULL;
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
   EVP_DigestFinal(pctx, kex_digest_buf, hlen);
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
   BN_clear_free((BIGNUM *) kex->e);
   kex->e = NULL;
   pr_memscrub(ptr, bufsz);
@@ -589,9 +596,9 @@ static const unsigned char *calculate_kexrsa_h(struct sftp_kex *kex,
     const BIGNUM *k, unsigned char *rsa_key, uint32_t rsa_keylen,
     uint32_t *hlen) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   unsigned char *buf, *ptr;
   uint32_t buflen, bufsz;
@@ -632,12 +639,12 @@ static const unsigned char *calculate_kexrsa_h(struct sftp_kex *kex,
   /* Shared secret. */
   sftp_msg_write_mpint(&buf, &buflen, k);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   /* In OpenSSL 0.9.6, many of the EVP_Digest* functions returned void, not
    * int.  Without these ugly OpenSSL version preprocessor checks, the
@@ -649,10 +656,10 @@ static const unsigned char *calculate_kexrsa_h(struct sftp_kex *kex,
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error initializing message digest: %s", sftp_crypto_get_errors());
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
@@ -664,10 +671,10 @@ static const unsigned char *calculate_kexrsa_h(struct sftp_kex *kex,
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error updating message digest: %s", sftp_crypto_get_errors());
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
@@ -679,20 +686,20 @@ static const unsigned char *calculate_kexrsa_h(struct sftp_kex *kex,
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error finalizing message digest: %s", sftp_crypto_get_errors());
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
   EVP_DigestFinal(pctx, kex_digest_buf, hlen);
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
   pr_memscrub(ptr, bufsz);
 
   return kex_digest_buf;
@@ -703,9 +710,9 @@ static const unsigned char *calculate_ecdh_h(struct sftp_kex *kex,
     const unsigned char *hostkey_data, uint32_t hostkey_datalen,
     const BIGNUM *k, uint32_t *hlen) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   unsigned char *buf, *ptr;
   uint32_t buflen, bufsz;
@@ -749,12 +756,12 @@ static const unsigned char *calculate_ecdh_h(struct sftp_kex *kex,
   /* Shared secret */
   sftp_msg_write_mpint(&buf, &buflen, k);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   /* In OpenSSL 0.9.6, many of the EVP_Digest* functions returned void, not
    * int.  Without these ugly OpenSSL version preprocessor checks, the
@@ -768,10 +775,10 @@ static const unsigned char *calculate_ecdh_h(struct sftp_kex *kex,
     BN_clear_free((BIGNUM *) kex->e);
     kex->e = NULL;
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
@@ -785,10 +792,10 @@ static const unsigned char *calculate_ecdh_h(struct sftp_kex *kex,
     BN_clear_free((BIGNUM *) kex->e);
     kex->e = NULL;
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
@@ -802,20 +809,20 @@ static const unsigned char *calculate_ecdh_h(struct sftp_kex *kex,
     BN_clear_free((BIGNUM *) kex->e);
     kex->e = NULL;
     pr_memscrub(ptr, bufsz);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return NULL;
   }
 #else
   EVP_DigestFinal(pctx, kex_digest_buf, hlen);
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000LL && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
   BN_clear_free((BIGNUM *) kex->e);
   kex->e = NULL;
   pr_memscrub(ptr, bufsz);
@@ -846,12 +853,12 @@ static int have_good_dh(DH *dh, const BIGNUM *pub_key) {
     return -1;
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DH_get0_pqg(dh, &dh_p, NULL, NULL);
 #else
   dh_p = dh->p;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   tmp = BN_new();
   if (!BN_sub(tmp, dh_p, BN_value_one()) ||
@@ -1095,13 +1102,13 @@ static int create_dh(struct sftp_kex *kex, int type) {
       return -1;
     }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     DH_set0_pqg(dh, (BIGNUM *) dh_p, NULL, (BIGNUM *) dh_g);
 #else
     dh->p = dh_p;
     dh->g = dh_g;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
     dh_priv_key = BN_new();
 
@@ -1116,13 +1123,13 @@ static int create_dh(struct sftp_kex *kex, int type) {
     }
 
     dh_pub_key = BN_new();
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     DH_set0_key(dh, (BIGNUM *) dh_pub_key, (BIGNUM *) dh_priv_key);
 #else
     dh->pub_key = dh_pub_key;
     dh->priv_key = dh_priv_key;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
     pr_trace_msg(trace_channel, 12, "generating DH key");
     if (DH_generate_key(dh) != 1) {
@@ -1132,12 +1139,12 @@ static int create_dh(struct sftp_kex *kex, int type) {
       return -1;
     }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     DH_get0_key(dh, &dh_pub_key, NULL);
 #else
     dh_pub_key = dh->pub_key;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
     if (have_good_dh(dh, dh_pub_key) < 0) {
       DH_free(dh);
@@ -1258,13 +1265,13 @@ static int finish_dh(struct sftp_kex *kex) {
 
     dh_pub_key = BN_new();
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     DH_set0_key(kex->dh, dh_pub_key, dh_priv_key);
 #else
     kex->dh->pub_key = dh_pub_key;
     kex->dh->priv_key = dh_priv_key;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
     pr_trace_msg(trace_channel, 12, "generating DH key");
     if (DH_generate_key(kex->dh) != 1) {
@@ -1275,7 +1282,7 @@ static int finish_dh(struct sftp_kex *kex) {
 
     if (have_good_dh(kex->dh, kex->e) < 0) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
       if (kex->dh->priv_key != NULL) {
         BN_clear_free(kex->dh->priv_key);
         kex->dh->priv_key = NULL;
@@ -1285,7 +1292,7 @@ static int finish_dh(struct sftp_kex *kex) {
         BN_clear_free(kex->dh->pub_key);
         kex->dh->pub_key = NULL;
       }
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
       continue;
     }
@@ -1612,6 +1619,16 @@ static const char *get_kexinit_exchange_list(pool *p) {
     res = pstrcat(p, res, *res ? "," : "", pstrdup(p, "ext-info-s"), NULL);
   }
 
+  if (!(sftp_opts & SFTP_OPT_NO_STRICT_KEX)) {
+    /* Indicate support for OpenSSH's custom "strict KEX" mode extension,
+     * but only if we have not done/completed our first KEX.
+     */
+    if (kex_done_first_kex == FALSE) {
+      res = pstrcat(p, res, *res ? "," : "",
+        pstrdup(p, "kex-strict-s-v00 at openssh.com"), NULL);
+    }
+  }
+
   return res;
 }
 
@@ -2323,6 +2340,21 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) {
     pr_trace_msg(trace_channel, 20, "client %s EXT_INFO support",
       kex->use_ext_info ? "signaled" : "did not signal" );
 
+    if (!(sftp_opts & SFTP_OPT_NO_STRICT_KEX)) {
+      /* Did the client indicate "strict kex" support (Issue 1760)?
+       *
+       * Note that we only check for this if it is our first KEXINIT.
+       * The "strict kex" extension is ignored in any subsequent KEXINITs, as
+       * for rekeys.
+       */
+      if (kex_done_first_kex == FALSE) {
+        use_strict_kex = sftp_misc_namelist_contains(kex->pool,
+          client_list, "kex-strict-c-v00 at openssh.com");
+        pr_trace_msg(trace_channel, 20, "client %s strict KEX support",
+          use_strict_kex ? "signaled" : "did not signal" );
+      }
+    }
+
   } else {
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "no shared key exchange algorithm found (client sent '%s', server sent "
@@ -3009,12 +3041,12 @@ static int write_dh_reply(struct ssh2_packet *pkt, struct sftp_kex *kex) {
   sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_MSG_KEX_DH_REPLY);
   sftp_msg_write_data(&buf, &buflen, hostkey_data, hostkey_datalen, TRUE);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DH_get0_key(kex->dh, &dh_pub_key, NULL);
 #else
   dh_pub_key = kex->dh->pub_key;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(&buf, &buflen, dh_pub_key);
 
   sftp_msg_write_data(&buf, &buflen, hsig, hsiglen, TRUE);
@@ -3420,13 +3452,13 @@ static int get_dh_gex_group(struct sftp_kex *kex, uint32_t min,
          * of them for our KEX DH.
          */
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
         DH_get0_pqg(chosen_dh, &dh_p, NULL, &dh_g);
 #else
         dh_p = chosen_dh->p;
         dh_g = chosen_dh->g;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
         dup_p = BN_dup(dh_p);
         if (dup_p == NULL) {
@@ -3448,13 +3480,13 @@ static int get_dh_gex_group(struct sftp_kex *kex, uint32_t min,
 
           } else {
             /* Now set those P, G copies into our KEX DH. */
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
             DH_set0_pqg(kex->dh, (BIGNUM *) dup_p, NULL, (BIGNUM *) dup_g);
 #else
             kex->dh->p = dup_p;
             kex->dh->g = dup_g;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
           }
         }
       }
@@ -3514,13 +3546,13 @@ static int get_dh_gex_group(struct sftp_kex *kex, uint32_t min,
       return -1;
     }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     DH_set0_pqg(kex->dh, dh_p, NULL, dh_g);
 #else
     kex->dh->p = dh_p;
     kex->dh->g = dh_g;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   }
 
   return 0;
@@ -3542,13 +3574,13 @@ static int write_dh_gex_group(struct ssh2_packet *pkt, struct sftp_kex *kex,
 
   sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_MSG_KEX_DH_GEX_GROUP);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
 #else
   dh_p = kex->dh->p;
   dh_g = kex->dh->g;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(&buf, &buflen, dh_p);
   sftp_msg_write_mpint(&buf, &buflen, dh_g);
 
@@ -3661,12 +3693,12 @@ static int write_dh_gex_reply(struct ssh2_packet *pkt, struct sftp_kex *kex,
   sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_MSG_KEX_DH_GEX_REPLY);
   sftp_msg_write_data(&buf, &buflen, hostkey_data, hostkey_datalen, TRUE);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DH_get0_key(kex->dh, &dh_pub_key, NULL);
 #else
   dh_pub_key = kex->dh->pub_key;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(&buf, &buflen, dh_pub_key);
 
   sftp_msg_write_data(&buf, &buflen, hsig, hsiglen, TRUE);
@@ -3863,13 +3895,13 @@ static int write_kexrsa_pubkey(struct ssh2_packet *pkt, struct sftp_kex *kex) {
    */
   sftp_msg_write_string(&buf, &buflen, "ssh-rsa");
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   RSA_get0_key(kex->rsa, &rsa_n, &rsa_e, NULL);
 #else
   rsa_e = kex->rsa->e;
   rsa_n = kex->rsa->n;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(&buf, &buflen, rsa_e);
   sftp_msg_write_mpint(&buf, &buflen, rsa_n);
 
@@ -3922,13 +3954,13 @@ static int write_kexrsa_done(struct ssh2_packet *pkt, struct sftp_kex *kex) {
    */
   sftp_msg_write_string(&buf2, &buflen2, "ssh-rsa");
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   RSA_get0_key(kex->rsa, &rsa_n, &rsa_e, NULL);
 #else
   rsa_e = kex->rsa->e;
   rsa_n = kex->rsa->n;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(&buf2, &buflen2, rsa_e);
   sftp_msg_write_mpint(&buf2, &buflen2, rsa_n);
 
@@ -4146,9 +4178,10 @@ static const unsigned char *calculate_curve25519_h(struct sftp_kex *kex,
     const unsigned char *hostkey_data, uint32_t hostkey_datalen,
     const BIGNUM *k, unsigned char *client_curve25519,
     unsigned char *server_curve25519, uint32_t *hlen) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   unsigned char *buf, *ptr;
   uint32_t buflen, bufsz;
@@ -4589,9 +4622,10 @@ static const unsigned char *calculate_curve448_h(struct sftp_kex *kex,
     const unsigned char *hostkey_data, uint32_t hostkey_datalen,
     const BIGNUM *k, unsigned char *client_curve448,
     unsigned char *server_curve448, uint32_t *hlen) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   unsigned char *buf, *ptr;
   uint32_t buflen, bufsz;
@@ -5056,7 +5090,6 @@ static int handle_kex_ecdh(struct ssh2_packet *pkt, struct sftp_kex *kex) {
   destroy_pool(pkt->pool);
   return 0;
 }
-
 #endif /* PR_USE_OPENSSL_ECC */
 
 static struct ssh2_packet *read_kex_packet(pool *p, struct sftp_kex *kex,
@@ -5107,6 +5140,10 @@ static struct ssh2_packet *read_kex_packet(pool *p, struct sftp_kex *kex,
     /* Per RFC 4253, Section 11, DEBUG, DISCONNECT, IGNORE, and UNIMPLEMENTED
      * messages can occur at any time, even during KEX.  We have to be prepared
      * for this, and Do The Right Thing(tm).
+     *
+     * However, due to the Terrapin attack, if we are using a "strict KEX"
+     * mode, then only DISCONNECT messages can occur during KEX; DEBUG,
+     * IGNORE, and UNIMPLEMENTED messages will lead to disconnecting.
      */
 
     msg_type = sftp_ssh2_packet_get_msg_type(pkt);
@@ -5135,35 +5172,43 @@ static struct ssh2_packet *read_kex_packet(pool *p, struct sftp_kex *kex,
     }
 
     switch (msg_type) {
-      case SFTP_SSH2_MSG_DEBUG:
-        sftp_ssh2_packet_handle_debug(pkt);
-        pr_response_set_pool(NULL);
-        pkt = NULL;
-        break;
-
+      /* DISCONNECT messages are always allowed. */
       case SFTP_SSH2_MSG_DISCONNECT:
         sftp_ssh2_packet_handle_disconnect(pkt);
         pr_response_set_pool(NULL);
         pkt = NULL;
         break;
 
+      case SFTP_SSH2_MSG_DEBUG:
+        if (use_strict_kex == FALSE) {
+          sftp_ssh2_packet_handle_debug(pkt);
+          pr_response_set_pool(NULL);
+          pkt = NULL;
+          break;
+        }
+
       case SFTP_SSH2_MSG_IGNORE:
-        sftp_ssh2_packet_handle_ignore(pkt);
-        pr_response_set_pool(NULL);
-        pkt = NULL;
-        break;
+        if (use_strict_kex == FALSE) {
+          sftp_ssh2_packet_handle_ignore(pkt);
+          pr_response_set_pool(NULL);
+          pkt = NULL;
+          break;
+        }
 
       case SFTP_SSH2_MSG_UNIMPLEMENTED:
-        sftp_ssh2_packet_handle_unimplemented(pkt);
-        pr_response_set_pool(NULL);
-        pkt = NULL;
-        break;
+        if (use_strict_kex == FALSE) {
+          sftp_ssh2_packet_handle_unimplemented(pkt);
+          pr_response_set_pool(NULL);
+          pkt = NULL;
+          break;
+        }
 
       default:
         /* For any other message type, it's considered a protocol error. */
         (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
-          "received %s (%d) unexpectedly, disconnecting",
-          sftp_ssh2_packet_get_msg_type_desc(msg_type), msg_type);
+          "received %s (%d) unexpectedly%s, disconnecting",
+          sftp_ssh2_packet_get_msg_type_desc(msg_type), msg_type,
+          use_strict_kex ? " during strict KEX" : "");
         pr_response_set_pool(NULL);
         destroy_kex(kex);
         destroy_pool(pkt->pool);
@@ -5185,7 +5230,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
    * initial connect (kex_first_kex not null), or because we
    * are in a server-initiated rekeying (kex_rekey_kex not null).
    */
-  if (kex_first_kex) {
+  if (kex_first_kex != NULL) {
     kex = kex_first_kex;
 
     /* We need to assign the client/server versions, which this struct
@@ -5194,7 +5239,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
     kex->client_version = kex_client_version;
     kex->server_version = kex_server_version;
 
-  } else if (kex_rekey_kex) {
+  } else if (kex_rekey_kex != NULL) {
     kex = kex_rekey_kex;
 
   } else {
@@ -5231,6 +5276,24 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
     return -1;
   }
 
+  if (use_strict_kex == TRUE &&
+      kex_done_first_kex == FALSE) {
+    uint32_t client_seqno;
+
+    client_seqno = sftp_ssh2_packet_get_client_seqno();
+    if (client_seqno != 1) {
+      /* Receiving any messages other than a KEXINIT as the first client
+       * message indicates the possibility of the Terrapin attack being
+       * conducted (Issue 1760).  Thus we disconnect the client in such
+       * cases.
+       */
+      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
+        "'strict KEX' violation, as KEXINIT was not the first message; disconnecting");
+      destroy_kex(kex);
+      SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL);
+    }
+  }
+
   /* Once we have received the client KEXINIT message, we can compare what we
    * want to send against what we already received from the client.
    *
@@ -5289,7 +5352,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
 
       destroy_pool(pkt->pool);
 
-      if (!kex_sent_kexinit) {
+      if (kex_sent_kexinit == FALSE) {
         pkt = sftp_ssh2_packet_create(kex_pool);
         res = write_kexinit(pkt, kex);
         if (res < 0) {
@@ -5312,7 +5375,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
       }
     }
 
-    if (!kex_sent_kexinit) {
+    if (kex_sent_kexinit == FALSE) {
       pkt = sftp_ssh2_packet_create(kex_pool);
       res = write_kexinit(pkt, kex);
       if (res < 0) {
@@ -5443,7 +5506,7 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
     NULL, 1, SFTP_SSH2_MSG_NEWKEYS);
 
   /* If we didn't send our NEWKEYS message earlier, do it now. */
-  if (!sent_newkeys) {
+  if (sent_newkeys == FALSE) {
     struct ssh2_packet *pkt2;
 
     pr_trace_msg(trace_channel, 9, "sending NEWKEYS message to client");
@@ -5467,6 +5530,11 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
     destroy_pool(pkt2->pool);
   }
 
+  if (use_strict_kex == TRUE) {
+    sftp_ssh2_packet_reset_client_seqno();
+    sftp_ssh2_packet_reset_server_seqno();
+  }
+
   /* Last but certainly not least, set up the keys for encryption and
    * authentication, based on H and K.
    */
@@ -5487,6 +5555,9 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
   destroy_pool(pkt->pool);
   cmd = NULL;
 
+  /* We've now completed our KEX, possibly our first. */
+  kex_done_first_kex = TRUE;
+
   /* If extension negotiation has not been disabled, AND if we have not
    * received a service request, AND if the client sent "ext-info-c", THEN
    * send our EXT_INFO.  We do not want send this during rekeys.
@@ -5540,6 +5611,12 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
     }
   }
 
+  /* Only start the TAP timer after we have completed our first KEX.
+   * Otherwise, we risk sending "illegal" packets prior to, or during,
+   * a "strict KEX" session (Issue 1760).
+   */
+  sftp_tap_start_policy();
+
   /* Reset this flag for the next time through. */
   kex_sent_kexinit = FALSE;
 
@@ -5569,7 +5646,7 @@ int sftp_kex_free(void) {
     destroy_kex(rekey_kex);
   }
 
-  if (kex_pool) {
+  if (kex_pool != NULL) {
     destroy_pool(kex_pool);
     kex_pool = NULL;
   }
@@ -5745,7 +5822,7 @@ int sftp_kex_send_first_kexinit(void) {
   struct ssh2_packet *pkt;
   int res;
 
-  if (!kex_pool) {
+  if (kex_pool == NULL) {
     kex_pool = make_sub_pool(sftp_pool);
     pr_pool_tag(kex_pool, "Kex Pool");
   }
@@ -5780,4 +5857,3 @@ int sftp_kex_send_first_kexinit(void) {
   destroy_pool(pkt->pool);
   return 0;
 }
-


=====================================
contrib/mod_sftp/keys.c
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp key mgmt (keys)
- * Copyright (c) 2008-2022 TJ Saunders
+ * Copyright (c) 2008-2023 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
@@ -178,8 +178,10 @@ static struct openssh_cipher ciphers[] = {
   { NULL,          0,  0, 0, 0, NULL, NULL }
 };
 
+#if defined(HAVE_X448_OPENSSL)
 static int handle_ed448_hostkey(pool *p, const unsigned char *key_data,
     uint32_t key_datalen, const char *file_path);
+#endif /* HAVE_X448_OPENSSL */
 static int read_openssh_private_key(pool *p, const char *path, int fd,
     const char *passphrase, enum sftp_key_type_e *key_type, EVP_PKEY **pkey,
     unsigned char **key, uint32_t *keylen);
@@ -1216,36 +1218,36 @@ static uint32_t read_pkey_from_data(pool *p, unsigned char *pkey_data,
       }
       len += res;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
       RSA_set0_crt_params(rsa, NULL, NULL, (BIGNUM *) rsa_iqmp);
       RSA_set0_factors(rsa, (BIGNUM *) rsa_p, (BIGNUM *) rsa_q);
 #else
       rsa->iqmp = rsa_iqmp;
       rsa->p = rsa_p;
       rsa->q = rsa_q;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
       /* Turns out that for OpenSSH formatted RSA keys, the 'e' and 'n' values
        * are in the opposite order than the normal PEM format.  Typical.
        */
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
       RSA_set0_key(rsa, (BIGNUM *) rsa_e, (BIGNUM *) rsa_n, (BIGNUM *) rsa_d);
 #else
       rsa->e = rsa_n;
       rsa->n = rsa_e;
       rsa->d = rsa_d;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
     } else {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
       RSA_set0_key(rsa, (BIGNUM *) rsa_n, (BIGNUM *) rsa_e, (BIGNUM *) rsa_d);
 #else
       rsa->e = rsa_e;
       rsa->n = rsa_n;
       rsa->d = rsa_d;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
     }
 
     if (EVP_PKEY_assign_RSA(*pkey, rsa) != 1) {
@@ -1339,8 +1341,8 @@ static uint32_t read_pkey_from_data(pool *p, unsigned char *pkey_data,
       len += res;
     }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     DSA_set0_pqg(dsa, (BIGNUM *) dsa_p, (BIGNUM *) dsa_q, (BIGNUM *) dsa_g);
     DSA_set0_key(dsa, (BIGNUM *) dsa_pub_key, (BIGNUM *) dsa_priv_key);
 #else
@@ -1349,7 +1351,7 @@ static uint32_t read_pkey_from_data(pool *p, unsigned char *pkey_data,
     dsa->g = dsa_g;
     dsa->pub_key = dsa_pub_key;
     dsa->priv_key = dsa_priv_key;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
     if (EVP_PKEY_assign_DSA(*pkey, dsa) != 1) {
       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
@@ -1983,8 +1985,8 @@ static int rsa_compare_keys(pool *p, EVP_PKEY *remote_pkey,
   debug_rsa_key(p, "local RSA key:", local_rsa);
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   RSA_get0_key(remote_rsa, &remote_rsa_n, &remote_rsa_e, NULL);
   RSA_get0_key(local_rsa, &local_rsa_n, &local_rsa_e, NULL);
 #else
@@ -1992,7 +1994,7 @@ static int rsa_compare_keys(pool *p, EVP_PKEY *remote_pkey,
   local_rsa_e = local_rsa->e;
   remote_rsa_n = remote_rsa->n;
   local_rsa_n = local_rsa->n;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   if (BN_cmp(remote_rsa_e, local_rsa_e) != 0) {
     pr_trace_msg(trace_channel, 17, "%s",
@@ -2044,8 +2046,8 @@ static int dsa_compare_keys(pool *p, EVP_PKEY *remote_pkey,
 
   remote_dsa = EVP_PKEY_get1_DSA(remote_pkey);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DSA_get0_pqg(remote_dsa, &remote_dsa_p, &remote_dsa_q, &remote_dsa_g);
   DSA_get0_pqg(local_dsa, &local_dsa_p, &local_dsa_q, &local_dsa_g);
   DSA_get0_key(remote_dsa, &remote_dsa_pub_key, NULL);
@@ -2059,7 +2061,7 @@ static int dsa_compare_keys(pool *p, EVP_PKEY *remote_pkey,
   local_dsa_q = local_dsa->q;
   local_dsa_g = local_dsa->g;
   local_dsa_pub_key = local_dsa->pub_key;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   if (BN_cmp(remote_dsa_p, local_dsa_p) != 0) {
     pr_trace_msg(trace_channel, 17, "%s",
@@ -2327,9 +2329,9 @@ int sftp_keys_compare_keys(pool *p,
 const char *sftp_keys_get_fingerprint(pool *p, unsigned char *key_data,
     uint32_t key_datalen, int digest_algo) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   const EVP_MD *digest;
   char *digest_name = "none", *fp;
@@ -2362,12 +2364,12 @@ const char *sftp_keys_get_fingerprint(pool *p, unsigned char *key_data,
       return NULL;
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   /* In OpenSSL 0.9.6, many of the EVP_Digest* functions returned void, not
    * int.  Without these ugly OpenSSL version preprocessor checks, the
@@ -2379,10 +2381,10 @@ const char *sftp_keys_get_fingerprint(pool *p, unsigned char *key_data,
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error initializing %s digest: %s", digest_name,
       sftp_crypto_get_errors());
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     errno = EPERM;
     return NULL;
   }
@@ -2394,10 +2396,10 @@ const char *sftp_keys_get_fingerprint(pool *p, unsigned char *key_data,
   if (EVP_DigestUpdate(pctx, key_data, key_datalen) != 1) {
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error updating %s digest: %s", digest_name, sftp_crypto_get_errors());
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     errno = EPERM;
     return NULL;
   }
@@ -2411,10 +2413,10 @@ const char *sftp_keys_get_fingerprint(pool *p, unsigned char *key_data,
   if (EVP_DigestFinal(pctx, fp_data, &fp_datalen) != 1) {
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error finishing %s digest: %s", digest_name, sftp_crypto_get_errors());
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     errno = EPERM;
     return NULL;
   }
@@ -2422,10 +2424,10 @@ const char *sftp_keys_get_fingerprint(pool *p, unsigned char *key_data,
   EVP_DigestFinal(pctx, fp_data, &fp_datalen);
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   /* Now encode that digest in fp_data as hex characters. */
   fp = "";
@@ -3786,13 +3788,13 @@ static int get_rsa_hostkey_data(pool *p, unsigned char **buf,
   *ptr = *buf = palloc(p, *buflen);
   sftp_msg_write_string(buf, buflen, "ssh-rsa");
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
 #else
   rsa_e = rsa->e;
   rsa_n = rsa->n;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(buf, buflen, rsa_e);
   sftp_msg_write_mpint(buf, buflen, rsa_n);
 
@@ -3817,8 +3819,8 @@ static int get_dsa_hostkey_data(pool *p, unsigned char **buf,
   *ptr = *buf = palloc(p, *buflen);
   sftp_msg_write_string(buf, buflen, "ssh-dss");
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DSA_get0_pqg(dsa, &dsa_p, &dsa_q, &dsa_g);
   DSA_get0_key(dsa, &dsa_pub_key, NULL);
 #else
@@ -3826,7 +3828,7 @@ static int get_dsa_hostkey_data(pool *p, unsigned char **buf,
   dsa_q = dsa->q;
   dsa_g = dsa->g;
   dsa_pub_key = dsa->pub_key;;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   sftp_msg_write_mpint(buf, buflen, dsa_p);
   sftp_msg_write_mpint(buf, buflen, dsa_q);
   sftp_msg_write_mpint(buf, buflen, dsa_g);
@@ -4236,9 +4238,9 @@ static const unsigned char *get_rsa_signed_data(pool *p,
     const char *sig_name, const EVP_MD *md) {
   RSA *rsa;
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   unsigned char dgst[EVP_MAX_MD_SIZE], *sig_data;
   unsigned char *buf, *ptr;
@@ -4268,12 +4270,12 @@ static const unsigned char *get_rsa_signed_data(pool *p,
     }
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   EVP_DigestInit(pctx, md);
   EVP_DigestUpdate(pctx, data, datalen);
@@ -4373,9 +4375,9 @@ static const unsigned char *dsa_sign_data(pool *p, const unsigned char *data,
   DSA_SIG *sig;
   const BIGNUM *sig_r = NULL, *sig_s = NULL;
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   const EVP_MD *sha1 = EVP_sha1();
   unsigned char dgst[EVP_MAX_MD_SIZE], *sig_data;
@@ -4412,12 +4414,12 @@ static const unsigned char *dsa_sign_data(pool *p, const unsigned char *data,
     }
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   EVP_DigestInit(pctx, sha1);
   EVP_DigestUpdate(pctx, data, datalen);
@@ -4440,13 +4442,13 @@ static const unsigned char *dsa_sign_data(pool *p, const unsigned char *data,
   /* Got the signature, no need for the digest memory. */
   pr_memscrub(dgst, dgstlen);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DSA_SIG_get0(sig, &sig_r, &sig_s);
 #else
   sig_r = sig->r;
   sig_s = sig->s;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   rlen = BN_num_bytes(sig_r);
   slen = BN_num_bytes(sig_s);
@@ -4500,9 +4502,9 @@ static const unsigned char *ecdsa_sign_data(pool *p, const unsigned char *data,
   ECDSA_SIG *sig;
   const BIGNUM *sig_r = NULL, *sig_s = NULL;
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   const EVP_MD *md;
   unsigned char dgst[EVP_MAX_MD_SIZE];
@@ -4588,21 +4590,21 @@ static const unsigned char *ecdsa_sign_data(pool *p, const unsigned char *data,
   buflen = bufsz = SFTP_MAX_SIG_SZ;
   ptr = buf = palloc(p, bufsz);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   EVP_DigestInit(pctx, md);
   EVP_DigestUpdate(pctx, data, datalen);
   EVP_DigestFinal(pctx, dgst, &dgstlen);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   sig = ECDSA_do_sign(dgst, dgstlen, ec);
   if (sig == NULL) {
@@ -4620,13 +4622,13 @@ static const unsigned char *ecdsa_sign_data(pool *p, const unsigned char *data,
    * selected, so we do no sanity checking of their lengths.
    */
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   ECDSA_SIG_get0(sig, &sig_r, &sig_s);
 #else
   sig_r = sig->r;
   sig_s = sig->s;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   /* XXX Is this buffer large enough?  Too large? */
   sig_buflen = sig_bufsz = 256;
@@ -5001,9 +5003,9 @@ static int verify_rsa_signed_data(pool *p, EVP_PKEY *pkey,
     unsigned char *signature, uint32_t signature_len,
     unsigned char *sig_data, size_t sig_datalen, const EVP_MD *md) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   RSA *rsa;
   uint32_t len, sig_len;
@@ -5082,21 +5084,21 @@ static int verify_rsa_signed_data(pool *p, EVP_PKEY *pkey,
     sig_len = (uint32_t) modulus_len;
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   EVP_DigestInit(pctx, md);
   EVP_DigestUpdate(pctx, sig_data, sig_datalen);
   EVP_DigestFinal(pctx, digest, &digest_len);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   ok = RSA_verify(EVP_MD_type(md), digest, digest_len, sig, sig_len, rsa);
   if (ok == 1) {
@@ -5143,9 +5145,9 @@ static int dsa_verify_signed_data(pool *p, EVP_PKEY *pkey,
     unsigned char *signature, uint32_t signature_len,
     unsigned char *sig_data, size_t sig_datalen) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   DSA *dsa;
   DSA_SIG *dsa_sig;
@@ -5198,13 +5200,13 @@ static int dsa_verify_signed_data(pool *p, EVP_PKEY *pkey,
   }
 
   dsa_sig = DSA_SIG_new();
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   DSA_SIG_get0(dsa_sig, &sig_r, &sig_s);
 #else
   sig_r = dsa_sig->r;
   sig_s = dsa_sig->s;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   sig_r = BN_bin2bn(sig, 20, (BIGNUM *) sig_r);
   if (sig_r == NULL) {
@@ -5227,24 +5229,24 @@ static int dsa_verify_signed_data(pool *p, EVP_PKEY *pkey,
     return -1;
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   EVP_DigestInit(pctx, EVP_sha1());
   EVP_DigestUpdate(pctx, sig_data, sig_datalen);
   EVP_DigestFinal(pctx, digest, &digest_len);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
 # if OPENSSL_VERSION_NUMBER >= 0x10100006L
   DSA_SIG_set0(dsa_sig, (BIGNUM *) sig_r, (BIGNUM *) sig_s);
 # else
@@ -5253,7 +5255,7 @@ static int dsa_verify_signed_data(pool *p, EVP_PKEY *pkey,
 #else
   dsa_sig->r = sig_r;
   dsa_sig->s = sig_s;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   ok = DSA_do_verify(digest, digest_len, dsa_sig, dsa);
   if (ok == 1) {
@@ -5277,9 +5279,9 @@ static int ecdsa_verify_signed_data(pool *p, EVP_PKEY *pkey,
     unsigned char *signature, uint32_t signature_len,
     unsigned char *sig_data, size_t sig_datalen, char *sig_type) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   const EVP_MD *md = NULL;
   EC_KEY *ec;
@@ -5329,13 +5331,13 @@ static int ecdsa_verify_signed_data(pool *p, EVP_PKEY *pkey,
     return -1;
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   ECDSA_SIG_get0(ecdsa_sig, &sig_r, &sig_s);
 #else
   sig_r = ecdsa_sig->r;
   sig_s = ecdsa_sig->s;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   len = sftp_msg_read_mpint2(p, &sig, &sig_len, &sig_r);
   if (len == 0) {
@@ -5381,26 +5383,26 @@ static int ecdsa_verify_signed_data(pool *p, EVP_PKEY *pkey,
     md = EVP_sha512();
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   pctx = EVP_MD_CTX_new();
 #else
   pctx = &ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   EVP_DigestInit(pctx, md);
   EVP_DigestUpdate(pctx, sig_data, sig_datalen);
   EVP_DigestFinal(pctx, digest, &digest_len);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   ec = EVP_PKEY_get1_EC_KEY(pkey);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
 # if OPENSSL_VERSION_NUMBER >= 0x10100006L
   ECDSA_SIG_set0(ecdsa_sig, (BIGNUM *) sig_r, (BIGNUM *) sig_s);
 # else
@@ -5409,7 +5411,7 @@ static int ecdsa_verify_signed_data(pool *p, EVP_PKEY *pkey,
 #else
   ecdsa_sig->r = sig_r;
   ecdsa_sig->s = sig_s;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   ok = ECDSA_do_verify(digest, digest_len, ecdsa_sig, ec);
   if (ok == 1) {


=====================================
contrib/mod_sftp/mac.c
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp MACs
- * Copyright (c) 2008-2022 TJ Saunders
+ * Copyright (c) 2008-2023 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
@@ -107,8 +107,8 @@ static void switch_read_mac(void) {
   /* First we can clear the read MAC, kept from rekeying. */
   if (read_macs[read_mac_idx].key) {
     clear_mac(&(read_macs[read_mac_idx]));
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     HMAC_CTX_reset(hmac_read_ctxs[read_mac_idx]);
 #elif OPENSSL_VERSION_NUMBER > 0x000907000L
     HMAC_CTX_cleanup(hmac_read_ctxs[read_mac_idx]);
@@ -138,8 +138,8 @@ static void switch_write_mac(void) {
   /* First we can clear the write MAC, kept from rekeying. */
   if (write_macs[write_mac_idx].key) {
     clear_mac(&(write_macs[write_mac_idx]));
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     HMAC_CTX_reset(hmac_write_ctxs[write_mac_idx]);
 #elif OPENSSL_VERSION_NUMBER > 0x000907000L
     HMAC_CTX_cleanup(hmac_write_ctxs[write_mac_idx]);
@@ -182,8 +182,8 @@ static int init_mac(pool *p, struct sftp_mac *mac, HMAC_CTX *hmac_ctx,
     return 0;
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   HMAC_CTX_reset(hmac_ctx);
 #elif OPENSSL_VERSION_NUMBER > 0x000907000L
   HMAC_CTX_init(hmac_ctx);
@@ -431,9 +431,9 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
     const unsigned char *k, uint32_t klen, const char *h, uint32_t hlen,
     char letter, const unsigned char *id, uint32_t id_len) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   EVP_MD_CTX ctx;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
   EVP_MD_CTX *pctx;
   unsigned char *key = NULL;
   size_t key_sz;
@@ -459,11 +459,11 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
   }
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   pctx = &ctx;
 #else
   pctx = EVP_MD_CTX_new();
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
   /* In OpenSSL 0.9.6, many of the EVP_Digest* functions returned void, not
    * int.  Without these ugly OpenSSL version preprocessor checks, the
@@ -475,10 +475,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error initializing message digest: %s", sftp_crypto_get_errors());
     free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return -1;
   }
 #else
@@ -490,10 +490,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error updating message digest with K: %s", sftp_crypto_get_errors());
     free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return -1;
   }
 #else
@@ -505,10 +505,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error updating message digest with H: %s", sftp_crypto_get_errors());
     free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return -1;
   }
 #else
@@ -521,10 +521,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
       "error updating message digest with '%c': %s", letter,
       sftp_crypto_get_errors());
     free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return -1;
   }
 #else
@@ -536,10 +536,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "error updating message digest with ID: %s", sftp_crypto_get_errors());
     free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return -1;
   }
 #else
@@ -552,10 +552,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
       "error finalizing message digest: %s", sftp_crypto_get_errors());
     pr_memscrub(key, key_sz);
     free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
     return -1;
   }
 #else
@@ -577,10 +577,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
         "error initializing message digest: %s", sftp_crypto_get_errors());
       pr_memscrub(key, key_sz);
       free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
       EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
       return -1;
     }
 #else
@@ -593,10 +593,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
         "error updating message digest with K: %s", sftp_crypto_get_errors());
       pr_memscrub(key, key_sz);
       free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
       EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
       return -1;
     }
 #else
@@ -609,10 +609,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
         "error updating message digest with H: %s", sftp_crypto_get_errors());
       pr_memscrub(key, key_sz);
       free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
       EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
       return -1;
     }
 #else
@@ -626,10 +626,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
         sftp_crypto_get_errors());
       pr_memscrub(key, key_sz);
       free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
       EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
       return -1;
     }
 #else
@@ -642,10 +642,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
         "error finalizing message digest: %s", sftp_crypto_get_errors());
       pr_memscrub(key, key_sz);
       free(key);
-# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-     !defined(HAVE_LIBRESSL)
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+     (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
       EVP_MD_CTX_free(pctx);
-# endif /* OpenSSL-1.1.0 and later */
+# endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
       return -1;
     }
 #else
@@ -658,10 +658,10 @@ static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash,
   mac->key = key;
   mac->keysz = key_sz;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   EVP_MD_CTX_free(pctx);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   if (mac->algo_type == SFTP_MAC_ALGO_TYPE_HMAC) {
     mac->key_len = EVP_MD_size(mac->digest);
@@ -1040,18 +1040,18 @@ int sftp_mac_write_data(struct ssh2_packet *pkt) {
 }
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
-/* In older versions of OpenSSL, there was not a way to dynamically allocate
- * an HMAC_CTX object.  Thus we have these static objects for those
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
+/* In older versions of OpenSSL/LibreSSL, there was not a way to dynamically
+ * allocate an HMAC_CTX object.  Thus we have these static objects for those
  * older versions.
  */
 static HMAC_CTX read_ctx1, read_ctx2;
 static HMAC_CTX write_ctx1, write_ctx2;
-#endif /* prior to OpenSSL-1.1.0 */
+#endif /* prior to OpenSSL-1.1.0/LibreSSL-3.5.0 */
 
 int sftp_mac_init(void) {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
   hmac_read_ctxs[0] = &read_ctx1;
   hmac_read_ctxs[1] = &read_ctx2;
   hmac_write_ctxs[0] = &write_ctx1;
@@ -1061,7 +1061,7 @@ int sftp_mac_init(void) {
   hmac_read_ctxs[1] = HMAC_CTX_new();
   hmac_write_ctxs[0] = HMAC_CTX_new();
   hmac_write_ctxs[1] = HMAC_CTX_new();
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
 
   umac_read_ctxs[0] = NULL;
   umac_read_ctxs[1] = NULL;
@@ -1072,12 +1072,12 @@ int sftp_mac_init(void) {
 }
 
 int sftp_mac_free(void) {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   HMAC_CTX_free(hmac_read_ctxs[0]);
   HMAC_CTX_free(hmac_read_ctxs[1]);
   HMAC_CTX_free(hmac_write_ctxs[0]);
   HMAC_CTX_free(hmac_write_ctxs[1]);
-#endif /* OpenSSL-1.1.0 and later */
+#endif /* OpenSSL-1.1.0/LibreSSL-3.5.0 and later */
   return 0;
 }


=====================================
contrib/mod_sftp/mod_sftp.c
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp
- * Copyright (c) 2008-2022 TJ Saunders
+ * Copyright (c) 2008-2023 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
@@ -1718,8 +1718,9 @@ MODRET set_sftpoptions(cmd_rec *cmd) {
   config_rec *c;
   unsigned long opts = 0UL;
 
-  if (cmd->argc-1 == 0)
+  if (cmd->argc-1 == 0) {
     CONF_ERROR(cmd, "wrong number of parameters");
+  }
 
   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
 
@@ -1786,6 +1787,9 @@ MODRET set_sftpoptions(cmd_rec *cmd) {
     } else if (strcmp(cmd->argv[i], "NoHostkeyRotation") == 0) {
       opts |= SFTP_OPT_NO_HOSTKEY_ROTATION;
 
+    } else if (strcmp(cmd->argv[i], "NoStrictKex") == 0) {
+      opts |= SFTP_OPT_NO_STRICT_KEX;
+
     } else {
       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown SFTPOption '",
         cmd->argv[i], "'", NULL));


=====================================
contrib/mod_sftp/mod_sftp.h.in
=====================================
@@ -112,7 +112,12 @@
 # include <openssl/provider.h>
 #endif /* HAVE_OSSL_PROVIDER_LOAD_OPENSSL */
 
-/* Define if you have the LibreSSL library.  */
+/* Define if you have the LibreSSL library.
+ *
+ * Note that in LibreSSL-3.5.0, the structs became opaque, as they are in
+ * OpenSSL-1.1.0, and thus these version-dependent macros became more
+ * complex.
+ */
 #if defined(LIBRESSL_VERSION_NUMBER)
 # define HAVE_LIBRESSL	1
 #endif
@@ -132,24 +137,25 @@
 #define SFTP_SESS_STATE_HAVE_EXT_INFO	0x00010
 
 /* mod_sftp option flags */
-#define SFTP_OPT_IGNORE_SFTP_UPLOAD_PERMS	0x00001
-#define SFTP_OPT_IGNORE_SCP_UPLOAD_PERMS	0x00002
-#define SFTP_OPT_PESSIMISTIC_KEXINIT		0x00004
-#define SFTP_OPT_OLD_PROTO_COMPAT		0x00008
-#define SFTP_OPT_MATCH_KEY_SUBJECT		0x00010
-#define SFTP_OPT_IGNORE_SFTP_SET_PERMS		0x00020
-#define SFTP_OPT_IGNORE_SFTP_SET_TIMES		0x00040
-#define SFTP_OPT_IGNORE_SFTP_SET_OWNERS		0x00080
-#define SFTP_OPT_IGNORE_SCP_UPLOAD_TIMES	0x00100
-#define SFTP_OPT_ALLOW_INSECURE_LOGIN		0x00200
-#define SFTP_OPT_INSECURE_HOSTKEY_PERMS		0x00400
-#define SFTP_OPT_ALLOW_WEAK_DH			0x00800
-#define SFTP_OPT_IGNORE_FIFOS			0x01000
-#define SFTP_OPT_IGNORE_SFTP_UPLOAD_XATTRS	0x02000
-#define SFTP_OPT_IGNORE_SFTP_SET_XATTRS		0x04000
-#define SFTP_OPT_INCLUDE_SFTP_TIMES		0x08000
-#define SFTP_OPT_NO_EXT_INFO			0x10000
-#define SFTP_OPT_NO_HOSTKEY_ROTATION		0x20000
+#define SFTP_OPT_IGNORE_SFTP_UPLOAD_PERMS	0x000001
+#define SFTP_OPT_IGNORE_SCP_UPLOAD_PERMS	0x000002
+#define SFTP_OPT_PESSIMISTIC_KEXINIT		0x000004
+#define SFTP_OPT_OLD_PROTO_COMPAT		0x000008
+#define SFTP_OPT_MATCH_KEY_SUBJECT		0x000010
+#define SFTP_OPT_IGNORE_SFTP_SET_PERMS		0x000020
+#define SFTP_OPT_IGNORE_SFTP_SET_TIMES		0x000040
+#define SFTP_OPT_IGNORE_SFTP_SET_OWNERS		0x000080
+#define SFTP_OPT_IGNORE_SCP_UPLOAD_TIMES	0x000100
+#define SFTP_OPT_ALLOW_INSECURE_LOGIN		0x000200
+#define SFTP_OPT_INSECURE_HOSTKEY_PERMS		0x000400
+#define SFTP_OPT_ALLOW_WEAK_DH			0x000800
+#define SFTP_OPT_IGNORE_FIFOS			0x001000
+#define SFTP_OPT_IGNORE_SFTP_UPLOAD_XATTRS	0x002000
+#define SFTP_OPT_IGNORE_SFTP_SET_XATTRS		0x004000
+#define SFTP_OPT_INCLUDE_SFTP_TIMES		0x008000
+#define SFTP_OPT_NO_EXT_INFO			0x010000
+#define SFTP_OPT_NO_HOSTKEY_ROTATION		0x020000
+#define SFTP_OPT_NO_STRICT_KEX			0x040000
 
 /* mod_sftp service flags */
 #define SFTP_SERVICE_FL_SFTP		0x0001


=====================================
contrib/mod_sftp/packet.c
=====================================
@@ -2066,6 +2066,18 @@ int sftp_ssh2_packet_rekey_set_size(off_t size) {
   return 0;
 }
 
+uint32_t sftp_ssh2_packet_get_client_seqno(void) {
+  return packet_client_seqno;
+}
+
+void sftp_ssh2_packet_reset_client_seqno(void) {
+  packet_client_seqno = 0;
+}
+
+void sftp_ssh2_packet_reset_server_seqno(void) {
+  packet_server_seqno = 0;
+}
+
 int sftp_ssh2_packet_send_version(void) {
   if (!sent_version_id) {
     int res;


=====================================
contrib/mod_sftp/packet.h
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp packet IO
- * Copyright (c) 2008-2021 TJ Saunders
+ * Copyright (c) 2008-2023 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
@@ -117,6 +117,13 @@ int sftp_ssh2_packet_rekey_reset(void);
 int sftp_ssh2_packet_rekey_set_seqno(uint32_t);
 int sftp_ssh2_packet_rekey_set_size(off_t);
 
+/* These are used for implementing the "strict KEX" mitigations of the Terrapin
+ * attack (Issue 1760).
+ */
+uint32_t sftp_ssh2_packet_get_client_seqno(void);
+void sftp_ssh2_packet_reset_client_seqno(void);
+void sftp_ssh2_packet_reset_server_seqno(void);
+
 int sftp_ssh2_packet_send_version(void);
 int sftp_ssh2_packet_set_poll_timeout(int);
 int sftp_ssh2_packet_set_version(const char *);


=====================================
contrib/mod_sftp/tap.c
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp traffic analysis protection
- * Copyright (c) 2008-2016 TJ Saunders
+ * Copyright (c) 2008-2023 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
@@ -149,7 +149,6 @@ static void set_policy_chance(struct sftp_tap_policy *policy) {
 }
 
 static void set_policy_timer(struct sftp_tap_policy *policy) {
-
   /* Start a timer which checks the last times we received and sent packets.
    * From there, we may want to inject a TAP message, depending on the
    * policy.
@@ -177,6 +176,16 @@ int sftp_tap_send_packet(void) {
   int rnd;
   unsigned int chance;
 
+  /* Due to chances of violating client-side "strict KEX" Terrapin
+   * mitigations, we will not send packets if we are in the middle of a KEX.
+   */
+  if (!(sftp_sess_state & SFTP_SESS_STATE_HAVE_KEX) ||
+      (sftp_sess_state & SFTP_SESS_STATE_REKEYING)) {
+    pr_trace_msg(trace_channel, 11,
+      "unwilling to send TAP packet during KEX");
+    return 0;
+  }
+
   if (!sftp_interop_supports_feature(SFTP_SSH2_FEAT_IGNORE_MSG)) {
     pr_trace_msg(trace_channel, 3,
       "unable to send TAP packet: IGNORE not supported by client");
@@ -205,7 +214,7 @@ int sftp_tap_send_packet(void) {
     struct ssh2_packet *pkt;
     unsigned int max_datalen = 8192;
 
-    if (curr_policy.max_datalen) {
+    if (curr_policy.max_datalen > 0) {
       max_datalen = curr_policy.max_datalen;
     }
 
@@ -246,15 +255,15 @@ int sftp_tap_send_packet(void) {
 int sftp_tap_set_policy(const char *policy) {
   register unsigned int i;
 
-  if (tap_pool) {
+  if (tap_pool != NULL) {
 
     /* Special case: IFF the existing policy is 'none' AND the given
      * policy is 'rogaway', just return.  The 'none' policy must have been
      * explicitly configured, and it should override the automatic use of
      * the 'rogaway' policy.
      */
-    if (strncmp(curr_policy.policy, "none", 5) == 0 &&
-        strncasecmp(policy, "rogaway", 8) == 0) {
+    if (strcasecmp(curr_policy.policy, "none") == 0 &&
+        strcasecmp(policy, "rogaway") == 0) {
       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
         "'none' traffic policy explicitly configured, ignoring '%s' policy",
         policy);
@@ -278,7 +287,6 @@ int sftp_tap_set_policy(const char *policy) {
     if (strcasecmp(tap_policies[i].policy, policy) == 0) {
       copy_policy(&curr_policy, &(tap_policies[i]));
       set_policy_chance(&curr_policy);
-      set_policy_timer(&curr_policy);
       return 0;
     }
   }
@@ -286,3 +294,7 @@ int sftp_tap_set_policy(const char *policy) {
   errno = ENOENT;
   return -1;
 }
+
+void sftp_tap_start_policy(void) {
+  set_policy_timer(&curr_policy);
+}


=====================================
contrib/mod_sftp/tap.h
=====================================
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp traffic analysis protection
- * Copyright (c) 2008-2016 TJ Saunders
+ * Copyright (c) 2008-2013 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
@@ -63,4 +63,7 @@ int sftp_tap_send_packet(void);
  */
 int sftp_tap_set_policy(const char *);
 
+/* Sets the configured TAP policy in motion. */
+void sftp_tap_start_policy(void);
+
 #endif /* MOD_SFTP_TAP_H */


=====================================
contrib/mod_tls.c
=====================================
@@ -42,7 +42,12 @@
 # include "mod_ctrls.h"
 #endif
 
-/* Define if you have the LibreSSL library.  */
+/* Define if you have the LibreSSL library.
+ *
+ * Note that in LibreSSL-3.5.0, the structs became opaque, as they are in
+ * OpenSSL-1.1.0, and thus these version-dependent macros became more
+ * complex.
+ */
 #if defined(LIBRESSL_VERSION_NUMBER)
 # define HAVE_LIBRESSL	1
 #endif
@@ -100,8 +105,8 @@ static DH *get_dh(BIGNUM *p, BIGNUM *g) {
     return NULL;
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   if (DH_set0_pqg(dh, p, NULL, g) != 1) {
     pr_trace_msg(trace_channel, 3, "error setting DH p/q parameters: %s",
       ERR_error_string(ERR_get_error(), NULL));
@@ -111,7 +116,7 @@ static DH *get_dh(BIGNUM *p, BIGNUM *g) {
 #else
   dh->p = p;
   dh->g = g;
-#endif /* OpenSSL 1.1.x and later */
+#endif /* OpenSSL 1.1.x/LibreSSL-3.5.x and later */
 
   return dh;
 }
@@ -120,14 +125,14 @@ static X509 *read_cert(FILE *fh, SSL_CTX *ctx) {
   pem_password_cb *cb;
   void *cb_data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   cb = SSL_CTX_get_default_passwd_cb(ctx);
   cb_data = SSL_CTX_get_default_passwd_cb_userdata(ctx);
 #else
   cb = ctx->default_passwd_callback;
   cb_data = ctx->default_passwd_callback_userdata;
-#endif /* OpenSSL-1.1.x and later */
+#endif /* OpenSSL-1.1.x/LibreSSL-3.5.x and later */
 
   return PEM_read_X509(fh, NULL, cb, cb_data);
 }
@@ -681,7 +686,7 @@ static char *tls_dsa_cert_file = NULL, *tls_dsa_key_file = NULL;
 static char *tls_pkcs12_file = NULL;
 static char *tls_rsa_cert_file = NULL, *tls_rsa_key_file = NULL;
 static char *tls_rand_file = NULL;
-#if !defined(OPENSSL_NO_TLSEXT) && \
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(HAVE_LIBRESSL) && \
     OPENSSL_VERSION_NUMBER >= 0x10002000L
 static char *tls_serverinfo_file = NULL;
 #endif /* OPENSSL_NO_TLSEXT */
@@ -2743,9 +2748,8 @@ static int tls_cert_match_ip_san(pool *p, X509 *cert, const char *ipstr) {
   return matched;
 }
 
-static int tls_cert_match_cn(pool *p, X509 *cert, const char *name,
-    int allow_wildcards) {
-  int matched = 0, idx = -1;
+static char *tls_get_cert_cn(pool *p, X509 *cert) {
+  int idx = -1;
   X509_NAME *subj_name = NULL;
   X509_NAME_ENTRY *cn_entry = NULL;
   ASN1_STRING *cn_asn1 = NULL;
@@ -2757,36 +2761,29 @@ static int tls_cert_match_cn(pool *p, X509 *cert, const char *name,
    */
   subj_name = X509_get_subject_name(cert);
   if (subj_name == NULL) {
-    pr_trace_msg(trace_channel, 12,
-      "unable to check certificate CommonName against '%s': "
-      "unable to get Subject", name);
-    return 0;
+    errno = ENOENT;
+    return NULL;
   }
 
   idx = X509_NAME_get_index_by_NID(subj_name, NID_commonName, -1);
   if (idx < 0) {
-    pr_trace_msg(trace_channel, 12,
-      "unable to check certificate CommonName against '%s': "
-      "no CommoName attribute found", name);
-    return 0;
+    errno = ENOENT;
+    return NULL;
   }
 
   cn_entry = X509_NAME_get_entry(subj_name, idx);
   if (cn_entry == NULL) {
-    pr_trace_msg(trace_channel, 12,
-      "unable to check certificate CommonName against '%s': "
-      "error obtaining CommoName attribute found: %s", name, tls_get_errors());
-    return 0;
+    errno = ENOENT;
+    return NULL;
   }
 
   /* Convert the CN field to a string, by way of an ASN1 object. */
   cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
   if (cn_asn1 == NULL) {
     pr_trace_msg(trace_channel, 12,
-      "unable to check certificate CommonName against '%s': "
-      "error converting CommoName attribute to ASN.1: %s", name,
-      tls_get_errors());
-    return 0;
+      "error converting CommoName attribute to ASN.1: %s", tls_get_errors());
+    errno = EPERM;
+    return NULL;
   }
 
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
@@ -2810,6 +2807,20 @@ static int tls_cert_match_cn(pool *p, X509 *cert, const char *name,
     tls_log("suspicious CommonName value: '%s'",
       get_printable_subjaltname(p, (const char *) cn_str,
         ASN1_STRING_length(cn_asn1)));
+    errno = EPERM;
+    return NULL;
+  }
+
+  return pstrdup(p, cn_str);
+}
+
+static int tls_cert_match_cn(pool *p, X509 *cert, const char *name,
+    int allow_wildcards) {
+  int matched = 0;
+  char *cert_cn = NULL;
+
+  cert_cn = tls_get_cert_cn(p, cert);
+  if (cert_cn == NULL) {
     return 0;
   }
 
@@ -2818,16 +2829,16 @@ static int tls_cert_match_cn(pool *p, X509 *cert, const char *name,
    * the case-insensitivity won't hurt anything.  In fact, it's needed for
    * e.g. IPv6 addresses.
    */
-  if (strncasecmp(name, cn_str, cn_len + 1) == 0) {
+  if (strcasecmp(name, cert_cn) == 0) {
     matched = 1;
   }
 
   if (matched == 0 &&
-      allow_wildcards) {
+      allow_wildcards == TRUE) {
 
     /* XXX Implement wildcard checking. */
   }
- 
+
   return matched;
 }
 
@@ -4033,13 +4044,13 @@ static int tls_renegotiate_timeout_cb(CALLBACK_FRAME) {
     int ctrl_renegotiated = FALSE;
 
     switch (SSL_version(ctrl_ssl)) {
-# if defined(TLS1_3_VERSION)
+# if defined(TLS1_3_VERSION) && !defined(HAVE_LIBRESSL)
       case TLS1_3_VERSION:
         if (SSL_get_key_update_type(ctrl_ssl) == SSL_KEY_UPDATE_NONE) {
           ctrl_renegotiated = TRUE;
         }
         break;
-# endif /* TLS1_3_VERSION */
+# endif /* TLS1_3_VERSION and no LibreSSL */
 
       default:
         if (SSL_renegotiate_pending(ctrl_ssl) == 0) {
@@ -4069,13 +4080,13 @@ static int tls_renegotiate_timeout_cb(CALLBACK_FRAME) {
 
     ssl = (SSL *) pr_table_get(tls_data_wr_nstrm->notes, TLS_NETIO_NOTE, NULL);
     switch (SSL_version(ssl)) {
-# if defined(TLS1_3_VERSION)
+# if defined(TLS1_3_VERSION) && !defined(HAVE_LIBRESSL)
       case TLS1_3_VERSION:
         if (SSL_get_key_update_type(ssl) == SSL_KEY_UPDATE_NONE) {
           data_renegotiated = TRUE;
         }
         break;
-# endif /* TLS1_3_VERSION */
+# endif /* TLS1_3_VERSION and no LibreSSL */
 
       default:
         if (SSL_renegotiate_pending(ssl) == 0) {
@@ -4109,7 +4120,7 @@ static int tls_ctrl_renegotiate_cb(CALLBACK_FRAME) {
 
   if (tls_flags & TLS_SESS_ON_CTRL) {
     switch (SSL_version(ctrl_ssl)) {
-#if defined(TLS1_3_VERSION)
+#if defined(TLS1_3_VERSION) && !defined(HAVE_LIBRESSL)
       /* If we're a TLSv1.3 session, use SSL_key_update() to request new
        * session keys; TLSv1.3 does not support renegotiations.
        */
@@ -4131,7 +4142,7 @@ static int tls_ctrl_renegotiate_cb(CALLBACK_FRAME) {
         }
       }
       break;
-#endif /* TLS1_3_VERSION */
+#endif /* TLS1_3_VERSION and no LibreSSL */
 
       default: {
 #if OPENSSL_VERSION_NUMBER >= 0x009080cfL
@@ -4536,12 +4547,12 @@ static int tls_sni_cb(SSL *ssl, int *alert_desc, void *user_data) {
         ctx = SSL_get_SSL_CTX(ssl);
         ctx_options = SSL_CTX_get_options(ctx);
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
         sess_version = SSL_SESSION_get_protocol_version(sess);
 #else
         sess_version = sess->ssl_version;
-#endif /* OpenSSL 1.1.x and later */
+#endif /* OpenSSL 1.1.x/LibreSSL-3.5.x and later */
 
         switch (sess_version) {
           case SSL3_VERSION:
@@ -5720,7 +5731,7 @@ static OCSP_RESPONSE *ocsp_request_response(pool *p, X509 *cert, SSL *ssl,
 }
 
 #if OPENSSL_VERSION_NUMBER < 0x10002000L || \
-    defined(HAVE_LIBRESSL)
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x3050000L)
 /* We need to provide our own backport of the ASN1_TIME_diff() function. */
 static time_t ASN1_TIME_seconds(const ASN1_TIME *a) {
   static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 };
@@ -6191,8 +6202,8 @@ static int tls_cert_must_staple(X509 *cert, int *v2) {
   register int i;
   int ext_count = 0, must_staple = FALSE;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   ext_count = X509_get_ext_count(cert);
 #else
   X509_CINF *ci;
@@ -6205,19 +6216,19 @@ static int tls_cert_must_staple(X509 *cert, int *v2) {
 
   exts = ci->extensions;
   ext_count = sk_X509_EXTENSION_num(exts);
-#endif /* Before OpenSSL-1.1.0, or libressl */
+#endif /* OpenSSL-1.1.x/LibreSSL-3.5.x or later */
 
   for (i = 0; i < ext_count; i++) {
     char buf[1024];
     X509_EXTENSION *ext;
     ASN1_OBJECT *obj;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     ext = X509_get_ext(cert, i);
 #else
     ext = sk_X509_EXTENSION_value(exts, i);
-#endif /* Before OpenSSL-1.1.0, or libressl */
+#endif /* OpenSSL-1.1.x/LibreSSL-3.5.x or later */
 
     obj = X509_EXTENSION_get_object(ext);
     memset(buf, '\0', sizeof(buf));
@@ -6228,12 +6239,12 @@ static int tls_cert_must_staple(X509 *cert, int *v2) {
       char status_request[] = TLS_X509V3_TLS_FEAT_STATUS_REQUEST;
       ASN1_OCTET_STRING *value;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
       value = X509_EXTENSION_get_data(ext);
 #else
       value = ext->value;
-#endif /* Before OpenSSL-1.1.0, or libressl */
+#endif /* OpenSSL-1.1.x/LibreSSL-3.5.x or later */
 
       /* Is the value of this extension the "status_request" value? */
       must_staple = tls_feature_cmp(value, status_request, 5);
@@ -7892,7 +7903,7 @@ static int tls_accept(conn_t *conn, unsigned char on_data) {
             break;
           }
 
-#if defined(SSL_R_VERSION_TOO_LOW)
+#if defined(SSL_R_VERSION_TOO_LOW) && !defined(HAVE_LIBRESSL)
           case SSL_R_VERSION_TOO_LOW: {
             int client_version;
 
@@ -9310,14 +9321,14 @@ static int tls_dotlogin_allow(const char *user) {
 
     pr_signals_handle();
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     X509_get0_signature(&client_sig, NULL, client_cert);
     X509_get0_signature(&file_sig, NULL, file_cert);
 #else
     client_sig = client_cert->signature;
     file_sig = file_cert->signature;
-#endif /* OpenSSL-1.1.x and later */
+#endif /* OpenSSL-1.1.x/LibreSSL-3.5.x and later */
 
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
     if (!ASN1_STRING_cmp(client_sig, file_sig)) {
@@ -9947,8 +9958,8 @@ static void tls_setup_cert_environ(pool *p, const char *env_prefix,
   if (tls_opts & TLS_OPT_STD_ENV_VARS) {
     char buf[80] = {'\0'};
     ASN1_INTEGER *serial = X509_get_serialNumber(cert);
-    const X509_ALGOR *algo;
-    X509_PUBKEY *pubkey;
+    const X509_ALGOR *algo = NULL;
+    X509_PUBKEY *pubkey = NULL;
 
     memset(buf, '\0', sizeof(buf));
     pr_snprintf(buf, sizeof(buf) - 1, "%lu", X509_get_version(cert) + 1);
@@ -10024,12 +10035,12 @@ static void tls_setup_cert_environ(pool *p, const char *env_prefix,
     BIO_free(bio);
 
     bio = BIO_new(BIO_s_mem());
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     X509_get0_signature(NULL, &algo, cert);
 #else
     algo = cert->cert_info->signature;
-#endif /* OpenSSL-1.1.x and later */
+#endif /* OpenSSL-1.1.x/LibreSSL-3.5.x and later */
     i2a_ASN1_OBJECT(bio, algo->algorithm);
     datalen = BIO_get_mem_data(bio, &data);
     data[datalen] = '\0';
@@ -10041,13 +10052,13 @@ static void tls_setup_cert_environ(pool *p, const char *env_prefix,
     BIO_free(bio);
 
     bio = BIO_new(BIO_s_mem());
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-    pubkey = X509_get_X509_PUBKEY(cert);
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
     X509_PUBKEY_get0_param(NULL, NULL, NULL, (X509_ALGOR **) &algo, pubkey);
 #else
     pubkey = cert->cert_info->key;
     algo = pubkey->algor;
-#endif /* OpenSSL-1.1.x and later */
+#endif /* OpenSSL-1.1.x/LibreSSL-3.5.x and later */
     i2a_ASN1_OBJECT(bio, algo->algorithm);
     datalen = BIO_get_mem_data(bio, &data);
     data[datalen] = '\0';
@@ -10214,6 +10225,7 @@ static void tls_setup_environ(pool *p, SSL *ssl) {
 }
 
 static void tls_setup_notes(pool *p, SSL *ssl) {
+  X509 *client_cert = NULL;
   SSL_CIPHER *cipher = NULL;
   const char *sni = NULL;
 
@@ -10226,15 +10238,61 @@ static void tls_setup_notes(pool *p, SSL *ssl) {
   if (cipher != NULL) {
     (void) pr_table_add_dup(session.notes, "TLS_CIPHER",
       SSL_CIPHER_get_name(cipher), 0);
+  }
 
-    sni = pr_table_get(session.notes, "mod_tls.sni", NULL);
-    if (sni != NULL) {
-      (void) pr_table_add_dup(session.notes, "TLS_SERVER_NAME", sni, 0);
+  sni = pr_table_get(session.notes, "mod_tls.sni", NULL);
+  if (sni != NULL) {
+    (void) pr_table_add_dup(session.notes, "TLS_SERVER_NAME", sni, 0);
+  }
+
+  client_cert = SSL_get_peer_certificate(ssl);
+  if (client_cert != NULL) {
+    const X509_ALGOR *algo = NULL;
+    X509_PUBKEY *pubkey = NULL;
+    BIO *bio = NULL;
+    char *data = NULL;
+    long datalen = 0;
+
+    /* Client cert CN */
+    data = tls_get_cert_cn(p, client_cert);
+    if (data != NULL) {
+      (void) pr_table_add_dup(session.notes, "TLS_CLIENT_S_DN_CN", data, 0);
     }
 
-    (void) pr_table_add_dup(session.notes, "TLS_LIBRARY_VERSIONS",
-      OPENSSL_VERSION_TEXT, 0);
+    /* Client cert key algo */
+    bio = BIO_new(BIO_s_mem());
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+    pubkey = X509_get_X509_PUBKEY(client_cert);
+    X509_PUBKEY_get0_param(NULL, NULL, NULL, (X509_ALGOR **) &algo, pubkey);
+#else
+    pubkey = client_cert->cert_info->key;
+    algo = pubkey->algor;
+#endif /* OpenSSL-1.1.x and later */
+    i2a_ASN1_OBJECT(bio, algo->algorithm);
+    datalen = BIO_get_mem_data(bio, &data);
+    data[datalen] = '\0';
+
+    (void) pr_table_add_dup(session.notes, "TLS_CLIENT_A_KEY", data, 0);
+    BIO_free(bio);
+
+    /* Client cert signature algorithm. */
+    bio = BIO_new(BIO_s_mem());
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
+    X509_get0_signature(NULL, &algo, client_cert);
+#else
+    algo = client_cert->cert_info->signature;
+#endif /* OpenSSL-1.1.x/Libre-3.5.x and later */
+    i2a_ASN1_OBJECT(bio, algo->algorithm);
+    datalen = BIO_get_mem_data(bio, &data);
+    data[datalen] = '\0';
+
+    (void) pr_table_add_dup(session.notes, "TLS_CLIENT_A_SIG", data, 0);
+    BIO_free(bio);
   }
+
+  (void) pr_table_add_dup(session.notes, "TLS_LIBRARY_VERSION",
+    OPENSSL_VERSION_TEXT, 0);
 }
 
 static int tls_verify_cb(int ok, X509_STORE_CTX *ctx) {
@@ -10611,12 +10669,12 @@ static int tls_verify_crl(int ok, X509_STORE_CTX *ctx) {
         if (revoked == NULL) {
           continue;
         }
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
         sn = X509_REVOKED_get0_serialNumber(revoked);
 #else
         sn = revoked->serialNumber;
-#endif /* OpenSSL-1.1.x and later */
+#endif /* OpenSSL-1.1.x/LibreSSL-3.5.x and later */
 
         if (ASN1_INTEGER_cmp(sn, X509_get_serialNumber(xs)) == 0) {
           long serial = ASN1_INTEGER_get(sn);
@@ -10877,12 +10935,12 @@ static int tls_verify_ocsp_url(X509_STORE_CTX *ctx, X509 *cert,
     return FALSE;
   }
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-    !defined(HAVE_LIBRESSL)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(HAVE_LIBRESSL)) || \
+    (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER >= 0x3050000L)
   store = X509_STORE_CTX_get0_store(ctx);
 #else
   store = ctx->ctx;
-#endif /* OpenSSL-1.1.x and later */
+#endif /* OpenSSL-1.1.x/LibreSSL-3.5.x and later */
   res = OCSP_basic_verify(basic_resp, NULL, store, 0);
   if (res != 1) {
     tls_log("error verifying basic response from OCSP responder at '%s': %s",
@@ -12350,7 +12408,7 @@ static void tls_data_renegotiate(SSL *ssl) {
       tls_data_renegotiate_current >= tls_data_renegotiate_limit) {
 
     switch (SSL_version(ssl)) {
-# if defined(TLS1_3_VERSION)
+# if defined(TLS1_3_VERSION) && !defined(HAVE_LIBRESSL)
       /* If we're a TLSv1.3 session, use SSL_key_update() to request new
        * session keys; TLSv1.3 does not support renegotiations.
        */
@@ -12373,7 +12431,7 @@ static void tls_data_renegotiate(SSL *ssl) {
         }
       }
       break;
-# endif /* TLS1_3_VERSION */
+# endif /* TLS1_3_VERSION and no LibreSSL */
 
       default: {
         tls_flags |= TLS_SESS_DATA_RENEGOTIATING;


=====================================
doc/contrib/mod_sftp.html
=====================================
@@ -1311,6 +1311,19 @@ The currently implemented options are:
     <code>proftpd-1.3.8rc3</code>.
   </li>
 
+  <p>
+  <li><code>NoStrictKex</code><br>
+    <p>
+    By default, <code>mod_sftp</code> will honor/support the OpenSSH
+    "strict KEX" mode extension, "kex-strict-c-v00 at openssh.com" and
+    "kex-strict-s-v00 at openssh.com".  Use this option to disable support for
+    these custom OpenSSH extensions.
+
+    <p>
+    <b>Note</b> that this option first appeared in
+    <code>proftpd-1.3.9rc1</code>.
+  </li>
+
   <p>
   <li><code>OldProtocolCompat</code><br>
     <p>
@@ -2843,7 +2856,7 @@ deal with this issue, then, you can hopefully upgrade to ProFTPD 1.3.6 or later,
 <p>
 <hr>
 <font size=2><b><i>
-© Copyright 2008-2022 TJ Saunders<br>
+© Copyright 2008-2023 TJ Saunders<br>
  All Rights Reserved<br>
 </i></b></font>
 <hr>


=====================================
include/version.h
=====================================
@@ -28,8 +28,8 @@
 #include "buildstamp.h"
 
 /* Application version (in various forms) */
-#define PROFTPD_VERSION_NUMBER		0x0001030806
-#define PROFTPD_VERSION_TEXT		"1.3.8a"
+#define PROFTPD_VERSION_NUMBER		0x0001030807
+#define PROFTPD_VERSION_TEXT		"1.3.8b"
 
 /* Module API version */
 #define PR_MODULE_API_VERSION		0x20


=====================================
tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm
=====================================
@@ -87,6 +87,11 @@ my $TESTS = {
     test_class => [qw(forking ssh2)],
   },
 
+  ssh2_ext_kex_strict_terrapin_issue1760 => {
+    order => ++$order,
+    test_class => [qw(bug forking ssh2)],
+  },
+
   ssh2_hostkey_rsa => {
     order => ++$order,
     test_class => [qw(forking ssh2)],
@@ -4012,6 +4017,218 @@ EOC
   unlink($log_file);
 }
 
+sub ssh2_ext_kex_strict_terrapin_issue1760 {
+  my $self = shift;
+  my $tmpdir = $self->{tmpdir};
+  my $setup = test_setup($tmpdir, 'sftp');
+
+  my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_rsa_key');
+  my $dsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_dsa_key');
+
+  my $rsa_priv_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/test_rsa_key');
+  my $rsa_pub_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/test_rsa_key.pub');
+  my $rsa_rfc4716_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/authorized_rsa_keys');
+
+  my $authorized_keys = File::Spec->rel2abs("$tmpdir/.authorized_keys");
+  unless (copy($rsa_rfc4716_key, $authorized_keys)) {
+    die("Can't copy $rsa_rfc4716_key to $authorized_keys: $!");
+  }
+
+  my $ssh_config = File::Spec->rel2abs("$tmpdir/ssh.conf");
+  if (open(my $fh, "> $ssh_config")) {
+    print $fh <<EOC;
+HostKeyAlgorithms rsa-sha2-256
+IdentityAgent none
+PubkeyAcceptedKeyTypes rsa-sha2-256
+EOC
+    unless (close($fh)) {
+      die("Can't write $ssh_config: $!");
+    }
+
+  } else {
+    die("Can't open $ssh_config: $!");
+  }
+
+  my $batch_file = File::Spec->rel2abs("$tmpdir/sftp-batch.conf");
+  if (open(my $fh, "> $batch_file")) {
+    print $fh "ls -l\n";
+
+    unless (close($fh)) {
+      die("Can't write $batch_file: $!");
+    }
+
+  } else {
+    die("Can't open $batch_file: $!");
+  }
+
+  my $config = {
+    PidFile => $setup->{pid_file},
+    ScoreboardFile => $setup->{scoreboard_file},
+    SystemLog => $setup->{log_file},
+    TraceLog => $setup->{log_file},
+    Trace => 'ssh2:30 sftp:20 scp:20',
+
+    AuthUserFile => $setup->{auth_user_file},
+    AuthGroupFile => $setup->{auth_group_file},
+    AuthOrder => 'mod_auth_file.c',
+
+    IfModules => {
+      'mod_delay.c' => {
+        DelayEngine => 'off',
+      },
+
+      'mod_sftp.c' => [
+        "SFTPEngine on",
+        "SFTPLog $setup->{log_file}",
+
+        "SFTPHostKey $rsa_host_key",
+        "SFTPHostKey $dsa_host_key",
+
+        "SFTPAuthorizedUserKeys file:~/.authorized_keys",
+      ],
+    },
+  };
+
+  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+    $config);
+
+  # Open pipes, for use between the parent and child processes.  Specifically,
+  # the child will indicate when it's done with its test by writing a message
+  # to the parent.
+  my ($rfh, $wfh);
+  unless (pipe($rfh, $wfh)) {
+    die("Can't open pipe: $!");
+  }
+
+  require Net::SSH2;
+
+  my $ex;
+
+  # Fork child
+  $self->handle_sigchld();
+  defined(my $pid = fork()) or die("Can't fork: $!");
+  if ($pid) {
+    eval {
+      # We use OpenSSH-9.6p1 to test our "strict KEX" Terrapin mitigations.
+      my $sftp = '/Users/tj/local/openssh-9.6p1/bin/sftp';
+
+      my @cmd = (
+        $sftp,
+        '-F',
+        $ssh_config,
+        '-oBatchMode=yes',
+        '-oCheckHostIP=no',
+        '-oCompression=yes',
+        "-oPort=$port",
+        "-oIdentityFile=$rsa_priv_key",
+        '-oPubkeyAuthentication=yes',
+        '-oStrictHostKeyChecking=no',
+        '-oUserKnownHostsFile=/dev/null',
+        '-vvv',
+        '-b',
+        $batch_file,
+        "$setup->{user}\@127.0.0.1",
+      );
+
+      my $sftp_rh = IO::Handle->new();
+      my $sftp_wh = IO::Handle->new();
+      my $sftp_eh = IO::Handle->new();
+
+      $sftp_wh->autoflush(1);
+
+      sleep(1);
+
+      local $SIG{CHLD} = 'DEFAULT';
+
+      # Make sure that the perms on the priv key are what OpenSSH wants
+      unless (chmod(0400, $rsa_priv_key)) {
+        die("Can't set perms on $rsa_priv_key to 0400: $!");
+      }
+
+      if ($ENV{TEST_VERBOSE}) {
+        print STDERR "Executing: ", join(' ', @cmd), "\n";
+      }
+
+      my $sftp_pid = open3($sftp_wh, $sftp_rh, $sftp_eh, @cmd);
+      waitpid($sftp_pid, 0);
+      my $exit_status = $?;
+
+      # Restore the perms on the priv key
+      unless (chmod(0644, $rsa_priv_key)) {
+        die("Can't set perms on $rsa_priv_key to 0644: $!");
+      }
+
+      my ($res, $errstr);
+      if ($exit_status >> 8 == 0) {
+        $errstr = join('', <$sftp_eh>);
+        $res = 0;
+
+      } else {
+        $errstr = join('', <$sftp_eh>);
+        if ($ENV{TEST_VERBOSE}) {
+          print STDERR "Stderr: $errstr\n";
+        }
+
+        $res = 1;
+      }
+
+      unless ($res == 0) {
+        die("Can't list files on server: $errstr");
+      }
+    };
+    if ($@) {
+      $ex = $@;
+    }
+
+    $wfh->print("done\n");
+    $wfh->flush();
+
+  } else {
+    eval { server_wait($setup->{config_file}, $rfh) };
+    if ($@) {
+      warn($@);
+      exit 1;
+    }
+
+    exit 0;
+  }
+
+  # Stop server
+  server_stop($setup->{pid_file});
+  $self->assert_child_ok($pid);
+
+  eval {
+    if (open(my $fh, "< $setup->{log_file}")) {
+      my $ok = 0;
+
+      while (my $line = <$fh>) {
+        chomp($line);
+
+        if ($ENV{TEST_VERBOSE}) {
+          print STDERR "# $line\n";
+        }
+
+        if ($line =~ /client signaled strict KEX support/) {
+          $ok = 1;
+          last;
+        }
+      }
+
+      close($fh);
+
+      $self->assert($ok, test_msg("Did not see expected 'strict KEX' TraceLog message"));
+
+    } else {
+      die("Can't read $setup->{log_file}: $!");
+    }
+  };
+  if ($@) {
+    $ex = $@;
+  }
+
+  test_cleanup($setup->{log_file}, $ex);
+}
+
 sub ssh2_hostkey_rsa {
   my $self = shift;
   my $tmpdir = $self->{tmpdir};



View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/209e4d5c3a76eb00c9adf2f8f1eace39bef1fcc7

-- 
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/209e4d5c3a76eb00c9adf2f8f1eace39bef1fcc7
You're receiving this email because of your account on salsa.debian.org.




More information about the Pkg-proftpd-maintainers mailing list