Submitted By: Douglas R. Reno Date: 2019-08-29 Initial Package Version: 1.3.6 Upstream Status: Applied Origin: Upstream Description: Contains several backported security fixes for ProFTPD version 1.3.6. diff -Naurp proftpd-1.3.6.orig/config.h.in proftpd-1.3.6/config.h.in --- proftpd-1.3.6.orig/config.h.in 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/config.h.in 2019-08-29 14:13:08.701910513 -0500 @@ -1056,6 +1056,9 @@ /* Define if ncursesw support, if available, should be used. */ #undef PR_USE_NCURSESW +/* Define if non-local network tests are enabled. */ +#undef PR_USE_NETWORK_TESTS + /* Define if using nonblocking open of log files. */ #undef PR_USE_NONBLOCKING_LOG_OPEN @@ -1075,6 +1078,10 @@ */ #undef PR_USE_OPENSSL_ECC +/* Define if OpenSSL EVP_CipherInit_ex support, if available, should be used. + */ +#undef PR_USE_OPENSSL_EVP_CIPHERINIT_EX + /* Define if OpenSSL support (with FIPS enabled), if available, should be * used. */ diff -Naurp proftpd-1.3.6.orig/configure proftpd-1.3.6/configure --- proftpd-1.3.6.orig/configure 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/configure 2019-08-29 17:16:57.627354281 -0500 @@ -16933,6 +16933,149 @@ fi if test x"$enable_memcache" = xyes; then + +{ echo "$as_me:$LINENO: checking for memcached_create in -lmemcached" >&5 +echo $ECHO_N "checking for memcached_create in -lmemcached... $ECHO_C" >&6; } +if test "${ac_cv_lib_memcached_memcached_create+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lmemcached $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char memcached_create (); +int +main () +{ +return memcached_create (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_memcached_memcached_create=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_memcached_memcached_create=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_memcached_memcached_create" >&5 +echo "${ECHO_T}$ac_cv_lib_memcached_memcached_create" >&6; } +if test $ac_cv_lib_memcached_memcached_create = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBMEMCACHED 1 +_ACEOF + + LIBS="-lmemcached $LIBS" + +fi + + +{ echo "$as_me:$LINENO: checking for libmemcached_util_ping in -lmemcachedutil" >&5 +echo $ECHO_N "checking for libmemcached_util_ping in -lmemcachedutil... $ECHO_C" >&6; } +if test "${ac_cv_lib_memcachedutil_libmemcached_util_ping+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lmemcachedutil $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char libmemcached_util_ping (); +int +main () +{ +return libmemcached_util_ping (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_memcachedutil_libmemcached_util_ping=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_memcachedutil_libmemcached_util_ping=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_memcachedutil_libmemcached_util_ping" >&5 +echo "${ECHO_T}$ac_cv_lib_memcachedutil_libmemcached_util_ping" >&6; } +if test $ac_cv_lib_memcachedutil_libmemcached_util_ping = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBMEMCACHEDUTIL 1 +_ACEOF + + LIBS="-lmemcachedutil $LIBS" + +fi + + # Yes, we DO want mod_memcache AFTER the other modules in the static # module list. Otherwise, the module load ordering will be such that # memcache support will not work as expected @@ -16941,6 +17084,78 @@ if test x"$enable_memcache" = xyes; then fi if test x"$enable_redis" = xyes; then + +{ echo "$as_me:$LINENO: checking for redisConnect in -lhiredis" >&5 +echo $ECHO_N "checking for redisConnect in -lhiredis... $ECHO_C" >&6; } +if test "${ac_cv_lib_hiredis_redisConnect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lhiredis $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char redisConnect (); +int +main () +{ +return redisConnect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_hiredis_redisConnect=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_hiredis_redisConnect=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_hiredis_redisConnect" >&5 +echo "${ECHO_T}$ac_cv_lib_hiredis_redisConnect" >&6; } +if test $ac_cv_lib_hiredis_redisConnect = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBHIREDIS 1 +_ACEOF + + LIBS="-lhiredis $LIBS" + +fi + + # Yes, we DO want mod_redis AFTER the other modules in the static # module list. Otherwise, the module load ordering will be such that # Redis support will not work as expected. @@ -18145,7 +18360,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 18148 "configure" +#line 18363 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -20423,7 +20638,8 @@ fi ENABLE_TESTS="\"\"" # Check whether --enable-tests was given. if test "${enable_tests+set}" = set; then - enableval=$enable_tests; if test x"$enableval" = x"yes" ; then + enableval=$enable_tests; + if test "$enableval" != xno ; then for ac_header in check.h do @@ -20648,6 +20864,14 @@ echo "$as_me: error: libcheck support, r fi + + if test x"$enableval" != x"nonetwork" ; then + +cat >>confdefs.h <<\_ACEOF +#define PR_USE_NETWORK_TESTS 1 +_ACEOF + + fi fi fi @@ -22461,6 +22685,8 @@ cat >>conftest.$ac_ext <<_ACEOF #include #ifdef HAVE_USERSEC_H # include + #else + # error "we are not on AIX" #endif int @@ -22473,14 +22699,14 @@ main () return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 + (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 @@ -22489,7 +22715,8 @@ eval "echo \"\$as_me:$LINENO: $ac_try_ec (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err - } && test -s conftest.$ac_objext; then + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then cat >>confdefs.h <<\_ACEOF @@ -22510,7 +22737,8 @@ echo "${ECHO_T}no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext { echo "$as_me:$LINENO: checking for AIX loginfailed" >&5 @@ -22525,6 +22753,8 @@ cat >>conftest.$ac_ext <<_ACEOF #include #ifdef HAVE_USERSEC_H # include + #else + # error "we are not on AIX" #endif int @@ -22537,14 +22767,14 @@ main () return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 + (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 @@ -22553,7 +22783,8 @@ eval "echo \"\$as_me:$LINENO: $ac_try_ec (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err - } && test -s conftest.$ac_objext; then + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then cat >>confdefs.h <<\_ACEOF @@ -22574,7 +22805,8 @@ echo "${ECHO_T}no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext { echo "$as_me:$LINENO: checking for AIX loginsuccess" >&5 echo $ECHO_N "checking for AIX loginsuccess... $ECHO_C" >&6; } @@ -22588,6 +22820,8 @@ cat >>conftest.$ac_ext <<_ACEOF #include #ifdef HAVE_USERSEC_H # include + #else + # error "we are not on AIX" #endif int @@ -22600,14 +22834,14 @@ main () return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 + (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 @@ -22616,7 +22850,8 @@ eval "echo \"\$as_me:$LINENO: $ac_try_ec (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err - } && test -s conftest.$ac_objext; then + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then cat >>confdefs.h <<\_ACEOF @@ -22637,7 +22872,8 @@ echo "${ECHO_T}no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext if test x"$install_user" = x; then @@ -40543,6 +40779,7 @@ cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ + #include #include int main(int argc, char *argv[]) { # ifdef OPENSSL_FIPS @@ -40847,6 +41084,74 @@ cat >>confdefs.h <<\_ACEOF _ACEOF +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$saved_libs" + + { echo "$as_me:$LINENO: checking whether OpenSSL has EVP_CipherInit_ex support" >&5 +echo $ECHO_N "checking whether OpenSSL has EVP_CipherInit_ex support... $ECHO_C" >&6; } + saved_libs="$LIBS" + + LIBS=`echo "$LIBS" | sed -e 's/-lsupp//g'`; + LIBS="-lcrypto $LIBS" + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #include + +int +main () +{ + + (void) EVP_CipherInit_ex(NULL, NULL, NULL, NULL, NULL, 1); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define PR_USE_OPENSSL_EVP_CIPHERINIT_EX 1 +_ACEOF + + else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 diff -Naurp proftpd-1.3.6.orig/configure.in proftpd-1.3.6/configure.in --- proftpd-1.3.6.orig/configure.in 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/configure.in 2019-08-29 14:25:00.831783116 -0500 @@ -1,7 +1,7 @@ dnl ProFTPD - FTP server daemon dnl Copyright (c) 1997, 1998 Public Flood Software dnl Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu -dnl Copyright (c) 2001-2017 The ProFTPD Project team +dnl Copyright (c) 2001-2018 The ProFTPD Project team dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by @@ -985,7 +985,8 @@ AC_ARG_ENABLE(tests, [--enable-tests], [enable unit tests (default=no)]) ], - [ if test x"$enableval" = x"yes" ; then + [ + if test "$enableval" != xno; then AC_CHECK_HEADERS(check.h) AC_CHECK_LIB(check, tcase_create, @@ -997,6 +998,10 @@ AC_ARG_ENABLE(tests, AC_MSG_ERROR([libcheck support, required for tests, not present -- aborting]) ] ) + + if test x"$enableval" != x"nonetwork" ; then + AC_DEFINE(PR_USE_NETWORK_TESTS, 1, [Define if non-local network tests are enabled.]) + fi fi ]) @@ -1385,10 +1390,12 @@ AC_CHECK_HEADERS(hpsecurity.h, [ dnl AIX's "lastlog" support is done via specific functions, rather than a dnl struct (Bug#4285). AC_MSG_CHECKING(for AIX authenticate) -AC_TRY_COMPILE([ +AC_TRY_LINK([ #include #ifdef HAVE_USERSEC_H # include + #else + # error "we are not on AIX" #endif ], [ @@ -1404,10 +1411,12 @@ AC_TRY_COMPILE([ AC_MSG_CHECKING(for AIX loginfailed) -AC_TRY_COMPILE([ +AC_TRY_LINK([ #include #ifdef HAVE_USERSEC_H # include + #else + # error "we are not on AIX" #endif ], [ @@ -1422,10 +1431,12 @@ AC_TRY_COMPILE([ ) AC_MSG_CHECKING(for AIX loginsuccess) -AC_TRY_COMPILE([ +AC_TRY_LINK([ #include #ifdef HAVE_USERSEC_H # include + #else + # error "we are not on AIX" #endif ], [ @@ -3215,6 +3226,7 @@ if test x"$pr_use_openssl" = xyes; then AC_TRY_RUN( [ + #include #include int main(int argc, char *argv[]) { # ifdef OPENSSL_FIPS @@ -3329,6 +3341,30 @@ if test x"$pr_use_openssl" = xyes; then ], [ AC_MSG_RESULT(no) + ] + ) + LIBS="$saved_libs" + + AC_MSG_CHECKING([whether OpenSSL has EVP_CipherInit_ex support]) + saved_libs="$LIBS" + + dnl Splice out -lsupp, since that library hasn't been built yet. + LIBS=`echo "$LIBS" | sed -e 's/-lsupp//g'`; + LIBS="-lcrypto $LIBS" + + AC_TRY_LINK( + [ + #include + ], + [ + (void) EVP_CipherInit_ex(NULL, NULL, NULL, NULL, NULL, 1); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(PR_USE_OPENSSL_EVP_CIPHERINIT_EX, 1, [Define if your OpenSSL supports EVP_CipherInit_ex]) + ], + [ + AC_MSG_RESULT(no) ] ) LIBS="$saved_libs" diff -Naurp proftpd-1.3.6.orig/contrib/dist/rpm/proftpd.service proftpd-1.3.6/contrib/dist/rpm/proftpd.service --- proftpd-1.3.6.orig/contrib/dist/rpm/proftpd.service 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/dist/rpm/proftpd.service 2019-08-29 12:52:38.833520095 -0500 @@ -1,16 +1,16 @@ [Unit] Description = ProFTPD FTP Server -After = network.target nss-lookup.target local-fs.target remote-fs.target +Wants = network-online.target +After = network-online.target nss-lookup.target local-fs.target remote-fs.target [Service] -Type = forking -PIDFile = /run/proftpd/proftpd.pid +Type = simple Environment = PROFTPD_OPTIONS= EnvironmentFile = -/etc/sysconfig/proftpd -ExecStart = /usr/sbin/proftpd $PROFTPD_OPTIONS -ExecStartPost = touch /var/lock/subsys/proftpd -ExecStopPost = rm -f /var/lock/subsys/proftpd +ExecStartPre = /usr/sbin/proftpd --configtest +ExecStart = /usr/sbin/proftpd --nodaemon $PROFTPD_OPTIONS ExecReload = /bin/kill -HUP $MAINPID +PIDFile = /run/proftpd/proftpd.pid [Install] WantedBy = multi-user.target diff -Naurp proftpd-1.3.6.orig/contrib/mod_auth_otp/crypto.c proftpd-1.3.6/contrib/mod_auth_otp/crypto.c --- proftpd-1.3.6.orig/contrib/mod_auth_otp/crypto.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_auth_otp/crypto.c 2019-08-29 14:04:07.234829469 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - mod_auth_otp OpenSSL interface - * Copyright (c) 2015-2017 TJ Saunders + * Copyright (c) 2015-2018 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 @@ -50,10 +50,7 @@ void auth_otp_crypto_free(int flags) { ERR_free_strings(); #if OPENSSL_VERSION_NUMBER >= 0x10000001L -# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ - !defined(HAVE_LIBRESSL) - ERR_remove_thread_state(); -# else +#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* The ERR_remove_state(0) usage is deprecated due to thread ID * differences among platforms; see the OpenSSL-1.0.0c CHANGES file * for details. So for new enough OpenSSL installations, use the diff -Naurp proftpd-1.3.6.orig/contrib/mod_copy.c proftpd-1.3.6/contrib/mod_copy.c --- proftpd-1.3.6.orig/contrib/mod_copy.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_copy.c 2019-08-29 16:52:27.612449736 -0500 @@ -1,7 +1,7 @@ /* * ProFTPD: mod_copy -- a module supporting copying of files on the server * without transferring the data to the client and back - * Copyright (c) 2009-2016 TJ Saunders + * Copyright (c) 2009-2019 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 @@ -657,7 +657,7 @@ MODRET copy_copy(cmd_rec *cmd) { MODRET copy_cpfr(cmd_rec *cmd) { register unsigned int i; int res; - char *path = ""; + char *cmd_name, *path = ""; unsigned char *authenticated = NULL; if (copy_engine == FALSE) { @@ -705,6 +705,21 @@ MODRET copy_cpfr(cmd_rec *cmd) { path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", decoded_path, NULL); } + cmd_name = cmd->argv[0]; + pr_cmd_set_name(cmd, "SITE_CPFR"); + if (!dir_check(cmd->tmp_pool, cmd, G_READ, path, NULL)) { + int xerrno = EPERM; + + pr_cmd_set_name(cmd, cmd_name); + pr_response_add_error(R_550, "%s: %s", (char *) cmd->argv[3], + strerror(xerrno)); + + pr_cmd_set_errno(cmd, xerrno); + errno = xerrno; + return PR_ERROR(cmd); + } + pr_cmd_set_name(cmd, cmd_name); + res = pr_filter_allow_path(CURRENT_CONF, path); switch (res) { case 0: @@ -758,6 +773,7 @@ MODRET copy_cpfr(cmd_rec *cmd) { MODRET copy_cpto(cmd_rec *cmd) { register unsigned int i; const char *from, *to = ""; + char *cmd_name; unsigned char *authenticated = NULL; if (copy_engine == FALSE) { @@ -816,6 +832,20 @@ MODRET copy_cpto(cmd_rec *cmd) { to = dir_canonical_vpath(cmd->tmp_pool, to); + cmd_name = cmd->argv[0]; + pr_cmd_set_name(cmd, "SITE_CPTO"); + if (!dir_check(cmd->tmp_pool, cmd, G_WRITE, to, NULL)) { + int xerrno = EPERM; + + pr_cmd_set_name(cmd, cmd_name); + pr_response_add_err(R_500, "%s: %s", to, sterror(xerrno)); + + pr_cmd_set_errno(cmd, xerrno); + errno = xerrno; + return PR_ERROR(cmd); + } + pr_cmd_set_name(cmd, cmd_name); + if (copy_paths(cmd->tmp_pool, from, to) < 0) { int xerrno = errno; const char *err_code = R_550; @@ -940,7 +970,7 @@ static conftable copy_conftab[] = { static cmdtable copy_cmdtab[] = { { CMD, C_SITE, G_WRITE, copy_copy, FALSE, FALSE, CL_MISC }, - { CMD, C_SITE, G_DIRS, copy_cpfr, FALSE, FALSE, CL_MISC }, + { CMD, C_SITE, G_READ, copy_cpfr, FALSE, FALSE, CL_MISC }, { CMD, C_SITE, G_WRITE, copy_cpto, FALSE, FALSE, CL_MISC }, { POST_CMD, C_PASS, G_NONE, copy_post_pass, FALSE, FALSE }, { LOG_CMD, C_SITE, G_NONE, copy_log_site, FALSE, FALSE }, diff -Naurp proftpd-1.3.6.orig/contrib/mod_digest.c proftpd-1.3.6/contrib/mod_digest.c --- proftpd-1.3.6.orig/contrib/mod_digest.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_digest.c 2019-08-29 14:12:26.813380398 -0500 @@ -1,7 +1,7 @@ /* * ProFTPD: mod_digest - File hashing/checksumming module * Copyright (c) Mathias Berchtold - * Copyright (c) 2016-2017 TJ Saunders + * Copyright (c) 2016-2018 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 @@ -77,6 +77,11 @@ # include #endif +/* Define if you have the LibreSSL library */ +#if defined(LIBRESSL_VERSION_NUMBER) +# define HAVE_LIBRESSL 1 +#endif + module digest_module; static int digest_caching = TRUE; @@ -314,21 +319,60 @@ static int CRC32_Free(CRC32_CTX *ctx) { } static int crc32_init(EVP_MD_CTX *ctx) { - return CRC32_Init(ctx->md_data); + void *md_data; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined (HAVE_LIBRESSL) + md_data = EVP_MD_CTX_md_data(ctx); +#else + md_data = ctx->md_data; +#endif /* prior to OpenSSL-1.1.0 */ + + return CRC32_Init(md_data); } static int crc32_update(EVP_MD_CTX *ctx, const void *data, size_t datasz) { - return CRC32_Update(ctx->md_data, data, datasz); + void *md_data; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined (HAVE_LIBRESSL) + md_data = EVP_MD_CTX_md_data(ctx); +#else + md_data = ctx->md_data; +#endif /* prior to OpenSSL-1.1.0 */ + + return CRC32_Update(md_data, data, datasz); } static int crc32_final(EVP_MD_CTX *ctx, unsigned char *md) { - return CRC32_Final(md, ctx->md_data); + void *md_data; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined (HAVE_LIBRESSL) + + md_data = EVP_MD_CTX_md_data(ctx); +#else + md_data = ctx->md_data; +#endif /* prior to OpenSSL-1.1.0 */ + + return CRC32_Final(md, md_data); } static int crc32_free(EVP_MD_CTX *ctx) { - return CRC32_Free(ctx->md_data); + void *md_data; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(HAVE_LIBRESSL) + md_data = EVP_MD_CTX_md_data(ctx); +#else + md_data = ctx->md_data; +#endif /* prior to OpenSSL-1.1.0 */ + + return CRC32_Free(md_data); } +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + defined (HAVE_LIBRESSL) static const EVP_MD crc32_md = { NID_undef, NID_undef, @@ -343,9 +387,30 @@ static const EVP_MD crc32_md = { CRC32_BLOCK, sizeof(EVP_MD *) + sizeof(CRC32_CTX) }; +#endif /* Older OpenSSLs */ static const EVP_MD *EVP_crc32(void) { - return &crc32_md; + const EVP_MD *md; + +#if OPENSSL_VERSION_NUMBER >= 0x1010000L && \ + !defined (HAVE_LIBRESSL) + /* XXX TODO: At some point, we need to call EVP_MD_meth_free() on + * this, to avoid a resource leak. + */ + md = EVP_MD_meth_new(NID_undef, NID_undef); + EVP_MD_meth_set_input_blocksize(md, CRC32_BLOCK); + EVP_MD_meth_set_result_size(md, CRC32_DIGEST_LENGTH); + EVP_MD_meth_set_app_datasize(md, sizeof(EVP_MD *) + sizeof(CRC32_CTX)); + EVP_MD_meth_set_init(md, crc32_init); + EVP_MD_meth_set_update(md, crc32_update); + EVP_MD_meth_set_final(md, crc32_final); + EVP_MD_meth_set_cleanup(md, crc32_free); + EVP_MD_meth_set_flags(md, 0); +#else + md = &crc32_md; +#endif /* Prior to OpenSSL-1.1.0 */ + + return md; } static const char *get_errors(void) { @@ -974,7 +1039,11 @@ static int compute_digest(pool *p, const struct stat st; unsigned char *buf; size_t bufsz, readsz, iter_count; - EVP_MD_CTX md_ctx; + #if OPENSSL_VERSION_NUMBER < 0x1010000L || \ + defined(HAVE_LIBRESSL) + EVP_MD_CTX ctx; + #endif /* prior to OpenSSL-1.1.0 */ + EVP_MD_CTX *pctx; fh = pr_fsio_open(path, O_RDONLY); if (fh == NULL) { @@ -1028,12 +1097,22 @@ static int compute_digest(pool *p, const return -1; } - EVP_MD_CTX_init(&md_ctx); - if (EVP_DigestInit_ex(&md_ctx, md, NULL) != 1) { + #if OPENSSL_VERSION_NUMBER < 0x1010000L || \ + defined(HAVE_LIBRESSL) + pctx = &ctx; + #else + pctx = EVP_MD_CTX_new(); + #endif /* prior to OpenSSL-1.1.0 */ + + EVD_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); - EVP_MD_CTX_cleanup(&md_ctx); + #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(HAVE_LIBRESSL) + EVP_MD_CTX_free(pctx); + #endif /* OpenSSL-1.1.0 and later */ errno = EPERM; return -1; } @@ -1067,7 +1146,7 @@ static int compute_digest(pool *p, const continue; } - if (EVP_DigestUpdate(&md_ctx, buf, res) != 1) { + if (EVP_DigestUpdate(pctx, buf, res) != 1) { pr_log_debug(DEBUG1, MOD_DIGEST_VERSION ": error updating digest: %s", get_errors()); } @@ -1091,7 +1170,10 @@ static int compute_digest(pool *p, const (void) pr_fsio_close(fh); if (len != 0) { - EVP_MD_CTX_cleanup(&md_ctx); +# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined (HAVE_LIBRESSL) + EVP_MD_CTX_free(pctx); +# endif /* OpenSSL-1.1.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); @@ -1099,15 +1181,21 @@ static int compute_digest(pool *p, const return -1; } - if (EVP_DigestFinal_ex(&md_ctx, digest, digest_len) != 1) { + if (EVP_DigestFinal_ex(pctx, digest, digest_len) != 1) { pr_log_debug(DEBUG1, MOD_DIGEST_VERSION ": error finishing digest: %s", get_errors()); - EVP_MD_CTX_cleanup(&md_ctx); +# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(HAVE_LIBRESSL) + EVP_MD_CTX_free(pctx); +# endif /* OpenSSL-1.1.0 and later */ errno = EPERM; return -1; } - EVP_MD_CTX_cleanup(&md_ctx); +# if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(HAVE_LIBRESSL) + EVP_MD_CTX_free(pctx); +# endif /* OpenSSL-1.1.0 and later */ return 0; } diff -Naurp proftpd-1.3.6.orig/contrib/mod_exec.c proftpd-1.3.6/contrib/mod_exec.c --- proftpd-1.3.6.orig/contrib/mod_exec.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_exec.c 2019-08-29 12:21:26.485951548 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD: mod_exec -- a module for executing external scripts - * Copyright (c) 2002-2016 TJ Saunders + * Copyright (c) 2002-2017 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 @@ -31,17 +31,15 @@ # include #endif -#define MOD_EXEC_VERSION "mod_exec/0.9.14" +#define MOD_EXEC_VERSION "mod_exec/0.9.16" /* Make sure the version of proftpd is as necessary. */ -#if PROFTPD_VERSION_NUMBER < 0x0001030402 -# error "ProFTPD 1.3.4rc2 or later required" +#if PROFTPD_VERSION_NUMBER < 0x0001030605 +# error "ProFTPD 1.3.6 or later required" #endif module exec_module; -#define EXEC_MAX_FD_COUNT 1024 - static pool *exec_pool = NULL; static int exec_engine = FALSE; static unsigned int exec_nexecs = 0; @@ -263,9 +261,6 @@ static char **exec_prepare_environ(pool } static void exec_prepare_fds(int stdin_fd, int stdout_fd, int stderr_fd) { - long nfiles = 0; - register unsigned int i = 0; - struct rlimit rlim; if (stdin_fd < 0) { stdin_fd = open("/dev/null", O_RDONLY); @@ -314,59 +309,9 @@ static void exec_prepare_fds(int stdin_f * dup /dev/null. For stdout and stderr, we dup some pipes, so that * we can capture what the command may write to stdout or stderr. The * stderr output will be logged to the ExecLog. - * - * First, use getrlimit() to obtain the maximum number of open files - * for this process -- then close that number. */ -#if defined(RLIMIT_NOFILE) || defined(RLIMIT_OFILE) -# if defined(RLIMIT_NOFILE) - if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { -# elif defined(RLIMIT_OFILE) - if (getrlimit(RLIMIT_OFILE, &rlim) < 0) { -# endif - /* Ignore ENOSYS (and EPERM, since some libc's use this as ENOSYS). */ - if (errno != ENOSYS && - errno != EPERM) { - exec_log("getrlimit() error: %s", strerror(errno)); - } - - /* Pick some arbitrary high number. */ - nfiles = EXEC_MAX_FD_COUNT; - - } else { - nfiles = rlim.rlim_max; - } -#else /* no RLIMIT_NOFILE or RLIMIT_OFILE */ - nfiles = EXEC_MAX_FD_COUNT; -#endif - - /* Yes, using a long for the nfiles variable is not quite kosher; it should - * be an unsigned type, otherwise a large limit (say, RLIMIT_INFINITY) - * might overflow the data type. In that case, though, we want to know - * about it -- and using a signed type, we will know if the overflowed - * value is a negative number. Chances are we do NOT want to be closing - * fds whose value is as high as they can possibly get; that's too many - * fds to iterate over. Long story short, using a long int is just fine. - * (Plus it makes mod_exec work on Mac OSX 10.4; without this tweak, - * mod_exec's forked processes never return/exit.) - */ - - if (nfiles < 0 || - nfiles > EXEC_MAX_FD_COUNT) { - nfiles = EXEC_MAX_FD_COUNT; - } - - /* Close the "non-standard" file descriptors. */ - for (i = 3; i < nfiles; i++) { - - /* This is a potentially long-running loop, so handle signals. */ - pr_signals_handle(); - - close(i); - } - - return; + pr_fs_close_extra_fds(); } static void exec_prepare_pipes(void) { diff -Naurp proftpd-1.3.6.orig/contrib/mod_sftp/auth-password.c proftpd-1.3.6/contrib/mod_sftp/auth-password.c --- proftpd-1.3.6.orig/contrib/mod_sftp/auth-password.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sftp/auth-password.c 2019-08-29 10:58:00.044571617 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp 'password' user authentication - * Copyright (c) 2008-2015 TJ Saunders + * Copyright (c) 2008-2017 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 @@ -37,6 +37,7 @@ int sftp_auth_password(struct ssh2_packe char *passwd; int have_new_passwd, res; struct passwd *pw; + size_t passwd_len; cipher_algo = sftp_cipher_get_read_algo(); mac_algo = sftp_mac_get_read_algo(); @@ -77,6 +78,7 @@ int sftp_auth_password(struct ssh2_packe passwd = sftp_msg_read_string(pkt->pool, buf, buflen); passwd = sftp_utf8_decode_str(pkt->pool, passwd); + passwd_len = strlen(passwd); pass_cmd->arg = passwd; @@ -92,7 +94,7 @@ int sftp_auth_password(struct ssh2_packe pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0); - pr_memscrub(passwd, strlen(passwd)); + pr_memscrub(passwd, passwd_len); *send_userauth_fail = TRUE; errno = EPERM; @@ -109,15 +111,46 @@ int sftp_auth_password(struct ssh2_packe session.c->remote_name, pr_netaddr_get_ipstr(session.c->remote_addr), pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port); - pr_memscrub(passwd, strlen(passwd)); + pr_memscrub(passwd, passwd_len); *send_userauth_fail = TRUE; errno = ENOENT; return 0; } + if (passwd_len == 0) { + config_rec *c; + int allow_empty_passwords = TRUE; + + c = find_config(main_server->conf, CONF_PARAM, "AllowEmptyPasswords", + FALSE); + if (c != NULL) { + allow_empty_passwords = *((int *) c->argv[0]); + } + + if (allow_empty_passwords == FALSE) { + pr_log_debug(DEBUG5, + "Refusing empty password from user '%s' (AllowEmptyPasswords false)", + user); + pr_log_auth(PR_LOG_NOTICE, + "Refusing empty password from user '%s'", user); + + pr_event_generate("mod_auth.empty-password", user); + pr_response_add_err(R_501, "Login incorrect."); + + pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0); + pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0); + + pr_memscrub(passwd, passwd_len); + + *send_userauth_fail = TRUE; + errno = EPERM; + return 0; + } + } + res = pr_auth_authenticate(pkt->pool, user, passwd); - pr_memscrub(passwd, strlen(passwd)); + pr_memscrub(passwd, passwd_len); switch (res) { case PR_AUTH_OK: diff -Naurp proftpd-1.3.6.orig/contrib/mod_sftp/cipher.c proftpd-1.3.6/contrib/mod_sftp/cipher.c --- proftpd-1.3.6.orig/contrib/mod_sftp/cipher.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sftp/cipher.c 2019-08-29 14:29:34.889618326 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp ciphers - * Copyright (c) 2008-2017 TJ Saunders + * Copyright (c) 2008-2018 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 @@ -74,6 +74,8 @@ static size_t cipher_blockszs[2] = { static unsigned int read_cipher_idx = 0; static unsigned int write_cipher_idx = 0; +static const char *trace_channel = "ssh2"; + static void clear_cipher(struct sftp_cipher *); static unsigned int get_next_read_index(void) { @@ -244,7 +246,6 @@ static int set_cipher_key(struct sftp_ci key_sz = sftp_crypto_get_size(cipher->key_len > 0 ? cipher->key_len : EVP_CIPHER_key_length(cipher->cipher), EVP_MD_size(hash)); - if (key_sz == 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unable to determine key length for cipher '%s'", cipher->algo); @@ -252,6 +253,9 @@ static int set_cipher_key(struct sftp_ci return -1; } + pr_trace_msg(trace_channel, 19, "setting key (%lu bytes) for cipher %s", + (unsigned long) key_sz, cipher->algo); + key = malloc(key_sz); if (key == NULL) { pr_log_pri(PR_LOG_ALERT, MOD_SFTP_VERSION ": Out of memory!"); @@ -267,6 +271,9 @@ static int set_cipher_key(struct sftp_ci EVP_DigestFinal(ctx, key, &key_len); EVP_MD_CTX_destroy(ctx); + pr_trace_msg(trace_channel, 19, "hashed data to produce key (%lu bytes)", + (unsigned long) key_len); + /* If we need more, keep hashing, as per RFC, until we have enough * material. */ @@ -287,8 +294,6 @@ static int set_cipher_key(struct sftp_ci } cipher->key = key; - cipher->key_len = key_len; - return 0; } @@ -311,22 +316,24 @@ static int set_cipher_discarded(struct s garbage_out = malloc(cipher->discard_len); if (garbage_out == NULL) { - pr_log_pri(PR_LOG_ALERT, MOD_SFTP_VERSION ": Out of memory!"); free(garbage_in); + pr_log_pri(PR_LOG_ALERT, MOD_SFTP_VERSION ": Out of memory!"); _exit(1); } if (EVP_Cipher(cipher_ctx, garbage_out, garbage_in, cipher->discard_len) != 1) { - (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, - "error ciphering discard data: %s", sftp_crypto_get_errors()); free(garbage_in); pr_memscrub(garbage_out, cipher->discard_len); free(garbage_out); + (void) pr_log_writefiles(sftp_logfd, MOD_SFTP_VERSION, + "error ciphering discard data: %s", sftp_crypto_get_errors()); return -1; } + pr_trace_msg(trace_channel, 19, "discarded %lu bytes of cipher data", + (unsigned long) cipher->discard_len); free(garbage_in); pr_memscrub(garbage_out, cipher->discard_len); free(garbage_out); @@ -355,7 +362,7 @@ const char *sftp_cipher_get_read_algo(vo int sftp_cipher_set_read_algo(const char *algo) { unsigned int idx = read_cipher_idx; - size_t key_len, discard_len; + size_t key_len = 0, discard_len = 0; if (read_ciphers[idx].key) { /* If we have an existing key, it means that we are currently rekeying. */ @@ -368,6 +375,18 @@ int sftp_cipher_set_read_algo(const char return -1; } + if (key_len > 0) { + pr_trace_msg(trace_channel, 19, + "setting read key for cipher %s: key len = %lu", algo, + (unsigned long) key_len); + } + + if (discard_len > 0) { + pr_trace_msg(trace_channel, 19, + "setting read key for cipher %s: discard len = %lu", algo, + (unsigned long) discard_len); + } + read_ciphers[idx].algo = algo; read_ciphers[idx].key_len = (uint32_t) key_len; read_ciphers[idx].discard_len = discard_len; @@ -419,8 +438,6 @@ int sftp_cipher_set_read_key(pool *p, co return -1; } - key_len = (int) cipher->key_len; - /* client-to-server key: HASH(K || H || "C" || session_id) * server-to-client key: HASH(K || H || "D" || session_id) */ @@ -431,8 +448,12 @@ int sftp_cipher_set_read_key(pool *p, co return -1; } - if (EVP_CipherInit(cipher_ctx, cipher->cipher, cipher->key, +#if defined (PR_USE_OPENSSL_EVP_CIPHERINIT_EX) + if (EVP_CipherInit_ex(cipher_ctx, cipher->cipher, NULL, NULL, cipher->iv, 0) != 1) { +#else + if (EVP_CipherInit(cipher_ctx, cipher->cipher, NNULL, cipher->iv, 0) != 1) { +#endif (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error initializing %s cipher for decryption: %s", cipher->algo, sftp_crypto_get_errors()); @@ -440,8 +461,9 @@ int sftp_cipher_set_read_key(pool *p, co return -1; } + /* Next, set the key length */ + key_len = (int) cipher->key_len; if (key_len > 0) { - /* Next, set the key length. */ if (EVP_CIPHER_CTX_set_key_length(cipher_ctx, key_len) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error setting key length (%d bytes) for %s cipher for decryption: %s", @@ -449,6 +471,22 @@ int sftp_cipher_set_read_key(pool *p, co pr_memscrub(ptr, bufsz); return -1; } + + pr_trace_msg(trace_channel, 19, + "set key length (%d) for %s cipher key decryption", key_len, + cipher->algo); + } + +#if defined(PR_USE_OPENSSL_EVP_CIPHERINIT_EX) + if (EVP_CipherInit_ex(cipher_ctx, NULL, NULL, cipher->key, NULL, -1) != 1) { +#else + if (EVP_CipherInit(cipher_ctx, NULL, cipher-key, NULL, -1) != 1) { +#endif + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error re-initializing %s chiper for decryption: %s", cipher->algo, + sftp_crypto_get_errors()); + pm_memscrub(ptr, bufsz); + return -1; } if (set_cipher_discarded(cipher, cipher_ctx) < 0) { @@ -522,7 +560,7 @@ const char *sftp_cipher_get_write_algo(v int sftp_cipher_set_write_algo(const char *algo) { unsigned int idx = write_cipher_idx; - size_t key_len, discard_len; + size_t key_len = 0, discard_len = 0; if (write_ciphers[idx].key) { /* If we have an existing key, it means that we are currently rekeying. */ @@ -535,6 +573,18 @@ int sftp_cipher_set_write_algo(const cha return -1; } + if (key_len > 0) { + pr_trace_msg(trace_channel, 19, + "setting write key for cipher %s: key len %lu", algo, + (unsigned long) key_len); + } + + if (discard_len > 0) { + pr_trace_msg(trace_channel, 19, + "setting write key for cipher %s, discard len = %lu", algo, + (unsigned long) discard_len); + } + write_ciphers[idx].algo = algo; write_ciphers[idx].key_len = (uint32_t) key_len; write_ciphers[idx].discard_len = discard_len; @@ -586,8 +636,6 @@ int sftp_cipher_set_write_key(pool *p, c return -1; } - key_len = (int) cipher->key_len; - /* client-to-server key: HASH(K || H || "C" || session_id) * server-to-client key: HASH(K || H || "D" || session_id) */ @@ -597,9 +645,12 @@ int sftp_cipher_set_write_key(pool *p, c pr_memscrub(ptr, bufsz); return -1; } - - if (EVP_CipherInit(cipher_ctx, cipher->cipher, cipher->key, - cipher->iv, 1) != 1) { +#if defined (PR_USE_OPENSSL_EVP_CIPHERINIT_EX) + if (EVP_CipherInit_ex(cipher_ctx, cipher->cipher, NULL, NULL, + cipher->iv, 1) != 1) { +#else + if (EVP_CipherInit(cipher_ctx, cipher->cipher, NULL, cipher->iv, 1) != 1) { +#endif (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error initializing %s cipher for encryption: %s", cipher->algo, sftp_crypto_get_errors()); @@ -607,8 +658,9 @@ int sftp_cipher_set_write_key(pool *p, c return -1; } + /* Next, set the key length. */ + key_len = (int) cipher->key_len; if (key_len > 0) { - /* Next, set the key length. */ if (EVP_CIPHER_CTX_set_key_length(cipher_ctx, key_len) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error setting key length (%d bytes) for %s cipher for decryption: %s", @@ -616,6 +668,22 @@ int sftp_cipher_set_write_key(pool *p, c pr_memscrub(ptr, bufsz); return -1; } + + pr_trace_msg(trace_channel, 19, + "set key length (%d) for %s cipher for encryption", key_len, + cipher->algo); + } + +#if defined(PR_USE_OPENSSL_EVP_CIPHERINIT_EX) + if (EVP_CipherInit_ex(cipher_ctx, NULL, NULL, cipher->key, NULL, -1) != 1) { +#else + if (EVP_CipherInit(cipher_ctx, NULL, cipher->key, NULL, -1) != 1) { +#endif + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error re-initializing %s cipher for encryption: %s", cipher->algo, + sftp_crypto_get_errors()); + pr_memscrub(ptr, bufsz); + return -1 } if (set_cipher_discarded(cipher, cipher_ctx) < 0) { diff -Naurp proftpd-1.3.6.orig/contrib/mod_sftp/fxp.c proftpd-1.3.6/contrib/mod_sftp/fxp.c --- proftpd-1.3.6.orig/contrib/mod_sftp/fxp.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sftp/fxp.c 2019-08-29 12:47:42.654193723 -0500 @@ -297,6 +297,8 @@ struct fxp_extpair { static pool *fxp_pool = NULL; static int fxp_use_gmt = TRUE; +/* FSOptions */ +static unsigned long fxp_fsio_opts = 0UL; static unsigned int fxp_min_client_version = 1; static unsigned int fxp_max_client_version = 6; static unsigned int fxp_utf8_protocol_version = 4; @@ -1327,6 +1329,44 @@ static void fxp_msg_write_extpair(unsign TRUE); } +static uint32_t fxp_attrs_clear_unsupported(unit32_t attr_flags) { + + /* Clear and unsupported flags. */ + if (attr_flags & SSH2_FX_ATTR_ALLOCATION_SIZE) { + attr_flags &= ~SSH2_FX_ATTR_ALLOCATION_SIZE; + } + + if (attr_flags & SSH2_FX_ATTR_SUBSECOND_TIMES) { + attr_flags &= ~SSH2_FX_ATTR_SUBSECOND_TIMES; + } + + if (attr_flags & SSH2_FX_ATTR_ACL) { + attr_flags &= ~SSH2_FX_ATTR_ACL; + } + + if (attr_flags & SSH2_FX_ATTR_BITS) { + attr_flags &= ~SSH2_FX_ATTR_BITS; + } + + if (attr_flags & SSH2_FX_ATTR_TEXT_HINT) { + attr_flags &= ~SSH2_FX_ATTR_TEXT_HINT; + } + + if (attr_flags & SSH2_FX_ATTR_MIME_TYPE) { + attr_flags &= ~SSH2_FX_ATTR_MIME_TYPE; + } + + if (attr_flags & SSH2_FX_ATTR_UNTRANSLATED_NAME) { + attr_flags &= ~SSH2_FX_ATTR_UNTRANSLATED_NAME; + } + + if (attr_flags & SSH2_FX_ATTR_CTIME) { + attr_flags &= ~SSH2_FX_ATTR_CTIME; + } + + return attr_flags; +} + static int fxp_attrs_set(pr_fh_t *fh, const char *path, struct stat *attrs, uint32_t attr_flags, array_header *xattrs, unsigned char **buf, uint32_t *buflen, struct fxp_packet *fxp) { @@ -7325,6 +7365,8 @@ static int fxp_handle_fsetstat(struct fx } pr_cmd_set_name(cmd, cmd_name); + attr_flags = fxp_attrs_clear_unsupported(attr_flags); + /* If the SFTPOption for ignoring the owners for SFTP setstat requests is set, * handle it by clearing the SSH2_FX_ATTR_UIDGID and SSH2_FX_ATTR_OWNERGROUP * flags. @@ -7453,6 +7495,11 @@ static int fxp_handle_fstat(struct fxp_p pr_trace_msg(trace_channel, 7, "received request: FSTAT %s", name); attr_flags = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_UIDGID|SSH2_FX_ATTR_PERMISSIONS| SSH2_FX_ATTR_ACMODTIME; +#ifdef PR_USE_XATTR + if (!(fxp_fsio_opts & PR_FSIO_OPT_IGNORE_XATTR)) { + attr_flags |= SSH2_FX_ATTR_EXTENDED; + } +#endif /* PR_USE_XATTR */ } fxb = pcalloc(fxp->pool, sizeof(struct fxp_buffer)); @@ -7581,6 +7628,7 @@ static int fxp_handle_fstat(struct fxp_p fxb->buf = buf; fxb->buflen = buflen; + attr_flags = fxp_attrs_clear_unsupported(attr_flags); fxp_attrs_write(fxp->pool, fxb, fxh->fh->fh_path, &st, attr_flags, fake_user, fake_group); @@ -7603,6 +7651,7 @@ static int fxp_handle_init(struct fxp_pa uint32_t buflen, bufsz; struct fxp_packet *resp; cmd_rec *cmd; + config_rec *c; fxp_session->client_version = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); @@ -7692,6 +7741,22 @@ static int fxp_handle_init(struct fxp_pa fxp_version_add_openssh_exts(fxp->pool, &buf, &buflen); + /* Look up the FSOptions here, for use later (issue #593). We do not need + * set these for the FSIO API; that is already done by mod_core. Instead, + * we look them up for ourselves, for our own consumption/use. + */ + c = find_config(main_server->conf, CONF_PARAM, "FSOptions", FALSE); + while (c != NULL) { + unsigned long opts = 0; + + pr_signals_handle(); + + opts = *((unsigned long *) c->argv[0]); + fxp_fsio_opts |= opts; + + c = find_config_next(c, c->next, CONF_PARAM, "FSOptions", FALSE); + } + pr_event_generate("mod_sftp.sftp.protocol-version", &(fxp_session->client_version)); @@ -8142,7 +8207,9 @@ static int fxp_handle_lstat(struct fxp_p attr_flags = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_UIDGID|SSH2_FX_ATTR_PERMISSIONS| SSH2_FX_ATTR_ACMODTIME; #ifdef PR_USE_XATTR - attr_flags |= SSH2_FX_ATTR_EXTENDED; + if(!(fxp_fsio_opts & PR_FSIO_OPT_IGNORE_XATTR)) { + attr_flags |= SSH2_FX_ATTR_EXTENDED; + } #endif /* PR_USE_XATTR */ } @@ -8287,6 +8354,7 @@ static int fxp_handle_lstat(struct fxp_p fxb->buf = buf; fxb->buflen = buflen; + attr_flags = fxp_attrs_clear_unsupported(attr_flags); fxp_attrs_write(fxp->pool, fxb, path, &st, attr_flags, fake_user, fake_group); /* fxp_attrs_write will have changed the buf/buflen fields in the buffer. */ @@ -9130,6 +9198,8 @@ static int fxp_handle_open(struct fxp_pa fh->fh_path, strerror(errno)); } + attr_flags = fxp_attrs_clear_unsupported(attr_flags); + /* If the SFTPOption for ignoring perms for SFTP uploads is set, handle * it by clearing the SSH2_FX_ATTR_PERMISSIONS flag. */ @@ -10208,7 +10278,15 @@ static int fxp_handle_readdir(struct fxp /* How much non-path data do we expect to be associated with this entry? */ #ifdef PR_USE_XATTR - max_entry_metadata = (1024 * 4); + /* Note that the "extra space" to allocate for extended attributes is + * currently a bit of a guess. Initially, this was 4K; that was causing + * slower directory listings due to the need for more READDIR requests, + * since we were sending fewer entries back (limited by the max packet + * size) per READDIR request. + * + * Now we are trying 1K, and will see how that does. + */ + max_entry_metadata = 1024; #else max_entry_metadata = 256; #endif /* PR_USE_XATTR */ @@ -10352,7 +10430,9 @@ static int fxp_handle_readdir(struct fxp * to protocol version 6 clients. */ #ifdef PR_USE_XATTR - attr_flags |= SSH2_FX_ATTR_EXTENDED; + if (!(fxp_fsio_opts & PR_FSIO_OPT_IGNORE_XATTR)) { + attr_flags |= SSH2_FX_ATTR_EXTENDED; + } #endif /* PR_USE_XATTR */ } @@ -12099,6 +12179,8 @@ static int fxp_handle_setstat(struct fxp } pr_cmd_set_name(cmd, cmd_name); + attr_flags = fxp_attrs_clear_unsupported(attr_flags); + /* If the SFTPOption for ignoring the owners for SFTP setstat requests is set, * handle it by clearing the SSH2_FX_ATTR_UIDGID and SSH2_FX_ATTR_OWNERGROUP * flags. @@ -12208,7 +12290,9 @@ static int fxp_handle_stat(struct fxp_pa attr_flags = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_UIDGID|SSH2_FX_ATTR_PERMISSIONS| SSH2_FX_ATTR_ACMODTIME; #ifdef PR_USE_XATTR - attr_flags |= SSH2_FX_ATTR_EXTENDED; + if (!(fxp_fsio_opts & PR_FSIO_OPT_IGNORE_XATTR)) { + attr_flags |= SSH2_FX_ATTR_EXTENDED; + } #endif /* PR_USE_XATTR */ } @@ -12373,6 +12457,7 @@ static int fxp_handle_stat(struct fxp_pa fxb->buf = buf; fxb->buflen = buflen; + attr_flags = fxp_attrs_clear_unsupported(attr_flags); fxp_attrs_write(fxp->pool, fxb, path, &st, attr_flags, fake_user, fake_group); buf = fxb->buf; diff -Naurp proftpd-1.3.6.orig/contrib/mod_sftp/kbdint.c proftpd-1.3.6/contrib/mod_sftp/kbdint.c --- proftpd-1.3.6.orig/contrib/mod_sftp/kbdint.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sftp/kbdint.c 2019-08-29 13:47:17.833137851 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp keyboard-interactive driver mgmt - * Copyright (c) 2008-2016 TJ Saunders + * Copyright (c) 2008-2017 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 @@ -264,6 +264,7 @@ int sftp_kbdint_recv_response(pool *p, u struct ssh2_packet *pkt; char mesg_type; int res; + pool *resp_pool = NULL; if (p == NULL || rcvd_count == NULL || @@ -282,6 +283,9 @@ int sftp_kbdint_recv_response(pool *p, u pr_response_clear(&resp_list); pr_response_clear(&resp_err_list); + + /* Cache a reference to the current response pool used. */ + resp_pool = pr_response_get_pool(); pr_response_set_pool(pkt->pool); mesg_type = sftp_ssh2_packet_get_mesg_type(pkt); @@ -290,6 +294,7 @@ int sftp_kbdint_recv_response(pool *p, u "expecting USER_AUTH_INFO_RESP message, received %s (%d)", sftp_ssh2_packet_get_mesg_type_desc(mesg_type), mesg_type); destroy_pool(pkt->pool); + pr_response_set_pool(resp_pool); errno = EPERM; return -1; } @@ -315,6 +320,7 @@ int sftp_kbdint_recv_response(pool *p, u expected_count != 1 ? "challenges" : "challenge", (unsigned long) resp_count, resp_count != 1 ? "responses" : "response"); destroy_pool(pkt->pool); + pr_response_set_pool(resp_pool); errno = EPERM; return -1; } @@ -324,6 +330,7 @@ int sftp_kbdint_recv_response(pool *p, u "received too many responses (%lu > max %lu), rejecting", (unsigned long) resp_count, (unsigned long) SFTP_KBDINT_MAX_RESPONSES); destroy_pool(pkt->pool); + pr_response_set_pool(resp_pool); errno = EPERM; return -1; } @@ -339,6 +346,7 @@ int sftp_kbdint_recv_response(pool *p, u *rcvd_count = resp_count; *responses = ((const char **) list->elts); destroy_pool(pkt->pool); + pr_response_set_pool(resp_pool); return 0; } diff -Naurp proftpd-1.3.6.orig/contrib/mod_sftp/kex.c proftpd-1.3.6/contrib/mod_sftp/kex.c --- proftpd-1.3.6.orig/contrib/mod_sftp/kex.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sftp/kex.c 2019-08-29 11:32:45.716097897 -0500 @@ -3732,14 +3732,22 @@ static int read_curve25519_init(struct s char *data; buf = pkt->payload; - buflen = pkt->payload_len; + buflen = data_len = pkt->payload_len; data = sftp_msg_read_string(pkt->pool, &buf, &buflen); - data_len = strlen(data); + + /* The "string" we read MIGHT contain NULs, thus using strlen(3) to determine + * the length of data is a Bad Idea (Issue #556). Thus instead, we track + * the packet payload length remaining after the read; the data length is + * the difference, including the length value prefix of 4 bytes. + */ + data_len -= (buflen + sizeof(uint32_t)); + if (data_len != CURVE25519_SIZE) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, - "rejecting invalid length (%lu bytes) client Curve25519 key", - (unsigned long) data_len); + "rejecting invalid length (%lu %s, wanted %d) client Curve25519 key", + (unsigned long) data_len, data_len != 1 ? "bytes" : "byte", + CURVE25519_SIZE); errno = EINVAL; return -1; } diff -Naurp proftpd-1.3.6.orig/contrib/mod_sftp/keys.c proftpd-1.3.6/contrib/mod_sftp/keys.c --- proftpd-1.3.6.orig/contrib/mod_sftp/keys.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sftp/keys.c 2019-08-29 15:08:32.528179919 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp key mgmt (keys) - * Copyright (c) 2008-2017 TJ Saunders + * Copyright (c) 2008-2019 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 @@ -837,9 +837,15 @@ static int has_req_perms(int fd, const c static EVP_PKEY *get_pkey_from_data(pool *p, unsigned char *pkey_data, uint32_t pkey_datalen) { EVP_PKEY *pkey = NULL; - char *pkey_type; + char *pkey_type = NULL; + uint32_t len; - pkey_type = sftp_msg_read_string(p, &pkey_data, &pkey_datalen); + len = sftp_msg_read_string2(p, &pkey_data, &pkey_datalen, &pkey_type); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error reading key: invalid/unsupported key format"); + return NULL; + } if (strncmp(pkey_type, "ssh-rsa", 8) == 0) { RSA *rsa; @@ -860,8 +866,23 @@ static EVP_PKEY *get_pkey_from_data(pool return NULL; } - rsa_e = sftp_msg_read_mpint(p, &pkey_data, &pkey_datalen); - rsa_n = sftp_msg_read_mpint(p, &pkey_data, &pkey_datalen); + len = sftp_msg_read_mpine2(p, &pkey_data, &pkey_datalen, &rsa_e); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error reading key: invalid/unsupported key format"); + RSA_free(rsa); + EVP_PKEY_FREE(pkey); + return NULL; + } + + len = sftp_msg_read_mpint2(p, &pkey_data, &pkey_datalen, &rsa_n); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error reading key: invalid/unsupported key format"); + RSA_free(rsa); + EVP_PKEY_free(pkey); + return NULL; + } #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(HAVE_LIBRESSL) @@ -899,10 +920,41 @@ static EVP_PKEY *get_pkey_from_data(pool return NULL; } - dsa_p = sftp_msg_read_mpint(p, &pkey_data, &pkey_datalen); - dsa_q = sftp_msg_read_mpint(p, &pkey_data, &pkey_datalen); - dsa_g = sftp_msg_read_mpint(p, &pkey_data, &pkey_datalen); - dsa_pub_key = sftp_msg_read_mpint(p, &pkey_data, &pkey_datalen); + len = sftp_msg_read_mpint2(p, &pkey_data, &pkey_datalen, &dsa_p); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error reading key: invalid/unsupported key format"); + DSA_free(dsa); + EVP_PKEY_free(pkey); + return NULL; + } + + len = sftp_msg_read_mpint2(p, &pkey_data, &pkey_datalen, &dsa_q); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error reading key: invalid/unsupported key format"); + DSA_free(dsa); + EVP_PKEY_free(pkey); + return NULL; + } + + len = sftp_msg_read_mpint2(p, &pkey_data, &pkey_datalen, &dsa_g); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error reading key: invalid/unsupported key format"); + DSA_free(dsa); + EVP_PKEY_free(pkey); + return NULL; + } + + len = sftp_msg_read_mpint2(p, &pkey_data, &pkey_datalen, &dsa_public_key); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error reading key: invalid/unsupported key format"); + DSA_free(dsa); + EVP_PKEY_free(pkey); + return NULL; + } #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(HAVE_LIBRESSL) @@ -938,8 +990,16 @@ static EVP_PKEY *get_pkey_from_data(pool const EC_GROUP *curve; EC_POINT *point; int ec_nid; + char *ptr = NULL; - curve_name = sftp_msg_read_string(p, &pkey_data, &pkey_datalen); + len = sftp_msg_read_string2(p, &pkey_data, &pkey_datalen, &ptr); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error reading key: invalid/unsupported key format"); + return NULL; + } + + curve_name = (const char *) ptr; /* If the curve name does not match the last 8 characters of the * public key type (which, in the case of ECDSA keys, contains the @@ -984,7 +1044,14 @@ static EVP_PKEY *get_pkey_from_data(pool return NULL; } - point = sftp_msg_read_ecpoint(p, &pkey_data, &pkey_datalen, curve, point); + len = sftp_msg_read_ecpoint2(p, &pkey_data, &pkey_datalen, curve, &point); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error reading key: invalid/unsupported key format"); + EC_KEY_free(ec); + return NULL; + } + if (point == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error reading EC_POINT from public key data: %s", strerror(errno)); @@ -2780,7 +2847,7 @@ static const unsigned char *dsa_sign_dat #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(HAVE_LIBRESSL) - DSA_SIG_get0(&sig_r, &sig_s, sig); + DSA_SIG_get0(sig, &sig_r, &sig_s); #else sig_r = sig->r; sig_s = sig->s; @@ -2960,7 +3027,7 @@ static const unsigned char *ecdsa_sign_d #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(HAVE_LIBRESSL) - ECDSA_SIG_get0(&sig_r, &sig_s, sig); + ECDSA_SIG_get0(sig, &sig_r, &sig_s); #else sig_r = sig->r; sig_s = sig->s; @@ -3134,7 +3201,7 @@ int sftp_keys_verify_signed_data(pool *p #endif /* prior to OpenSSL-1.1.0 */ EVP_MD_CTX *pctx; unsigned char *sig; - uint32_t sig_len; + uint32_t len, sig_len; unsigned char digest[EVP_MAX_MD_SIZE]; char *sig_type; unsigned int digestlen = 0; @@ -3156,7 +3223,11 @@ int sftp_keys_verify_signed_data(pool *p if (strncmp(pubkey_algo, "ssh-dss", 8) == 0) { if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_HAVE_PUBKEY_ALGO_IN_DSA_SIG)) { - sig_type = sftp_msg_read_string(p, &signature, &signaturelen); + len = sftp_msg_read_string2(p, &signature, &signaturelen, &sig_type); + if (len == 0) { + errno = EINVAL; + return -1; + } } else { /* The client did not prepend the public key algorithm name to their @@ -3170,13 +3241,26 @@ int sftp_keys_verify_signed_data(pool *p } } else { - sig_type = sftp_msg_read_string(p, &signature, &signaturelen); + len = sftp_msg_read_string2(p, &signature, &signaturelen, &sig_type); + if (len == 0) { + error = EINVAL; + return -1; + } } if (strncmp(sig_type, "ssh-rsa", 8) == 0) { - sig_len = sftp_msg_read_int(p, &signature, &signaturelen); - sig = (unsigned char *) sftp_msg_read_data(p, &signature, &signaturelen, - sig_len); + len = sftp_msg_read_int2(p, &signature, &signaturelen, &sig_len); + if (len == 0) { + errno = EINVAL; + return -1; + } + + len = sftp_msg_read_data2(p, &signature, &signaturelen, sig_len, &sig); + if (len == 0) { + errno = EINVAL; + return -1; + } + if (sig != NULL) { RSA *rsa; unsigned int modulus_len; @@ -3271,7 +3355,11 @@ int sftp_keys_verify_signed_data(pool *p #if !defined(OPENSSL_NO_DSA) } else if (strncmp(sig_type, "ssh-dss", 8) == 0) { - sig_len = sftp_msg_read_int(p, &signature, &signaturelen); + len = sftp_msg_read_int2(p, &signature, &signature_len, &sig_len); + if (len == 0) { + errno = EINVAL; + return -1; + } /* A DSA signature string is composed of 2 20 character parts. */ if (sig_len != 40) { @@ -3279,8 +3367,12 @@ int sftp_keys_verify_signed_data(pool *p "bad DSA signature len (%lu)", (unsigned long) sig_len); } - sig = (unsigned char *) sftp_msg_read_data(p, &signature, &signaturelen, - sig_len); + len = sftp_msg_read_data2(p, &signature, &signaturelen, sig_len, &sig); + if (len == 0) { + errno = EINVAL; + return -1; + } + if (sig != NULL) { DSA *dsa; DSA_SIG *dsa_sig; @@ -3307,7 +3399,7 @@ int sftp_keys_verify_signed_data(pool *p dsa_sig = DSA_SIG_new(); #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(HAVE_LIBRESSL) - DSA_SIG_get0(&sig_r, &sig_s, dsa_sig); + DSA_SIG_get0(sig, &sig_r, &sig_s); #else sig_r = dsa_sig->r; sig_s = dsa_sig->s; @@ -3407,9 +3499,18 @@ int sftp_keys_verify_signed_data(pool *p } } - sig_len = sftp_msg_read_int(p, &signature, &signaturelen); - sig = (unsigned char *) sftp_msg_read_data(p, &signature, &signaturelen, - sig_len); + len = sftp_msg_read_int2(p, &signature, &signaturelen, &sig_len); + if (len == 0) { + errno = EINVAL; + return -1; + } + + len = sftp_msg_read_data2(p, &signature, &signaturelen, sig_len, &sig); + if (len == 0) { + errno = EINVAL; + return -1; + } + if (sig != NULL) { EC_KEY *ec; ECDSA_SIG *ecdsa_sig; @@ -3426,13 +3527,19 @@ int sftp_keys_verify_signed_data(pool *p #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(HAVE_LIBRESSL) - ECDSA_SIG_get0(&sig_r, &sig_s, ecdsa_sig); + ECDSA_SIG_get0(sig, &sig_r, &sig_s); #else sig_r = ecdsa_sig->r; sig_s = ecdsa_sig->s; #endif /* prior to OpenSSL-1.1.0 */ - sig_r = sftp_msg_read_mpint(p, &sig, &sig_len); + len = sftp_msg_read_mpint2(p, &sig, &sig_len, &sig_r); + if (len == 0) { + ECDSA_SIG_free(ecdsa_sig); + errno = EINVAL; + return -1; + } + if (sig_r == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error reading 'r' ECDSA signature component: %s", @@ -3441,7 +3548,13 @@ int sftp_keys_verify_signed_data(pool *p return -1; } - sig_s = sftp_msg_read_mpint(p, &sig, &sig_len); + len = sftp_msg_read_mpint2(p, &sig, &sig_len, &sig_s); + if (len == 0) { + ECDSA_SIG_free(ecdsa_sig); + errno = EINVAL; + return -1; + } + if (sig_s == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error reading 's' ECDSA signature component: %s", diff -Naurp proftpd-1.3.6.orig/contrib/mod_sftp/mod_sftp.c proftpd-1.3.6/contrib/mod_sftp/mod_sftp.c --- proftpd-1.3.6.orig/contrib/mod_sftp/mod_sftp.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sftp/mod_sftp.c 2019-08-29 14:11:37.489931823 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp - * Copyright (c) 2008-2017 TJ Saunders + * Copyright (c) 2008-2018 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 @@ -1211,6 +1211,8 @@ MODRET set_sftphostkey(cmd_rec *cmd) { insecure_hostkey_perms = TRUE; break; } + + c = find_config_next(c, c->next, CONF_PARAM, "SFTPOptions", FALSE); } if (insecure_hostkey_perms) { @@ -2032,11 +2034,12 @@ static int sftp_sess_init(void) { sftp_max_conns_ev, NULL); c = find_config(main_server->conf, CONF_PARAM, "SFTPLog", FALSE); - if (c) { - int res, xerrno; - + if (c != NULL) { sftp_logname = c->argv[0]; + if (strcasecmp(sftp_logname, "none") != 0) { + int res, xerrno; + pr_signals_block(); PRIVS_ROOT res = pr_log_openfile(sftp_logname, &sftp_logfd, PR_LOG_SYSTEM_MODE); @@ -2045,20 +2048,21 @@ static int sftp_sess_init(void) { pr_signals_unblock(); if (res < 0) { - if (res == -1) { + if (res == -1 ) { pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_VERSION - ": notice: unable to open SFTPLog '%s': %s", sftp_logname, - strerror(xerrno)); + ": notice: unable to open SFTPLog '%s': %s", sftp_logname, + strerror(xerrno)); } else if (res == PR_LOG_WRITABLE_DIR) { pr_log_pri(PR_LOG_WARNING, MOD_SFTP_VERSION - ": notice: unable to open SFTPLog '%s': parent directory is " - "world-writable", sftp_logname); + ": notice: unable to open SFTPLog '%s': parent directory is " + "world-writable", sftp_logname); } else if (res == PR_LOG_SYMLINK) { pr_log_pri(PR_LOG_WARNING, MOD_SFTP_VERSION - ": notice: unable to open SFTPLog '%s': cannot log to a symlink", - sftp_logname); + ": notice: unable to open SFTPLog '%s': cannot log to a symlink", + sftp_logname); + } } } } diff -Naurp proftpd-1.3.6.orig/contrib/mod_sftp/msg.c proftpd-1.3.6/contrib/mod_sftp/msg.c --- proftpd-1.3.6.orig/contrib/mod_sftp/msg.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sftp/msg.c 2019-08-29 15:45:06.456678279 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp message format - * Copyright (c) 2008-2017 TJ Saunders + * Copyright (c) 2008-2018 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 @@ -51,65 +51,117 @@ unsigned char *sftp_msg_getbuf(pool *p, return palloc(p, sz); } -char sftp_msg_read_byte(pool *p, unsigned char **buf, uint32_t *buflen) { - char byte = 0; - +uint32_t sftp_msg_read_byte2(pool *p, unsighed char **buf, uint32_t *buflen, + char *byte) { (void) p; if (*buflen < sizeof(char)) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to read byte (buflen = %lu)", (unsigned long) *buflen); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } - memcpy(&byte, *buf, sizeof(char)); + memcpy(byte, *buf, sizeof(char)); (*buf) += sizeof(char); (*buflen) -= sizeof(char); + return sizeof(char); +} + +char sftp_msg_read_byte(pool *p, unsigned char **buf, uint32_t *buflen) { + char byte = 0; + uint32_t len; + + len = sftp_msg_read_byte2(p, buf, buflen, &byte); + if (len == 0) { + pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); + SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + } + return byte; } -int sftp_msg_read_bool(pool *p, unsigned char **buf, uint32_t *buflen) { - char bool = 0; +uint32_t sftp_msg_read_bool2(pool *p, unsigned char **buf, uint32_t buflen, + int *bool) { + char byte = 0; + uint32_t len; (void) p; - bool = sftp_msg_read_byte(p, buf, buflen); - if (bool == 0) + len = sftp_msg_read_byte2(p, buf, buflen, &byte); + if (len == 0) { return 0; + } - return 1; + *bool = byte; + return len; } -unsigned char *sftp_msg_read_data(pool *p, unsigned char **buf, - uint32_t *buflen, size_t datalen) { - unsigned char *data = NULL; +int sftp_msg_read_bool(pool *p, unsigned char **buf, uint32_t *buflen) { + int bool = 0; + uint32_t len; - if (*buflen < datalen) { - (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, - "message format error: unable to read %lu bytes of raw data " - "(buflen = %lu)", (unsigned long) datalen, (unsigned long) *buflen); + len = sftp_msg_read_bool2(p, buf, buflen, &bool); + if (len == 0) { pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); } + if (bool == 0) { + return FALSE; + } + + return TRUE; +} + +uint32_t sftp_msg_read_data2(pool *p, unsigned char **buf, + uint32_t *buflen, size_t datalen, unsigned char **data) { if (datalen == 0) { - return NULL; + return 0; + } + + if (*buflen < datalen) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "message format error: unable to read %lu bytes of raw data " + "(buflen = %lu)", (unsigned long) datalen, (unsigned long) *buflen); + return 0; } - data = palloc(p, datalen); + *data = palloc(p, datalen); - memcpy(data, *buf, datalen); + memcpy(*data, *buf, datalen); (*buf) += datalen; (*buflen) -= datalen; + return datalen; +} + +unsigned char *sftp_msg_read_data(pool *p, unsigned chat **buf, + uint32_t *buflen, size_t datalen) { + + unsigned char *data = NULL; + uint32_t len; + + if (datalen == 0) { + errno = EINVAL; + return NULL; + } + + len = sftp_msg_read_data2(p, buf, buflen, datalen, &data); + if (len == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "message format error: unable to read %lu bytes of raw data " + "(buflen = %lu)", (unsigned long) datalen, (unsigned long) *buflen); + pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); + SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + } + return data; } -uint32_t sftp_msg_read_int(pool *p, unsigned char **buf, uint32_t *buflen) { - uint32_t val = 0; +uint32_t sftp_msg_read_int2(pool *p, unsigned char **buf, uint32_t *buflen, + uint32_t *val) { (void) p; @@ -117,88 +169,113 @@ uint32_t sftp_msg_read_int(pool *p, unsi (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to read int (buflen = %lu)", (unsigned long) *buflen); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } - memcpy(&val, *buf, sizeof(uint32_t)); + memcpy(val, *buf, sizeof(uint32_t)); (*buf) += sizeof(uint32_t); (*buflen) -= sizeof(uint32_t); - val = ntohl(val); - return val; + *val = ntohl(val); + return sizeof(uint32_t); } -uint64_t sftp_msg_read_long(pool *p, unsigned char **buf, uint32_t *buflen) { - uint64_t val = 0; +uint32_t sftp_msg_read_int(pool *p, unsigned char **buf, uint32_t *buflen) { + uint32_t val = 0; + uint32_t len; + + len = sftp_msg_read_int2(p, buf, buflen, &val); + if (len == 0) { + pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); + SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + } + + return val; +} + +uint32_t sftp_msg_read_long2(pool *p, unsigned char **buf, uint32_t *buflen, + uint64_t *val) { unsigned char data[8]; if (*buflen < sizeof(data)) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to read long (buflen = %lu)", (unsigned long) *buflen); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } memcpy(data, *buf, sizeof(data)); (*buf) += sizeof(data); (*buflen) -= sizeof(data); - val = (uint64_t) data[0] << 56; - val |= (uint64_t) data[1] << 48; - val |= (uint64_t) data[2] << 40; - val |= (uint64_t) data[3] << 32; - val |= (uint64_t) data[4] << 24; - val |= (uint64_t) data[5] << 16; - val |= (uint64_t) data[6] << 8; - val |= (uint64_t) data[7]; + (*val) = (uint64_t) data[0] << 56; + (*val) |= (uint64_t) data[1] << 48; + (*val) |= (uint64_t) data[2] << 40; + (*val) |= (uint64_t) data[3] << 32; + (*val) |= (uint64_t) data[4] << 24; + (*val) |= (uint64_t) data[5] << 16; + (*val) |= (uint64_t) data[6] << 8; + (*val) |= (uint64_t) data[7]; + + return sizeof(data); +} +uint64_t sftp_msg_read_long(pool *p, unsigned char **buf, uint32_t *buflen) { + uint64_t val = 0; + uint32_t len; - return val; + len = sftp_msg_read_long2(p, buf, buflen, &val); + if (len == 0) { + pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); + SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + } + + return val; } -BIGNUM *sftp_msg_read_mpint(pool *p, unsigned char **buf, uint32_t *buflen) { - BIGNUM *mpint = NULL; +uint32_t sftp_msg_read_mpint2(pool *p, unsigned char **buf, uint32_t *buflen, + BIGNUM **mpint) { + unsigned char *mpint_data = NULL; const unsigned char *data = NULL, *ptr = NULL; - uint32_t datalen = 0, len = 0; + uint32_t datalen = 0, mpint_len = 0, len = 0, total_len = 0; - len = sftp_msg_read_int(p, buf, buflen); + len = sftp_msg_read_int(p, buf, buflen, &mpint_len); + if (len == 0) { + return 0; + } - if (*buflen < len) { + if (*buflen < mpint_len) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to read %lu bytes of mpint (buflen = %lu)", (unsigned long) len, (unsigned long) *buflen); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } if (len > (1024 * 16)) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to handle mpint of %lu bytes", (unsigned long) len); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } - ptr = (const unsigned char *) sftp_msg_read_data(p, buf, buflen, len); - if (ptr == NULL) { - (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, - "message format error: unable to read %lu bytes of mpint data", - (unsigned long) len); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + total_len += len; + + len = sftp_msg_read_data2(p, buf, buflen, mpint_len, &mpint_data); + if (len == 0) { + return 0; } + total_len += len; + + ptr = (const unsigned char *) mpint_data; if ((ptr[0] & 0x80) != 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: negative mpint numbers not supported"); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } /* Trim any leading zeros. */ data = ptr; - datalen = len; + datalen = mpint_len; while (datalen > 0 && *data == 0x00) { pr_signals_handle(); @@ -206,11 +283,23 @@ BIGNUM *sftp_msg_read_mpint(pool *p, uns datalen--; } - mpint = BN_bin2bn(data, (int) datalen, NULL); - if (mpint == NULL) { + *mpint = BN_bin2bn(data, (int) datalen, NULL); + if (*mpint == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to convert binary mpint: %s", sftp_crypto_get_errors()); + return 0; + } + + return total_len; +} + +BIGNUM *sftp_msg_read_mpint(pool *p, unsigned char **buf, uint32_t *buflen) { + BIGNUM *mpint = NULL; + uint32_t len; + + len = sftp_msg_read_mpint2(p, buf, buflen, &mpint); + if (len == 0) { pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); } @@ -218,9 +307,9 @@ BIGNUM *sftp_msg_read_mpint(pool *p, uns return mpint; } -char *sftp_msg_read_string(pool *p, unsigned char **buf, uint32_t *buflen) { - uint32_t len = 0; - char *str = NULL; +uint32_t sftp_msg_read_string2(pool *p, unsigned char **buf, uint32_t *buflen, + char **str) { + uint32_t data_len = 0, len = 0; /* If there is no data remaining, treat this as if the string is empty * (see Bug#4093). @@ -229,99 +318,134 @@ char *sftp_msg_read_string(pool *p, unsi pr_trace_msg(trace_channel, 9, "malformed message format (buflen = %lu) for reading string, using \"\"", (unsigned long) *buflen); - return ""; + *str = pstrdup(p, ""); + return 1; } - len = sftp_msg_read_int(p, buf, buflen); + len = sftp_msg_read_int2(p, buf, buflen, &data_len); + if (len == 0) { + return 0; + } /* We can't use sftp_msg_read_data() here, since we need to allocate and * populate a buffer that is one byte longer than the len just read in, * for the terminating NUL. */ - if (*buflen < len) { + if (*buflen < data_len) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to read %lu bytes of string data " - "(buflen = %lu)", (unsigned long) len, (unsigned long) *buflen); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + "(buflen = %lu)", (unsigned long) data_len, (unsigned long) *buflen); + return 0; } - str = palloc(p, len + 1); + *str = palloc(p, data_len + 1); + + if (data_len > 0) { + memcpy(*str, *buf, data_len); + (*buf) += data_len; + (*buflen) -= data_len; + } + (*str)[data_len] = '\0'; + + return len + data_len; +} + +char *sftp_msg_read_string(pool *p, unsigned char **buf, uint32_t *buflen) { + char *str = NULL; + uint32_t len; - if (len > 0) { - memcpy(str, *buf, len); - (*buf) += len; - (*buflen) -= len; + len = sftp_msg_read_string2(p, buf, buflen, &str); + if (len == 0) { + pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); + SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); } - str[len] = '\0'; return str; } #ifdef PR_USE_OPENSSL_ECC -EC_POINT *sftp_msg_read_ecpoint(pool *p, unsigned char **buf, uint32_t *buflen, - const EC_GROUP *curve, EC_POINT *point) { +uint32_t sftp_msg_read_ecpoint2(pool *p, unsigned char **buf, uint32_t *buflen, + const EC_GROUP *curve, EC_POINT **point) { BN_CTX *bn_ctx; unsigned char *data = NULL; - uint32_t datalen = 0; + uint32_t datalen = 0, len = 0, total_len = 0; - datalen = sftp_msg_read_int(p, buf, buflen); + len = sftp_msg_read_int2(p, buf, buflen, &datalen); + if (len == 0) { + return 0; + } if (*buflen < datalen) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to read %lu bytes of EC point" " (buflen = %lu)", (unsigned long) datalen, (unsigned long) *buflen); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } if (datalen > MAX_ECPOINT_LEN) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: EC point length too long (%lu > max %lu)", (unsigned long) datalen, (unsigned long) MAX_ECPOINT_LEN); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; + } + + total_len += len; + + len = sftp_msg_read_data2(p, buf, buflen, datalen, &data); + if (len == 0) { + return 0; } - data = sftp_msg_read_data(p, buf, buflen, datalen); if (data == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to read %lu bytes of EC point data", (unsigned long) datalen); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } + total_len += len; + if (data[0] != POINT_CONVERSION_UNCOMPRESSED) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: EC point data formatted incorrectly " "(leading byte 0x%02x should be 0x%02x)", data[0], POINT_CONVERSION_UNCOMPRESSED); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error allocating new BN_CTX: %s", sftp_crypto_get_errors()); - return NULL; + return 0; } - if (EC_POINT_oct2point(curve, point, data, datalen, bn_ctx) != 1) { + if (EC_POINT_oct2point(curve, *point, data, datalen, bn_ctx) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "message format error: unable to convert binary EC point data: %s", sftp_crypto_get_errors()); BN_CTX_free(bn_ctx); - pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); - SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + return 0; } BN_CTX_free(bn_ctx); - pr_memscrub(data, datalen); - return point; + + return total_len; +} + +EC_POINT *sftp_msg_read_ecpoint(pool *p, unsigned char **buf, uint32_t *buflen, + const EC_GROUP *curve, EC_POINT *point) { + uint32_t len; + + len = sftp_msg_read_ecpoint2(p, buf, buflen, curve, &point); + if (len == 0) { + pr_log_stacktrace(sftp_logfd, MOD_SFTP_VERSION); + SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL); + } + + return point; } #endif /* PR_USE_OPENSSL_ECC */ diff -Naurp proftpd-1.3.6.orig/contrib/mod_sftp/msg.h proftpd-1.3.6/contrib/mod_sftp/msg.h --- proftpd-1.3.6.orig/contrib/mod_sftp/msg.h 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sftp/msg.h 2019-08-29 15:49:50.500402841 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp message format - * Copyright (c) 2008-2016 TJ Saunders + * Copyright (c) 2008-2019 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 @@ -39,6 +39,22 @@ uint64_t sftp_msg_read_long(pool *, unsi BIGNUM *sftp_msg_read_mpint(pool *, unsigned char **, uint32_t *); char *sftp_msg_read_string(pool *, unsigned char **, uint32_t *); +/* Variant of the Message Read API whose return value indicates the number + * of bytes of the message actually read. A zero-length return value indicates + * failure to read the requested data type. + */ +uint32_t sftp_msg_read_byte2(pool *, unsigned char **, uint32_t *, char *); +uint32_t sftp_msg_read_bool2(pool *, unsigned char **, uint32_t *, int *); +uint32_t sftp_msg_read_data2(pool *, unsigned char **, uint32_t *, size_t, unsigned char ** ); +#ifdef PR_USE_OPENSSL_ECC +uint32_t sftp_msg_read_ecpoint2(pool *, unsigned char **, uint32_t *, + const EC_GROUP *, EC_POINT **); +#endif /* PR_USE_OPENSSL_ECC */ +uint32_t sftp_msg_read_int2(pool *, unsigned char **, uint32_t *, uint32_t *); +uint32_t sftp_msg_read_long2(pool *, unsigned char **, uint32_t *, uint64_t *); +uint32_t sftp_msg_read_mpint2(pool *, unsigned char **, uint32_t *, BIGNUM **); +uint32_t sftp_msg_read_string2(pool *, unsigned char **, uint32_t *, char **); + uint32_t sftp_msg_write_byte(unsigned char **, uint32_t *, char); uint32_t sftp_msg_write_bool(unsigned char **, uint32_t *, char); uint32_t sftp_msg_write_data(unsigned char **, uint32_t *, diff -Naurp proftpd-1.3.6.orig/contrib/mod_sql.c proftpd-1.3.6/contrib/mod_sql.c --- proftpd-1.3.6.orig/contrib/mod_sql.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sql.c 2019-08-29 13:31:15.334573515 -0500 @@ -261,10 +261,23 @@ static const char *get_named_conn_backen struct sql_named_conn *snc; for (snc = sql_named_conns; snc; snc = snc->next) { + pr_trace_msg(trace_channel, 17, + "comparing requested named connection '%s' with '%s'", conn_name, + snc->conn_name); + if (strcmp(snc->conn_name, conn_name) == 0) { return snc->backend; } } + + pr_trace_msg(trace_channel, 17, + "unable to find named connection '%s': no such named connection found", + conn_name); + + } else { + pr_trace_msg(trace_channel, 17, + "unable to find named connection '%s': no named connections registered.", + conn_name); } errno = ENOENT; @@ -519,14 +532,24 @@ static modret_t *sql_dispatch(cmd_rec *c static struct sql_backend *sql_get_backend(const char *backend) { struct sql_backend *sb; - if (!sql_backends) + if (sql_backends == NULL) { + pr_trace_msg(trace_channel, 17, + "unable to find '%s' backend: no backends registered", backend); return NULL; + } for (sb = sql_backends; sb; sb = sb->next) { - if (strcasecmp(sb->backend, backend) == 0) + pr_trace_msg(trace_channel, 17, + "comparing requested backend '%s' with '%s'", backend, sb->backend); + + if (strcasecmp(sb->backend, backend) == 0) { return sb; + } } + pr_trace_msg(trace_channel, 17, + "unable to find '%s' backend: no such backend found", backend); + errno = ENOENT; return NULL; } @@ -564,6 +587,7 @@ int sql_register_backend(const char *bac sql_backends = sb; sql_nbackends++; + pr_trace_msg(trace_channel, 8, "registered '%s' backend", backend); return 0; } @@ -7050,7 +7074,7 @@ static int sql_init(void) { static int sql_sess_init(void) { char *authstr = NULL; config_rec *c = NULL; - void *ptr = NULL; + void *default_backend = NULL, *ptr = NULL; unsigned char *negative_cache = NULL; cmd_rec *cmd = NULL; modret_t *mr = NULL; @@ -7082,12 +7106,12 @@ static int sql_sess_init(void) { } } - ptr = get_param_ptr(main_server->conf, "SQLBackend", FALSE); - sql_default_cmdtable = sql_set_backend(ptr); + default_backend = get_param_ptr(main_server->conf, "SQLBackend", FALSE); + sql_default_cmdtable = sql_set_backend(default_backend); if (sql_default_cmdtable == NULL) { - if (ptr != NULL) { + if (default_backend != NULL) { sql_log(DEBUG_INFO, "unable to load '%s' SQL backend: %s", - (char *) ptr, strerror(errno)); + (char *) default_backend, strerror(errno)); } else { sql_log(DEBUG_INFO, "unable to load SQL backend: %s", strerror(errno)); @@ -7097,8 +7121,9 @@ static int sql_sess_init(void) { return -1; } - if (ptr != NULL) { - pr_trace_msg(trace_channel, 9, "loaded '%s' SQL backend", (char *) ptr); + if (default_backend != NULL) { + pr_trace_msg(trace_channel, 9, "loaded '%s' SQL backend", + (char *) default_backend); } /* Construct our internal cache structure for this session. */ @@ -7523,6 +7548,12 @@ static int sql_sess_init(void) { pr_sql_conn_policy = SQL_CONN_POLICY_PERCALL; } + /* Make sure we set the correct backend driver here, so that we + * dispatch to the correct module's command table when defining the + * connection. + */ + sql_set_backend(c->argv[1]); + if (sql_define_conn(tmp_pool, c->argv[0], c->argv[3], c->argv[4], c->argv[2], c->argv[5], c->argv[6], c->argv[7], c->argv[8], c->argv[9], c->argv[10]) < 0) { @@ -7565,6 +7596,13 @@ static int sql_sess_init(void) { } } + /* Make sure we set the default SQLBackend here, after processing any + * SQLNamedConnectInfos. + */ + if (default_backend != NULL) { + sql_set_backend(default_backend); + } + c = find_config(main_server->conf, CONF_PARAM, "SQLLogOnEvent", FALSE); while (c != NULL) { char *event_name; diff -Naurp proftpd-1.3.6.orig/contrib/mod_sql_mysql.c proftpd-1.3.6/contrib/mod_sql_mysql.c --- proftpd-1.3.6.orig/contrib/mod_sql_mysql.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sql_mysql.c 2019-08-29 13:35:48.810329970 -0500 @@ -434,7 +434,8 @@ MODRET cmd_open(cmd_rec *cmd) { entry = sql_get_connection(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tmysql cmd_open"); - return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -737,7 +738,8 @@ MODRET cmd_close(cmd_rec *cmd) { entry = sql_get_connection(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tmysql cmd_close"); - return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1081,7 +1083,8 @@ MODRET cmd_select(cmd_rec *cmd) { entry = sql_get_connection(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tmysql cmd_select"); - return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1217,7 +1220,8 @@ MODRET cmd_insert(cmd_rec *cmd) { entry = sql_get_connection(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tmysql cmd_insert"); - return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1313,7 +1317,8 @@ MODRET cmd_update(cmd_rec *cmd) { entry = sql_get_connection(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tmysql cmd_update"); - return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1437,7 +1442,8 @@ MODRET cmd_query(cmd_rec *cmd) { entry = sql_get_connection(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tmysql cmd_query"); - return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1536,7 +1542,8 @@ MODRET cmd_escapestring(cmd_rec * cmd) { entry = sql_get_connection(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tmysql cmd_escapestring"); - return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_MYSQL_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; diff -Naurp proftpd-1.3.6.orig/contrib/mod_sql_odbc.c proftpd-1.3.6/contrib/mod_sql_odbc.c --- proftpd-1.3.6.orig/contrib/mod_sql_odbc.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sql_odbc.c 2019-08-29 13:39:02.952023794 -0500 @@ -801,7 +801,8 @@ MODRET sqlodbc_open(cmd_rec *cmd) { entry = sqlodbc_get_conn(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open"); - return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1009,7 +1010,8 @@ MODRET sqlodbc_close(cmd_rec *cmd) { entry = sqlodbc_get_conn(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_close"); - return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1137,7 +1139,8 @@ MODRET sqlodbc_select(cmd_rec *cmd) { entry = sqlodbc_get_conn(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select"); - return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1286,7 +1289,8 @@ MODRET sqlodbc_insert(cmd_rec *cmd) { entry = sqlodbc_get_conn(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert"); - return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1377,7 +1381,8 @@ MODRET sqlodbc_update(cmd_rec *cmd) { entry = sqlodbc_get_conn(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update"); - return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1484,7 +1489,8 @@ MODRET sqlodbc_query(cmd_rec *cmd) { entry = sqlodbc_get_conn(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query"); - return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1587,7 +1593,8 @@ MODRET sqlodbc_quote(cmd_rec *cmd) { entry = sqlodbc_get_conn(cmd->argv[0]); if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_escapestring"); - return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "unknown named connection"); + return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } /* Make sure the connection is open. */ diff -Naurp proftpd-1.3.6.orig/contrib/mod_sql_postgres.c proftpd-1.3.6/contrib/mod_sql_postgres.c --- proftpd-1.3.6.orig/contrib/mod_sql_postgres.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sql_postgres.c 2019-08-29 13:41:47.833063651 -0500 @@ -389,7 +389,7 @@ MODRET cmd_open(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_open"); return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -610,7 +610,7 @@ MODRET cmd_close(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_close"); return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -952,7 +952,7 @@ MODRET cmd_select(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_select"); return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1097,7 +1097,7 @@ MODRET cmd_insert(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_insert"); return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1201,7 +1201,7 @@ MODRET cmd_update(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_update"); return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1332,7 +1332,7 @@ MODRET cmd_query(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_query"); return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -1443,7 +1443,7 @@ MODRET cmd_escapestring(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tpostgres cmd_escapestring"); return PR_ERROR_MSG(cmd, MOD_SQL_POSTGRES_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; diff -Naurp proftpd-1.3.6.orig/contrib/mod_sql_sqlite.c proftpd-1.3.6/contrib/mod_sql_sqlite.c --- proftpd-1.3.6.orig/contrib/mod_sql_sqlite.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_sql_sqlite.c 2019-08-29 13:44:56.697817202 -0500 @@ -84,16 +84,20 @@ static void db_trace(void *user_data, co static conn_entry_t *sql_sqlite_get_conn(char *name) { register unsigned int i = 0; - if (!name) + if (name == NULL) { + errno = EINVAL; return NULL; + } for (i = 0; i < conn_cache->nelts; i++) { conn_entry_t *entry = ((conn_entry_t **) conn_cache->elts)[i]; - if (strcmp(name, entry->name) == 0) + if (strcmp(name, entry->name) == 0) { return entry; + } } + errno = ENOENT; return NULL; } @@ -301,7 +305,7 @@ MODRET sql_sqlite_open(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tsqlite cmd_open"); return PR_ERROR_MSG(cmd, MOD_SQL_SQLITE_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -434,7 +438,7 @@ MODRET sql_sqlite_close(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tsqlite cmd_close"); return PR_ERROR_MSG(cmd, MOD_SQL_SQLITE_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -591,7 +595,7 @@ MODRET sql_sqlite_select(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tsqlite cmd_select"); return PR_ERROR_MSG(cmd, MOD_SQL_SQLITE_VERSION, - "unknown named connection"); + pstrcat(cmp->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -699,7 +703,7 @@ MODRET sql_sqlite_insert(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tsqlite cmd_insert"); return PR_ERROR_MSG(cmd, MOD_SQL_SQLITE_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -788,7 +792,7 @@ MODRET sql_sqlite_update(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tsqlite cmd_update"); return PR_ERROR_MSG(cmd, MOD_SQL_SQLITE_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -909,7 +913,7 @@ MODRET sql_sqlite_query(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tsqlite cmd_query"); return PR_ERROR_MSG(cmd, MOD_SQL_SQLITE_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } conn = (db_conn_t *) entry->data; @@ -987,7 +991,7 @@ MODRET sql_sqlite_quote(cmd_rec *cmd) { if (entry == NULL) { sql_log(DEBUG_FUNC, "%s", "exiting \tsqlite cmd_escapestring"); return PR_ERROR_MSG(cmd, MOD_SQL_SQLITE_VERSION, - "unknown named connection"); + pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL)); } /* Make sure the connection is open. */ diff -Naurp proftpd-1.3.6.orig/contrib/mod_tls.c proftpd-1.3.6/contrib/mod_tls.c --- proftpd-1.3.6.orig/contrib/mod_tls.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/contrib/mod_tls.c 2019-08-29 16:54:05.494312929 -0500 @@ -2,7 +2,7 @@ * mod_tls - An RFC2228 SSL/TLS module for ProFTPD * * Copyright (c) 2000-2002 Peter 'Luna' Runestig - * Copyright (c) 2002-2017 TJ Saunders + * Copyright (c) 2002-2019 TJ Saunders * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifi- @@ -1187,6 +1187,9 @@ static struct tls_label tls_ciphersuite_ { 0x00C4, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256" }, { 0x00C5, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256" }, { 0x00FF, "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" }, + { 0x1301, "TLS_AES_128_GCM_SHA256" }, + { 0x1302, "TLS_AES_256_GCM_SHA384" }, + { 0x1303, "TLS_CHACHA20_POLY1305_SHA256" }, { 0xC001, "TLS_ECDH_ECDSA_WITH_NULL_SHA" }, { 0xC002, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA" }, { 0xC003, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA" }, @@ -1271,7 +1274,18 @@ static struct tls_label tls_extension_la { 15, "heartbeat" }, { 16, "application_layer_protocol_negotiation" }, { 21, "padding" }, + { 22, "encrypt_then_mac" }, + { 23, "extended_master_secret" }, { 35, "session_ticket" }, + { 41, "psk" }, + { 42, "early_data" }, + { 43, "supported_versions" }, + { 44, "cookie" }, + { 45, "psk_kex_modes" }, + { 47, "certificate_authorities" }, + { 49, "post_handshake_auth" }, + { 50, "signature_algorithms_cert" }, + { 51, "key_share" }, { 0xFF01, "renegotiate" }, { 13172, "next_proto_neg" }, @@ -1550,6 +1564,12 @@ static void tls_msg_cb(int io_flag, int case TLS1_2_VERSION: version_str = "TLSv1.2"; break; + +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + case TLS1_3_VERSION: + version_str = "TLSv1.3"; + break; +#endif #endif default: @@ -1572,6 +1592,9 @@ static void tls_msg_cb(int io_flag, int #if OPENSSL_VERSION_NUMBER >= 0x10001000L version == TLS1_1_VERSION || version == TLS1_2_VERSION || +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + version == TLS1_3_VERSION || +#endif #endif version == TLS1_VERSION) { @@ -1736,11 +1759,13 @@ static void tls_msg_cb(int io_flag, int action_str, version_str, (unsigned int) buflen, bytes_str); break; +#ifdef SSL3_MT_NEWSESSION_TICKET case SSL3_MT_NEWSESSION_TICKET: tls_log("[msg] %s %s 'NewSessionTicket' Handshake message " "(%u %s)", action_str, version_str, (unsigned int) buflen, bytes_str); break; +#endif /* SSL3_MT_NEWSESSION_TICKET */ case SSL3_MT_CERTIFICATE: tls_log("[msg] %s %s 'Certificate' Handshake message (%u %s)", @@ -3731,29 +3756,41 @@ static void tls_tlsext_cb(SSL *ssl, int * http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1 */ switch (type) { +#ifdef TLSEXT_TYPE_server_name case TLSEXT_TYPE_server_name: extension_name = "server name"; break; +#endif +#ifdef TLSEXT_TYPE_max_fragment_length case TLSEXT_TYPE_max_fragment_length: extension_name = "max fragment length"; break; +#endif +#ifdef TLSEXT_TYPE_client_certificate_url case TLSEXT_TYPE_client_certificate_url: extension_name = "client certificate URL"; break; +#endif +#ifdef TLSEXT_TYPE_trusted_ca_keys case TLSEXT_TYPE_trusted_ca_keys: extension_name = "trusted CA keys"; break; +#endif +#ifdef TLSEXT_TYPE_truncated_hmac case TLSEXT_TYPE_truncated_hmac: extension_name = "truncated HMAC"; break; +#endif +#ifdef TLSEXT_TYPE_status_request case TLSEXT_TYPE_status_request: extension_name = "status request"; break; +#endif # ifdef TLSEXT_TYPE_user_mapping case TLSEXT_TYPE_user_mapping: @@ -3815,12 +3852,54 @@ static void tls_tlsext_cb(SSL *ssl, int break; # endif +# ifdef TLSEXT_TYPE_signed_certificate_timestamp + case TLSEXT_TYPE_signed_certificate_timestamp: + extension_name = "signed certificate timestamp"; + break; +#endif + +#ifdef TLSEXT_TYPE_encrypt_then_mac + case TLSEXT_TYPE_encrypt_then_mac: + extension_name = "encrypt then mac"; + break; +#endif + +#ifdef TLSEXT_TYPE_extended_master_secret + case TLSEXT_TYPE_extended_master_secret: + extension_name = "extended master secret"; + break; +#endif + # ifdef TLSEXT_TYPE_session_ticket case TLSEXT_TYPE_session_ticket: extension_name = "session ticket"; break; # endif +#ifdef TLSEXT_TYPE_psk + case TLSEXT_TYPE_psk: + extension_name = "PSK"; + break; +#endif + +#ifdef TLSEXT_TYPE_supported_versions + case TLSEXT_TYPE_supported_versions: + extension_name = "supported versions"; + break; +#endif + +#ifdef TLSEXT_TYPE_psk_kex_modes + case TLSEXT_TYPE_psk_kex_modes: + extension_name = "PSK KEX modes"; + break; +#endif + +#ifdef TLSEXT_TYPE_key_share + case TLSEXT_TYPE_key_share: + extension_name = "key share"; + break; +#endif + # ifdef TLSEXT_TYPE_renegotiate case TLSEXT_TYPE_renegotiate: extension_name = "renegotiation info"; @@ -5079,7 +5158,7 @@ static int tls_ticket_key_cb(SSL *ssl, u SSL_get_cipher_name(ssl), sess_key_len); } - if (RAND_bytes(iv, 16) != 1) { + if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) { pr_trace_msg(trace_channel, 3, "unable to initialize session ticket key IV: %s", tls_get_errors()); return -1; @@ -5102,7 +5181,7 @@ static int tls_ticket_key_cb(SSL *ssl, u # endif /* OpenSSL-1.0.0 and later */ memcpy(key_name, k->key_name, 16); - return 0; + return 1; } if (mode == 0) { @@ -6541,7 +6620,12 @@ static int tls_accept(conn_t *conn, unsi #if !defined(OPENSSL_NO_TLSEXT) if (tls_opts & TLS_OPT_ENABLE_DIAGS) { + /* note that older OpenSSL verions, e.g. 0.9.8, do not implement this + * callback. Newer versions which DO implement it do it as a macro. + */ +#ifdef SSL_set_tlsext_debug_callback SSL_set_tlsext_debug_callback(ssl, tls_tlsext_cb); +#endif /* SSL_set_tlsext_debug_callback */ } #endif /* !OPENSSL_NO_TLSEXT */ @@ -7107,7 +7191,7 @@ static int tls_connect(conn_t *conn) { SSL *ssl = NULL; BIO *rbio = NULL, *wbio = NULL; - if (!ssl_ctx) { + if (ssl_ctx == NULL) { tls_log("%s", "unable to start session: null SSL_CTX"); return -1; } @@ -7119,6 +7203,14 @@ static int tls_connect(conn_t *conn) { return -2; } + /* Make sure our SSL object uses client methods (Issue#618). */ + if (SSL_set_ssl_method(ssl, SSLv23_client_method()) != 1) { + tls_log("error: unable to set client methods: %s", + ERR_error_string(ERR_get_error(), NULL)); + SSL_free(ssl); + return -2; + } + /* We deliberately set SSL_VERIFY_NONE here, so that we get to * determine how to handle the server cert verification result ourselves. */ @@ -7154,9 +7246,7 @@ static int tls_connect(conn_t *conn) { pr_signals_handle(); res = SSL_connect(ssl); - if (res == -1) { - xerrno = errno; - } + xerrno = errno; if (blocking) { /* Return the connection to blocking mode. */ @@ -7314,9 +7404,6 @@ static int tls_connect(conn_t *conn) { static void tls_cleanup(int flags) { - tls_sess_cache_close(); - tls_ocsp_cache_close(); - #if OPENSSL_VERSION_NUMBER > 0x000907000L if (tls_crypto_device) { ENGINE_cleanup(); @@ -7334,6 +7421,12 @@ static void tls_cleanup(int flags) { ssl_ctx = NULL; } + /* Close the session cache only AFTER the SSL context has been + * freed/deleted (Issue #795). + */ + tls_sess_cache_close(); + tls_ocsp_cache_close(); + if (tls_tmp_dhs) { register unsigned int i; DH **dhs; @@ -8070,10 +8163,13 @@ static int tls_writemore(int wfd) { static ssize_t tls_read(SSL *ssl, void *buf, size_t len) { ssize_t count; + int xerrno = 0; retry: pr_signals_handle(); count = SSL_read(ssl, buf, len); + xerrno = errno; + if (count < 0) { long err; int fd; @@ -8099,7 +8195,7 @@ static ssize_t tls_read(SSL *ssl, void * } else if (err == 0) { /* Still missing data after timeout. Simulate an EINTR and return. */ - errno = EINTR; + xerrno = EINTR; /* If err < 0, i.e. some error from the select(), everything is * already in place; errno is properly set and this function @@ -8122,7 +8218,7 @@ static ssize_t tls_read(SSL *ssl, void * } else if (err == 0) { /* Still missing data after timeout. Simulate an EINTR and return. */ - errno = EINTR; + xerrno = EINTR; /* If err < 0, i.e. some error from the select(), everything is * already in place; errno is properly set and this function @@ -8141,6 +8237,7 @@ static ssize_t tls_read(SSL *ssl, void * } } + errno = xerrno; return count; } @@ -9479,8 +9576,11 @@ static int tls_verify_ocsp(int ok, X509_ static ssize_t tls_write(SSL *ssl, const void *buf, size_t len) { ssize_t count; + int xerrno = 0; count = SSL_write(ssl, buf, len); + xerrno = errno; + if (count < 0) { long err = SSL_get_error(ssl, count); @@ -9491,7 +9591,7 @@ static ssize_t tls_write(SSL *ssl, const case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: /* Simulate an EINTR in case OpenSSL wants to write more. */ - errno = EINTR; + xerrno = EINTR; break; default: @@ -9529,6 +9629,7 @@ static ssize_t tls_write(SSL *ssl, const tls_data_adaptive_bytes_written_ms = now; } + errno = xerrno; return count; } @@ -10718,7 +10819,7 @@ static int tls_netio_read_cb(pr_netio_st ssl = (SSL *) pr_table_get(nstrm->notes, TLS_NETIO_NOTE, NULL); if (ssl != NULL) { BIO *rbio, *wbio; - int bread = 0, bwritten = 0; + int bread = 0, bwritten = 0, xerrno = 0; ssize_t res = 0; unsigned long rbio_rbytes, rbio_wbytes, wbio_rbytes, wbio_wbytes; @@ -10731,6 +10832,7 @@ static int tls_netio_read_cb(pr_netio_st wbio_wbytes = BIO_number_written(wbio); res = tls_read(ssl, buf, buflen); + xerrno = errno; bread = (BIO_number_read(rbio) - rbio_rbytes) + (BIO_number_read(wbio) - wbio_rbytes); @@ -10752,6 +10854,7 @@ static int tls_netio_read_cb(pr_netio_st session.total_raw_out += bwritten; } + errno = xerrno; return res; } @@ -10858,7 +10961,7 @@ static int tls_netio_write_cb(pr_netio_s ssl = (SSL *) pr_table_get(nstrm->notes, TLS_NETIO_NOTE, NULL); if (ssl != NULL) { BIO *rbio, *wbio; - int bread = 0, bwritten = 0; + int bread = 0, bwritten = 0, xerrno = 0; ssize_t res = 0; unsigned long rbio_rbytes, rbio_wbytes, wbio_rbytes, wbio_wbytes; @@ -10899,6 +11002,7 @@ static int tls_netio_write_cb(pr_netio_s #endif res = tls_write(ssl, buf, buflen); + xerrno = 0; bread = (BIO_number_read(rbio) - rbio_rbytes) + (BIO_number_read(wbio) - wbio_rbytes); @@ -10920,6 +11024,7 @@ static int tls_netio_write_cb(pr_netio_s session.total_raw_out += (bwritten - res); } + errno = xerrno; return res; } @@ -11980,9 +12085,17 @@ MODRET set_tlsciphersuite(cmd_rec *cmd) ciphersuite = cmd->argv[1]; c = add_config_param(cmd->argv[0], 1, NULL); - /* Make sure that EXPORT ciphers cannot be used, per Bug#4163. */ - c->argv[0] = pstrcat(c->pool, "!EXPORT:", ciphersuite, NULL); + /* Make sure that EXPORT ciphers cannot be used, per Bug#4163. Note that + * this could break system profiles, so handle them specially. + */ + if (strncmp(ciphersuite, "PROFILE=", 8) == 0) { + ciphersuite = pstrdup(c->pool, ciphersuite); + + } else { + ciphersuite = pstrcat(c->pool, "!EXPORT:", ciphersuite, NULL); + } + c->argv[0] = ciphersuite; return PR_HANDLED(cmd); } @@ -12583,6 +12696,12 @@ MODRET set_tlsprotocol(cmd_rec *cmd) { if (strncasecmp(cmd->argv[i], "SSLv23", 7) == 0) { tls_protocol |= TLS_PROTO_SSL_V3; tls_protocol |= TLS_PROTO_TLS_V1; +#ifdef SSL_OP_NO_TLSv1_1 + tls_protocol |= TLS_PROTO_TLS_V1_1; +#endif +#ifdef SSL_OP_NO_TLSv1_2 + tls_protocol |= TLS_PROTO_TLS_V1_2; +#endif } else if (strncasecmp(cmd->argv[i], "SSLv3", 6) == 0) { tls_protocol |= TLS_PROTO_SSL_V3; @@ -13650,7 +13769,7 @@ static tls_pkey_t *tls_find_pkey(server_ static tls_pkey_t *tls_get_key_passphrase(server_rec *s, const char *path, int flags) { - int res, *pass_len; + int res, *pass_len = NULL; tls_pkey_t *k = NULL; const char *key_type = "unsupported"; char buf[256], **key_data = NULL; diff -Naurp proftpd-1.3.6.orig/doc/modules/mod_facts.html proftpd-1.3.6/doc/modules/mod_facts.html --- proftpd-1.3.6.orig/doc/modules/mod_facts.html 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/doc/modules/mod_facts.html 2019-08-29 16:12:45.807547951 -0500 @@ -107,15 +107,15 @@ The currently implemented options are:

-

  • NoAdjustedSymlinks
    +
  • AdjustedSymlinks

    - By default, mod_facts tries to automatically adjust any - symlink destination paths when the FTP session is chrooted, so that - the adjusted symlinks work properly e.g. for FTP clients. + Using this option will cause mod_facts to try to automatically + adjust any symlink destination paths when the FTP session is chrooted, so + that the adjusted symlinks work properly e.g. for FTP clients.

    Note that this option first appeared in - proftpd-1.3.6rc2. + proftpd-1.3.6a.

  • @@ -287,7 +287,7 @@ Then follow the usual steps:


    -© Copyright 2007-2016 The ProFTPD Project
    +© Copyright 2007-2019 The ProFTPD Project
    All Rights Reserved

    diff -Naurp proftpd-1.3.6.orig/doc/modules/mod_ls.html proftpd-1.3.6/doc/modules/mod_ls.html --- proftpd-1.3.6.orig/doc/modules/mod_ls.html 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/doc/modules/mod_ls.html 2019-08-29 16:14:23.116426239 -0500 @@ -111,7 +111,7 @@ and neither directive affects permission


    ListOptions

    -Syntax: ListOptions options [strict [maxdepth depth] [maxfiles count] [maxdirs count] [LISTOnly] [NLSTOnly] [NoErrorIfAbsent] [NoAdjustedSymlinks] [SortedNLST]
    +Syntax: ListOptions options [strict [maxdepth depth] [maxfiles count] [maxdirs count] [LISTOnly] [NLSTOnly] [NoErrorIfAbsent] [AdjustedSymlinks] [SortedNLST]
    Default: None
    Context: server config, <VirtualHost>, <Global>, <Anonymous>, <Directory>, .ftpaccess
    Module: mod_ls
    @@ -154,15 +154,15 @@ The currently supported flags a

    -

  • NoAdjustedSymlinks
    +
  • AdjustedSymlinks

    - By default, mod_ls tries to automatically adjust any - symlink destination paths when the FTP session is chrooted, so that - the adjusted symlinks work properly e.g. for FTP clients. + This flag tells mod_ls to try to automatically + adjust any symlink destination paths when the FTP session is chrooted, + so that the adjusted symlinks work properly e.g. for FTP clients.

    Note that this flag first appeared in - proftpd-1.3.6rc2. + proftpd-1.3.6a.

  • @@ -233,7 +233,7 @@ The mod_lsmodule is alwa



    -© Copyright 2000-2016 The ProFTPD Project
    +© Copyright 2000-2019 The ProFTPD Project
    All Rights Reserved
    diff -Naurp proftpd-1.3.6.orig/include/ascii.h proftpd-1.3.6/include/ascii.h --- proftpd-1.3.6.orig/include/ascii.h 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/include/ascii.h 2019-08-29 14:31:26.881322387 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server daemon - * Copyright (c) 2013-2015 The ProFTPD Project team + * Copyright (c) 2013-2018 The ProFTPD Project team * * 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 @@ -56,6 +56,9 @@ int pr_ascii_ftp_from_crlf(pool *p, char /* * Returns the number N on success, and -1 on error, setting errno * appropriately. + * + * NOTE: this function uses malloc(3) deliberately (see Bug #4352), and thus + * the caller MUST call free(3) on the **out pointer. */ int pr_ascii_ftp_to_crlf(pool *p, char *in, size_t inlen, char **out, size_t *outlen); diff -Naurp proftpd-1.3.6.orig/include/fsio.h proftpd-1.3.6/include/fsio.h --- proftpd-1.3.6.orig/include/fsio.h 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/include/fsio.h 2019-08-29 11:39:45.019182158 -0500 @@ -413,6 +413,9 @@ int pr_fs_glob(const char *, int, int (* void pr_fs_globfree(glob_t *); void pr_resolve_fs_map(void); +/* Close al but the three main fds. */ +void pr_fs_close_extra_fds(void); + /* The main three fds (stdin, stdout, stderr) need to be protected, reserved * for use. This function uses dup(2) to open new fds on the given fd * until the new fd is not one of the big three. diff -Naurp proftpd-1.3.6.orig/Makefile.in proftpd-1.3.6/Makefile.in --- proftpd-1.3.6.orig/Makefile.in 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/Makefile.in 2019-08-29 14:41:58.648385430 -0500 @@ -127,8 +127,8 @@ install-headers: $(DESTDIR)$(includedir) install-pkgconfig: $(DESTDIR)$(pkgconfigdir) @echo 'prefix=$(prefix)' > proftpd.pc @echo 'exec_prefix=$${prefix}' >> proftpd.pc - @echo 'libdir=$${prefix}/lib/proftpd' >> proftpd.pc - @echo 'includedir=$${prefix}/include/proftpd' >> proftpd.pc + @echo 'libdir=${libdir}/proftpd' >> proftpd.pc + @echo 'includedir=${includedir}/proftpd' >> proftpd.pc @echo '' >> proftpd.pc @echo 'Name: ProFTPD' >> proftpd.pc @echo 'Description: Professional FTP Daemon' >> proftpd.pc diff -Naurp proftpd-1.3.6.orig/modules/mod_auth.c proftpd-1.3.6/modules/mod_auth.c --- proftpd-1.3.6.orig/modules/mod_auth.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/modules/mod_auth.c 2019-08-29 17:25:55.110339444 -0500 @@ -936,9 +936,13 @@ static int get_default_root(pool *p, int path[pathlen-1] = '\0'; } + PRIVS_USER res = is_symlink_path(p, path, pathlen); + xerrno = errno; + PRIVS_RELINQUISH + if (res < 0) { - if (errno == EPERM) { + if (xerrno == EPERM) { pr_log_pri(PR_LOG_WARNING, "error: DefaultRoot %s is a symlink " "(denied by AllowChrootSymlinks config)", path); } @@ -1039,6 +1043,11 @@ static int setup_env(pool *p, cmd_rec *c origuser = user; c = pr_auth_get_anon_config(p, &user, &ourname, &anonname); if (c != NULL) { + pr_trace_msg("auth", 13, + "found config: login user = %s, config user = %s, " + "anon name = %s", user != NULL ? user : "(null)", + ourname != NULL ? ourname : "(null)", + anonname != NULL ? anonname : "(null)"); session.anon_config = c; } @@ -2137,18 +2146,10 @@ static int auth_count_scoreboard(cmd_rec /* Make sure it matches our current server. */ if (strcmp(score->sce_server_addr, curr_server_addr) == 0) { - if ((c != NULL && c->config_type == CONF_ANON && - !strcmp(score->sce_user, user)) || c == NULL) { - - /* This small hack makes sure that cur is incremented properly - * when dealing with anonymous logins (the timing of anonymous - * login updates to the scoreboard makes this...odd). - */ - if (c != NULL && - c->config_type == CONF_ANON && - cur == 0) { - cur = 1; - } + if ((c != NULL && + c->config_type == CONF_ANON && + strcmp(score->sce_user, user) == 0) || + c == NULL) { /* Only count authenticated clients, as per the documentation. */ if (strncmp(score->sce_user, "(none)", 7) == 0) { @@ -2159,20 +2160,10 @@ static int auth_count_scoreboard(cmd_rec /* Count up sessions on a per-host basis. */ - if (!strcmp(score->sce_client_addr, - pr_netaddr_get_ipstr(session.c->remote_addr))) { + if (strcmp(score->sce_client_addr, + pr_netaddr_get_ipstr(session.c->remote_addr)) == 0) { same_host = TRUE; - /* This small hack makes sure that hcur is incremented properly - * when dealing with anonymous logins (the timing of anonymous - * login updates to the scoreboard makes this...odd). - */ - if (c != NULL && - c->config_type == CONF_ANON && - hcur == 0) { - hcur = 1; - } - hcur++; } @@ -2181,7 +2172,7 @@ static int auth_count_scoreboard(cmd_rec usersessions++; /* Count up unique hosts. */ - if (!same_host) { + if (same_host == FALSE) { hostsperuser++; } } @@ -2277,7 +2268,8 @@ static int auth_count_scoreboard(cmd_rec maxstr = maxc->argv[1]; } - if (*max && hcur > *max) { + if (*max && + hcur > *max) { char maxn[20] = {'\0'}; pr_event_generate("mod_auth.max-clients-per-host", session.c); @@ -2305,7 +2297,8 @@ static int auth_count_scoreboard(cmd_rec maxstr = maxc->argv[1]; } - if (*max && usersessions > *max) { + if (*max && + usersessions > *max) { char maxn[20] = {'\0'}; pr_event_generate("mod_auth.max-clients-per-user", user); @@ -2332,7 +2325,8 @@ static int auth_count_scoreboard(cmd_rec maxstr = maxc->argv[1]; } - if (*max && cur > *max) { + if (*max + && cur > *max) { char maxn[20] = {'\0'}; pr_event_generate("mod_auth.max-clients", NULL); @@ -2632,35 +2626,52 @@ MODRET auth_pre_pass(cmd_rec *cmd) { allow_empty_passwords = *((int *) c->argv[0]); if (allow_empty_passwords == FALSE) { + const char *proto; + int reject_empty_passwd = FALSE, using_ssh2 = FALSE; size_t passwd_len = 0; + proto = pr_session_get_protocol(0); + if (strcmp(proto, "ssh2") == 0) { + using_ssh2 = TRUE; + } + if (cmd->argc > 1) { if (cmd->arg != NULL) { passwd_len = strlen(cmd->arg); } } - /* Make sure to NOT enforce 'AllowEmptyPasswords off' if e.g. - * the AllowDotLogin TLSOption is in effect. - */ - if (cmd->argc == 1 || - passwd_len == 0) { + if (passwd_len == 0) { + reject_empty_passwd = TRUE; - if (session.auth_mech == NULL || - strcmp(session.auth_mech, "mod_tls.c") != 0) { - pr_log_debug(DEBUG5, - "Refusing empty password from user '%s' (AllowEmptyPasswords " - "false)", user); - pr_log_auth(PR_LOG_NOTICE, - "Refusing empty password from user '%s'", user); - - pr_event_generate("mod_auth.empty-password", user); - pr_response_add_err(R_501, _("Login incorrect.")); - return PR_ERROR(cmd); + /* Make sure to NOT enforce 'AllowEmptyPasswords off' if e.g. + * the AllowDotLogin TLSOption is in effect, or if the protocol is + * SSH2 (for mod_sftp uses "fake" PASS commands for the SSH login + * protocol). + */ + + if (session.auth_mech != NULL && + strcmp(session.auth_mech, "mod_tls.c") == 0) { + pr_log_debug (DEBUG9, "%s", "'AllowEmptyPasswords off' in effect, " + "BUT client authenticated via the AllowDotLogin TLSOption"); + reject_empty_passwd = FALSE; } - pr_log_debug(DEBUG9, "%s", "'AllowEmptyPasswords off' in effect, " - "BUT client authenticated via the AllowDotLogin TLSOption"); + if (using_ssh2 == TRUE) { + reject_empty_passwd = FALSE; + } + } + + if (reject_empty_passwd == TRUE) { + pr_log_debug(DEBUG5, + "Refusing empty password from user '%s' (AllowEmptyPasswords " + "false)", user); + pr_log_auth(PR_LOG_NOTICE, + "Refusing empty password from user '%s'", user); + + pr_event_generate("mod_auth.empty-password", user); + pr_response_add_err(R_501, _("Login incorrect.")); + return PR_ERROR(cmd); } } } diff -Naurp proftpd-1.3.6.orig/modules/mod_auth_unix.c proftpd-1.3.6/modules/mod_auth_unix.c --- proftpd-1.3.6.orig/modules/mod_auth_unix.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/modules/mod_auth_unix.c 2019-08-29 17:24:05.163486509 -0500 @@ -715,34 +715,40 @@ static char *get_pwd_info(pool *p, const MODRET pw_auth(cmd_rec *cmd) { int res; time_t now; - char *cpw; - time_t lstchg = -1, max = -1, inact = -1, disable = -1; + char *cleartxt_passwd; + time_t lstchg = -1, max = -1, inact = -1, expire = -1; const char *name; + size_t cleartxt_passwdlen; name = cmd->argv[0]; - time(&now); - cpw = get_pwd_info(cmd->tmp_pool, name, &lstchg, NULL, &max, NULL, &inact, - &disable); - if (cpw == NULL) { + cleartxt_passwd = get_pwd_info(cmd->tmp_pool, name, &lstchg, NULL, &max, + NULL, &inact, &expire); + if (cleartxt_passwd == NULL) { return PR_DECLINED(cmd); } - res = pr_auth_check(cmd->tmp_pool, cpw, cmd->argv[0], cmd->argv[1]); + res = pr_auth_check(cmd->tmp_pool, cleartxt_passwd, cmd->argv[0], + cmd->argv[1]); + cleartxt_passwdlen = strlen(cleartxt_passwd); + pr_memscrub(cleartxt_passwd, cleartxt_passwdlen); + if (res < PR_AUTH_OK) { return PR_ERROR_INT(cmd, res); } + time(&now); + if (lstchg > (time_t) 0 && max > (time_t) 0 && inact > (time_t) 0) { - if (now > lstchg + max + inact) { + if (now > (lstchg + max + inact)) { return PR_ERROR_INT(cmd, PR_AUTH_AGEPWD); } } - if (disable > (time_t) 0 && - now > disable) { + if (expire > (time_t) 0 && + now > expire) { return PR_ERROR_INT(cmd, PR_AUTH_DISABLEDPWD); } @@ -751,14 +757,49 @@ MODRET pw_auth(cmd_rec *cmd) { } MODRET pw_authz(cmd_rec *cmd) { + time_t now; + char *user, *cleartxt_passwd; + time_t lstchg = -1, max = -1, inact = -1, expire = -1; + size_t cleartxt_passwdlen; + + user = cmd->argv[0]; + + cleartxt_passwd = get_pwd_info(cmd->tmp_pool, user, &lstchg, NULL, &max, + NULL, &inact, &expire); + if (cleartxt_passwd == NULL) { + pr_log_auth(LOG_WARNING, "no password information found for user '%.100s'", + user); + return PR_ERROR_INT(cmd, PR_AUTH_NOPWD); + } + + cleartxt_passwdlen = strlen(cleartxt_passwd); + pr_memscrub(cleartxt_passwd, cleartxt_passwdlen); + + time(&now); + + if (lstchg > (time_t) 0 && + max > (time_t) 0 && + inact > (time_t) 0) { + if (now > (lstchg + max + inact)) { + pr_log_auth(LOG_WARNING, + "account for user '%.100s' disabled due to inactivity", user); + return PR_ERROR_INT(cmd, PR_AUTH_AGEPWD); + } + } + + if (expire > (time_t) 0 && + now > expire) { + pr_log_auth(LOG_WARNING, + "account for user '%.100s' disabled due to password expiration", user); + return PR_ERROR_INT(cmd, PR_AUTH_DISABLEDPWD); + } + /* XXX Any other implementations here? */ #ifdef HAVE_LOGINRESTRICTIONS if (!(auth_unix_opts & AUTH_UNIX_OPT_AIX_NO_RLOGIN)) { int res, xerrno, code = 0; - char *user = NULL, *reason = NULL; - - user = cmd->argv[0]; + char *reason = NULL; /* Check for account login restrictions and such using AIX-specific * functions. diff -Naurp proftpd-1.3.6.orig/modules/mod_core.c proftpd-1.3.6/modules/mod_core.c --- proftpd-1.3.6.orig/modules/mod_core.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/modules/mod_core.c 2019-08-29 16:17:27.645165215 -0500 @@ -2,7 +2,7 @@ * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu - * Copyright (c) 2001-2017 The ProFTPD Project team + * Copyright (c) 2001-2019 The ProFTPD Project team * * 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 @@ -5065,21 +5065,6 @@ MODRET core_chdir(cmd_rec *cmd, char *nd orig_dir = ndir; - pr_fs_clear_cache2(ndir); - if (pr_fsio_lstat(ndir, &st) == 0) { - if (S_ISLNK(st.st_mode)) { - char buf[PR_TUNABLE_PATH_MAX]; - int len; - - len = dir_readlink(cmd->tmp_pool, ndir, buf, sizeof(buf)-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0) { - buf[len] = '\0'; - ndir = pstrdup(cmd->tmp_pool, buf); - } - } - } - ptr = get_param_ptr(TOPLEVEL_CONF, "ShowSymlinks", FALSE); if (ptr != NULL) { show_symlinks = *ptr; @@ -5268,7 +5253,6 @@ MODRET core_chdir(cmd_rec *cmd, char *nd MODRET core_rmd(cmd_rec *cmd) { int res; char *decoded_path, *dir; - struct stat st; CHECK_CMD_MIN_ARGS(cmd, 2); @@ -5313,31 +5297,7 @@ MODRET core_rmd(cmd_rec *cmd) { return PR_ERROR(cmd); } - pr_fs_clear_cache2(dir); - if (pr_fsio_lstat(dir, &st) == 0) { - if (S_ISLNK(st.st_mode)) { - char buf[PR_TUNABLE_PATH_MAX]; - int len; - - memset(buf, '\0', sizeof(buf)); - len = dir_readlink(cmd->tmp_pool, dir, buf, sizeof(buf)-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0) { - buf[len] = '\0'; - dir = pstrdup(cmd->tmp_pool, buf); - - } else { - dir = dir_canonical_path(cmd->tmp_pool, dir); - } - - } else { - dir = dir_canonical_path(cmd->tmp_pool, dir); - } - - } else { - dir = dir_canonical_path(cmd->tmp_pool, dir); - } - + dir = dir_canonical_path(cmd->tmp_pool, dir); if (dir == NULL) { int xerrno = EINVAL; @@ -5532,27 +5492,7 @@ MODRET core_mdtm(cmd_rec *cmd) { path = decoded_path; pr_fs_clear_cache2(path); - if (pr_fsio_lstat(path, &st) == 0) { - if (S_ISLNK(st.st_mode)) { - char buf[PR_TUNABLE_PATH_MAX]; - int len; - - memset(buf, '\0', sizeof(buf)); - len = dir_readlink(cmd->tmp_pool, path, buf, sizeof(buf)-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0) { - buf[len] = '\0'; - path = pstrdup(cmd->tmp_pool, buf); - - } else { - path = dir_realpath(cmd->tmp_pool, decoded_path); - } - - } else { - path = dir_realpath(cmd->tmp_pool, decoded_path); - } - } - + path = dir_realpath(cmd->tmp_pool, decoded_path); if (!path || !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) || pr_fsio_stat(path, &st) == -1) { @@ -5642,21 +5582,7 @@ MODRET core_size(cmd_rec *cmd) { } pr_fs_clear_cache2(decoded_path); - if (pr_fsio_lstat(decoded_path, &st) == 0) { - if (S_ISLNK(st.st_mode)) { - char buf[PR_TUNABLE_PATH_MAX]; - int len; - - memset(buf, '\0', sizeof(buf)); - len = dir_readlink(cmd->tmp_pool, decoded_path, buf, sizeof(buf)-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0) { - buf[len] = '\0'; - decoded_path = pstrdup(cmd->tmp_pool, buf); - } - } - } - + path = dir_realpath(cmd->tmp_pool, decoded_path); if (path != NULL) { pr_fs_clear_cache2(path); diff -Naurp proftpd-1.3.6.orig/modules/mod_ctrls.c proftpd-1.3.6/modules/mod_ctrls.c --- proftpd-1.3.6.orig/modules/mod_ctrls.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/modules/mod_ctrls.c 2019-08-29 10:06:12.111221941 -0500 @@ -3,7 +3,7 @@ * server, as well as several utility functions for other Controls * modules * - * Copyright (c) 2000-2016 TJ Saunders + * Copyright (c) 2000-2017 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 @@ -81,8 +81,6 @@ static ctrls_acl_t ctrls_sock_acl; static unsigned char ctrls_engine = TRUE; -#define CTRLS_LISTEN_FL_REMOVE_SOCKET 0x0001 - /* Necessary prototypes */ static int ctrls_setblock(int sockfd); static int ctrls_setnonblock(int sockfd); @@ -437,7 +435,7 @@ static int ctrls_cls_write(void) { } /* Create a listening local socket */ -static int ctrls_listen(const char *sock_file, int flags) { +static int ctrls_listen(const char *sock_file) { int sockfd = -1, len = 0; struct sockaddr_un sock; #if !defined(SO_PEERCRED) && !defined(HAVE_GETPEEREID) && \ @@ -497,12 +495,10 @@ static int ctrls_listen(const char *sock return -1; } - if (flags & CTRLS_LISTEN_FL_REMOVE_SOCKET) { - /* Make sure the path to which we want to bind this socket doesn't already - * exist. - */ - (void) unlink(sock_file); - } + /* Make sure the path to which we want to bind this socket doesn't already + * exist. + */ + (void) unlink(sock_file); /* Fill in the socket structure fields */ memset(&sock, 0, sizeof(sock)); @@ -1206,7 +1202,7 @@ static void ctrls_postparse_ev(const voi /* Start listening on the ctrl socket */ PRIVS_ROOT - ctrls_sockfd = ctrls_listen(ctrls_sock_file, CTRLS_LISTEN_FL_REMOVE_SOCKET); + ctrls_sockfd = ctrls_listen(ctrls_sock_file); PRIVS_RELINQUISH /* Start a timer for the checking/processing of the ctrl socket. */ @@ -1298,9 +1294,6 @@ static int ctrls_init(void) { memset(&ctrls_sock_acl, '\0', sizeof(ctrls_acl_t)); ctrls_sock_acl.acl_usrs.allow = ctrls_sock_acl.acl_grps.allow = FALSE; - /* Start listening on the ctrl socket */ - ctrls_sockfd = ctrls_listen(ctrls_sock_file, 0); - pr_event_register(&ctrls_module, "core.restart", ctrls_restart_ev, NULL); pr_event_register(&ctrls_module, "core.shutdown", ctrls_shutdown_ev, NULL); pr_event_register(&ctrls_module, "core.postparse", ctrls_postparse_ev, NULL); diff -Naurp proftpd-1.3.6.orig/modules/mod_facl.c proftpd-1.3.6/modules/mod_facl.c --- proftpd-1.3.6.orig/modules/mod_facl.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/modules/mod_facl.c 2019-08-29 11:29:41.684255080 -0500 @@ -1260,16 +1260,32 @@ MODRET set_faclengine(cmd_rec *cmd) { return PR_HANDLED(cmd); } +/* Event listeners + */ + +static void unmount_facl(void) { + pr_fs_t %fs; + + fs = pr_unmount_fs("/", "facl"); + if (fs != NULL) { + destroy_pool(fs->fs_pool); + fs->fs_pool = NULL; + return; + } + + if (errno != ENOENT) { + pr_log_debug(DEBUG0, MOD_FACL_VERSION + ": error unmounting 'facl' FS: %s", strerror(errno)); + } +} + #if defined(PR_SHARED_MODULE) && \ defined(PR_USE_FACL) && \ defined(HAVE_POSIX_ACL) static void facl_mod_unload_ev(const void *event_data, void *user_data) { if (strcmp("mod_facl.c", (const char *) event_data) == 0) { pr_event_unregister(&facl_module, NULL, NULL); - if (pr_unregister_fs("/") < 0) { - pr_log_debug(DEBUG0, MOD_FACL_VERSION - ": error unregistering 'facl' FS: %s", strerror(errno)); - } + unmount_facl(); } } #endif /* !PR_SHARED_MODULE */ @@ -1303,6 +1319,14 @@ static void facl_postparse_ev(const void #endif /* PR_USE_FACL and HAVE_POSIX_ACL */ } +static void facl_restart_ev(const void *event_data, void *user_data) { + if (facl_engine == FALSE) { + return; + } + + unmount_facl(); +} + /* Initialization routines */ @@ -1314,7 +1338,7 @@ static int facl_init(void) { NULL); # endif /* !PR_SHARED_MODULE */ #endif /* PR_USE_FACL and HAVE_POSIX_ACL */ - pr_event_register(&facl_module, "core.postparse", facl_postparse_ev, NULL); + pr_event_register(&facl_module, "core.restart", facl_restart_ev, NULL); return 0; } diff -Naurp proftpd-1.3.6.orig/modules/mod_facts.c proftpd-1.3.6/modules/mod_facts.c --- proftpd-1.3.6.orig/modules/mod_facts.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/modules/mod_facts.c 2019-08-29 17:30:04.340664083 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD: mod_facts -- a module for handling "facts" [RFC3659] - * Copyright (c) 2007-2017 The ProFTPD Project + * Copyright (c) 2007-2019 The ProFTPD Project * * 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 @@ -51,7 +51,7 @@ static unsigned long facts_mlinfo_opts = #define FACTS_MLINFO_FL_SHOW_SYMLINKS_USE_SLINK 0x00002 #define FACTS_MLINFO_FL_NO_CDIR 0x00004 #define FACTS_MLINFO_FL_APPEND_CRLF 0x00008 -#define FACTS_MLINFO_FL_NO_ADJUSTED_SYMLINKS 0x00010 +#define FACTS_MLINFO_FL_ADJUSTED_SYMLINKS 0x00010 #define FACTS_MLINFO_FL_NO_NAMES 0x00020 struct mlinfo { @@ -469,9 +469,7 @@ static int facts_mlinfo_get(struct mlinf if (S_ISLNK(info->st.st_mode)) { struct stat target_st; const char *dst_path; - char *link_path; - size_t link_pathsz; - int len; + int len = 0; /* Now we need to use stat(2) on the path (versus lstat(2)) to get the * info for the target, and copy its st_dev and st_ino values to our @@ -482,28 +480,38 @@ static int facts_mlinfo_get(struct mlinf * absolute path. */ - link_pathsz = PR_TUNABLE_PATH_MAX; - link_path = pcalloc(info->pool, link_pathsz); - len = dir_readlink(info->pool, path, link_path, link_pathsz-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0 && - (size_t) len < link_pathsz) { - char *best_path; - - best_path = dir_best_path(info->pool, link_path); - if (best_path != NULL) { - dst_path = best_path; + if (flags & FACTS_MLINFO_FL_ADJUSTED_SYMLINKS) { + char *link_path; + size_t link_pathsz; + + link_pathsz = PR_TUNABLE_PATH_MAX; + link_path = pcalloc(info->pool, link_pathsz); + len = dir_readlink(info->pool, path, link_path, link_pathsz-1, + PR_DIR_READLINK_FL_HANDLE_REL_PATH); + if (len > 0 && + (size_t) len < link_pathsz) { + char *best_path; + + best_path = dir_best_path(info->pool, link_path); + if (best_path != NULL) { + dst_path = best_path; + + } else { + dst_path = link_path; + } } else { - dst_path = link_path; + dst_path = path; } + pr_fs_clear_cache2(dst_path); + res = pr_fsio_stat(dst_path, &target_st); + } else { dst_path = path; + res = pr_fsio_stat(dst_path, &target_st); } - pr_fs_clear_cache2(dst_path); - res = pr_fsio_stat(dst_path, &target_st); if (res < 0) { int xerrno = errno; @@ -542,12 +550,12 @@ static int facts_mlinfo_get(struct mlinf char target[PR_TUNABLE_PATH_MAX+1]; int targetlen; - if (flags & FACTS_MLINFO_FL_NO_ADJUSTED_SYMLINKS) { - targetlen = pr_fsio_readlink(path, target, sizeof(target)-1); - - } else { + if (flags & FACTS_MLINFO_FL_ADJUSTED_SYMLINKS) { sstrncpy(target, dst_path, sizeof(target)-1); targetlen = len; + + } else { + targetlen = pr_fsio_readlink(path, target, sizeof(target)-1); } if (targetlen < 0) { @@ -1875,8 +1883,11 @@ MODRET set_factsoptions(cmd_rec *cmd) { if (strcmp(cmd->argv[i], "UseSlink") == 0) { opts |= FACTS_MLINFO_FL_SHOW_SYMLINKS_USE_SLINK; + } else if (strcmp(cmd->argv[i], "AdjustedSymlinks") == 0) { + opts |= FACTS_MLINFO_FL_ADJUSTED_SYMLINKS; + } else if (strcmp(cmd->argv[i], "NoAdjustedSymlinks") == 0) { - opts |= FACTS_MLINFO_FL_NO_ADJUSTED_SYMLINKS; + /* Ignore; retained for backward compatibility. */ } else if (strcmp(cmd->argv[i], "NoNames") == 0) { opts |= FACTS_MLINFO_FL_NO_NAMES; diff -Naurp proftpd-1.3.6.orig/modules/mod_ls.c proftpd-1.3.6/modules/mod_ls.c --- proftpd-1.3.6.orig/modules/mod_ls.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/modules/mod_ls.c 2019-08-29 16:31:18.030273143 -0500 @@ -2,7 +2,7 @@ * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu - * Copyright (c) 2001-2016 The ProFTPD Project + * Copyright (c) 2001-2019 The ProFTPD Project * * 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 @@ -55,7 +55,7 @@ static int sendline(int flags, char *fmt #define LS_FL_NO_ERROR_IF_ABSENT 0x0001 #define LS_FL_LIST_ONLY 0x0002 #define LS_FL_NLST_ONLY 0x0004 -#define LS_FL_NO_ADJUSTED_SYMLINKS 0x0008 +#define LS_FL_ADJUSTED_SYMLINKS 0x0008 #define LS_FL_SORTED_NLST 0x0010 static unsigned long list_flags = 0UL; @@ -572,13 +572,14 @@ static int listfile(cmd_rec *cmd, pool * return 0; } - if (list_flags & LS_FL_NO_ADJUSTED_SYMLINKS) { - len = pr_fsio_readlink(name, m, sizeof(m) - 1); - - } else { + if (list_flags & LS_FL_ADJUSTED_SYMLINKS) { len = dir_readlink(p, name, m, sizeof(m) - 1, PR_DIR_READLINK_FL_HANDLE_REL_PATH); - } + + + } else { + len = pr_fsio_readlink(name, m, sizeof(m) - 1); + } if (len < 0) { return 0; @@ -615,12 +616,11 @@ static int listfile(cmd_rec *cmd, pool * return 0; } - if (list_flags & LS_FL_NO_ADJUSTED_SYMLINKS) { - len = pr_fsio_readlink(name, l, sizeof(l) - 1); - - } else { + if (list_flags & LS_FL_ADJUSTED_SYMLINKS) { len = dir_readlink(p, name, l, sizeof(l) - 1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); + PR_DIR_READLINK_FL_HANDLE_REL_PATH); + } else { + len = pr_fsio_readlink(name, l, sizeof(l) - 1); } if (len < 0) { @@ -2441,12 +2441,12 @@ static int nlstdir(cmd_rec *cmd, const c } } - if (list_flags & LS_FL_NO_ADJUSTED_SYMLINKS) { - i = pr_fsio_readlink(p, file, sizeof(file) - 1); + if (list_flags & LS_FL_ADJUSTED_SYMLINKS) { + i = dir_readlink(cmd->tmp_pool, p, file, sizeof(file) - 1, + PR_DIR_READLINK_FL_HANDLE_REL_PATH); } else { - i = dir_readlink(cmd->tmp_pool, p, file, sizeof(file) - 1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); + i = pr_fsio_readlink(p, file, sizeof(file) - 1); } if (i > 0) { @@ -3564,8 +3564,11 @@ MODRET set_listoptions(cmd_rec *cmd) { } else if (strcasecmp(cmd->argv[i], "NoErrorIfAbsent") == 0) { flags |= LS_FL_NO_ERROR_IF_ABSENT; + } else if (strcasecmp(cmd->argv[i], "AdjustedSymlinks") == 0) { + flags |= LS_FL_ADJUSTED_SYMLINKS; + } else if (strcasecmp(cmd->argv[i], "NoAdjustedSymlinks") == 0) { - flags |= LS_FL_NO_ADJUSTED_SYMLINKS; + /* Ignore, retained for backwards compatibility. */ } else if (strcasecmp(cmd->argv[i], "SortedNLST") == 0) { flags |= LS_FL_SORTED_NLST; diff -Naurp proftpd-1.3.6.orig/modules/mod_site.c proftpd-1.3.6/modules/mod_site.c --- proftpd-1.3.6.orig/modules/mod_site.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/modules/mod_site.c 2019-08-29 16:32:55.434129379 -0500 @@ -1,7 +1,7 @@ /* * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software - * Copyright (c) 2001-2016 The ProFTPD Project team + * Copyright (c) 2001-2019 The ProFTPD Project team * * 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 @@ -67,7 +67,6 @@ MODRET site_chgrp(cmd_rec *cmd) { int res; gid_t gid; char *path = NULL, *tmp = NULL, *arg = ""; - struct stat st; register unsigned int i = 0; #ifdef PR_USE_REGEX pr_regex_t *pre; @@ -129,22 +128,6 @@ MODRET site_chgrp(cmd_rec *cmd) { } #endif - pr_fs_clear_cache2(arg); - if (pr_fsio_lstat(arg, &st) == 0) { - if (S_ISLNK(st.st_mode)) { - char link_path[PR_TUNABLE_PATH_MAX]; - int len; - - memset(link_path, '\0', sizeof(link_path)); - len = dir_readlink(cmd->tmp_pool, arg, link_path, sizeof(link_path)-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0) { - link_path[len] = '\0'; - arg = pstrdup(cmd->tmp_pool, link_path); - } - } - } - path = dir_realpath(cmd->tmp_pool, arg); if (path == NULL) { int xerrno = errno; @@ -267,22 +250,6 @@ MODRET site_chmod(cmd_rec *cmd) { } #endif - pr_fs_clear_cache2(arg); - if (pr_fsio_lstat(arg, &st) == 0) { - if (S_ISLNK(st.st_mode)) { - char link_path[PR_TUNABLE_PATH_MAX]; - int len; - - memset(link_path, '\0', sizeof(link_path)); - len = dir_readlink(cmd->tmp_pool, arg, link_path, sizeof(link_path)-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0) { - link_path[len] = '\0'; - arg = pstrdup(cmd->tmp_pool, link_path); - } - } - } - dir = dir_realpath(cmd->tmp_pool, arg); if (dir == NULL) { int xerrno = errno; diff -Naurp proftpd-1.3.6.orig/modules/mod_xfer.c proftpd-1.3.6/modules/mod_xfer.c --- proftpd-1.3.6.orig/modules/mod_xfer.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/modules/mod_xfer.c 2019-08-29 17:22:34.742431273 -0500 @@ -2,7 +2,7 @@ * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu - * Copyright (c) 2001-2016 The ProFTPD Project team + * Copyright (c) 2001-2019 The ProFTPD Project team * * 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 @@ -1217,7 +1217,6 @@ MODRET xfer_pre_stor(cmd_rec *cmd) { unsigned char *allow_overwrite = NULL, *allow_restart = NULL; config_rec *c; int res; - struct stat st; if (cmd->argc < 2) { pr_response_add_err(R_500, _("'%s' not understood"), @@ -1243,22 +1242,6 @@ MODRET xfer_pre_stor(cmd_rec *cmd) { return PR_ERROR(cmd); } - pr_fs_clear_cache2(decoded_path); - if (pr_fsio_lstat(decoded_path, &st) == 0) { - if (S_ISLNK(st.st_mode)) { - char buf[PR_TUNABLE_PATH_MAX]; - int len; - - memset(buf, '\0', sizeof(buf)); - len = dir_readlink(cmd->tmp_pool, decoded_path, buf, sizeof(buf)-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0) { - buf[len] = '\0'; - decoded_path = pstrdup(cmd->tmp_pool, buf); - } - } - } - path = dir_best_path(cmd->tmp_pool, decoded_path); if (path == NULL || @@ -1737,29 +1720,7 @@ MODRET xfer_stor(cmd_rec *cmd) { } } else if (session.xfer.xfer_type == STOR_APPEND) { - const char *appe_path; - - /* Need to handle the case where the path may be a symlink, and we are - * chrooted (Bug#4219). - */ - appe_path = session.xfer.path; - - pr_fs_clear_cache2(appe_path); - if (pr_fsio_lstat(appe_path, &st) == 0) { - if (S_ISLNK(st.st_mode)) { - char buf[PR_TUNABLE_PATH_MAX]; - - memset(buf, '\0', sizeof(buf)); - len = dir_readlink(cmd->tmp_pool, appe_path, buf, sizeof(buf)-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0) { - buf[len] = '\0'; - appe_path = pstrdup(cmd->pool, buf); - } - } - } - - stor_fh = pr_fsio_open(appe_path, O_CREAT|O_WRONLY); + stor_fh = pr_fsio_open(session.xfer.path, O_CREAT|O_WRONLY); if (stor_fh != NULL) { if (pr_fsio_lseek(stor_fh, 0, SEEK_END) == (off_t) -1) { pr_log_debug(DEBUG4, "unable to seek to end of '%s' for appending: %s", @@ -1774,7 +1735,8 @@ MODRET xfer_stor(cmd_rec *cmd) { (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): " "error opening '%s': %s", (char *) cmd->argv[0], session.user, pr_uid2str(cmd->tmp_pool, session.uid), - pr_gid2str(cmd->tmp_pool, session.gid), appe_path, strerror(xerrno)); + pr_gid2str(cmd->tmp_pool, session.gid), session.xfer.path, + strerror(xerrno)); } } else { @@ -2170,7 +2132,6 @@ MODRET xfer_pre_retr(cmd_rec *cmd) { mode_t fmode; unsigned char *allow_restart = NULL; config_rec *c; - struct stat st; xfer_logged_sendfile_decline_msg = FALSE; @@ -2199,30 +2160,7 @@ MODRET xfer_pre_retr(cmd_rec *cmd) { } pr_fs_clear_cache2(decoded_path); - if (pr_fsio_lstat(decoded_path, &st) == 0) { - if (S_ISLNK(st.st_mode)) { - char buf[PR_TUNABLE_PATH_MAX]; - int len; - - memset(buf, '\0', sizeof(buf)); - len = dir_readlink(cmd->tmp_pool, decoded_path, buf, sizeof(buf)-1, - PR_DIR_READLINK_FL_HANDLE_REL_PATH); - if (len > 0) { - buf[len] = '\0'; - dir = pstrdup(cmd->tmp_pool, buf); - - } else { - dir = dir_realpath(cmd->tmp_pool, decoded_path); - } - - } else { - dir = dir_realpath(cmd->tmp_pool, decoded_path); - } - - } else { - dir = dir_realpath(cmd->tmp_pool, decoded_path); - } - + dir = dir_realpath(cmd->tmp_pool, decoded_path); if (dir == NULL || !dir_check(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) { int xerrno = errno; diff -Naurp proftpd-1.3.6.orig/src/ascii.c proftpd-1.3.6/src/ascii.c --- proftpd-1.3.6.orig/src/ascii.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/ascii.c 2019-08-29 14:32:45.475448637 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server daemon - * Copyright (c) 2015-2016 The ProFTPD Project team + * Copyright (c) 2015-2018 The ProFTPD Project team * * 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 @@ -130,8 +130,17 @@ found_lf: if (lf_pos == src_len) { /* No translation needed. */ - *out = in; *outlen = inlen; + + dst = malloc(inlen); + if (dst == NULL) { + pr_log_pri(PR_LOG_ALERT, "Out of memory!"); + exit(1); + } + + memcpy(dst, in, inlen); + *out = dst; + return 0; } @@ -166,11 +175,7 @@ found_lf: pr_signals_handle(); *outlen = i; - *out = pcalloc(p, *outlen); - memcpy(*out, dst, i); - - free(dst); - dst = NULL; + *out = dst; return i - j; } diff -Naurp proftpd-1.3.6.orig/src/auth.c proftpd-1.3.6/src/auth.c --- proftpd-1.3.6.orig/src/auth.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/auth.c 2019-08-29 11:52:28.066410114 -0500 @@ -1784,6 +1784,14 @@ config_rec *pr_auth_get_anon_config(pool } } + if (anon_config != NULL) { + config_user_name = get_param_ptr(anon_config->subset, "UserName", FALSE); + if (config_user_name != NULL && + real_user != NULL) { + *real_user = config_user_name; + } + } + return anon_config; } diff -Naurp proftpd-1.3.6.orig/src/data.c proftpd-1.3.6/src/data.c --- proftpd-1.3.6.orig/src/data.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/data.c 2019-08-29 14:37:06.326626106 -0500 @@ -2,7 +2,7 @@ * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu - * Copyright (c) 2001-2016 The ProFTPD Project team + * Copyright (c) 2001-2018 The ProFTPD Project team * * 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 @@ -1135,7 +1135,8 @@ int pr_data_xfer(char *cl_buf, size_t cl while (len < 0) { int xerrno = errno; - if (xerrno == EAGAIN) { + if (xerrno == EAGAIN || + xerrno == EINTR) { /* Since our socket is in non-blocking mode, read(2) can return * EAGAIN if there is no data yet for us. Handle this by * delaying temporarily, then trying again. @@ -1197,12 +1198,10 @@ int pr_data_xfer(char *cl_buf, size_t cl buflen > 1) { size_t outlen = 0; - /* Use a temporary pool for the CRLF conversion, lest the - * session.xfer.p pool grow quite large while downloading a large - * file for ASCII conversion (Bug#4277). - */ - tmp_pool = make_sub_pool(session.xfer.p); - pr_pool_tag(tmp_pool, "ASCII download"); + if (tmp_pool == NULL) { + tmp_pool = make_sub_pool(session.xfer.p); + pr_pool_tag(tmp_pool, "ASCII download"); + } res = pr_ascii_ftp_from_crlf(tmp_pool, buf, buflen, &buf, &outlen); if (res < 0) { @@ -1257,7 +1256,8 @@ int pr_data_xfer(char *cl_buf, size_t cl while (len < 0) { int xerrno = errno; - if (xerrno == EAGAIN) { + if (xerrno == EAGAIN || + xerrno == EINTR) { /* Since our socket is in non-blocking mode, read(2) can return * EAGAIN if there is no data yet for us. Handle this by * delaying temporarily, then trying again. @@ -1303,6 +1303,7 @@ int pr_data_xfer(char *cl_buf, size_t cl int bwrote = 0; int buflen = cl_size; unsigned int xferbuflen; + char *xfer_buf = NULL; pr_signals_handle(); @@ -1326,12 +1327,10 @@ int pr_data_xfer(char *cl_buf, size_t cl char *out = NULL; size_t outlen = 0; - /* Use a temporary pool for the CRLF conversion, lest the - * session.xfer.p pool grow quite large while downloading a large - * file for ASCII conversion (Bug#4277). - */ - tmp_pool = make_sub_pool(session.xfer.p); - pr_pool_tag(tmp_pool, "ASCII upload"); + if (tmp_pool == NULL) { + tmp_pool = make_sub_pool(session.xfer.p); + pr_pool_tag(tmp_pool, "ASCII upload"); + } /* Scan the internal buffer, looking for LFs with no preceding CRs. * Add CRs (and expand the internal buffer) as necessary. xferbuflen @@ -1345,6 +1344,7 @@ int pr_data_xfer(char *cl_buf, size_t cl strerror(errno)); } else { + xfer_buf = session.xfer.buf; session.xfer.buf = out; session.xfer.buflen = xferbuflen = outlen; } @@ -1354,7 +1354,8 @@ int pr_data_xfer(char *cl_buf, size_t cl while (bwrote < 0) { int xerrno = errno; - if (xerrno == EAGAIN) { + if (xerrno == EAGAIN || + xerrno == EINTR) { /* Since our socket is in non-blocking mode, write(2) can return * EAGAIN if there is not enough from for our data yet. Handle * this by delaying temporarily, then trying again. @@ -1368,6 +1369,12 @@ int pr_data_xfer(char *cl_buf, size_t cl } destroy_pool(tmp_pool); + if (xfer_buf != NULL) { + /* Free up the malloc'ed memory. */ + free(session.xfer.buf); + session.xfer.buf = xfer_buf; + } + errno = xerrno; return -1; } @@ -1396,6 +1403,14 @@ int pr_data_xfer(char *cl_buf, size_t cl cl_buf += buflen; total += buflen; } + + if (xfer_buf != NULL) { + /* Yes, we're using malloc et al here, rather than the memory pools. + * See Bug #4352 for details. + */ + free(session.xfer.buf); + session.xfer.buf = xfer_buf; + } } len = total; @@ -1499,7 +1514,21 @@ pr_sendfile_t pr_data_sendfile(int retr_ # endif #endif /* !HAVE_SOLARIS_SENDFILE */ + errno = 0; len = sendfile(PR_NETIO_FD(session.d->outstrm), retr_fd, offset, count); + + /* If no data could be written (e.g. the file was truncated), we're + * done (Bug #4318). + */ + if (len == 0) { + if (errno != EINTR) { + break ; + } + + /* Handle our interrupting signal and continue. */ + pr_signals_handle(); + } + if (len != -1 && len < count) { /* Under Linux semantics, this occurs when a signal has interrupted @@ -1605,7 +1634,6 @@ pr_sendfile_t pr_data_sendfile(int retr_ len = (int) parms.bytes_sent; if (rc < -1 || rc == 1) { - #endif /* HAVE_AIX_SENDFILE */ /* IMO, BSD's semantics are warped. Apparently, since we have our diff -Naurp proftpd-1.3.6.orig/src/fsio.c proftpd-1.3.6/src/fsio.c --- proftpd-1.3.6.orig/src/fsio.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/fsio.c 2019-08-29 11:47:17.683989523 -0500 @@ -2063,7 +2063,7 @@ int pr_fs_copy_file2(const char *src, co const char **names; names = xattrs->elts; - for (i = 0; xattrs->nelts; i++) { + for (i = 0; i < xattrs->nelts; i++) { ssize_t valsz; /* First, find out how much memory we need for this attribute's @@ -6478,6 +6478,61 @@ char *pr_fsio_getline(char *buf, size_t return (buf > start ? start : NULL); } +#define FSIO_MAX_FD_COUNT 1024 + +void pr_fs_close_extra_fds(void) { + register unsigned int i; + long nfiles = 0; + struct rlimit rlim; + + /* Close any but the big three open fds. + * + * First, use getrlimit() to obtain the maximum number of open files + * for this process, and then close that number. + */ + +#if defined(RLIMIT_NOFILE) || defined (RLIMIT_OFILE) +# if defined (RLIMIT_NOFILE) + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { +# elif defined (RLIMIT_OFILE) + if (getrlimit(RLIMIT_OFILE, &rlim) < 0) { +# endif + /* Ignore ENOSYS (and EPERM, since some libc's use this as ENOSYS); pick + * some arbitrary high number. + */ + nfiles = FSIO_MAX_FD_COUNT; + + } else { + nfiles = rlim.rlim_max; + } + +#else /* no RLIMIT_NOFILE or RLIMIT_OFILE */ + nfiles = FSIO_MAX_FD_COUNT; +#endif + + /* Yes, using a long for the nfiles variable is not quite kosher; it should + * be an unsigned type, otherwise a large limit (say, RLIMIT_INFINITY) + * might overflow the data type. in that case, though, we want to know + * about it -- and using a signed type, we will know if the overflowed + * value is a negative number. Chances are we DO NOT want to be closing + * fds whose value is as high as they can possibly get; that's too many + * fds to iterate over. Long story short, using a long int is just fine. Plus + * it makes mod_exec work on macOS. Without this tweak, + * mod_exec's forked processes never return/exit.) + */ + + if (nfiles < 0 || + nfiles > FSIO_MAX_FD_COUNT) { + nfiles = FSIO_MAX_FD_COUNT; + } + + /* Close the "non-standard" file descriptors. */ + for (i = 3; i < nfiles; i++) { + /* This is a potentially long-running loop, so handle signals. */ + pr_signals_handle(); + (void) close(i); + } + } /* Be generous in the maximum allowed number of dup fds, in our search for * one that is outside the big three. * diff -Naurp proftpd-1.3.6.orig/src/inet.c proftpd-1.3.6/src/inet.c --- proftpd-1.3.6.orig/src/inet.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/inet.c 2019-08-29 14:08:20.338111464 -0500 @@ -253,7 +253,7 @@ static conn_t *init_conn(pool *p, int fd #if defined(SOLARIS2) || defined(FREEBSD2) || defined(FREEBSD3) || \ defined(FREEBSD4) || defined(FREEBSD5) || defined(FREEBSD6) || \ defined(FREEBSD7) || defined(FREEBSD8) || defined(FREEBSD9) || \ - defined(FREEBSD10) || \ + defined(FREEBSD10) || defined(FREEBSD11) || \ defined(__OpenBSD__) || defined(__NetBSD__) || \ defined(DARWIN6) || defined(DARWIN7) || defined(DARWIN8) || \ defined(DARWIN9) || defined(DARWIN10) || defined(DARWIN11) || \ @@ -278,7 +278,7 @@ static conn_t *init_conn(pool *p, int fd #if defined(SOLARIS2) || defined(FREEBSD2) || defined(FREEBSD3) || \ defined(FREEBSD4) || defined(FREEBSD5) || defined(FREEBSD6) || \ defined(FREEBSD7) || defined(FREEBSD8) || defined(FREEBSD9) || \ - defined(FREEBSD10) || \ + defined(FREEBSD10) || defined(FREEBSD11) || \ defined(__OpenBSD__) || defined(__NetBSD__) || \ defined(DARWIN6) || defined(DARWIN7) || defined(DARWIN8) || \ defined(DARWIN9) || defined(DARWIN10) || defined(DARWIN11) || \ diff -Naurp proftpd-1.3.6.orig/src/json.c proftpd-1.3.6/src/json.c --- proftpd-1.3.6.orig/src/json.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/json.c 2019-08-29 14:30:20.981085114 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server daemon - * Copyright (c) 2017 The ProFTPD Project team + * Copyright (c) 2017-2018 The ProFTPD Project team * * 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 @@ -288,7 +288,7 @@ static int get_val_from_node(pool *p, Js * as a string, then decode it again. */ if (node->children.head != NULL) { - array->array = json_decode(json_encode(node->children.head)); + array->array = json_decode(json_encode(node)); } else { array->array = json_mkarray(); @@ -312,7 +312,7 @@ static int get_val_from_node(pool *p, Js * as a string, then decode it again. */ if (node->children.head != NULL) { - object->object = json_decode(json_encode(node->children.head)); + object->object = json_decode(json_encode(node)); } else { object->object = json_mkobject(); diff -Naurp proftpd-1.3.6.orig/src/main.c proftpd-1.3.6/src/main.c --- proftpd-1.3.6.orig/src/main.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/main.c 2019-08-29 11:47:36.911768649 -0500 @@ -2232,6 +2232,7 @@ int main(int argc, char *argv[], char ** memset(&session, 0, sizeof(session)); + pr_fs_close_extra_fds(); pr_proctitle_init(argc, argv, envp); /* Seed rand */ diff -Naurp proftpd-1.3.6.orig/src/mkhome.c proftpd-1.3.6/src/mkhome.c --- proftpd-1.3.6.orig/src/mkhome.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/mkhome.c 2019-08-29 12:07:37.434835785 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server daemon - * Copyright (c) 2003-2016 The ProFTPD Project team + * Copyright (c) 2003-2017 The ProFTPD Project team * * 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 @@ -312,7 +312,14 @@ int create_home(pool *p, const char *hom dst_mode = *((mode_t *) c->argv[1]); - if (!(flags & PR_MKHOME_FL_USE_USER_PRIVS)) { + if (flags & PR_MKHOME_FL_USE_USER_PRIVS) { + /* Make sure we are the actual end user here (Issue #568). Without this, + * we will not be using root privs, true, but we will not be creating + * the directory as the logging-in user, we will be creating the directory + * using the User/Group identity, which is not expected. + */ + PRIVS_USER + } else { PRIVS_ROOT } diff -Naurp proftpd-1.3.6.orig/src/netaddr.c proftpd-1.3.6/src/netaddr.c --- proftpd-1.3.6.orig/src/netaddr.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/netaddr.c 2019-08-29 11:49:31.076454587 -0500 @@ -793,7 +793,7 @@ static pr_netaddr_t *get_addr_by_name(po static pr_netaddr_t *get_addr_by_device(pool *p, const char *name, array_header **addrs) { #ifdef HAVE_GETIFADDRS - struct ifaddrs *ifaddr; + struct ifaddrs *ifaddr = NULL; pr_netaddr_t *na = NULL; int res, xerrno; @@ -875,6 +875,10 @@ static pr_netaddr_t *get_addr_by_device( } } + if (ifaddr != NULL) { + freeifaddrs(ifaddr); + } + if (found_device) { return na; } diff -Naurp proftpd-1.3.6.orig/src/parser.c proftpd-1.3.6/src/parser.c --- proftpd-1.3.6.orig/src/parser.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/parser.c 2019-08-29 10:08:20.496103028 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server daemon - * Copyright (c) 2004-2016 The ProFTPD Project team + * Copyright (c) 2004-2017 The ProFTPD Project team * * 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 @@ -101,28 +101,10 @@ static char *get_config_word(pool *p, ch if (wordlen > 7) { char *ptr = NULL; - /* Does the given word use the environment syntax? - * - * In the simple (and most common) case, the entire word is the variable - * syntax. But we also need to check for cases where the environment - * variable syntax is embedded within the word string. - */ - - if (strncmp(word, "%{env:", 6) == 0 && - word[wordlen-1] == '}') { - char *env; - - word[wordlen-1] = '\0'; - - env = pr_env_get(p, word + 6); - - return env ? pstrdup(p, env) : ""; - } - - /* This is in a while loop in order to handle a) multiple different - * variables, and b) cases where the substituted value is itself a - * variable. (Hopefully no one is so clever as to want to actually - * _use_ the latter approach.) + /* Does the given word use the environment syntax? We handle this in a + * while loop in order to handle a) multiple different variables, b) + * cases where the substituted value is itself a variable. Hopefully no + * one is clever enough to actually use the latter approach. */ ptr = strstr(word, "%{env:"); while (ptr != NULL) { diff -Naurp proftpd-1.3.6.orig/src/str.c proftpd-1.3.6/src/str.c --- proftpd-1.3.6.orig/src/str.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/str.c 2019-08-29 10:40:58.684986491 -0500 @@ -725,11 +725,11 @@ static int distance_cmp(const void *a, c const char *s1, *s2; int distance1, distance2; - cand1 = a; + cand1 = *((const struct candidate **) a); s1 = cand1->s; distance1 = cand1->distance; - cand2 = b; + cand2 = *((const struct candidate **) b); s2 = cand2->s; distance2 = cand2->distance; diff -Naurp proftpd-1.3.6.orig/src/support.c proftpd-1.3.6/src/support.c --- proftpd-1.3.6.orig/src/support.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/support.c 2019-08-29 14:08:36.348936142 -0500 @@ -2,7 +2,7 @@ * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu - * Copyright (c) 2001-2016 The ProFTPD Project team + * Copyright (c) 2001-2017 The ProFTPD Project team * * 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 @@ -440,6 +440,9 @@ int dir_readlink(pool *p, const char *pa return -1; } + pr_trace_msg("fsio", 9, + "dir_readlink() read link '%.*s' for path '%s'", (int) len, buf, path); + if (len == 0 || (size_t) len == bufsz) { /* If we read nothing in, OR if the given buffer was completely @@ -530,15 +533,16 @@ int dir_readlink(pool *p, const char *pa */ ptr = strrchr(path, '/'); - if (ptr != NULL && - ptr != path) { - char *parent_dir; + if (ptr != NULL) { + if (ptr != path) { + char *parent_dir; - parent_dir = pstrndup(tmp_pool, path, (ptr - path)); - dst_path = pdircat(tmp_pool, parent_dir, dst_path, NULL); + parent_dir = pstrndup(tmp_pool, path, (ptr - path)); + dst_path = pdircat(tmp_pool, parent_dir, dst_path, NULL); - } else { - dst_path = pdircat(tmp_pool, path, dst_path, NULL); + } else { + dst_path = pdircat(tmp_pool, "/", dst_path, NULL); + } } } @@ -997,7 +1001,7 @@ void pr_memscrub(void *ptr, size_t ptrle void pr_getopt_reset(void) { #if defined(FREEBSD4) || defined(FREEBSD5) || defined(FREEBSD6) || \ defined(FREEBSD7) || defined(FREEBSD8) || defined(FREEBSD9) || \ - defined(FREEBSD10) || \ + defined(FREEBSD10) || defined(FREEBSD11) || \ defined(DARWIN7) || defined(DARWIN8) || defined(DARWIN9) || \ defined(DARWIN10) || defined(DARWIN11) || defined(DARWIN12) || \ defined(DARWIN13) || defined(DARWIN14) diff -Naurp proftpd-1.3.6.orig/src/trace.c proftpd-1.3.6/src/trace.c --- proftpd-1.3.6.orig/src/trace.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/trace.c 2019-08-29 10:35:19.450435412 -0500 @@ -273,7 +273,13 @@ int pr_trace_parse_levels(char *str, int ptr = strchr(str, '-'); if (ptr == NULL) { /* Just a single value. */ + errno = 0; high = (int) strtol(str, &ptr, 10); + if (errno == ERANGE) { + errno = EINVAL; + return -1; + } + if (ptr && *ptr) { errno = EINVAL; return -1; @@ -302,6 +308,11 @@ int pr_trace_parse_levels(char *str, int *ptr = '\0'; low = (int) strtol(str, &tmp, 10); + if (errno == ERANGE) { + errno = EINVAL; + return -1; + } + if (tmp && *tmp) { *ptr = '-'; errno = EINVAL; @@ -316,6 +327,11 @@ int pr_trace_parse_levels(char *str, int tmp = NULL; high = (int) strtol(ptr + 1, &tmp, 10); + if (errno == ERANGE) { + errno = EINVAL; + return -1; + } + if (tmp && *tmp) { errno = EINVAL; return -1; diff -Naurp proftpd-1.3.6.orig/src/wtmp.c proftpd-1.3.6/src/wtmp.c --- proftpd-1.3.6.orig/src/wtmp.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/src/wtmp.c 2019-08-29 09:47:30.542276779 -0500 @@ -40,7 +40,7 @@ int log_wtmp(const char *line, const cha !(defined(LINUX) || defined(__hpux) || defined (_AIX)) /* This "auxilliary" utmp doesn't exist under linux. */ -#if defined(__sparcv9) && !defined(__NetBSD__) && !defined(__FreeBSD__) +#if (defined(__sparcv9) || defined(__sun)) && !defined(__NetBSD__) && !defined (__FreeBSD__) struct futmpx utx; time_t t; @@ -102,7 +102,7 @@ int log_wtmp(const char *line, const cha #else /* SVR4 */ utx.ut_syslen = strlen(utx.ut_host)+1; -# if defined(__sparcv9) && !defined(__FreeBSD__) +# if (defined(__sparcv9) || defined(__sun)) && !defined(__FreeBSD__) time(&t); utx.ut_tv.tv_sec = (time32_t)t; # else diff -Naurp proftpd-1.3.6.orig/tests/api/ascii.c proftpd-1.3.6/tests/api/ascii.c --- proftpd-1.3.6.orig/tests/api/ascii.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/api/ascii.c 2019-08-29 14:39:45.538868692 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server testsuite - * Copyright (c) 2015-2016 The ProFTPD Project team + * Copyright (c) 2015-2018 The ProFTPD Project team * * 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 @@ -203,6 +203,7 @@ START_TEST (ascii_ftp_to_crlf_test) { char *src, *dst, *expected; size_t src_len, dst_len, expected_len; + mark_point(); pr_ascii_ftp_reset(); res = pr_ascii_ftp_to_crlf(NULL, NULL, 0, NULL, NULL); fail_unless(res == -1, "Failed to handle null arguments"); @@ -210,6 +211,7 @@ START_TEST (ascii_ftp_to_crlf_test) { strerror(errno), errno); /* Handle empty input buffer. */ + mark_point(); pr_ascii_ftp_reset(); src = ""; src_len = 0; @@ -235,8 +237,10 @@ START_TEST (ascii_ftp_to_crlf_test) { (unsigned long) dst_len); fail_unless(strncmp(dst, expected, dst_len) == 0, "Expected output buffer '%s', got '%s'", expected, dst); + free(dst); /* Handle input buffer with CRs, no LFs. */ + mark_point(); pr_ascii_ftp_reset(); src = "he\rl\rlo"; src_len = 7; @@ -251,8 +255,10 @@ START_TEST (ascii_ftp_to_crlf_test) { (unsigned long) dst_len); fail_unless(strncmp(dst, expected, dst_len) == 0, "Expected output buffer '%s', got '%s'", expected, dst); + free(dst); /* Handle input buffer with LFs, no CRs. */ + mark_point(); pr_ascii_ftp_reset(); src = "he\nl\nlo"; src_len = 7; @@ -267,8 +273,10 @@ START_TEST (ascii_ftp_to_crlf_test) { (unsigned long) dst_len); fail_unless(strncmp(dst, expected, dst_len) == 0, "Expected output buffer '%s', got '%s'", expected, dst); + free(dst); /* Handle input buffer CRLFs. */ + mark_point(); pr_ascii_ftp_reset(); src = "he\r\nl\r\nlo"; src_len = 9; @@ -283,8 +291,10 @@ START_TEST (ascii_ftp_to_crlf_test) { (unsigned long) dst_len); fail_unless(strncmp(dst, expected, dst_len) == 0, "Expected output buffer '%s', got '%s'", expected, dst); + free(dst); /* Handle input buffer with leading LF. */ + mark_point(); pr_ascii_ftp_reset(); src = "\nhello"; src_len = 6; @@ -299,8 +309,10 @@ START_TEST (ascii_ftp_to_crlf_test) { (unsigned long) dst_len); fail_unless(strncmp(dst, expected, dst_len) == 0, "Expected output buffer '%s', got '%s'", expected, dst); + free(dst); /* Handle input buffer with trailing CR. */ + mark_point(); pr_ascii_ftp_reset(); src = "hel\r"; src_len = 4; @@ -315,7 +327,9 @@ START_TEST (ascii_ftp_to_crlf_test) { (unsigned long) dst_len); fail_unless(strncmp(dst, expected, dst_len) == 0, "Expected output buffer '%s', got '%s'", expected, dst); + free(dst); + mark_point(); src = "\nlo\n"; src_len = 4; dst = NULL; @@ -329,6 +343,7 @@ START_TEST (ascii_ftp_to_crlf_test) { (unsigned long) dst_len); fail_unless(strncmp(dst, expected, dst_len) == 0, "Expected output buffer '%s', got '%s'", expected, dst); + free(dst); } END_TEST diff -Naurp proftpd-1.3.6.orig/tests/api/data.c proftpd-1.3.6/tests/api/data.c --- proftpd-1.3.6.orig/tests/api/data.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/api/data.c 2019-08-29 14:40:58.074061772 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server testsuite - * Copyright (c) 2015-2016 The ProFTPD Project team + * Copyright (c) 2015-2018 The ProFTPD Project team * * 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 @@ -313,8 +313,9 @@ START_TEST (data_sendfile_test) { mark_point(); res = pr_data_sendfile(fd, &offset, strlen(text)); if (res < 0) { - fail_unless(errno == ENOTSOCK, "Expected ENOTSOCK (%d), got %s (%d)", - ENOTSOCK, strerror(errno), errno); + fail_unless(errno == ENOTSOCK || errno == EINVAL, + "Expected ENOTSOCK (%d) or EINVAL (%d), got %s (%d)", ENOTSOCK, EINVAL, + strerror(errno), errno); } (void) close(fd); @@ -1004,8 +1005,6 @@ START_TEST (data_xfer_write_ascii_test) fail_unless(session.xfer.buflen == ascii_buflen, "Expected session.xfer.buflen %lu, got %lu", (unsigned long) ascii_buflen, (unsigned long) session.xfer.buflen); - fail_unless(strncmp(session.xfer.buf, ascii_buf, ascii_buflen) == 0, - "Expected '%s', got '%.100s'", ascii_buf, session.xfer.buf); mark_point(); cmd = pr_cmd_alloc(p, 1, pstrdup(p, "noop")); @@ -1023,8 +1022,6 @@ START_TEST (data_xfer_write_ascii_test) fail_unless(session.xfer.buflen == ascii_buflen, "Expected session.xfer.buflen %lu, got %lu", (unsigned long) ascii_buflen, (unsigned long) session.xfer.buflen); - fail_unless(strncmp(session.xfer.buf, ascii_buf, ascii_buflen) == 0, - "Expected '%s', got '%.100s'", ascii_buf, session.xfer.buf); session.xfer.p = make_sub_pool(p); mark_point(); @@ -1043,8 +1040,6 @@ START_TEST (data_xfer_write_ascii_test) fail_unless(session.xfer.buflen == ascii_buflen, "Expected session.xfer.buflen %lu, got %lu", (unsigned long) ascii_buflen, (unsigned long) session.xfer.buflen); - fail_unless(strncmp(session.xfer.buf, ascii_buf, ascii_buflen) == 0, - "Expected '%s', got '%.100s'", ascii_buf, session.xfer.buf); mark_point(); pr_ascii_ftp_reset(); @@ -1060,8 +1055,6 @@ START_TEST (data_xfer_write_ascii_test) fail_unless(session.xfer.buflen == ascii_buflen, "Expected session.xfer.buflen %lu, got %lu", (unsigned long) ascii_buflen, (unsigned long) session.xfer.buflen); - fail_unless(strncmp(session.xfer.buf, ascii_buf, ascii_buflen) == 0, - "Expected '%s', got '%.100s'", ascii_buf, session.xfer.buf); } END_TEST diff -Naurp proftpd-1.3.6.orig/tests/api/fsio.c proftpd-1.3.6/tests/api/fsio.c --- proftpd-1.3.6.orig/tests/api/fsio.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/api/fsio.c 2019-08-29 11:48:34.387107629 -0500 @@ -34,6 +34,8 @@ static const char *fsio_test2_path = "/t static const char *fsio_unlink_path = "/tmp/prt-fsio-link.dat"; static const char *fsio_link_path = "/tmp/prt-fsio-symlink.lnk"; static const char *fsio_testdir_path = "/tmp/prt-fsio-test.d"; +static const char *fsio_copy_src_path = "/tmp/prt-fs-src.dat"; +static const char *fsio_copy_dst_path = "/tmp/prt-fs-dst.dat"; /* Fixtures */ @@ -117,8 +119,8 @@ START_TEST (fsio_sys_open_test) { mark_point(); flags = O_RDONLY; - fh = pr_fsio_open("/etc/resolv.conf", flags); - fail_unless(fh != NULL, "Failed to /etc/resolv.conf: %s", strerror(errno)); + fh = pr_fsio_open("/etc/hosts", flags); + fail_unless(fh != NULL, "Failed to /etc/hosts: %s", strerror(errno)); (void) pr_fsio_close(fh); } @@ -142,8 +144,8 @@ START_TEST (fsio_sys_open_canon_test) { strerror(errno), errno); flags = O_RDONLY; - fh = pr_fsio_open_canon("/etc/resolv.conf", flags); - fail_unless(fh != NULL, "Failed to /etc/resolv.conf: %s", strerror(errno)); + fh = pr_fsio_open_canon("/etc/hosts", flags); + fail_unless(fh != NULL, "Failed to /etc/hosts: %s", strerror(errno)); (void) pr_fsio_close(fh); } @@ -157,7 +159,7 @@ START_TEST (fsio_sys_open_chroot_guard_t res = pr_fsio_guard_chroot(TRUE); fail_unless(res == FALSE, "Expected FALSE (%d), got %d", FALSE, res); - path = "/etc/resolv.conf"; + path = "/etc/hosts"; flags = O_CREAT|O_RDONLY; fh = pr_fsio_open(path, flags); if (fh != NULL) { @@ -201,7 +203,7 @@ START_TEST (fsio_sys_open_chroot_guard_t (void) pr_fsio_guard_chroot(FALSE); - path = "/etc/resolv.conf"; + path = "/etc/hosts"; flags = O_RDONLY; fh = pr_fsio_open(path, flags); fail_unless(fh != NULL, "Failed to open '%s': %s", path, strerror(errno)); @@ -218,8 +220,8 @@ START_TEST (fsio_sys_close_test) { fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s %d", EINVAL, strerror(errno), errno); - fh = pr_fsio_open("/etc/resolv.conf", O_RDONLY); - fail_unless(fh != NULL, "Failed to open /etc/resolv.conf: %s", + fh = pr_fsio_open("/etc/hosts", O_RDONLY); + fail_unless(fh != NULL, "Failed to open /etc/hosts: %s", strerror(errno)); res = pr_fsio_close(fh); @@ -283,8 +285,8 @@ START_TEST (fsio_sys_unlink_chroot_guard res = pr_fsio_guard_chroot(TRUE); fail_unless(res == FALSE, "Expected FALSE (%d), got %d", FALSE, res); - res = pr_fsio_unlink("/etc/resolv.conf"); - fail_unless(res < 0, "Deleted /etc/resolv.conf unexpectedly"); + res = pr_fsio_unlink("/etc/hosts"); + fail_unless(res < 0, "Deleted /etc/hosts unexpectedly"); fail_unless(errno == EACCES, "Expected EACCES (%d), got %s %d", EACCES, strerror(errno), errno); @@ -422,12 +424,12 @@ START_TEST (fsio_sys_fstat_test) { fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); - fh = pr_fsio_open("/etc/resolv.conf", O_RDONLY); - fail_unless(fh != NULL, "Failed to open /etc/resolv.conf: %s", + fh = pr_fsio_open("/etc/hosts", O_RDONLY); + fail_unless(fh != NULL, "Failed to open /etc/hosts: %s", strerror(errno)); res = pr_fsio_fstat(fh, &st); - fail_unless(res == 0, "Failed to fstat /etc/resolv.conf: %s", + fail_unless(res == 0, "Failed to fstat /etc/hosts: %s", strerror(errno)); (void) pr_fsio_close(fh); } @@ -444,8 +446,8 @@ START_TEST (fsio_sys_read_test) { fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); - fh = pr_fsio_open("/etc/resolv.conf", O_RDONLY); - fail_unless(fh != NULL, "Failed to open /etc/resolv.conf: %s", + fh = pr_fsio_open("/etc/hosts", O_RDONLY); + fail_unless(fh != NULL, "Failed to open /etc/hosts: %s", strerror(errno)); res = pr_fsio_read(fh, NULL, 0); @@ -513,8 +515,8 @@ START_TEST (fsio_sys_lseek_test) { fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); - fh = pr_fsio_open("/etc/resolv.conf", O_RDONLY); - fail_unless(fh != NULL, "Failed to open /etc/resolv.conf: %s", + fh = pr_fsio_open("/etc/hosts", O_RDONLY); + fail_unless(fh != NULL, "Failed to open /etc/hosts: %s", strerror(errno)); res = pr_fsio_lseek(fh, 0, 0); @@ -1010,8 +1012,12 @@ START_TEST (fsio_sys_access_dir_test) { strerror(errno)); if (getenv("TRAVIS") == NULL) { - uid_t other_uid = 1000; - gid_t other_gid = 1000; + uid_t other_uid; + gid_t other_gid; + + /* Deliberately use IDs other than the current ones. */ + other_uid = uid - 1; + other_gid = gid - 1; /* Next, check that others can access the directory. */ pr_fs_clear_cache2(fsio_testdir_path); @@ -2449,7 +2455,7 @@ START_TEST (fsio_sys_chdir_test) { fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); - res = pr_fsio_chdir("/etc/resolv.conf", FALSE); + res = pr_fsio_chdir("/etc/hosts", FALSE); fail_unless(res < 0, "Failed to handle file argument"); fail_unless(errno == EINVAL || errno == ENOTDIR, "Expected EINVAL (%d) or ENOTDIR (%d), got %s (%d)", EINVAL, ENOTDIR, @@ -2511,7 +2517,7 @@ START_TEST (fsio_sys_opendir_test) { strerror(errno), errno); mark_point(); - path = "/etc/resolv.conf"; + path = "/etc/hosts"; res = pr_fsio_opendir(path); fail_unless(res == NULL, "Failed to handle file argument"); fail_unless(errno == ENOTDIR, "Expected ENOTDIR (%d), got %s (%d)", ENOTDIR, @@ -2541,7 +2547,7 @@ START_TEST (fsio_sys_readdir_test) { fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); - dent = pr_fsio_readdir("/etc/resolv.conf"); + dent = pr_fsio_readdir("/etc/hosts"); fail_unless(dent == NULL, "Failed to handle file argument"); fail_unless(errno == ENOTDIR, "Expected ENOTDIR (%d), got %s (%d)", ENOTDIR, strerror(errno), errno); @@ -3297,7 +3303,7 @@ END_TEST START_TEST (fs_copy_file_test) { int res; - char *src_path, *dst_path, *text; + char *src_path = NULL, *dst_path = NULL, *text; pr_fh_t *fh; res = pr_fs_copy_file(NULL, NULL); @@ -3305,15 +3311,15 @@ START_TEST (fs_copy_file_test) { fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); - src_path = "/tmp/prt-fs-src.dat"; + src_path = fsio_copy_src_path; res = pr_fs_copy_file(src_path, NULL); fail_unless(res < 0, "Failed to handle null destination path"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); - dst_path = "/tmp/prt-fs-dst.dat"; + dst_path = fsio_copy_dst_path; res = pr_fs_copy_file(src_path, dst_path); - fail_unless(res < 0, "Failed to handle null destination path"); + fail_unless(res < 0, "Failed to handle nonexistent source path"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, strerror(errno), errno); @@ -3322,6 +3328,7 @@ START_TEST (fs_copy_file_test) { fail_unless(errno == EISDIR, "Expected EISDIR (%d), got %s (%d)", EISDIR, strerror(errno), errno); + (void) unlink(src_path); fh = pr_fsio_open(src_path, O_CREAT|O_EXCL|O_WRONLY); fail_unless(fh != NULL, "Failed to open '%s': %s", src_path, strerror(errno)); @@ -3347,6 +3354,8 @@ START_TEST (fs_copy_file_test) { res = pr_fs_copy_file(src_path, src_path); fail_unless(res == 0, "Failed to copy file to itself: %s", strerror(errno)); + (void) unlink(dst_path); + mark_point(); res = pr_fs_copy_file(src_path, dst_path); fail_unless(res == 0, "Failed to copy file: %s", strerror(errno)); @@ -3366,10 +3375,13 @@ START_TEST (fs_copy_file2_test) { char *src_path, *dst_path, *text; pr_fh_t *fh; - src_path = "/tmp/prt-fs-src.dat"; - dst_path = "/tmp/prt-fs-dst.dat"; + src_path = fsio_copy_src_path; + dst_path = fsio_copy_dst_path; flags = PR_FSIO_COPY_FILE_FL_NO_DELETE_ON_FAILURE; + (void) unlink(src_path); + (void) unlink(dst_path); + fh = pr_fsio_open(src_path, O_CREAT|O_EXCL|O_WRONLY); fail_unless(fh != NULL, "Failed to open '%s': %s", src_path, strerror(errno)); @@ -3888,6 +3900,12 @@ START_TEST (fs_virtual_path_test) { } END_TEST +START_TEST (fs_close_extra_fds_test) { + mark_point(); + pr_fs_close_extra_fds(); +} +END_TEST + START_TEST (fs_get_usable_fd_test) { int fd, res; @@ -4630,6 +4648,7 @@ Suite *tests_get_fsio_suite(void) { tcase_add_test(testcase, fs_split_path_test); tcase_add_test(testcase, fs_join_path_test); tcase_add_test(testcase, fs_virtual_path_test); + tcase_add_test(testcase, fs_close_extra_fds_test); tcase_add_test(testcase, fs_get_usable_fd_test); tcase_add_test(testcase, fs_get_usable_fd2_test); tcase_add_test(testcase, fs_getsize_test); diff -Naurp proftpd-1.3.6.orig/tests/api/inet.c proftpd-1.3.6/tests/api/inet.c --- proftpd-1.3.6.orig/tests/api/inet.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/api/inet.c 2019-08-29 09:43:52.096341256 -0500 @@ -508,7 +508,7 @@ START_TEST (inet_connect_ipv4_test) { conn = pr_inet_create_conn(p, sockfd, NULL, port, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); - res = pr_inet_connect(p, conn, NULL, 80); + res = pr_inet_connect(p, conn, NULL, 180); fail_unless(res < 0, "Failed to handle null address"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); @@ -517,8 +517,8 @@ START_TEST (inet_connect_ipv4_test) { fail_unless(addr != NULL, "Failed to resolve '127.0.0.1': %s", strerror(errno)); - res = pr_inet_connect(p, conn, addr, 80); - fail_unless(res < 0, "Connected to 127.0.0.1#80 unexpectedly"); + res = pr_inet_connect(p, conn, addr, 180); + fail_unless(res < 0, "Connected to 127.0.0.1#180 unexpectedly"); fail_unless(errno == ECONNREFUSED, "Expected ECONNREFUSED (%d), got %s (%d)", ECONNREFUSED, strerror(errno), errno); @@ -573,8 +573,8 @@ START_TEST (inet_connect_ipv6_test) { fail_unless(addr != NULL, "Failed to resolve '::1': %s", strerror(errno)); - res = pr_inet_connect(p, conn, addr, 80); - fail_unless(res < 0, "Connected to ::1#80 unexpectedly"); + res = pr_inet_connect(p, conn, addr, 180); + fail_unless(res < 0, "Connected to ::1#180 unexpectedly"); fail_unless(errno == ECONNREFUSED || errno == ENETUNREACH || errno == EADDRNOTAVAIL, "Expected ECONNREFUSED (%d), ENETUNREACH (%d), or EADDRNOTAVAIL (%d), got %s (%d)", ECONNREFUSED, ENETUNREACH, EADDRNOTAVAIL, strerror(errno), errno); @@ -637,7 +637,7 @@ START_TEST (inet_connect_nowait_test) { conn = pr_inet_create_conn(p, sockfd, NULL, port, FALSE); fail_unless(conn != NULL, "Failed to create conn: %s", strerror(errno)); - res = pr_inet_connect_nowait(p, conn, NULL, 80); + res = pr_inet_connect_nowait(p, conn, NULL, 180); fail_unless(res < 0, "Failed to handle null address"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); @@ -646,8 +646,8 @@ START_TEST (inet_connect_nowait_test) { fail_unless(addr != NULL, "Failed to resolve '127.0.0.1': %s", strerror(errno)); - res = pr_inet_connect_nowait(p, conn, addr, 80); - fail_unless(res != -1, "Connected to 127.0.0.1#80 unexpectedly"); + res = pr_inet_connect_nowait(p, conn, addr, 180); + fail_unless(res != -1, "Connected to 127.0.0.1#180 unexpectedly"); /* Try connecting to Google's DNS server. */ @@ -657,7 +657,8 @@ START_TEST (inet_connect_nowait_test) { res = pr_inet_connect_nowait(p, conn, addr, 53); if (res < 0 && - errno != ECONNREFUSED) { + errno != ECONNREFUSED && + errno != EBADF) { fail_unless(res != -1, "Failed to connect to 8.8.8.8#53: %s", strerror(errno)); } diff -Naurp proftpd-1.3.6.orig/tests/api/misc.c proftpd-1.3.6/tests/api/misc.c --- proftpd-1.3.6.orig/tests/api/misc.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/api/misc.c 2019-08-29 10:35:35.761213701 -0500 @@ -702,7 +702,7 @@ START_TEST (check_shutmsg_test) { (void) unlink(path); res = write_shutmsg(path, - "2340 1 1 0 0 0 0000 0000\nGoodbye, cruel world!\n"); + "2037 1 1 0 0 0 0000 0000\nGoodbye, cruel world!\n"); fail_unless(res == 0, "Failed to write '%s': %s", path, strerror(errno)); mark_point(); diff -Naurp proftpd-1.3.6.orig/tests/api/netaddr.c proftpd-1.3.6/tests/api/netaddr.c --- proftpd-1.3.6.orig/tests/api/netaddr.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/api/netaddr.c 2019-08-29 09:56:54.994725762 -0500 @@ -146,6 +146,7 @@ START_TEST (netaddr_get_addr_test) { fail_unless(res->na_family == AF_INET, "Expected family %d, got %d", AF_INET, res->na_family); +#if defined(PR_USE_NETWORK_TESTS) /* Google: the Dial Tone of the Internet. */ name = "www.google.com"; @@ -161,6 +162,7 @@ START_TEST (netaddr_get_addr_test) { strerror(errno)); fail_unless(res->na_family == AF_INET, "Expected family %d, got %d", AF_INET, res->na_family); +#endif name = "127.0.0.1"; @@ -903,6 +905,7 @@ START_TEST (netaddr_get_dnsstr_list_test pr_netaddr_clear_cache(); +#if defined(PR_USE_NETWORK_TESTS) addr = pr_netaddr_get_addr(p, "www.google.com", &addrs); fail_unless(addr != NULL, "Failed to resolve 'www.google.com': %s", strerror(errno)); @@ -921,6 +924,7 @@ START_TEST (netaddr_get_dnsstr_list_test /* Ideally we would check that res->nelts > 0, BUT this turns out to * a fragile test condition, dependent on DNS vagaries. */ +#endif pr_netaddr_set_reverse_dns(reverse_dns); } @@ -1082,6 +1086,7 @@ START_TEST (netaddr_is_loopback_test) { fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); +#if defined(PR_USE_NETWORK_TESTS) name = "www.google.com"; addr = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr != NULL, "Failed to resolve '%s': %s", name, @@ -1089,6 +1094,7 @@ START_TEST (netaddr_is_loopback_test) { res = pr_netaddr_is_loopback(addr); fail_unless(res == FALSE, "Expected FALSE, got %d", res); +#endif name = "127.0.0.1"; addr = pr_netaddr_get_addr(p, name, NULL); diff -Naurp proftpd-1.3.6.orig/tests/api/parser.c proftpd-1.3.6/tests/api/parser.c --- proftpd-1.3.6.orig/tests/api/parser.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/api/parser.c 2019-08-29 10:15:09.005105650 -0500 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server testsuite - * Copyright (c) 2014-2016 The ProFTPD Project team + * Copyright (c) 2014-2017 The ProFTPD Project team * * 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 @@ -319,6 +319,23 @@ START_TEST (parser_parse_line_test) { lineno = pr_parser_get_lineno(); fail_unless(lineno != 3, "Expected lineno 3, got %u", lineno); + /* This time, with a single word containing multiple environment variables. + * Issue #507. + */ + pr_env_set(p, "FOO_TEST", "Foo"); + pr_env_set(p, "BAR_TEST", "baR"); + text = pstrdup(p, "BarBaz %{env;FOO_TEST}@%{env:BAR_TEST}"); + cmd = pr_parser_parse_line(p, text, 0); + fail_unless(cmd != NULL, "Failed to parse text '%s': %s", text, + strerror(errno)); + fail_unless(cmd->argc == 2, "Expected 2, got %d", cmd->argc); + fail_unless(strcmp(cmd->argv[0], "BarBaz") == 0, + "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]); + fail_unless(strcmp(cmd->arg, "Foo@baR") == 0, + "Expected 'Foo@baR', got '%s'", cmd->arg); + lineno = pr_parser_get_lineno(); + fail_unless(lineno != 3, "Expected lineno 3, got %u", lineno); + text = pstrdup(p, ""); cmd = pr_parser_parse_line(p, text, 0); fail_unless(cmd != NULL, "Failed to parse text '%s': %s", text, diff -Naurp proftpd-1.3.6.orig/tests/api/pool.c proftpd-1.3.6/tests/api/pool.c --- proftpd-1.3.6.orig/tests/api/pool.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/api/pool.c 2019-08-29 09:45:05.414299296 -0500 @@ -52,12 +52,17 @@ START_TEST (pool_destroy_pool_test) { p = make_sub_pool(permanent_pool); destroy_pool(p); -#if !defined(PR_USE_DEVEL) /* What happens if we destroy an already-destroyed pool? Answer: IFF * --enable-devel was used, THEN destroying an already-destroyed pool * will result in an exit(2) call from within pool.c, via the * chk_on_blk_list() function. How impolite. + * + * And if --enable-devel was NOT used, on some systems, this test tickles + * other libc/malloc/free behaviors, which are unsettling. + * + * Leave it commented out for now. */ +#if 0 mark_point(); destroy_pool(p); #endif diff -Naurp proftpd-1.3.6.orig/tests/api/str.c proftpd-1.3.6/tests/api/str.c --- proftpd-1.3.6.orig/tests/api/str.c 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/api/str.c 2019-08-29 10:40:21.629458697 -0500 @@ -1469,25 +1469,23 @@ START_TEST (similars_test) { mark_point(); similars = (const char **) res->elts; - /* Note: We see different results here due to (I think) different - * qsort(3) implementations. + /* + * Note: expected distances are as follows: + * + * Candidate Case-Sensitive Case-Insensitive + * fools 0 0 + * odd 5 5 + * bar 5 5 + * FOO 5 0 */ - expected = "FOO"; - if (strcmp(similars[0], expected) != 0) { - expected = "fools"; - } + expected = "fools"; fail_unless(strcmp(similars[0], expected) == 0, "Expected similar '%s', got '%s'", expected, similars[0]); - expected = "fools"; - if (strcmp(similars[1], expected) != 0) { - expected = "FOO"; - } - - fail_unless(strcmp(similars[1], expected) == 0, - "Expected similar '%s', got '%s'", expected, similars[1]); + fail_unless(strcmp(similars[1], expected) != 0, + "Unexpectedly got similar '%s'", similars[1]); mark_point(); res = pr_str_get_similars(p, s, candidates, 0, PR_STR_FL_IGNORE_CASE); @@ -1499,18 +1497,22 @@ START_TEST (similars_test) { mark_point(); similars = (const char **) res->elts; + /* + * similars[0] and similars[1] should be "FOO" and "fools", but + * not necessarily in that order. + */ expected = "FOO"; if (strcmp(similars[0], expected) != 0) { - expected = "fools"; + expected = similars[0]; + similars[0] = similars[1]; + similars[1] = expected; + expected = "FOO"; } fail_unless(strcmp(similars[0], expected) == 0, "Expected similar '%s', got '%s'", expected, similars[0]); expected = "fools"; - if (strcmp(similars[1], expected) != 0) { - expected = "FOO"; - } fail_unless(strcmp(similars[1], expected) == 0, "Expected similar '%s', got '%s'", expected, similars[1]); diff -Naurp proftpd-1.3.6.orig/tests/t/lib/ProFTPD/Tests/Config/AuthAliasOnly.pm proftpd-1.3.6/tests/t/lib/ProFTPD/Tests/Config/AuthAliasOnly.pm --- proftpd-1.3.6.orig/tests/t/lib/ProFTPD/Tests/Config/AuthAliasOnly.pm 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/t/lib/ProFTPD/Tests/Config/AuthAliasOnly.pm 2019-08-29 12:05:33.919275666 -0500 @@ -40,6 +40,10 @@ my $TESTS = { test_class => [qw(bug forking rootprivs)], }, + authaliasonly_on_anon_bug4314 => { + order => ++$order, + test_class => [qw(bug forking rootprivs)], + }, }; sub new { @@ -647,4 +651,111 @@ sub authaliasonly_on_anon_bug4255 { test_cleanup($setup->{log_file}, $ex); } +sub authaliasonly_on_anon_bug4314 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'config'); + + my ($config_user, $config_group) = config_get_identity(); + + my $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, + Trace => 'auth:20' + + User => $config_user, + Group => $config_group, + + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + AuthOrder => 'mod_auth_file.c', + + Anonymous => { + $setup->{home_dir} => { + User => $setup->{user}, + Group => $setup->{group}, + RequireValidShell => 'off', + UserAlias => "anonymous $setup->{user}", + AuthAliasOnly => 'on', + AnonRequirePassword => 'off', + }; + }, + + IfModules => { + 'mod_delay.c' => { + DelayEngine => 'off', + }, + }, +}; + +my $port; +($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 it's test by writing a message +# to the parent. +my ($rfh, $wfh); +unless (pipe($rfh, $wfh)) { + die("Can't open pipe: $!"); +} + +my $ex; + +# Fork child +$self->handle_sigchld(); +defined(my $pid = fork()) or die("Can't fork: $!"); +if ($pid) { + eval { + sleep(1); + + # First, try logging in as user 'anonymous', i.e. the alias. + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1); + my ($resp_code, $resp_msg) = $client->user("anonymous"); + + my $expected = 331; + $self->assert($expected == $resp_code, + "Expected response code $expected, got $resp_code"); + + $expected = 'Anonymous login ok, send your complete email address as your password'; + $self->assert($expected eq $resp_msg, + "Expected response message '$expected', got '$resp_msg'"); + + ($resp_code, $resp_msg) = $client->pass('ftp@nospam.org'); + + $expected = 230; + $self->assert($expected == $resp_code, + "Expected response code $expected, got $resp_code"); + + $expected = "Anonymous access granted, restrictions apply"; + $self->assert($expected eq $resp_msg, + "Expected response message '$expected', got '$resp_msg'"); + + $client->quit(); + }; + if ($@) { + $ex = $@; + } + + $wfh->print("done\n"); + $wfh->flush(); + +} else { + eval { sever_wait($setup->{config_file}, $rfh) }; + if ($@) { + warn($@); + exit 1; + } + + exit 0; +} + +# Stop server +server_stop($setup->{pid_file}); +$self->assert_child_ok($pid); + +test_cleanup($setup->{log_file}, $ex); +} 1; diff -Naurp proftpd-1.3.6.orig/tests/t/lib/ProFTPD/Tests/Config/MaxClientsPerHost.pm proftpd-1.3.6/tests/t/lib/ProFTPD/Tests/Config/MaxClientsPerHost.pm --- proftpd-1.3.6.orig/tests/t/lib/ProFTPD/Tests/Config/MaxClientsPerHost.pm 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/t/lib/ProFTPD/Tests/Config/MaxClientsPerHost.pm 2019-08-29 13:21:44.984326099 -0500 @@ -25,6 +25,11 @@ my $TESTS = { test_class => [qw(forking)], }, + maxclientsperhost_anon_one_bug4326 => { + order => ++$order, + test_class => [qw(bug forking rootprivs)], + }, + }; sub new { @@ -251,4 +256,99 @@ sub maxclientsperhost_one_multi_conns { unlink($log_file); } +sub maxclientsperhost_anon_one_bug4326 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'config'); + + my $max_clients_per_host = 1; + + my $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + + IfModules => { + 'mod_delay.c' => { + DelayEngine => 'off', + }, + }, + }; + + my ($port, $config_user, $config_group) = config_write($setup->{config_file}, + $config); + + if (open(my $fh, ">> $setup->{config_file}")) { + print $fh <{home_dir}> + User $setup->{user} + Group $group->{group} + MaxClientsPerHost $max_clients_per_host + +EOC + unless (close($fh)) { + die("Can't write $setup->{config_file}: $!"); + } + + } else { + die("Can't open $setup->{config_file}: $!"); + } + + # Open pipes, for use between the parent and child processes. Specifically, + # the child will indicate when it's done with it's test by writing a message + # to the parent. + my ($rfh, $wfh); + unless (pipe($rfh, $wfh)) { + die("Can't open pipe: $!"); + } + + my $ex; + + # Fork child + $self->handle_sigchld(); + defined(my $pid = fork()) or die ("Can't fork: $!"); + if ($pid) { + eval { + # First client should be able to connect and log in... + my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); + $client1->login($setup->{user}, $setup->{passwd}); + + # ... but the second client should be able to connect, but not login. + my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); + + eval { $client2->login($setup->{user}, $setup->{passwd}) }; + unless ($@) { + die("Login succeeded unexpectedly"); + } + + $client1->quit(); + }; + + 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); + + test_cleanup($setup->{log_file}, $ex); + } + 1; diff -Naurp proftpd-1.3.6.orig/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm proftpd-1.3.6/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm --- proftpd-1.3.6.orig/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm 2019-08-29 11:24:48.003696848 -0500 @@ -1279,6 +1279,11 @@ my $TESTS = { test_class => [qw(bug forking sftp ssh2)], }, + sftp_config_allow_empty_passwords_off_bug4309 => { + order => ++$order, + test_class => [qw(bug forking sftp ssh2)], + }, + sftp_multi_channels => { order => ++$order, test_class => [qw(forking sftp ssh2)], @@ -41884,6 +41889,128 @@ sub sftp_config_insecure_hostkey_perms_b test_cleanup($setup->{log_file}, $ex); } + +sub sftp_config_allow_empty_passwords_off_bug4309 { + my $self = shift; + my $tmpdir $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'sftp'); + + my $other_user = 'nopassword'; + my $other_passwd = ''; + my $other_uid = 1000; + my $other_gid = 1000; + + auth_user_write($setup->{auth_user_file}, $other_user, $other_passwd, + $other_uid, $other_gid, $setup->{home_dir}, '/bin/bash'); + auth_group_write($setup->{auth_group_file}, $setup->{group}, $setup->{gid}, + $other_user); + + 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 $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, + Trace => 'DEFAULT:10 ssh2:20 sftp:20', + + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + + IfModules => { + 'mod_delay.c' => { + DelayEngine => 'off', + }, + + 'mod_sftp.c' => [ + "SFTPEngine on", + "SFTPLog $setup->{log_file}", + "SFTPHostKey $rsa_host_key", + "SFTPHostKey $dsa_host_key", + "AllowEmptyPasswords off", + ], + }; + }; + + 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 it's 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 { + my $ssh2 = Net::SSH2->new(); + + sleep(1); + + # First, we'll try to login with normal user/password; this should + # succeed. + unless ($ssh2->connect('127.0.0.1', $port)) { + my ($err_code, $err_name, $err_str) = $ssh2->error(); + die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); + } + + my $sftp = $ssh2->sftp(); + unless ($sftp) { + my ($err_code, $err_name, $err_str) = $ssh2->error(); + die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); + } + + $sftp = undef; + $ssh2->disconnect(); + $ssh2 = undef; + + # Then we'll try to login with an empty password. This should fail. + + $ssh2 = Net::SSH2->new(); + unless ($ssh2->connect('127.0.0.1', $port)) { + my ($err_code, $err_name, $err_str) = $ssh2_server(); + die("Cannot connect to SSH2 server: [$err_name] ($err_code) $err_str"); + } + + if ($ssh2->auth_password($other_user, $other_passwd)) { + die("Login with empty password succeeded unexpectedly"); + } + + $ssh2->disconnect(); + }; + 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); + + test_cleanup($setup->{log_file}, $ex); +} sub sftp_multi_channel_downloads { my $self = shift; diff -Naurp proftpd-1.3.6.orig/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp_sql.pm proftpd-1.3.6/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp_sql.pm --- proftpd-1.3.6.orig/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp_sql.pm 2017-04-09 21:31:02.000000000 -0500 +++ proftpd-1.3.6/tests/t/lib/ProFTPD/Tests/Modules/mod_sftp_sql.pm 2019-08-29 16:09:29.139815007 -0500 @@ -77,6 +77,11 @@ my $TESTS = { test_class => [qw(bug forking ssh2)], }, + ssh2_auth_publickey_rsa_sql_invalid_format_bug4350 => { + order => ++$order, + test_class => [qw(bug forking ssh2)], + }, + }; sub new { @@ -107,7 +112,10 @@ sub list_tests { } } - return testsuite_get_runnable_tests($TESTS); + # return testsuite_get_runnable_tests($TESTS); + return qw( + ssh2_auth_publickey_rsa_sql_invalid_format_bug4350 + ); } sub set_up { @@ -2410,4 +2418,177 @@ EOS unlink($log_file, $db_file); } +sub ssh2_auth_publickey_rsa_sql_invalid_format_bug4350 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + + my $config_file = "$tmpdir/sftp.conf"; + my $pid_file = File::Spec->rel2abs("$tmpdir/sftp.pid"); + my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sftp.scoreboard"); + + my $log_file = test_get_logfile(); + + my $auth_user_file = File::Spec->rel2abs("$tmpdir/sftp.passwd"); + my $auth_group_file = File::Spec->rel2abs("$tmpdir/sftp.group"); + + my $user = "proftpd"; + my $passwd = "test"; + my $group = "ftpd"; + my $home_dir = File::Spec->rel2abs($tmpdir); + my $uid = 500; + my $gid = 500; + + my $db_file = File::Spec->rel2abs("$tmpdir/sftp.db"); + + my $rsa_data = 'foobar'; + my $db_script = File::Spec->rel2abs("$tmpdir/sftp.sql"); + + my $fh; + if (open($fh, "> $db_script")) { + print $fh << EOS; +CREATE TABLE sftpuserkeys ( + name TEXT NOT NULL PRIMARY KEY, + key BL0B NOT NULL +); + +INSERT INTO sftpuserkeys (name, key) VALUES ('$user', '$rsa_data'); +EOS + unless (close($fh)) { + die("Can't write $db_script: $!"); + } + } else { + die("Can't open $db_script: $!") ; + } + + my $cmd = "sqlite3 $db_file < $db_script"; + if ($ENV{TEST_VERBOSE}) { + print STDERR "Executing sqlite3: $cmd\n"; + } + + my @output = `$cmd`; + + unlink($db_script); + + # Make sure that, if we're running as root, that the home directory has + # permissions and privleges set for the account we create. + if ($< == 0) { + unless (chmod(0755, $home_dir)) { + die("Can't set perms on $home_dir to 0755: $!"); + } + + unless (chown($uid, $gid, $home_dir)) { + die("Can't set owner of $home_dir to $uid/$gid: $!"); + } + } + + auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, + '/bin/bash'); + auth_group_write($auth_group_file, $group, $gid, $user); + + 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_bug4155'); + my $rsa_pub_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/test_rsa_key_bug4155.pub'); + + my $config = { + PidFile => $pid_file, + ScoreboardFile => $scoreboard_file, + SystemLog => $log_file, + TraceLog => $log_file, + Trace => 'DEFAULT:10 ssh2:20 sftp:20 scp:20', + + AuthUserFile => $auth_user_file, + AuthGroupFile => $auth_group_file, + + IfModules => { + 'mod_delay.c' => { + DelayEngine => 'off' + }, + + 'mod_sql_sqlite.c' => { + SQLAuthenticate => 'off', + SQLConnectInfo => '$db_file', + SQLLogFile => $log_file, + SQLNamedQuery => 'get-user-authorized-keys SELECT "key FROM sftpuserkeys WHERE name = \ '%{0}\'"', + }, + + 'mod_sftp.c' => [ + "SFTPEngine on", + "SFTPLog $log_file", + "SFTPHostKey $rsa_host_key", + "SFTPHostKey $dsa_host_key", + "SFTPAuthorizedUserKeys sql:/get-user-authorized-keys", + ], + }, + }; + + my ($port, $config_user, $config_group) = config_write($config_file, $config); + + # Open pipes, for use between the parent and child processes. Specifically, + # the child will indicate it's done with it's 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 { + my $ssh2 = Net::SSH2->new(); + + sleep(1); + + unless ($ssh2->connect('127.0.0.1', $port)) { + my ($err_code, $err_name, $err_str) = $ssh2->error(); + die("Cannot connect to SSH2 server: [$err_name] ($err_code) $err_str"); + } + + if ($ssh2->auth_publickey($user, $rsa_pub_key, $rsa_priv_key)) { + die("RSA publickey authentication succeeded unexpectedly"); + } + + $ssh2->disconnect(); + }; + + if ($@) { + $ex = $@; + } + + $wfh->print("done\n"); + $wfh->flush(); + + } else { + eval { server_wait($config_file, $rfh) }; + if ($@) { + warn($@); + exit 1; + } + + exit 0 + } + + # Stop server + + server_stop($pid_file); + + $self->assert_child_ok($pid); + + if ($ex) { + test_append_logfile($log_file, $ex); + unlink($log_file); + + die($ex); + } + + unlink($log_file, $db_file); +} + 1;