[proftpd-dfsg] 01/03: New upstream version 1.3.5b

Francesco Lovergine frankie at moszumanska.debian.org
Sat Dec 10 20:29:16 UTC 2016


This is an automated email from the git hooks/post-receive script.

frankie pushed a commit to branch master
in repository proftpd-dfsg.

commit 94ac34911477ee35402f5de62b6fc182b59bf016
Author: Francesco Paolo Lovergine <frankie at debian.org>
Date:   Sat Dec 10 20:47:53 2016 +0100

    New upstream version 1.3.5b
---
 Make.rules.in                  |   1 +
 NEWS                           |  23 ++++-
 RELEASE_NOTES                  |   7 ++
 configure                      |  22 ++++-
 configure.in                   |  22 ++++-
 contrib/dist/rpm/proftpd.spec  |   2 +-
 contrib/mod_geoip.c            |  24 ++---
 contrib/mod_ldap.c             |   9 ++
 contrib/mod_sftp/fxp.c         |  53 ++++++++---
 contrib/mod_sftp/kbdint.c      |   8 +-
 contrib/mod_sftp/kex.c         | 201 ++++++++++++++++++++++++++++++-----------
 contrib/mod_sftp/keys.c        |  45 +++++----
 contrib/mod_sftp/mod_sftp.c    |   8 +-
 contrib/mod_sftp/mod_sftp.h.in |   4 +-
 contrib/mod_sftp/scp.c         | 119 ++++++++++++++----------
 contrib/mod_sftp/utf8.c        |   6 +-
 contrib/mod_sftp_pam.c         |   2 +-
 contrib/mod_sql_mysql.c        |  12 ++-
 contrib/mod_tls.c              | 181 +++++++++++++++++++++++++++++++------
 include/version.h              |   4 +-
 lib/Makefile.in                |   4 +-
 lib/tpl.c                      |   4 -
 modules/Makefile.in            |   4 +-
 modules/mod_auth.c             |  36 +++++---
 modules/mod_core.c             |   4 +-
 modules/mod_facts.c            |  69 +++++++-------
 modules/mod_log.c              |  26 ++++--
 modules/mod_ls.c               |  54 +++++++++++
 modules/mod_xfer.c             |  37 +++++---
 src/Makefile.in                |   4 +-
 src/data.c                     |  30 ++++--
 src/encode.c                   |   8 +-
 src/fsio.c                     |  27 +++++-
 src/ftpscrub.c                 | 145 -----------------------------
 src/inet.c                     |   2 +
 src/main.c                     |  11 ++-
 src/pool.c                     |  59 ++++++------
 src/support.c                  |   6 +-
 tests/api/fsio.c               |  16 ++++
 tests/api/str.c                |   3 +-
 utils/Makefile.in              |   4 +-
 41 files changed, 841 insertions(+), 465 deletions(-)

diff --git a/Make.rules.in b/Make.rules.in
index 9f48eb4..32796d7 100644
--- a/Make.rules.in
+++ b/Make.rules.in
@@ -20,6 +20,7 @@ LDFLAGS=@LDFLAGS@ @LIBDIRS@
 LIBEXECDIR=@LIBEXECDIR@
 LIBS=@LIBS@ @LIBRARIES@
 LIBTOOL=@LIBTOOL@
+MAKEDEPEND=makedepend -Y
 RANLIB=@RANLIB@
 
 CURSES_LIBS=@CURSES_LIBS@
diff --git a/NEWS b/NEWS
index 795323c..3c06cfd 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,3 @@
-$Id: NEWS,v 1.1597 2014-05-15 16:24:13 castaglia Exp $
 
 -----------------------------------------------------------------------------
   More details on the bugs listed below can be found by using the bug number
@@ -9,6 +8,28 @@ $Id: NEWS,v 1.1597 2014-05-15 16:24:13 castaglia Exp $
   where `N' is the bug number.
 -----------------------------------------------------------------------------
 
+1.3.5b - Released 10-Mar-2016
+--------------------------------
+- Bug 4187 - mod_geoip does not load all of the GeoIPTables properly.
+- Bug 4191 - "Incorrect string value" reported by mod_sql_mysql for some UTF8
+  characters.
+- Bug 4097 - SSH rekey fails when using RSA hostkey smaller than 2048 bits.
+- Bug 4198 - MLSD/MLST fact type "cdir" is incorrectly used for the current
+  working directory.
+- Bug 4201 - HiddenStores temporary files not removed when exceeding quota
+  using SCP.
+- Bug 4202 - MLSD lines not properly terminated with CRLF.
+- Bug 4209 - Zero-length memory allocation possible, with undefined results.
+- Bug 4210 - Avoid unbounded SFTP extended attribute key/values.
+- Bug 4212 - Ensure that FTP data transfer commands fail appropriately when
+  "RootRevoke on" is in effect.
+- Bug 4217 - Handle FTP re-authentication attempts better.
+- Bug 4223 - Permissions on files uploaded via STOU do not honor configured
+  Umask.
+- Bug 4227 - Support SFTP clients that send multiple INIT requests.
+- Bug 4230 - TLSDHParamFile directive appears ignored because unexpected DH is
+  chosen.
+
 1.3.5a - Released 27-May-2015
 --------------------------------
 - Bug 4055 - "error setting listen fd IPV6_TCLASS: Protocol not available" log
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index bc2c7a7..e075dc7 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -6,6 +6,13 @@ This file contains a description of the major changes to ProFTPD for the
 releases.  More information on these changes can be found in the NEWS and
 ChangeLog files.
 
+1.3.5b
+---------
+
+  + SSH RSA hostkeys smaller than 2048 bits now work properly.
+  + MLSD response lines are now properly CRLF terminated.
+
+
 1.3.5a
 ---------
 
diff --git a/configure b/configure
index 070be4e..8152a82 100755
--- a/configure
+++ b/configure
@@ -38007,6 +38007,7 @@ rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
       conftest$ac_exeext conftest.$ac_ext
   LIBS="$saved_libs"
 
+  pr_use_pthread_for_openssl="no"
   if test x"$openssl_cmdline" != xno; then
     if `$openssl_cmdline version 2>/dev/null 1>&2`; then
       openssl_cflags=`$openssl_cmdline version -f 2>/dev/null`
@@ -38015,11 +38016,30 @@ rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
         # with threads support (see Bug#3795)
         for openssl_cflag in $openssl_cflags; do
           if test x"$openssl_cflag" = x"-pthread"; then
-            LIBS="$LIBS -pthread"
+            pr_use_pthread_for_openssl="yes"
           fi
         done
+
+        if test x"$pr_use_pthread_for_openssl" = xno ; then
+          # If we're on FreeBSD, AND if OpenSSL is being used, AND if
+          # openssl version -f shows no flags, then ASSUME that we do need
+          # the -pthread flag, to avoid regressions of Bug#3795.
+          if test `echo $ostype | grep -c FREEBSD` != "0" ; then
+            pr_use_pthread_for_openssl="yes"
+          fi
+        fi
       fi
     fi
+  else
+    # If we're on FreeBSD, AND if OpenSSL is being used, then ASSUME that we
+    # do need the -pthread flag, to avoid regressions of Bug#3795.
+    if test `echo $ostype | grep -c DFREEBSD` != "0" ; then
+      pr_use_pthread_for_openssl="yes"
+    fi
+  fi
+
+  if test x"$pr_use_pthread_for_openssl" = xyes ; then
+    LIBS="$LIBS -pthread"
   fi
 fi
 
diff --git a/configure.in b/configure.in
index be7e6ca..dc27852 100644
--- a/configure.in
+++ b/configure.in
@@ -2665,6 +2665,7 @@ if test x"$pr_use_openssl" = xyes; then
   )
   LIBS="$saved_libs"
 
+  pr_use_pthread_for_openssl="no"
   if test x"$openssl_cmdline" != xno; then
     if `$openssl_cmdline version 2>/dev/null 1>&2`; then
       openssl_cflags=`$openssl_cmdline version -f 2>/dev/null`
@@ -2673,11 +2674,30 @@ if test x"$pr_use_openssl" = xyes; then
         # with threads support (see Bug#3795)
         for openssl_cflag in $openssl_cflags; do 
           if test x"$openssl_cflag" = x"-pthread"; then
-            LIBS="$LIBS -pthread"
+            pr_use_pthread_for_openssl="yes"
           fi
         done
+
+        if test x"$pr_use_pthread_for_openssl" = xno ; then
+          # If we're on FreeBSD, AND if OpenSSL is being used, AND if
+          # openssl version -f shows no flags, then ASSUME that we do need
+          # the -pthread flag, to avoid regressions of Bug#3795.
+          if test `echo $ostype | grep -c FREEBSD` != "0" ; then
+            pr_use_pthread_for_openssl="yes"
+          fi
+        fi
       fi
     fi
+  else
+    # If we're on FreeBSD, AND if OpenSSL is being used, then ASSUME that we
+    # do need the -pthread flag, to avoid regressions of Bug#3795.
+    if test `echo $ostype | grep -c DFREEBSD` != "0" ; then
+      pr_use_pthread_for_openssl="yes"
+    fi
+  fi
+
+  if test x"$pr_use_pthread_for_openssl" = xyes ; then
+    LIBS="$LIBS -pthread"
   fi
 fi
 
diff --git a/contrib/dist/rpm/proftpd.spec b/contrib/dist/rpm/proftpd.spec
index 98c5f25..8b4328e 100644
--- a/contrib/dist/rpm/proftpd.spec
+++ b/contrib/dist/rpm/proftpd.spec
@@ -50,7 +50,7 @@
 #
 # NOTE: rpmbuild is really bloody stupid, and CANNOT handle a leading '#'
 # character followed by a '%' character.  
-%global release_cand_version      a
+%global release_cand_version      b
 
 %global usecvsversion             0%{?_with_cvs:1}
 
diff --git a/contrib/mod_geoip.c b/contrib/mod_geoip.c
index 9936dce..a0ec648 100644
--- a/contrib/mod_geoip.c
+++ b/contrib/mod_geoip.c
@@ -1,7 +1,7 @@
 /*
  * ProFTPD: mod_geoip -- a module for looking up country/city/etc for clients
  *
- * Copyright (c) 2010-2014 TJ Saunders
+ * Copyright (c) 2010-2015 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
@@ -36,7 +36,7 @@
  * module for Apache.
  */
 
-#define MOD_GEOIP_VERSION		"mod_geoip/0.6"
+#define MOD_GEOIP_VERSION		"mod_geoip/0.7"
 
 /* Make sure the version of proftpd is as necessary. */
 #if PROFTPD_VERSION_NUMBER < 0x0001030402
@@ -388,7 +388,8 @@ static const char *get_geoip_filter_value(int filter_id) {
   return NULL;
 }
 
-static void get_geoip_tables(array_header *geoips, int filter_flags) {
+static void get_geoip_tables(array_header *geoips, int filter_flags,
+    int skip_standard) {
   config_rec *c;
 
   c = find_config(main_server->conf, CONF_PARAM, "GeoIPTable", FALSE);
@@ -406,8 +407,9 @@ static void get_geoip_tables(array_header *geoips, int filter_flags) {
     /* Make sure we open tables that are marked with the default
      * GEOIP_STANDARD flag, which has a value of zero.
      */
-    if ((flags == GEOIP_STANDARD && filter_flags != GEOIP_STANDARD) || 
-        !(flags & filter_flags)) {
+    if (flags == GEOIP_STANDARD && skip_standard == TRUE) { 
+      pr_trace_msg(trace_channel, 15,
+        "skipping loading GeoIP table '%s'", path);
       c = find_config_next(c, c->next, CONF_PARAM, "GeoIPTable", FALSE);
       continue;
     } 
@@ -638,8 +640,8 @@ static void get_geoip_data(array_header *geoips, const char *ip_addr) {
 
       case GEOIP_REGION_EDITION_REV0:
       case GEOIP_REGION_EDITION_REV1: {
-        GeoIPRegion *geoip_region;
-        const char *region_name, *tz;
+        GeoIPRegion *geoip_region = NULL;
+        const char *region_name = NULL, *tz = NULL;
 
         geoip_region = GeoIP_region_by_addr(gis[i], ip_addr);
 #ifdef PR_USE_IPV6
@@ -681,7 +683,7 @@ static void get_geoip_data(array_header *geoips, const char *ip_addr) {
 
       case GEOIP_CITY_EDITION_REV0:
       case GEOIP_CITY_EDITION_REV1: {
-        GeoIPRecord *geoip_record;
+        GeoIPRecord *geoip_record = NULL;
         char area_code_str[32], lat_str[64], lon_str[64];
 
         geoip_record = GeoIP_record_by_addr(gis[i], ip_addr);
@@ -1128,7 +1130,7 @@ MODRET set_geoiptable(cmd_rec *cmd) {
         use_utf8 = TRUE;
 
       } else {
-        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown GeoIP flag '",
+        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown GeoIPTable flag '",
           cmd->argv[i], "'", NULL));
       }
     }
@@ -1194,7 +1196,7 @@ static void geoip_postparse_ev(const void *event_data, void *user_data) {
   filter_flags = GEOIP_MEMORY_CACHE|GEOIP_MMAP_CACHE|GEOIP_INDEX_CACHE;
 
   pr_log_debug(DEBUG8, MOD_GEOIP_VERSION ": loading static GeoIP tables");
-  get_geoip_tables(static_geoips, filter_flags);
+  get_geoip_tables(static_geoips, filter_flags, TRUE);
 }
 
 static void geoip_restart_ev(const void *event_data, void *user_data) {
@@ -1283,7 +1285,7 @@ static int geoip_sess_init(void) {
   sess_geoips = make_array(tmp_pool, 0, sizeof(GeoIP *));
 
   pr_log_debug(DEBUG8, MOD_GEOIP_VERSION ": loading session GeoIP tables");
-  get_geoip_tables(sess_geoips, GEOIP_STANDARD|GEOIP_CHECK_CACHE);
+  get_geoip_tables(sess_geoips, GEOIP_CHECK_CACHE, FALSE);
 
   if (static_geoips->nelts == 0 &&
       sess_geoips->nelts == 0) {
diff --git a/contrib/mod_ldap.c b/contrib/mod_ldap.c
index 915c743..e613da5 100644
--- a/contrib/mod_ldap.c
+++ b/contrib/mod_ldap.c
@@ -64,6 +64,15 @@ static char *ldap_server;
 static int ldap_port = LDAP_PORT;
 #endif
 
+/* On some systems LDAP_OPT_DIAGNOSTIC_MESSAGE isn't there (e.g. OpenLDAP-2.3.x)
+ * but LDAP_OPT_ERROR_STRING is.
+ */
+#ifndef LDAP_OPT_DIAGNOSTIC_MESSAGE
+# ifdef LDAP_OPT_ERROR_STRING
+#  define LDAP_OPT_DIAGNOSTIC_MESSAGE LDAP_OPT_ERROR_STRING
+# endif
+#endif
+
 #if LDAP_API_VERSION >= 2000
 # define LDAP_VALUE_T struct berval
 # define LDAP_GET_VALUES(ld, entry, attr) ldap_get_values_len(ld, entry, attr)
diff --git a/contrib/mod_sftp/fxp.c b/contrib/mod_sftp/fxp.c
index 5d9ae17..82080bb 100644
--- a/contrib/mod_sftp/fxp.c
+++ b/contrib/mod_sftp/fxp.c
@@ -235,6 +235,12 @@ static size_t fxp_packet_data_allocsz = 0;
 #define FXP_PACKET_DATA_DEFAULT_SZ		(1024 * 16)
 #define FXP_RESPONSE_DATA_DEFAULT_SZ		512
 
+#define FXP_MAX_PACKET_LEN			(1024 * 512)
+#define FXP_MAX_EXTENDED_ATTRIBUTES		100
+
+/* Maximum length of SFTP extended attribute name OR value. */
+#define FXP_MAX_EXTENDED_ATTR_LEN		1024
+
 struct fxp_extpair {
   char *ext_name;
   uint32_t ext_datalen;
@@ -1240,6 +1246,14 @@ static struct fxp_extpair *fxp_msg_read_extpair(pool *p, unsigned char **buf,
     SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL);
   }
 
+  if (namelen > FXP_MAX_EXTENDED_ATTR_LEN) {
+    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
+      "received too-long extended attribute name (%lu > max %lu), ignoring",
+      (unsigned long) namelen, (unsigned long) FXP_MAX_EXTENDED_ATTR_LEN);
+    errno = EINVAL;
+    return NULL;
+  }
+
   name = palloc(p, namelen + 1);
   memcpy(name, *buf, namelen);
   (*buf) += namelen;
@@ -1248,6 +1262,15 @@ static struct fxp_extpair *fxp_msg_read_extpair(pool *p, unsigned char **buf,
 
   datalen = sftp_msg_read_int(p, buf, buflen);
   if (datalen > 0) {
+    if (datalen > FXP_MAX_EXTENDED_ATTR_LEN) {
+      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
+        "received too-long extended attribute '%s' value (%lu > max %lu), "
+        "ignoring", name, (unsigned long) datalen,
+        (unsigned long) FXP_MAX_EXTENDED_ATTR_LEN);
+      errno = EINVAL;
+      return NULL;
+    }
+
     data = sftp_msg_read_data(p, buf, buflen, datalen);
 
   } else {
@@ -2210,11 +2233,13 @@ static struct stat *fxp_attrs_read(struct fxp_packet *fxp, unsigned char **buf,
         struct fxp_extpair *ext;
 
         ext = fxp_msg_read_extpair(fxp->pool, buf, buflen);
-        pr_trace_msg(trace_channel, 15,
-          "protocol version %lu: read EXTENDED attribute: "
-          "extension '%s' (%lu bytes of data)",
-          (unsigned long) fxp_session->client_version, ext->ext_name,
-          (unsigned long) ext->ext_datalen);
+        if (ext != NULL) {
+          pr_trace_msg(trace_channel, 15,
+            "protocol version %lu: read EXTENDED attribute: "
+            "extension '%s' (%lu bytes of data)",
+            (unsigned long) fxp_session->client_version, ext->ext_name,
+            (unsigned long) ext->ext_datalen);
+        }
       }
     }
 
@@ -11580,18 +11605,20 @@ int sftp_fxp_handle_packet(pool *p, void *ssh2, uint32_t channel_id,
       case SFTP_SSH2_FXP_INIT:
         /* If we already know the version, then the client has sent
          * FXP_INIT before, and should NOT be sending it again.
+         *
+         * However, per Bug#4227, there ARE clients which do send INIT
+         * multiple times; I don't know why.  And since OpenSSH handles
+         * these repeated INITs without disconnecting clients, that is the
+         * de facto expected behavior.  We will do the same, but at least
+         * log about it.
          */
-        if (fxp_session->client_version == 0) {
-          res = fxp_handle_init(fxp);
-
-        } else {
+        if (fxp_session->client_version > 0) {
           (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
-            "already received SFTP INIT request from client");
-          destroy_pool(fxp->pool);
-          fxp_session = NULL;
-          return -1;
+            "already received SFTP INIT %u request from client",
+            (unsigned int) fxp_session->client_version);
         }
 
+        res = fxp_handle_init(fxp);
         break;
 
       case SFTP_SSH2_FXP_CLOSE:
diff --git a/contrib/mod_sftp/kbdint.c b/contrib/mod_sftp/kbdint.c
index 8cbd2a5..8ade59a 100644
--- a/contrib/mod_sftp/kbdint.c
+++ b/contrib/mod_sftp/kbdint.c
@@ -190,7 +190,7 @@ int sftp_kbdint_unregister_driver(const char *name) {
  */
 
 int sftp_kbdint_send_challenge(const char *user, const char *instruction,
-    unsigned int count, sftp_kbdint_challenge_t *challenges) {
+    uint32_t count, sftp_kbdint_challenge_t *challenges) {
   register unsigned int i;
   unsigned char *buf, *ptr;
   uint32_t buflen, bufsz;
@@ -252,8 +252,8 @@ int sftp_kbdint_send_challenge(const char *user, const char *instruction,
   return res;
 }
 
-int sftp_kbdint_recv_response(pool *p, unsigned int expected_count,
-    unsigned int *rcvd_count, const char ***responses) {
+int sftp_kbdint_recv_response(pool *p, uint32_t expected_count,
+    uint32_t *rcvd_count, const char ***responses) {
   register unsigned int i;
   unsigned char *buf;
   cmd_rec *cmd;
@@ -330,7 +330,7 @@ int sftp_kbdint_recv_response(pool *p, unsigned int expected_count,
     *((char **) push_array(list)) = pstrdup(p, sftp_utf8_decode_str(p, resp));
   }
 
-  *rcvd_count = (unsigned int) resp_count;
+  *rcvd_count = resp_count;
   *responses = ((const char **) list->elts);
   destroy_pool(pkt->pool);
 
diff --git a/contrib/mod_sftp/kex.c b/contrib/mod_sftp/kex.c
index c57191c..60b8b01 100644
--- a/contrib/mod_sftp/kex.c
+++ b/contrib/mod_sftp/kex.c
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp key exchange (kex)
- * Copyright (c) 2008-2013 TJ Saunders
+ * Copyright (c) 2008-2015 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
@@ -20,8 +20,6 @@
  * give permission to link this program with OpenSSL, and distribute the
  * resulting executable, without including the source code for OpenSSL in the
  * source distribution.
- *
- * $Id: kex.c,v 1.39 2013-10-02 06:18:53 castaglia Exp $
  */
 
 #include "mod_sftp.h"
@@ -39,8 +37,6 @@
 #include "interop.h"
 #include "tap.h"
 
-#define SFTP_DH_PRIV_KEY_RANDOM_BITS	2048
-
 extern module sftp_module;
 
 /* For managing the kexinit process */
@@ -621,8 +617,94 @@ static int have_good_dh(DH *dh, BIGNUM *pub_key) {
   return 0;
 }
 
+static int get_dh_nbits(struct sftp_kex *kex) {
+  int dh_nbits = 0, dh_size = 0;
+  const char *algo;
+  const EVP_CIPHER *cipher;
+  const EVP_MD *digest;
+
+  algo = kex->session_names->c2s_encrypt_algo;
+  cipher = sftp_crypto_get_cipher(algo, NULL, NULL);
+  if (cipher != NULL) {
+    int block_size, key_len;
+
+    key_len = EVP_CIPHER_key_length(cipher);
+    if (dh_size < key_len) {
+      dh_size = key_len;
+      pr_trace_msg(trace_channel, 19,
+        "set DH size to %d bytes, matching client-to-server '%s' cipher "
+        "key length", dh_size, algo);
+    }
+
+    block_size = EVP_CIPHER_block_size(cipher);
+    if (dh_size < block_size) {
+      dh_size = block_size;
+      pr_trace_msg(trace_channel, 19,
+        "set DH size to %d bytes, matching client-to-server '%s' cipher "
+        "block size", dh_size, algo);
+    }
+  }
+
+  algo = kex->session_names->s2c_encrypt_algo;
+  cipher = sftp_crypto_get_cipher(algo, NULL, NULL);
+  if (cipher != NULL) {
+    int block_size, key_len;
+
+    key_len = EVP_CIPHER_key_length(cipher);
+    if (dh_size < key_len) {
+      dh_size = key_len;
+      pr_trace_msg(trace_channel, 19,
+        "set DH size to %d bytes, matching server-to-client '%s' cipher "
+        "key length", dh_size, algo);
+    }
+
+    block_size = EVP_CIPHER_block_size(cipher);
+    if (dh_size < block_size) {
+      dh_size = block_size;
+      pr_trace_msg(trace_channel, 19,
+        "set DH size to %d bytes, matching server-to-client '%s' cipher "
+        "block size", dh_size, algo);
+    }
+  }
+
+  algo = kex->session_names->c2s_mac_algo;
+  digest = sftp_crypto_get_digest(algo, NULL);
+  if (digest != NULL) {
+    int mac_len;
+
+    mac_len = EVP_MD_size(digest);
+    if (dh_size < mac_len) {
+      dh_size = mac_len;
+      pr_trace_msg(trace_channel, 19,
+        "set DH size to %d bytes, matching client-to-server '%s' digest size",
+        dh_size, algo);
+    }
+  }
+
+  algo = kex->session_names->s2c_mac_algo;
+  digest = sftp_crypto_get_digest(algo, NULL);
+  if (digest != NULL) {
+    int mac_len;
+
+    mac_len = EVP_MD_size(digest);
+    if (dh_size < mac_len) {
+      dh_size = mac_len;
+      pr_trace_msg(trace_channel, 19,
+        "set DH size to %d bytes, matching server-to-client '%s' digest size",
+        dh_size, algo);
+    }
+  }
+
+  /* We want to return bits, not bytes. */
+  dh_nbits = dh_size * 8;
+
+  pr_trace_msg(trace_channel, 8, "requesting DH size of %d bits", dh_nbits);
+  return dh_nbits;
+}
+
 static int create_dh(struct sftp_kex *kex, int type) {
   unsigned int attempts = 0;
+  int dh_nbits;
   DH *dh;
 
   if (type != SFTP_DH_GROUP1_SHA1 &&
@@ -656,6 +738,8 @@ static int create_dh(struct sftp_kex *kex, int type) {
     kex->dh = NULL;
   }
 
+  dh_nbits = get_dh_nbits(kex);
+
   /* We have 10 attempts to make a DH key which passes muster. */
   while (attempts <= 10) {
     pr_signals_handle();
@@ -665,7 +749,7 @@ static int create_dh(struct sftp_kex *kex, int type) {
       attempts);
 
     dh = DH_new();
-    if (!dh) {
+    if (dh == NULL) {
       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
         "error creating DH: %s", sftp_crypto_get_errors());
       return -1;
@@ -699,10 +783,11 @@ static int create_dh(struct sftp_kex *kex, int type) {
       return -1;
     }
 
-    if (!BN_rand(dh->priv_key, SFTP_DH_PRIV_KEY_RANDOM_BITS, 0, 0)) {
+    /* Generate a random private exponent of the desired size, in bits. */
+    if (!BN_rand(dh->priv_key, dh_nbits, 0, 0)) {
       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
-        "error generating DH random key (%d bytes): %s",
-        SFTP_DH_PRIV_KEY_RANDOM_BITS, sftp_crypto_get_errors());
+        "error generating DH random key (%d bits): %s", dh_nbits,
+        sftp_crypto_get_errors());
       DH_free(dh);
       return -1;
     }
@@ -787,6 +872,9 @@ static int prepare_dh(struct sftp_kex *kex, int type) {
 
 static int finish_dh(struct sftp_kex *kex) {
   unsigned int attempts = 0;
+  int dh_nbits;
+
+  dh_nbits = get_dh_nbits(kex);
 
   /* We have 10 attempts to make a DH key which passes muster. */
   while (attempts <= 10) {
@@ -797,11 +885,12 @@ static int finish_dh(struct sftp_kex *kex) {
       attempts);
 
     kex->dh->priv_key = BN_new();
-  
-    if (!BN_rand(kex->dh->priv_key, SFTP_DH_PRIV_KEY_RANDOM_BITS, 0, 0)) {
+ 
+    /* Generate a random private exponent of the desired size, in bits. */ 
+    if (!BN_rand(kex->dh->priv_key, dh_nbits, 0, 0)) {
       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
-        "error generating DH random key (%d bytes): %s",
-        SFTP_DH_PRIV_KEY_RANDOM_BITS, sftp_crypto_get_errors());
+        "error generating DH random key (%d bits): %s", dh_nbits,
+        sftp_crypto_get_errors());
       return -1;
     }
 
@@ -1514,77 +1603,71 @@ static int setup_hostkey_algo(struct sftp_kex *kex, const char *algo) {
 }
 
 static int setup_c2s_encrypt_algo(struct sftp_kex *kex, const char *algo) {
-  (void) kex;
-
-  if (sftp_cipher_set_read_algo(algo) < 0)
+  if (sftp_cipher_set_read_algo(algo) < 0) {
     return -1;
+  }
 
+  kex->session_names->c2s_encrypt_algo = algo;
   return 0;
 }
 
 static int setup_s2c_encrypt_algo(struct sftp_kex *kex, const char *algo) {
-  (void) kex;
-
-  if (sftp_cipher_set_write_algo(algo) < 0)
+  if (sftp_cipher_set_write_algo(algo) < 0) {
     return -1;
+  }
 
+  kex->session_names->s2c_encrypt_algo = algo;
   return 0;
 }
 
 static int setup_c2s_mac_algo(struct sftp_kex *kex, const char *algo) {
-  (void) kex;
-
-  if (sftp_mac_set_read_algo(algo) < 0)
+  if (sftp_mac_set_read_algo(algo) < 0) {
     return -1;
+  }
 
+  kex->session_names->c2s_mac_algo = algo;
   return 0;
 }
 
 static int setup_s2c_mac_algo(struct sftp_kex *kex, const char *algo) {
-  (void) kex;
-
-  if (sftp_mac_set_write_algo(algo) < 0)
+  if (sftp_mac_set_write_algo(algo) < 0) {
     return -1;
+  }
 
+  kex->session_names->s2c_mac_algo = algo;
   return 0;
 }
 
 static int setup_c2s_comp_algo(struct sftp_kex *kex, const char *algo) {
-  (void) kex;
-
-  if (sftp_compress_set_read_algo(algo) < 0)
+  if (sftp_compress_set_read_algo(algo) < 0) {
     return -1;
+  }
 
+  kex->session_names->c2s_comp_algo = algo;
   return 0;
 }
 
 static int setup_s2c_comp_algo(struct sftp_kex *kex, const char *algo) {
-  (void) kex;
-
-  if (sftp_compress_set_write_algo(algo) < 0)
+  if (sftp_compress_set_write_algo(algo) < 0) {
     return -1;
+  }
 
+  kex->session_names->s2c_comp_algo = algo;
   return 0;
 }
 
 static int setup_c2s_lang(struct sftp_kex *kex, const char *lang) {
-  (void) kex;
-
-  /* XXX Need to implement the functionality here. */
-
+  kex->session_names->c2s_lang = lang;
   return 0;
 }
 
 static int setup_s2c_lang(struct sftp_kex *kex, const char *lang) {
-  (void) kex;
-
-  /* XXX Need to implement the functionality here. */
-
+  kex->session_names->s2c_lang = lang;
   return 0;
 }
 
 static int get_session_names(struct sftp_kex *kex, int *correct_guess) {
-  const char *shared, *client_list, *server_list;
+  const char *kex_algo, *shared, *client_list, *server_list;
   const char *client_pref, *server_pref;
   pool *tmp_pool;
 
@@ -1624,17 +1707,16 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) {
     }
   }
 
-  shared = get_shared_name(kex_pool, client_list, server_list);
-  if (shared) {
-    if (setup_kex_algo(kex, shared) < 0) {
-      destroy_pool(tmp_pool);
-      return -1;
-    }
-
+  kex_algo = get_shared_name(kex_pool, client_list, server_list);
+  if (kex_algo != NULL) {
+    /* Unlike the following algorithms, we wait to setup the chosen kex algo
+     * until the end.  Why?  The kex algo setup may require knowledge of the
+     * ciphers chosen for encryption, MAC, etc (Bug#4097).
+     */
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
-      " + Session key exchange: %s", shared);
+      " + Session key exchange: %s", kex_algo);
     pr_trace_msg(trace_channel, 20, "session key exchange algorithm: %s",
-      shared);
+      kex_algo);
 
   } else {
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
@@ -1902,6 +1984,14 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) {
 #endif
   }
 
+  /* Now that we've finished setting up the other bits, we can set up the
+   * kex algo.
+   */
+  if (setup_kex_algo(kex, kex_algo) < 0) {
+    destroy_pool(tmp_pool);
+    return -1;
+  }
+
   destroy_pool(tmp_pool);
   return 0;
 }
@@ -3659,12 +3749,6 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
   pkt = read_kex_packet(kex_pool, kex, SFTP_SSH2_DISCONNECT_PROTOCOL_ERROR,
     NULL, 1, SFTP_SSH2_MSG_NEWKEYS);
 
-  cmd = pr_cmd_alloc(pkt->pool, 1, pstrdup(pkt->pool, "NEWKEYS"));
-  cmd->arg = "";
-  cmd->cmd_class = CL_AUTH;
-
-  pr_cmd_dispatch_phase(cmd, LOG_CMD, 0);
-
   /* If we didn't send our NEWKEYS message earlier, do it now. */
   if (!sent_newkeys) {
     pr_trace_msg(trace_channel, 9, "sending NEWKEYS message to client");
@@ -3699,6 +3783,13 @@ int sftp_kex_handle(struct ssh2_packet *pkt) {
     SFTP_DISCONNECT_CONN(SFTP_SSH2_DISCONNECT_BY_APPLICATION, NULL);
   }
 
+  cmd = pr_cmd_alloc(pkt->pool, 1, pstrdup(pkt->pool, "NEWKEYS"));
+  cmd->arg = "";
+  cmd->cmd_class = CL_AUTH;
+
+  pr_cmd_dispatch_phase(cmd, LOG_CMD, 0);
+  destroy_pool(pkt->pool);
+
   /* Reset this flag for the next time through. */
   kex_sent_kexinit = FALSE;
 
diff --git a/contrib/mod_sftp/keys.c b/contrib/mod_sftp/keys.c
index 1247ea6..371de28 100644
--- a/contrib/mod_sftp/keys.c
+++ b/contrib/mod_sftp/keys.c
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp key mgmt (keys)
- * Copyright (c) 2008-2014 TJ Saunders
+ * Copyright (c) 2008-2015 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
@@ -20,8 +20,6 @@
  * give permission to link this program with OpenSSL, and distribute the
  * resulting executable, without including the source code for OpenSSL in the
  * source distribution.
- *
- * $Id: keys.c,v 1.39 2014-01-28 17:26:17 castaglia Exp $
  */
 
 #include "mod_sftp.h"
@@ -36,6 +34,7 @@
 extern xaset_t *server_list;
 extern module sftp_module;
 
+/* Note: Should this size be made bigger, in light of larger hostkeys? */
 #define SFTP_DEFAULT_HOSTKEY_SZ		4096
 #define SFTP_MAX_SIG_SZ			4096
 
@@ -2022,7 +2021,7 @@ const unsigned char *sftp_keys_get_hostkey_data(pool *p,
       }
 
       /* XXX Is this buffer large enough?  Too large? */
-      ptr = buf = sftp_msg_getbuf(p, buflen);
+      ptr = buf = palloc(p, buflen);
       sftp_msg_write_string(&buf, &buflen, "ssh-rsa");
       sftp_msg_write_mpint(&buf, &buflen, rsa->e);
       sftp_msg_write_mpint(&buf, &buflen, rsa->n);
@@ -2042,7 +2041,7 @@ const unsigned char *sftp_keys_get_hostkey_data(pool *p,
       }
 
       /* XXX Is this buffer large enough?  Too large? */
-      ptr = buf = sftp_msg_getbuf(p, buflen);
+      ptr = buf = palloc(p, buflen);
       sftp_msg_write_string(&buf, &buflen, "ssh-dss");
       sftp_msg_write_mpint(&buf, &buflen, dsa->p);
       sftp_msg_write_mpint(&buf, &buflen, dsa->q);
@@ -2065,8 +2064,7 @@ const unsigned char *sftp_keys_get_hostkey_data(pool *p,
       }
 
       /* XXX Is this buffer large enough?  Too large? */
-      ptr = buf = sftp_msg_getbuf(p, buflen);
-
+      ptr = buf = palloc(p, buflen);
       sftp_msg_write_string(&buf, &buflen, "ecdsa-sha2-nistp256");
       sftp_msg_write_string(&buf, &buflen, "nistp256");
       sftp_msg_write_ecpoint(&buf, &buflen, EC_KEY_get0_group(ec),
@@ -2087,8 +2085,7 @@ const unsigned char *sftp_keys_get_hostkey_data(pool *p,
       }
 
       /* XXX Is this buffer large enough?  Too large? */
-      ptr = buf = sftp_msg_getbuf(p, buflen);
-
+      ptr = buf = palloc(p, buflen);
       sftp_msg_write_string(&buf, &buflen, "ecdsa-sha2-nistp384");
       sftp_msg_write_string(&buf, &buflen, "nistp384");
       sftp_msg_write_ecpoint(&buf, &buflen, EC_KEY_get0_group(ec),
@@ -2109,8 +2106,7 @@ const unsigned char *sftp_keys_get_hostkey_data(pool *p,
       }
 
       /* XXX Is this buffer large enough?  Too large? */
-      ptr = buf = sftp_msg_getbuf(p, buflen);
-
+      ptr = buf = palloc(p, buflen);
       sftp_msg_write_string(&buf, &buflen, "ecdsa-sha2-nistp521");
       sftp_msg_write_string(&buf, &buflen, "nistp521");
       sftp_msg_write_ecpoint(&buf, &buflen, EC_KEY_get0_group(ec),
@@ -2131,8 +2127,14 @@ const unsigned char *sftp_keys_get_hostkey_data(pool *p,
   *datalen = SFTP_DEFAULT_HOSTKEY_SZ - buflen;
 
   /* If the caller provided a pool, make a copy of the data from the
-   * given pool, and return the copy.  Make sure the scrub the original
+   * given pool, and return the copy.  Make sure to scrub the original
    * after making the copy.
+   *
+   * Note that we do this copy, even though we use the given pool, since
+   * we only know the actual size of the data after the fact.  And we need
+   * to provide the size of the data to the caller, NOT the optimistic size
+   * we allocate out of the pool for writing the data in the first place.
+   * Hence the copy.
    */
   if (p) {
     buf = palloc(p, *datalen);
@@ -2161,27 +2163,38 @@ int sftp_keys_have_ecdsa_hostkey(pool *p, int **nids) {
 #ifdef PR_USE_OPENSSL_ECC
   int count = 0;
 
-  *nids = palloc(p, sizeof(int) * 3);
+  if (nids != NULL) {
+    *nids = palloc(p, sizeof(int) * 3);
+  }
 
   if (sftp_ecdsa256_hostkey != NULL) {
     EC_KEY *ec;
 
     ec = EVP_PKEY_get1_EC_KEY(sftp_ecdsa256_hostkey->pkey);
-    (*nids)[count++] = get_ecdsa_nid(ec);
+    if (nids != NULL) {
+      (*nids)[count] = get_ecdsa_nid(ec);
+    }
+    count++;
     EC_KEY_free(ec);
 
   } else if (sftp_ecdsa384_hostkey != NULL) {
     EC_KEY *ec;
 
     ec = EVP_PKEY_get1_EC_KEY(sftp_ecdsa384_hostkey->pkey);
-    (*nids)[count++] = get_ecdsa_nid(ec);
+    if (nids != NULL) {
+      (*nids)[count] = get_ecdsa_nid(ec);
+    }
+    count++;
     EC_KEY_free(ec);
 
   } else if (sftp_ecdsa521_hostkey != NULL) {
     EC_KEY *ec;
 
     ec = EVP_PKEY_get1_EC_KEY(sftp_ecdsa521_hostkey->pkey);
-    (*nids)[count++] = get_ecdsa_nid(ec);
+    if (nids != NULL) {
+      (*nids)[count] = get_ecdsa_nid(ec);
+    }
+    count++;
     EC_KEY_free(ec);
   }
 
diff --git a/contrib/mod_sftp/mod_sftp.c b/contrib/mod_sftp/mod_sftp.c
index bc733ee..95cbd9d 100644
--- a/contrib/mod_sftp/mod_sftp.c
+++ b/contrib/mod_sftp/mod_sftp.c
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp
- * Copyright (c) 2008-2014 TJ Saunders
+ * Copyright (c) 2008-2015 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
@@ -21,10 +21,9 @@
  * resulting executable, without including the source code for OpenSSL in the
  * source distribution.
  *
- * DO NOT EDIT BELOW THIS LINE
+ * -----DO NOT EDIT BELOW THIS LINE-----
  * $Archive: mod_sftp.a $
  * $Libraries: -lcrypto -lz $
- * $Id: mod_sftp.c,v 1.86 2014-03-02 22:05:43 castaglia Exp $
  */
 
 #include "mod_sftp.h"
@@ -1764,7 +1763,8 @@ static int sftp_sess_init(void) {
    * we have to have at least one hostkey.
    */
   if (sftp_keys_have_dsa_hostkey() < 0 &&
-      sftp_keys_have_rsa_hostkey() < 0) {
+      sftp_keys_have_rsa_hostkey() < 0 &&
+      sftp_keys_have_ecdsa_hostkey(sftp_pool, NULL) < 0) {
     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
       "no available host keys, unable to handle session");
     errno = EACCES;
diff --git a/contrib/mod_sftp/mod_sftp.h.in b/contrib/mod_sftp/mod_sftp.h.in
index a020a29..1765bde 100644
--- a/contrib/mod_sftp/mod_sftp.h.in
+++ b/contrib/mod_sftp/mod_sftp.h.in
@@ -180,9 +180,9 @@ typedef struct kbdint_st {
 
 int sftp_kbdint_register_driver(const char *name, sftp_kbdint_driver_t *driver);
 int sftp_kbdint_unregister_driver(const char *name);
-int sftp_kbdint_send_challenge(const char *, const char *, unsigned int,
+int sftp_kbdint_send_challenge(const char *, const char *, uint32_t,
   sftp_kbdint_challenge_t *);
-int sftp_kbdint_recv_response(pool *, unsigned int, unsigned int *,
+int sftp_kbdint_recv_response(pool *, uint32_t, uint32_t *,
   const char ***);
 
 /* API for modules that which to register keystores, for the
diff --git a/contrib/mod_sftp/scp.c b/contrib/mod_sftp/scp.c
index 03b83da..6186805 100644
--- a/contrib/mod_sftp/scp.c
+++ b/contrib/mod_sftp/scp.c
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp SCP
- * Copyright (c) 2008-2014 TJ Saunders
+ * Copyright (c) 2008-2015 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
@@ -20,8 +20,6 @@
  * give permission to link this program with OpenSSL, and distribute the
  * resulting executable, without including the source code for OpenSSL in the
  * source distribution.
- *
- * $Id: scp.c,v 1.87 2014-01-20 20:49:04 castaglia Exp $
  */
 
 #include "mod_sftp.h"
@@ -1054,8 +1052,9 @@ static int recv_data(pool *p, uint32_t channel_id, struct scp_path *sp,
           pstrcat(p, sp->filename, ": write error: ", strerror(xerrno), NULL));
         sp->wrote_errors = TRUE;
 
-        pr_fsio_close(sp->fh);
-        sp->fh = NULL;
+        /* Note that we do NOT explicitly close the filehandle here; we leave
+         * that to the calling function, so that it can do e.g. other cleanup.
+         */
 
         errno = xerrno;
         return 1;
@@ -1080,8 +1079,9 @@ static int recv_data(pool *p, uint32_t channel_id, struct scp_path *sp,
           pstrcat(p, sp->filename, ": write error: ", strerror(xerrno), NULL));
         sp->wrote_errors = TRUE;
 
-        pr_fsio_close(sp->fh);
-        sp->fh = NULL;
+        /* Note that we do NOT explicitly close the filehandle here; we leave
+         * that to the calling function, so that it can do e.g. other cleanup.
+         */
 
         errno = xerrno;
         return 1;
@@ -1217,6 +1217,7 @@ static int recv_path(pool *p, uint32_t channel_id, struct scp_path *sp,
     unsigned char *data, uint32_t datalen) {
   int res;
   cmd_rec *cmd = NULL;
+  char *curr_path = NULL;
 
   if (!sp->checked_errors) {
     res = recv_errors(p, channel_id, sp, data, datalen);
@@ -1367,53 +1368,55 @@ static int recv_path(pool *p, uint32_t channel_id, struct scp_path *sp,
     }
   }
 
-  /* The uploaded file may be smaller than an existing file; call
-   * pr_fsio_truncate() to ensure proper file size.
-   */
-  if (S_ISREG(sp->st_mode)) {
-    pr_trace_msg(trace_channel, 9, "truncating file '%s' to %" PR_LU " bytes",
-      sp->fh->fh_path, (pr_off_t) sp->filesz);
+  if (sp->wrote_errors == FALSE) {
+    /* The uploaded file may be smaller than an existing file; call
+     * pr_fsio_truncate() to ensure proper file size.
+     */
+    if (S_ISREG(sp->st_mode)) {
+      pr_trace_msg(trace_channel, 9, "truncating file '%s' to %" PR_LU " bytes",
+        sp->fh->fh_path, (pr_off_t) sp->filesz);
 
-    if (pr_fsio_ftruncate(sp->fh, sp->filesz) < 0) {
-      int xerrno = errno;
+      if (pr_fsio_ftruncate(sp->fh, sp->filesz) < 0) {
+        int xerrno = errno;
 
-      pr_trace_msg(trace_channel, 2, "error truncating '%s' to %" PR_LU
-        " bytes: %s", sp->best_path, (pr_off_t) sp->filesz, strerror(xerrno));
-      write_confirm(p, channel_id, 1,
-        pstrcat(p, sp->filename, ": error truncating file: ", strerror(xerrno),
-        NULL));
+        pr_trace_msg(trace_channel, 2, "error truncating '%s' to %" PR_LU
+          " bytes: %s", sp->best_path, (pr_off_t) sp->filesz, strerror(xerrno));
+        write_confirm(p, channel_id, 1,
+          pstrcat(p, sp->filename, ": error truncating file: ",
+          strerror(xerrno), NULL));
 
-      sp->wrote_errors = TRUE;
+        sp->wrote_errors = TRUE;
+      }
     }
   }
 
-  /* If the SFTPOption for ignoring perms for SCP uploads is set, then
-   * skip the chmod on the upload file.
-   */
-  if (!(sftp_opts & SFTP_OPT_IGNORE_SCP_UPLOAD_PERMS)) { 
-    pr_trace_msg(trace_channel, 9, "setting perms %04o on file '%s'",
-      (unsigned int) sp->perms, sp->fh->fh_path);
+  if (sp->wrote_errors == FALSE) {
+    /* If the SFTPOption for ignoring perms for SCP uploads is set, then
+     * skip the chmod on the upload file.
+     */
+    if (!(sftp_opts & SFTP_OPT_IGNORE_SCP_UPLOAD_PERMS)) { 
+      pr_trace_msg(trace_channel, 9, "setting perms %04o on file '%s'",
+        (unsigned int) sp->perms, sp->fh->fh_path);
 
-    if (pr_fsio_fchmod(sp->fh, sp->perms) < 0) {
-      int xerrno = errno;
+      if (pr_fsio_fchmod(sp->fh, sp->perms) < 0) {
+        int xerrno = errno;
 
-      pr_trace_msg(trace_channel, 2, "error setting mode %04o on '%s': %s",
-        (unsigned int) sp->perms, sp->best_path, strerror(xerrno));
-      write_confirm(p, channel_id, 1,
-        pstrcat(p, sp->filename, ": error setting mode: ", strerror(xerrno),
-        NULL));
+        pr_trace_msg(trace_channel, 2, "error setting mode %04o on '%s': %s",
+          (unsigned int) sp->perms, sp->best_path, strerror(xerrno));
+        write_confirm(p, channel_id, 1,
+          pstrcat(p, sp->filename, ": error setting mode: ", strerror(xerrno),
+          NULL));
 
-      sp->wrote_errors = TRUE;
-    }
+        sp->wrote_errors = TRUE;
+      }
 
-  } else {
-    pr_trace_msg(trace_channel, 7, "SFTPOption 'IgnoreSCPUploadPerms' "
-      "configured, ignoring perms sent by client");
+    } else {
+      pr_trace_msg(trace_channel, 7, "SFTPOption 'IgnoreSCPUploadPerms' "
+        "configured, ignoring perms sent by client");
+    }
   }
 
   if (sp->fh) {
-    char *curr_path;
-
     curr_path = pstrdup(scp_pool, sp->fh->fh_path);
 
     /* Set session.curr_cmd, for any FSIO callbacks that might be interested. */
@@ -1432,15 +1435,33 @@ static int recv_path(pool *p, uint32_t channel_id, struct scp_path *sp,
     }
 
     sp->fh = NULL;
+  }
+
+  if (sp->hiddenstore == TRUE &&
+      curr_path != NULL) {
+
+    if (sp->wrote_errors == TRUE) {
+      /* There was an error writing this HiddenStores file; be sure to clean
+       * things up.
+       */
+      pr_trace_msg(trace_channel, 8, "deleting HiddenStores path '%s'",
+        curr_path); 
 
-    if (sp->hiddenstore == TRUE &&
-        res == 0) {
+      if (pr_fsio_unlink(curr_path) < 0) {
+        if (errno != ENOENT) {
+          pr_log_debug(DEBUG0, MOD_SFTP_VERSION
+            ": error deleting HiddenStores file '%s': %s", curr_path,
+            strerror(errno));
+        }
+      }
+
+    } else {
       /* This is a HiddenStores file, and needs to be renamed to the real
        * path (i.e. sp->best_path).
        */
 
-      pr_trace_msg(trace_channel, 8, "renaming HiddenStores path '%s' to '%s'",
-        curr_path, sp->best_path);
+      pr_trace_msg(trace_channel, 8,
+        "renaming HiddenStores path '%s' to '%s'", curr_path, sp->best_path);
 
       res = pr_fsio_rename(curr_path, sp->best_path);
       if (res < 0) {
@@ -1453,7 +1474,13 @@ static int recv_path(pool *p, uint32_t channel_id, struct scp_path *sp,
           "renaming of HiddenStore path '%s' to '%s' failed: %s",
           curr_path, sp->best_path, strerror(xerrno));
 
-        pr_fsio_unlink(curr_path);
+        if (pr_fsio_unlink(curr_path) < 0) {
+          if (errno != ENOENT) {
+            pr_trace_msg(trace_channel, 1,
+              "error deleting HiddenStores file '%s': %s", curr_path,
+              strerror(errno));
+          }
+        }
       }
     }
   }
diff --git a/contrib/mod_sftp/utf8.c b/contrib/mod_sftp/utf8.c
index 549b3d5..b11c96f 100644
--- a/contrib/mod_sftp/utf8.c
+++ b/contrib/mod_sftp/utf8.c
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - mod_sftp UTF8 encoding
- * Copyright (c) 2008-2013 TJ Saunders
+ * Copyright (c) 2008-2015 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
@@ -20,8 +20,6 @@
  * give permission to link this program with OpenSSL, and distribute the
  * resulting executable, without including the source code for OpenSSL in the
  * source distribution.
- *
- * $Id: utf8.c,v 1.18 2013-01-29 06:51:11 castaglia Exp $
  */
 
 #include "mod_sftp.h"
@@ -58,7 +56,7 @@ static int utf8_convert(iconv_t conv, const char *inbuf, size_t *inbuflen,
      */
 #if defined(LINUX) || defined(DARWIN6) || defined(DARWIN7) || \
     defined(DARWIN8) || defined(DARWIN9) || defined(DARWIN10) || \
-    defined(DARWIN11)
+    defined(DARWIN11) || defined(DARWIN12)
  
     nconv = iconv(conv, (char **) &inbuf, inbuflen, &outbuf, outbuflen);
 #else
diff --git a/contrib/mod_sftp_pam.c b/contrib/mod_sftp_pam.c
index 1b757fa..44c1022 100644
--- a/contrib/mod_sftp_pam.c
+++ b/contrib/mod_sftp_pam.c
@@ -119,7 +119,7 @@ static int sftppam_converse(int nmsgs, PR_PAM_CONST struct pam_message **msgs,
     struct pam_response **resps, void *app_data) {
   register unsigned int i = 0, j = 0;
   array_header *list;
-  unsigned int recvd_count = 0;
+  uint32_t recvd_count = 0;
   const char **recvd_responses = NULL;
   struct pam_response *res = NULL;
 
diff --git a/contrib/mod_sql_mysql.c b/contrib/mod_sql_mysql.c
index b50847d..d836a01 100644
--- a/contrib/mod_sql_mysql.c
+++ b/contrib/mod_sql_mysql.c
@@ -1,7 +1,7 @@
 /*
  * ProFTPD: mod_sql_mysql -- Support for connecting to MySQL databases.
  * Copyright (c) 2001 Andrew Houghton
- * Copyright (c) 2004-2013 TJ Saunders
+ * Copyright (c) 2004-2015 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
@@ -22,10 +22,7 @@
  * the resulting executable, without including the source code for OpenSSL in
  * the source distribution.
  *
- * $Id: mod_sql_mysql.c,v 1.72 2013-10-07 05:51:29 castaglia Exp $
- */
-
-/*
+ * -----DO NOT EDIT-----
  * $Libraries: -lm -lmysqlclient -lz $
  */
 
@@ -538,8 +535,13 @@ MODRET cmd_open(cmd_rec *cmd) {
      */
 
      if (strcasecmp(encoding, "UTF-8") == 0) {
+#  if MYSQL_VERSION_ID >= 50503
+       /* MySQL prefers the name "utf8mb4", not "UTF-8" */
+       encoding = pstrdup(cmd->tmp_pool, "utf8mb4");
+#  else
        /* MySQL prefers the name "utf8", not "UTF-8" */
        encoding = pstrdup(cmd->tmp_pool, "utf8");
+#  endif /* MySQL before 5.5.3 */
      }
 
     if (mysql_set_character_set(conn->mysql, encoding) != 0) {
diff --git a/contrib/mod_tls.c b/contrib/mod_tls.c
index df92658..ecd9f56 100644
--- a/contrib/mod_tls.c
+++ b/contrib/mod_tls.c
@@ -2,7 +2,7 @@
  * mod_tls - An RFC2228 SSL/TLS module for ProFTPD
  *
  * Copyright (c) 2000-2002 Peter 'Luna' Runestig <peter at runestig.com>
- * Copyright (c) 2002-2014 TJ Saunders <tj at castaglia.org>
+ * Copyright (c) 2002-2016 TJ Saunders <tj at castaglia.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without modifi-
@@ -411,6 +411,13 @@ static int tls_required_on_ctrl = 0;
 static int tls_required_on_data = 0;
 static unsigned char *tls_authenticated = NULL;
 
+/* Define the minimum DH group length we allow (unless the AllowWeakDH
+ * TLSOption is used).  Ideally this would be 2048, per https://weakdh.org,
+ * but for compatibility with older Java versions, which only support up to
+ * 1024, we'll use 1024.  For now.
+ */
+#define TLS_DH_MIN_LEN				1024
+
 /* mod_tls session flags */
 #define	TLS_SESS_ON_CTRL			0x0001
 #define TLS_SESS_ON_DATA			0x0002
@@ -438,6 +445,7 @@ static unsigned char *tls_authenticated = NULL;
 #define TLS_OPT_USE_IMPLICIT_SSL			0x0200
 #define TLS_OPT_ALLOW_CLIENT_RENEGOTIATIONS		0x0400
 #define TLS_OPT_VERIFY_CERT_CN				0x0800
+#define TLS_OPT_ALLOW_WEAK_DH				0x1000
 
 /* mod_tls SSCN modes */
 #define TLS_SSCN_MODE_SERVER				0
@@ -2415,26 +2423,141 @@ static int tls_ctrl_renegotiate_cb(CALLBACK_FRAME) {
 }
 #endif
 
-static DH *tls_dh_cb(SSL *ssl, int is_export, int keylength) {
+static DH *tls_dh_cb(SSL *ssl, int is_export, int keylen) {
   DH *dh = NULL;
+  EVP_PKEY *pkey;
+  int pkeylen = 0, use_pkeylen = FALSE;
+
+  /* OpenSSL will only ever call us (currently) with a keylen of 512 or 1024;
+   * see the SSL_EXPORT_PKEYLENGTH macro in ssl_locl.h.  Sigh.
+   *
+   * Thus we adjust the DH parameter length according to the size of the
+   * RSA/DSA private key used for the current connection.
+   *
+   * NOTE: This MAY cause interoperability issues with some clients, notably
+   * Java 7 (and earlier) clients, since Java 7 and earlier supports
+   * Diffie-Hellman only up to 1024 bits.  More sighs.  To deal with these
+   * clients, then, you need to configure a certificate/key of 1024 bits.
+   */
+  pkey = SSL_get_privatekey(ssl);
+  if (pkey != NULL) {
+    if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA ||
+        EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) {
+      pkeylen = EVP_PKEY_bits(pkey);
+
+      if (pkeylen < TLS_DH_MIN_LEN) {
+        if (!(tls_opts & TLS_OPT_ALLOW_WEAK_DH)) {
+          pr_trace_msg(trace_channel, 11,
+            "certificate private key length %d less than %d bits, using %d "
+            "(see AllowWeakDH TLSOption)", pkeylen, TLS_DH_MIN_LEN,
+            TLS_DH_MIN_LEN);
+          pkeylen = TLS_DH_MIN_LEN;
+        }
+      }
+
+      if (pkeylen != keylen) {
+        pr_trace_msg(trace_channel, 13,
+          "adjusted DH parameter length from %d to %d bits", keylen, pkeylen);
+        use_pkeylen = TRUE;
+      }
+    }
+  }
 
   if (tls_tmp_dhs != NULL &&
       tls_tmp_dhs->nelts > 0) {
     register unsigned int i;
-    DH **dhs;
+    DH *best_dh = NULL, **dhs;
+    int best_dhlen = 0;
 
     dhs = tls_tmp_dhs->elts;
+
+    /* Search the configured list of DH parameters twice: once for any sizes
+     * matching the actual requested size (usually 1024), and once for any
+     * matching the certificate private key size (pkeylen).
+     *
+     * This behavior allows site admins to configure a TLSDHParamFile that
+     * contains 1024-bit parameters, for e.g. Java 7 (and earlier) clients.
+     */
+
+    /* Note: the keylen argument is in BITS, but DH_size() returns the number
+     * of BYTES.
+     */
     for (i = 0; i < tls_tmp_dhs->nelts; i++) {
-      /* Note: the keylength argument is in BITS, but DH_size() returns
-       * the number of BYTES.
+      int dhlen;
+
+      dhlen = DH_size(dhs[i]) * 8;
+      if (dhlen == keylen) {
+        pr_trace_msg(trace_channel, 11,
+          "found matching DH parameter for key length %d", keylen);
+        return dhs[i];
+      }
+
+      /* Try to find the next "best" DH to use, where "best" means
+       * the smallest DH that is larger than the necessary keylen.
        */
-      if (DH_size(dhs[i]) == (keylength / 8)) {
+      if (dhlen > keylen) {
+        if (best_dh != NULL) {
+          if (dhlen < best_dhlen) {
+            best_dh = dhs[i];
+            best_dhlen = dhlen;
+          }
+
+        } else {
+          best_dh = dhs[i];
+          best_dhlen = dhlen;
+        }
+      }
+    }
+
+    for (i = 0; i < tls_tmp_dhs->nelts; i++) {
+      int dhlen;
+
+      dhlen = DH_size(dhs[i]) * 8;
+      if (dhlen == pkeylen) {
+        pr_trace_msg(trace_channel, 11,
+          "found matching DH parameter for certificate private key length %d",
+          pkeylen);
         return dhs[i];
       }
+
+      if (dhlen > pkeylen) {
+        if (best_dh != NULL) {
+          if (dhlen < best_dhlen) {
+            best_dh = dhs[i];
+            best_dhlen = dhlen;
+          }
+
+        } else {
+          best_dh = dhs[i];
+          best_dhlen = dhlen;
+        }
+      }
+    }
+
+    if (best_dh != NULL) {
+      pr_trace_msg(trace_channel, 11,
+        "using best DH parameter for key length %d (length %d)", keylen,
+        best_dhlen);
+      return best_dh;
     }
   }
 
-  switch (keylength) {
+  /* Still no DH parameters found?  Use the built-in ones. */
+
+  if (keylen < TLS_DH_MIN_LEN) {
+    if (!(tls_opts & TLS_OPT_ALLOW_WEAK_DH)) {
+      pr_trace_msg(trace_channel, 11,
+        "requested key length %d less than %d bits, using %d "
+        "(see AllowWeakDH TLSOption)", keylen, TLS_DH_MIN_LEN, TLS_DH_MIN_LEN);
+      keylen = TLS_DH_MIN_LEN;
+    }
+  }
+
+  if (use_pkeylen) {
+    keylen = pkeylen;
+  }
+
+  switch (keylen) {
     case 512:
       dh = get_dh512();
       break;
@@ -2443,37 +2566,38 @@ static DH *tls_dh_cb(SSL *ssl, int is_export, int keylength) {
       dh = get_dh768();
       break;
 
-     case 1024:
-       dh = get_dh1024();
-       break;
+    case 1024:
+      dh = get_dh1024();
+      break;
 
-     case 1536:
-       dh = get_dh1536();
-       break;
+    case 1536:
+      dh = get_dh1536();
+      break;
 
-     case 2048:
-       dh = get_dh2048();
-       break;
+    case 2048:
+      dh = get_dh2048();
+      break;
 
-     default:
-       tls_log("unsupported DH key length %d requested, returning 1024 bits",
-         keylength);
-       dh = get_dh1024();
-       break;
+    default:
+      tls_log("unsupported DH key length %d requested, returning 1024 bits",
+        keylen);
+      dh = get_dh1024();
+      break;
   }
 
+  pr_trace_msg(trace_channel, 11, "using builtin DH for %d bits", keylen);
+
   /* Add this DH to the list, so that it can be freed properly later. */
   if (tls_tmp_dhs == NULL) {
     tls_tmp_dhs = make_array(session.pool, 1, sizeof(DH *));
   }
 
   *((DH **) push_array(tls_tmp_dhs)) = dh;
-
   return dh;
 }
 
 #ifdef PR_USE_OPENSSL_ECC
-static EC_KEY *tls_ecdh_cb(SSL *ssl, int is_export, int keylength) {
+static EC_KEY *tls_ecdh_cb(SSL *ssl, int is_export, int keylen) {
   static EC_KEY *ecdh = NULL;
   static int init = 0;
 
@@ -4940,7 +5064,7 @@ static ssize_t tls_read(SSL *ssl, void *buf, size_t len) {
   return count;
 }
 
-static RSA *tls_rsa_cb(SSL *ssl, int is_export, int keylength) {
+static RSA *tls_rsa_cb(SSL *ssl, int is_export, int keylen) {
   BIGNUM *e = NULL;
 
   if (tls_tmp_rsa) {
@@ -4958,13 +5082,13 @@ static RSA *tls_rsa_cb(SSL *ssl, int is_export, int keylength) {
     return NULL;
   }
 
-  if (RSA_generate_key_ex(tls_tmp_rsa, keylength, e, NULL) != 1) {
+  if (RSA_generate_key_ex(tls_tmp_rsa, keylen, e, NULL) != 1) {
     BN_free(e);
     return NULL;
   }
 
 #else
-  tls_tmp_rsa = RSA_generate_key(keylength, RSA_F4, NULL, NULL);
+  tls_tmp_rsa = RSA_generate_key(keylen, RSA_F4, NULL, NULL);
 #endif /* OpenSSL version 0.9.8 and later */
 
   if (e != NULL) {
@@ -8445,6 +8569,9 @@ MODRET set_tlsoptions(cmd_rec *cmd) {
                strcmp(cmd->argv[i], "AllowClientRenegotiations") == 0) {
       opts |= TLS_OPT_ALLOW_CLIENT_RENEGOTIATIONS;
 
+    } else if (strcmp(cmd->argv[i], "AllowWeakDH") == 0) {
+      opts |= TLS_OPT_ALLOW_WEAK_DH;
+
     } else if (strcmp(cmd->argv[i], "EnableDiags") == 0) {
       opts |= TLS_OPT_ENABLE_DIAGS;
 
@@ -10051,7 +10178,7 @@ static conftable tls_conftab[] = {
 static cmdtable tls_cmdtab[] = {
   { PRE_CMD,	C_ANY,	G_NONE,	tls_any,	FALSE,	FALSE },
   { CMD,	C_AUTH,	G_NONE,	tls_auth,	FALSE,	FALSE,	CL_SEC },
-  { CMD,	C_CCC,	G_NONE,	tls_ccc,	FALSE,	FALSE,	CL_SEC },
+  { CMD,	C_CCC,	G_NONE,	tls_ccc,	TRUE,	FALSE,	CL_SEC },
   { CMD,	C_PBSZ,	G_NONE,	tls_pbsz,	FALSE,	FALSE,	CL_SEC },
   { CMD,	C_PROT,	G_NONE,	tls_prot,	FALSE,	FALSE,	CL_SEC },
   { CMD,	"SSCN",	G_NONE,	tls_sscn,	TRUE,	FALSE,	CL_SEC },
diff --git a/include/version.h b/include/version.h
index e6ff92c..4a27172 100644
--- a/include/version.h
+++ b/include/version.h
@@ -1,8 +1,8 @@
 #include "buildstamp.h"
 
 /* Application version (in various forms) */
-#define PROFTPD_VERSION_NUMBER		0x0001030507
-#define PROFTPD_VERSION_TEXT		"1.3.5a"
+#define PROFTPD_VERSION_NUMBER		0x0001030508
+#define PROFTPD_VERSION_TEXT		"1.3.5b"
 
 /* Module API version */
 #define PR_MODULE_API_VERSION		0x20
diff --git a/lib/Makefile.in b/lib/Makefile.in
index f476488..8667ffe 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -39,8 +39,8 @@ clean:
 	test -z $(LIB_DEPS) || (cd libltdl/ && $(MAKE) clean)
 
 depend:
-	makedepend $(CPPFLAGS) -Y *.c 2>/dev/null
-	makedepend $(CPPFLAGS) -Y -fMakefile.in *.c 2>/dev/null
+	$(MAKEDEPEND) $(CPPFLAGS) *.c 2>/dev/null
+	$(MAKEDEPEND) $(CPPFLAGS) -fMakefile.in *.c 2>/dev/null
 
 distclean:
 	test -z $(LIB_DEPS) || (cd libltdl/ && $(MAKE) distclean)
diff --git a/lib/tpl.c b/lib/tpl.c
index a354c8f..5d10abf 100755
--- a/lib/tpl.c
+++ b/lib/tpl.c
@@ -23,9 +23,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #define TPL_VERSION 1.5
 
-static const char id[]="$Id: tpl.c,v 1.4 2012-06-18 16:48:30 castaglia Exp $";
-
-
 #include <stdlib.h>  /* malloc */
 #include <stdarg.h>  /* va_list */
 #include <string.h>  /* memcpy, memset, strchr */
@@ -229,7 +226,6 @@ tpl_hook_t tpl_hook = {
 };
 
 static const char tpl_fmt_chars[] = "AS($)BiucsfIUjv#"; /* valid format chars */
-static const char tpl_S_fmt_chars[] = "iucsfIUjv#$()"; /* valid within S(...) */
 static const char tpl_datapeek_ok_chars[] = "iucsfIUjv"; /* valid in datapeek */
 static const struct tpl_type_t tpl_types[] = {
     /* [TPL_TYPE_ROOT] =   */  {'r', 0},
diff --git a/modules/Makefile.in b/modules/Makefile.in
index 7057eb1..11e8971 100644
--- a/modules/Makefile.in
+++ b/modules/Makefile.in
@@ -69,5 +69,5 @@ clean:
 
 depend:
 	$(RM) module_glue.c
-	makedepend $(CPPFLAGS) -Y *.c 2>/dev/null
-	makedepend $(CPPFLAGS) -Y -fMakefile.in *.c 2>/dev/null
+	$(MAKEDEPEND) $(CPPFLAGS) *.c 2>/dev/null
+	$(MAKEDEPEND) $(CPPFLAGS) -fMakefile.in *.c 2>/dev/null
diff --git a/modules/mod_auth.c b/modules/mod_auth.c
index faadf83..abe6ff8 100644
--- a/modules/mod_auth.c
+++ b/modules/mod_auth.c
@@ -2,7 +2,7 @@
  * ProFTPD - FTP server daemon
  * Copyright (c) 1997, 1998 Public Flood Software
  * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver at tos.net>
- * Copyright (c) 2001-2014 The ProFTPD Project team
+ * Copyright (c) 2001-2016 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
@@ -24,9 +24,7 @@
  * the source code for OpenSSL in the source distribution.
  */
 
-/* Authentication module for ProFTPD
- * $Id: mod_auth.c,v 1.317 2013-12-29 20:17:09 castaglia Exp $
- */
+/* Authentication module for ProFTPD */
 
 #include "conf.h"
 #include "privs.h"
@@ -591,7 +589,7 @@ MODRET auth_post_pass(cmd_rec *cmd) {
        */
       if (session.c->local_port < 1024) {
         pr_log_debug(DEBUG0,
-          "RootRevoke in effect, disabling active transfers");
+          "RootRevoke in effect, active data transfers may not succeed");
       }
     }
 
@@ -2168,11 +2166,25 @@ MODRET auth_user(cmd_rec *cmd) {
   int failnopwprompt = 0, aclp, i;
   unsigned char *anon_require_passwd = NULL, *login_passwd_prompt = NULL;
 
-  if (logged_in)
-    return PR_ERROR_MSG(cmd, R_500, _("Bad sequence of commands"));
-
-  if (cmd->argc < 2)
+  if (cmd->argc < 2) {
     return PR_ERROR_MSG(cmd, R_500, _("USER: command requires a parameter"));
+  }
+
+  if (logged_in) {
+    /* If the client has already authenticated, BUT the given USER command
+     * here is for the exact same user name, then allow the command to
+     * succeed (Bug#4217).
+     */
+    origuser = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
+    if (origuser != NULL &&
+        strcmp(origuser, cmd->arg) == 0) {
+      pr_response_add(R_230, _("User %s logged in"), origuser);
+      return PR_HANDLED(cmd);
+    }
+
+    pr_response_add_err(R_501, "%s", _("Reauthentication not supported"));
+    return PR_ERROR(cmd);
+  }
 
   user = cmd->arg;
 
@@ -2292,10 +2304,10 @@ MODRET auth_user(cmd_rec *cmd) {
     pr_response_add(R_331, _("Anonymous login ok, send your complete email "
       "address as your password"));
 
-  /* Check to see if a password from the client is required.  In the
-   * vast majority of cases, a password will be required.
-   */
   } else if (pr_auth_requires_pass(cmd->tmp_pool, user) == FALSE) {
+    /* Check to see if a password from the client is required.  In the
+     * vast majority of cases, a password will be required.
+     */
 
     /* Act as if we received a PASS command from the client. */
     cmd_rec *fakecmd = pr_cmd_alloc(cmd->pool, 2, NULL);
diff --git a/modules/mod_core.c b/modules/mod_core.c
index 7659630..080a97c 100644
--- a/modules/mod_core.c
+++ b/modules/mod_core.c
@@ -3569,7 +3569,7 @@ MODRET core_port(cmd_rec *cmd) {
       *root_revoke == 1 &&
       session.c->local_port < 1024) {
     pr_log_debug(DEBUG0, "RootRevoke in effect, unable to bind to local "
-      "port %d for active transfer", session.c->local_port);
+      "port %d for active transfer", session.c->local_port-1);
     pr_response_add_err(R_500, _("Unable to service PORT commands"));
     errno = EPERM;
     return PR_ERROR(cmd);
@@ -3773,7 +3773,7 @@ MODRET core_eprt(cmd_rec *cmd) {
       *root_revoke == 1 &&
       session.c->local_port < 1024) {
     pr_log_debug(DEBUG0, "RootRevoke in effect, unable to bind to local "
-      "port %d for active transfer", session.c->local_port);
+      "port %d for active transfer", session.c->local_port-1);
     pr_response_add_err(R_500, _("Unable to service EPRT commands"));
     errno = EPERM;
     return PR_ERROR(cmd);
diff --git a/modules/mod_facts.c b/modules/mod_facts.c
index 7291f4d..09f8e2e 100644
--- a/modules/mod_facts.c
+++ b/modules/mod_facts.c
@@ -1,7 +1,7 @@
 /*
  * ProFTPD: mod_facts -- a module for handling "facts" [RFC3659]
  *
- * Copyright (c) 2007-2013 The ProFTPD Project
+ * Copyright (c) 2007-2015 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
@@ -21,14 +21,12 @@
  * give permission to link this program with OpenSSL, and distribute the
  * resulting executable, without including the source code for OpenSSL in the
  * source distribution.
- *
- * $Id: mod_facts.c,v 1.60 2013-04-16 16:04:43 castaglia Exp $
  */
 
 #include "conf.h"
 #include "privs.h"
 
-#define MOD_FACTS_VERSION		"mod_facts/0.3"
+#define MOD_FACTS_VERSION		"mod_facts/0.4"
 
 #if PROFTPD_VERSION_NUMBER < 0x0001030101
 # error "ProFTPD 1.3.1rc1 or later required"
@@ -49,6 +47,8 @@ static unsigned long facts_opts = 0;
 static unsigned long facts_mlinfo_opts = 0;
 #define FACTS_MLINFO_FL_SHOW_SYMLINKS			0x00001
 #define FACTS_MLINFO_FL_SHOW_SYMLINKS_USE_SLINK		0x00002
+#define FACTS_MLINFO_FL_NO_CDIR				0x00004
+#define FACTS_MLINFO_FL_APPEND_CRLF			0x00008
 
 struct mlinfo {
   pool *pool;
@@ -203,7 +203,8 @@ static time_t facts_mktime(unsigned int year, unsigned int month,
   return res;
 }
 
-static size_t facts_mlinfo_fmt(struct mlinfo *info, char *buf, size_t bufsz) {
+static size_t facts_mlinfo_fmt(struct mlinfo *info, char *buf, size_t bufsz,
+    int flags) {
   char *ptr;
   size_t buflen = 0;
 
@@ -267,13 +268,8 @@ static size_t facts_mlinfo_fmt(struct mlinfo *info, char *buf, size_t bufsz) {
     ptr = buf + buflen;
   }
 
-  /* MLST entries are not sent via pr_data_xfer(), and thus we do not need
-   * to include an LF at the end; it is appended by pr_response_send_raw().
-   * But MLSD entries DO need the trailing LF, so that it can be converted
-   * into a CRLF sequence by pr_data_xfer().
-   */
-  if (strcmp(session.curr_cmd, C_MLSD) == 0) {
-    snprintf(ptr, bufsz - buflen, " %s\n", info->path);
+  if (flags & FACTS_MLINFO_FL_APPEND_CRLF) {
+    snprintf(ptr, bufsz - buflen, " %s\r\n", info->path);
 
   } else {
     snprintf(ptr, bufsz - buflen, " %s", info->path);
@@ -311,11 +307,11 @@ static void facts_mlinfobuf_init(void) {
   mlinfo_buflen = 0;
 }
 
-static void facts_mlinfobuf_add(struct mlinfo *info) {
+static void facts_mlinfobuf_add(struct mlinfo *info, int flags) {
   char buf[PR_TUNABLE_BUFFER_SIZE];
   size_t buflen;
  
-  buflen = facts_mlinfo_fmt(info, buf, sizeof(buf));
+  buflen = facts_mlinfo_fmt(info, buf, sizeof(buf), flags);
 
   /* If this buffer will exceed the capacity of mlinfo_buf, then flush
    * mlinfo_buf.
@@ -491,20 +487,17 @@ static int facts_mlinfo_get(struct mlinfo *info, const char *path,
   } else {
     info->type = "dir";
 
-    if (dent_name[0] != '.') {
-      if (strcmp(path, pr_fs_getcwd()) == 0) {
-        info->type = "cdir";
-      }
-
-    } else {
-      if (dent_name[1] == '\0') {
-        info->type = "cdir";
-      }
+    if (!(flags & FACTS_MLINFO_FL_NO_CDIR)) {
+      if (dent_name[0] == '.') {
+        if (dent_name[1] == '\0') {
+          info->type = "cdir";
+        }
 
-      if (strlen(dent_name) >= 2) {
-        if (dent_name[1] == '.' &&
-            dent_name[2] == '\0') {
-          info->type = "pdir";
+        if (strlen(dent_name) >= 2) {
+          if (dent_name[1] == '.' &&
+              dent_name[2] == '\0') {
+            info->type = "pdir";
+          }
         }
       }
     }
@@ -538,10 +531,10 @@ static int facts_mlinfo_get(struct mlinfo *info, const char *path,
   return 0;
 }
 
-static void facts_mlinfo_add(struct mlinfo *info) {
+static void facts_mlinfo_add(struct mlinfo *info, int flags) {
   char buf[PR_TUNABLE_BUFFER_SIZE];
 
-  (void) facts_mlinfo_fmt(info, buf, sizeof(buf));
+  (void) facts_mlinfo_fmt(info, buf, sizeof(buf), flags);
 
   /* The trailing CRLF will be added by pr_response_add(). */
   pr_response_add(R_DUP, "%s", buf);
@@ -1239,7 +1232,14 @@ MODRET facts_mlsd(cmd_rec *cmd) {
 
   /* Open data connection */
   if (pr_data_open(NULL, C_MLSD, PR_NETIO_IO_WR, 0) < 0) {
+    int xerrno = errno;
+
     pr_fsio_closedir(dirh);
+
+    pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[0],
+      strerror(xerrno));
+
+    errno = xerrno;
     return PR_ERROR(cmd);
   }
   session.sf_flags |= SF_ASCII_OVERRIDE;
@@ -1292,7 +1292,7 @@ MODRET facts_mlsd(cmd_rec *cmd) {
      */
     info.path = pr_fs_encode_path(cmd->tmp_pool, dent->d_name);
 
-    facts_mlinfobuf_add(&info);
+    facts_mlinfobuf_add(&info, FACTS_MLINFO_FL_APPEND_CRLF);
 
     if (XFER_ABORTED) {
       pr_data_abort(0, 0);
@@ -1425,6 +1425,13 @@ MODRET facts_mlst(cmd_rec *cmd) {
 
   info.pool = cmd->tmp_pool;
 
+  /* Since this is an MLST command, we are not listing the contents of
+   * of a directory, we're only showing the entry for a path, whether
+   * directory or not.  Thus the "cdir" type fact should not be used
+   * (Bug#4198).
+   */
+  flags |= FACTS_MLINFO_FL_NO_CDIR;
+
   pr_fs_clear_cache();
   if (facts_mlinfo_get(&info, decoded_path, decoded_path, flags, fake_uid,
       fake_gid, fake_mode) < 0) {
@@ -1458,7 +1465,7 @@ MODRET facts_mlst(cmd_rec *cmd) {
   }
 
   pr_response_add(R_250, _("Start of list for %s"), path);
-  facts_mlinfo_add(&info);
+  facts_mlinfo_add(&info, 0);
   pr_response_add(R_250, _("End of list"));
 
   return PR_HANDLED(cmd);
diff --git a/modules/mod_log.c b/modules/mod_log.c
index 38f2cd1..5200b40 100644
--- a/modules/mod_log.c
+++ b/modules/mod_log.c
@@ -2,7 +2,7 @@
  * ProFTPD - FTP server daemon
  * Copyright (c) 1997, 1998 Public Flood Software
  * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver at tos.net>
- * Copyright (c) 2001-2013 The ProFTPD Project team
+ * Copyright (c) 2001-2015 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
@@ -24,9 +24,7 @@
  * the source code for OpenSSL in the source distribution.
  */
 
-/* Flexible logging module for proftpd
- * $Id: mod_log.c,v 1.155 2013-11-11 01:34:04 castaglia Exp $
- */
+/* Flexible logging module for proftpd */
 
 #include "conf.h"
 #include "privs.h"
@@ -38,6 +36,7 @@ module log_module;
 #define EXTENDED_LOG_BUFFER_SIZE		(PR_TUNABLE_PATH_MAX + 128)
 
 #define EXTENDED_LOG_MODE			0644
+#define EXTENDED_LOG_FORMAT_DEFAULT		"default"
 
 typedef struct logformat_struc	logformat_t;
 typedef struct logfile_struc 	logfile_t;
@@ -433,8 +432,9 @@ static void logformat(const char *directive, char *nickname, char *fmts) {
   lf->lf_format = palloc(log_pool, outs - format);
   memcpy(lf->lf_format, format, outs - format);
 
-  if (!format_set)
+  if (format_set == NULL) {
     format_set = xaset_create(log_pool, NULL);
+  }
 
   xaset_insert_end(format_set, (xasetmember_t *) lf);
   formats = (logformat_t *) format_set->xas_list;
@@ -1721,8 +1721,9 @@ MODRET log_any(cmd_rec *cmd) {
 
       if (!session.anon_config &&
           lf->lf_conf &&
-          lf->lf_conf->config_type == CONF_ANON)
+          lf->lf_conf->config_type == CONF_ANON) {
         continue;
+      }
 
       do_log(cmd, lf);
     }
@@ -1892,6 +1893,19 @@ static void find_extendedlogs(void) {
       }
 
       if (logfmt == NULL) {
+        if (strcasecmp(logfmt_s, EXTENDED_LOG_FORMAT_DEFAULT) == 0) {
+          /* Try again, this time looking for the default LogFormat
+           * name, which is registered using a nickname of "".
+           */
+          for (logfmt = formats; logfmt; logfmt = logfmt->next) {
+            if (strcmp(logfmt->lf_nickname, "") == 0) {
+              break;
+            }
+          }
+        }
+      }
+
+      if (logfmt == NULL) {
         pr_log_pri(PR_LOG_NOTICE,
           "ExtendedLog '%s' uses unknown format nickname '%s'", logfname,
           logfmt_s);
diff --git a/modules/mod_ls.c b/modules/mod_ls.c
index e55f1e4..e9a9049 100644
--- a/modules/mod_ls.c
+++ b/modules/mod_ls.c
@@ -1816,6 +1816,12 @@ static int dolist(cmd_rec *cmd, const char *opt, int clearflags) {
     /* Open data connection */
     if (!opt_STAT) {
       if (pr_data_open(NULL, "file list", PR_NETIO_IO_WR, 0) < 0) {
+        int xerrno = errno;
+
+        pr_response_add_err(R_450, "%s: %s", (char *) cmd->argv[0],
+          strerror(xerrno));
+
+        errno = xerrno;
         return -1;
       }
 
@@ -2044,6 +2050,12 @@ static int dolist(cmd_rec *cmd, const char *opt, int clearflags) {
     /* Open data connection */
     if (!opt_STAT) {
       if (pr_data_open(NULL, "file list", PR_NETIO_IO_WR, 0) < 0) {
+        int xerrno = errno;
+
+        pr_response_add_err(R_450, "%s: %s", (char *) cmd->argv[0], 
+          strerror(xerrno));
+
+        errno = xerrno;
         return -1;
       }
 
@@ -2826,6 +2838,12 @@ MODRET ls_nlst(cmd_rec *cmd) {
         } else {
           if (list_flags & LS_FL_NO_ERROR_IF_ABSENT) {
             if (pr_data_open(NULL, "file list", PR_NETIO_IO_WR, 0) < 0) {
+              int xerrno = errno;
+
+              pr_response_add_err(R_450, "%s: %s", (char *) cmd->argv[0], 
+                strerror(xerrno));
+
+              errno = xerrno;
               return PR_ERROR(cmd);
             }
 
@@ -2843,6 +2861,12 @@ MODRET ls_nlst(cmd_rec *cmd) {
       } else {
         if (list_flags & LS_FL_NO_ERROR_IF_ABSENT) {
           if (pr_data_open(NULL, "file list", PR_NETIO_IO_WR, 0) < 0) {
+            int xerrno = errno;
+
+            pr_response_add_err(R_450, "%s: %s", (char *) cmd->argv[0],
+              strerror(xerrno));
+
+            errno = xerrno;
             return PR_ERROR(cmd);
           }
 
@@ -2859,6 +2883,12 @@ MODRET ls_nlst(cmd_rec *cmd) {
     }
 
     if (pr_data_open(NULL, "file list", PR_NETIO_IO_WR, 0) < 0) {
+      int xerrno = errno;
+
+      pr_response_add_err(R_450, "%s: %s", (char *) cmd->argv[0],
+        strerror(xerrno));
+
+      errno = xerrno;
       return PR_ERROR(cmd);
     }
 
@@ -2935,6 +2965,12 @@ MODRET ls_nlst(cmd_rec *cmd) {
       if (xerrno == ENOENT &&
           (list_flags & LS_FL_NO_ERROR_IF_ABSENT)) {
         if (pr_data_open(NULL, "file list", PR_NETIO_IO_WR, 0) < 0) {
+          xerrno = errno;
+
+          pr_response_add_err(R_450, "%s: %s", (char *) cmd->argv[0],
+            strerror(xerrno));
+
+          errno = xerrno;
           return PR_ERROR(cmd);
         }
         session.sf_flags |= SF_ASCII_OVERRIDE;
@@ -2964,6 +3000,12 @@ MODRET ls_nlst(cmd_rec *cmd) {
 
           if (list_flags & LS_FL_NO_ERROR_IF_ABSENT) {
             if (pr_data_open(NULL, "file list", PR_NETIO_IO_WR, 0) < 0) {
+              int xerrno = errno;
+
+              pr_response_add_err(R_450, "%s: %s", (char *) cmd->argv[0],
+                strerror(xerrno));
+
+              errno = xerrno;
               return PR_ERROR(cmd);
             }
             session.sf_flags |= SF_ASCII_OVERRIDE;
@@ -2995,6 +3037,12 @@ MODRET ls_nlst(cmd_rec *cmd) {
       if (xerrno == ENOENT &&
           (list_flags & LS_FL_NO_ERROR_IF_ABSENT)) {
         if (pr_data_open(NULL, "file list", PR_NETIO_IO_WR, 0) < 0) {
+          xerrno = errno;
+
+          pr_response_add_err(R_450, "%s: %s", (char *) cmd->argv[0],
+            strerror(xerrno));
+
+          errno = xerrno;
           return PR_ERROR(cmd);
         }
         session.sf_flags |= SF_ASCII_OVERRIDE;
@@ -3012,6 +3060,12 @@ MODRET ls_nlst(cmd_rec *cmd) {
 
     if (S_ISREG(st.st_mode)) {
       if (pr_data_open(NULL, "file list", PR_NETIO_IO_WR, 0) < 0) {
+        int xerrno = errno;
+
+        pr_response_add_err(R_450, "%s: %s", (char *) cmd->argv[0],
+          strerror(xerrno));
+
+        errno = xerrno;
         return PR_ERROR(cmd);
       }
       session.sf_flags |= SF_ASCII_OVERRIDE;
diff --git a/modules/mod_xfer.c b/modules/mod_xfer.c
index f09046d..6e1b2a2 100644
--- a/modules/mod_xfer.c
+++ b/modules/mod_xfer.c
@@ -2,7 +2,7 @@
  * ProFTPD - FTP server daemon
  * Copyright (c) 1997, 1998 Public Flood Software
  * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver at tos.net>
- * Copyright (c) 2001-2015 The ProFTPD Project team
+ * Copyright (c) 2001-2016 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
@@ -1351,7 +1351,7 @@ MODRET xfer_pre_stor(cmd_rec *cmd) {
 MODRET xfer_pre_stou(cmd_rec *cmd) {
   config_rec *c = NULL;
   char *prefix = "ftp", *filename = NULL;
-  int tmpfd;
+  int stou_fd;
   mode_t mode;
   unsigned char *allow_overwrite = NULL;
 
@@ -1386,16 +1386,17 @@ MODRET xfer_pre_stou(cmd_rec *cmd) {
    * unique filename prefix.
    */
   c = find_config(CURRENT_CONF, CONF_PARAM, "StoreUniquePrefix", FALSE);
-  if (c != NULL)
+  if (c != NULL) {
     prefix = c->argv[0];
+  }
 
   /* Now, construct the unique filename using the cmd_rec's pool, the
    * prefix, and mkstemp().
    */
   filename = pstrcat(cmd->pool, prefix, "XXXXXX", NULL);
 
-  tmpfd = mkstemp(filename);
-  if (tmpfd < 0) {
+  stou_fd = mkstemp(filename);
+  if (stou_fd < 0) {
     int xerrno = errno;
 
     pr_log_pri(PR_LOG_WARNING, "error: unable to use mkstemp(): %s",
@@ -1416,13 +1417,13 @@ MODRET xfer_pre_stou(cmd_rec *cmd) {
      * opens the unique file, but this may have to do, as closing that
      * race would involve some major restructuring.
      */
-    close(tmpfd);
+    (void) close(stou_fd);
   }
 
   /* It's OK to reuse the char * pointer for filename. */
   filename = dir_best_path(cmd->tmp_pool, cmd->arg);
 
-  if (!filename ||
+  if (filename == NULL ||
       !dir_check(cmd->tmp_pool, cmd, cmd->group, filename, NULL)) {
     int xerrno = errno;
 
@@ -1477,22 +1478,30 @@ MODRET xfer_pre_stou(cmd_rec *cmd) {
 }
 
 /* xfer_post_stou() is a POST_CMD handler that changes the mode of the
- * STOU file from 0600, which is what mkstemp() makes it, to 0666,
- * the default for files uploaded via STOR.  This is to prevent users
+ * STOU file from 0600, which is what mkstemp() makes it, to 0666 (modulo
+ * Umask), the default for files uploaded via STOR.  This is to prevent users
  * from being surprised.
  */
 MODRET xfer_post_stou(cmd_rec *cmd) {
+  mode_t mask, perms, *umask;
 
-  /* This is the same mode as used in src/fs.c.  Should probably be
-   * available as a macro.
+  /* mkstemp(3) creates a file with 0600 perms; we need to adjust this
+   * for the Umask (Bug#4223).
    */
-  mode_t mode = 0666;
+  umask = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
+  if (umask != NULL) {
+    mask = *umask;
+
+  } else {
+    mask = (mode_t) 0022;
+  }
 
-  if (pr_fsio_chmod(cmd->arg, mode) < 0) {
+  perms = (0666 & ~mask);
 
+  if (pr_fsio_chmod(cmd->arg, perms) < 0) {
     /* Not much to do but log the error. */
     pr_log_pri(PR_LOG_NOTICE, "error: unable to chmod '%s' to %04o: %s",
-      cmd->arg, mode, strerror(errno));
+      cmd->arg, perms, strerror(errno));
   }
 
   return PR_DECLINED(cmd);
diff --git a/src/Makefile.in b/src/Makefile.in
index c9e4702..3a42ccb 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -25,5 +25,5 @@ clean:
 	rm -f *.o
 
 depend:
-	makedepend $(CPPFLAGS) -Y *.c 2>/dev/null
-	makedepend $(CPPFLAGS) -Y -fMakefile.in *.c 2>/dev/null
+	$(MAKEDEPEND) $(CPPFLAGS) *.c 2>/dev/null
+	$(MAKEDEPEND) $(CPPFLAGS) -fMakefile.in *.c 2>/dev/null
diff --git a/src/data.c b/src/data.c
index 5da377b..5136f5f 100644
--- a/src/data.c
+++ b/src/data.c
@@ -358,14 +358,30 @@ static int data_active_open(char *reason, off_t size) {
    */
   bind_port = session.c->local_port-1;
 
-  /* A RootRevoke value of 0 indicates 'false', 1 indicates 'true', and
-   * 2 indicates 'NonCompliantActiveTransfer'.  We change the source port for
-   * a RootRevoke value of 2.
-   */
   root_revoke = get_param_ptr(TOPLEVEL_CONF, "RootRevoke", FALSE);
-  if (root_revoke != NULL &&
-      *root_revoke == 2) {
-    bind_port = INPORT_ANY;
+  if (root_revoke != NULL) {
+    /* A RootRevoke value of 0 indicates 'false', 1 indicates 'true', and
+     * 2 indicates 'NonCompliantActiveTransfer'.  We change the source port for
+     * a RootRevoke value of 2, and for a value of 1, we make sure that
+     * that the port is not a privileged port.
+     */
+    switch (*root_revoke) {
+      case 1:
+        if (bind_port < 1024) {
+          pr_log_debug(DEBUG0, "RootRevoke in effect, unable to bind to local "
+            "port %d for active transfer", bind_port);
+          errno = EPERM;
+          return -1;
+        }
+        break;
+
+      case 2:
+        bind_port = INPORT_ANY;
+        break;
+
+      default:
+        break;
+    }
   }
 
   session.d = pr_inet_create_conn(session.pool, -1, bind_addr, bind_port, TRUE);
diff --git a/src/encode.c b/src/encode.c
index dd4bf79..b2ceeae 100644
--- a/src/encode.c
+++ b/src/encode.c
@@ -1,6 +1,6 @@
 /*
  * ProFTPD - FTP server daemon
- * Copyright (c) 2006-2012 The ProFTPD Project team
+ * Copyright (c) 2006-2015 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
@@ -22,9 +22,7 @@
  * OpenSSL in the source distribution.
  */
 
-/* UTF8/charset encoding/decoding
- * $Id: encode.c,v 1.33 2012-04-06 16:44:40 castaglia Exp $
- */
+/* UTF8/charset encoding/decoding */
 
 #include "conf.h"
 
@@ -65,7 +63,7 @@ static int str_convert(iconv_t conv, const char *inbuf, size_t *inbuflen,
      */
 #if defined(LINUX) || defined(DARWIN6) || defined(DARWIN7) || \
     defined(DARWIN8) || defined(DARWIN9) || defined(DARWIN10) || \
-    defined(DARWIN11)
+    defined(DARWIN11) || defined(DARWIN12)
 
     nconv = iconv(conv, (char **) &inbuf, inbuflen, &outbuf, outbuflen);
 #else
diff --git a/src/fsio.c b/src/fsio.c
index 333a17f..f088202 100644
--- a/src/fsio.c
+++ b/src/fsio.c
@@ -1717,6 +1717,30 @@ int pr_fs_dircat(char *buf, int buflen, const char *dir1, const char *dir2) {
   dir1len = strlen(dir1);
   dir2len = strlen(dir2);
 
+  /* If both strings are empty, then the "concatenation" becomes trivial. */
+  if (dir1len == 0 &&
+      dir2len == 0) {
+    buf[0] = '/';
+    buf[1] = '\0';
+    return 0;
+  }
+
+  /* If dir2 is non-empty, but dir1 IS empty... */
+  if (dir1len == 0) {
+    sstrncpy(buf, dir2, buflen);
+    buflen -= dir2len;
+    sstrcat(buf, "/", buflen);
+    return 0;
+  }
+
+  /* Likewise, if dir1 is non-empty, but dir2 IS empty... */
+  if (dir2len == 0) {
+    sstrncpy(buf, dir1, buflen);
+    buflen -= dir1len;
+    sstrcat(buf, "/", buflen);
+    return 0;
+  }
+
   if ((dir1len + dir2len + 1) >= PR_TUNABLE_PATH_MAX) {
     errno = ENAMETOOLONG;
     buf[0] = '\0';  
@@ -1751,7 +1775,8 @@ int pr_fs_dircat(char *buf, int buflen, const char *dir1, const char *dir2) {
   buflen -= dir1len;
 
   if (buflen > 0 &&
-     *(_dir1 + (dir1len-1)) != '/') {
+      dir1len >= 1 &&
+      *(_dir1 + (dir1len-1)) != '/') {
     sstrcat(ptr, "/", buflen);
     ptr += 1;
     buflen -= 1;
diff --git a/src/ftpscrub.c b/src/ftpscrub.c
deleted file mode 100644
index e7dd8f3..0000000
--- a/src/ftpscrub.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * ProFTPD - FTP server daemon
- * Copyright (c) 2001-2008 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
- *
- * As a special exemption, The ProFTPD Project and other respective copyright
- * holders give permission to link this program with OpenSSL, and distribute
- * the resulting executable, without including the source code for OpenSSL in
- * the source distribution.
- */
-
-/* "Scrubs" the scoreboard file, clearing it of old/stale entries.
- * $Id: ftpscrub.c,v 1.1 2009-02-18 21:33:17 castaglia Exp $
- */
-
-#include "utils.h"
-
-static const char *config_filename = PR_CONFIG_FILE_PATH;
-
-static int check_scoreboard_file(void) {
-  struct stat st;
-
-  if (stat(util_get_scoreboard(), &st) < 0)
-    return -1;
-
-  return 0;
-}
-
-static struct option_help {
-  const char *long_opt, *short_opt, *desc;
-} opts_help[] = {
-  { "--config",	"-c",	"specify full path to proftpd configuration file" },
-  { "--file",	"-f",	"specify full path to scoreboard file" },
-  { "--help",	"-h",	NULL },
-  { "--verbose","-v",	NULL },
-  { NULL }
-};
-
-#ifdef HAVE_GETOPT_LONG
-static struct option opts[] = {
-  { "config",  1, NULL, 'c' },
-  { "file",    1, NULL, 'f' },
-  { "help",    0, NULL, 'h' },
-  { "verbose", 0, NULL, 'v' },
-  { NULL,      0, NULL, 0   }
-};
-#endif /* HAVE_GETOPT_LONG */
-
-static void show_usage(const char *progname, int exit_code) {
-  struct option_help *h = NULL;
-
-  printf("usage: %s [options]\n", progname);
-  for (h = opts_help; h->long_opt; h++) {
-#ifdef HAVE_GETOPT_LONG
-    printf("  %s, %s\n", h->short_opt, h->long_opt);
-#else /* HAVE_GETOPT_LONG */
-    printf("  %s\n", h->short_opt);
-#endif
-    if (!h->desc)
-      printf("    display %s usage\n", progname);
-    else
-      printf("    %s\n", h->desc);
-  }
-
-  exit(exit_code);
-}
-
-int main(int argc, char **argv) {
-  int c = 0, res = 0;
-  int verbose = FALSE;
-  char *cp, *progname = *argv;
-  const char *cmdopts = "c:f:hv";
-
-  cp = strrchr(progname, '/');
-  if (cp != NULL)
-    progname = cp+1;
-
-  opterr = 0;
-  while ((c =
-#ifdef HAVE_GETOPT_LONG
-	 getopt_long(argc, argv, cmdopts, opts, NULL)
-#else /* HAVE_GETOPT_LONG */
-	 getopt(argc, argv, cmdopts)
-#endif /* HAVE_GETOPT_LONG */
-	 ) != -1) {
-    switch (c) {
-      case 'h':
-        show_usage(progname, 0);
-
-      case 'f':
-        util_set_scoreboard(optarg);
-        break;
-
-      case 'c':
-        config_filename = strdup(optarg);
-        break;
-
-      case 'v':
-        verbose = TRUE;
-        break;
-
-      case '?':
-        fprintf(stderr, "unknown option: %c\n", (char) optopt);
-        show_usage(progname, 1);
-    }
-  }
-
-  /* First attempt to check the supplied/default scoreboard path.  If this is
-   * incorrect, try the config file kludge.
-   */
-  if (check_scoreboard_file() < 0) {
-    const char *file = util_scan_config(config_filename, "ScoreboardFile");
-    if (file)
-      util_set_scoreboard(file);
-
-    if (check_scoreboard_file() < 0) {
-      fprintf(stderr, "%s: %s\n", util_get_scoreboard(), strerror(errno));
-      fprintf(stderr, "(Perhaps you need to specify the ScoreboardFile with -f, or change\n");
-      fprintf(stderr," the compile-time default directory?)\n");
-      exit(1);
-    }
-  }
-
-  res = util_scoreboard_scrub(verbose);
-  if (res < 0) {
-    fprintf(stderr, "error scrubbing scoreboard %s: %s\n",
-      util_get_scoreboard(), strerror(errno));
-    return 1;
-  }
-
-  return 0;
-}
diff --git a/src/inet.c b/src/inet.c
index 7676cd9..4a3e7df 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -236,6 +236,7 @@ static conn_t *init_conn(pool *p, int fd, pr_netaddr_t *bind_addr,
     defined(__OpenBSD__) || defined(__NetBSD__) || \
     defined(DARWIN6) || defined(DARWIN7) || defined(DARWIN8) || \
     defined(DARWIN9) || defined(DARWIN10) || defined(DARWIN11) || \
+    defined(DARWIN12) || \
     defined(SCO3) || defined(CYGWIN) || defined(SYSV4_2MP) || \
     defined(SYSV5SCO_SV6) || defined(SYSV5UNIXWARE7)
 # ifdef SOLARIS2
@@ -260,6 +261,7 @@ static conn_t *init_conn(pool *p, int fd, pr_netaddr_t *bind_addr,
     defined(__OpenBSD__) || defined(__NetBSD__) || \
     defined(DARWIN6) || defined(DARWIN7) || defined(DARWIN8) || \
     defined(DARWIN9) || defined(DARWIN10) || defined(DARWIN11) || \
+    defined(DARWIN12) || \
     defined(SCO3) || defined(CYGWIN) || defined(SYSV4_2MP) || \
     defined(SYSV5SCO_SV6) || defined(SYSV5UNIXWARE7)
 # ifdef SOLARIS2
diff --git a/src/main.c b/src/main.c
index 0945bc8..98f7526 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1530,14 +1530,20 @@ static void daemon_loop(void) {
     }
 
     running = 1;
+    xerrno = errno = 0;
 
     PR_DEVEL_CLOCK(i = select(maxfd + 1, &listenfds, NULL, NULL, &tv));
     if (i < 0) {
       xerrno = errno;
     }
 
-    if (i == -1 && xerrno == EINTR) {
+    if (i == -1 &&
+        xerrno == EINTR) {
+      errno = xerrno;
       pr_signals_handle();
+
+      /* We handled our signal; clear errno. */
+      xerrno = errno = 0;
       continue;
     }
 
@@ -1679,6 +1685,9 @@ void pr_signals_handle(void) {
       (unsigned long) tv.tv_usec, tv.tv_usec != 1 ? "microsecs" : "microsec");
 
     pr_timer_usleep(interval_usecs);
+
+    /* Clear the EINTR errno, now we've dealt with it. */
+    errno = 0;
   }
 
   while (recvd_signal_flags) {
diff --git a/src/pool.c b/src/pool.c
index 43b53c8..f620694 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -86,43 +86,37 @@ static void oom_printf(const char *fmt, ...) {
 /* Lowest level memory allocation functions
  */
 
-static void *null_alloc(size_t size) {
-  void *ret = NULL;
-
-  if (size == 0) {
-    /* Yes, this code is correct.
-     *
-     * The size argument is the originally requested amount of memory.
-     * null_alloc() is called because smalloc() returned NULL.  But why,
-     * exactly?  If the requested size is zero, then it may not have been
-     * an error -- or it may be because the system is actually out of memory.
-     * To differentiate, we do a malloc(0) call here if the requested size is
-     * zero.  If malloc(0) returns NULL, then we really do have an error.
-     */
-    ret = malloc(size);
-  }
-
-  if (ret == NULL) {
-    pr_log_pri(PR_LOG_ALERT, "Out of memory!");
+static void null_alloc(void) {
+  pr_log_pri(PR_LOG_ALERT, "Out of memory!");
 #ifdef PR_USE_DEVEL
-    if (debug_flags & PR_POOL_DEBUG_FL_OOM_DUMP_POOLS) {
-      pr_pool_debug_memory(oom_printf);
-    }
-#endif
-    exit(1);
+  if (debug_flags & PR_POOL_DEBUG_FL_OOM_DUMP_POOLS) {
+    pr_pool_debug_memory(oom_printf);
   }
+#endif
 
-  return ret;
+  exit(1);
 }
 
 static void *smalloc(size_t size) {
-  void *ret;
+  void *res;
 
-  ret = malloc(size);
-  if (ret == 0)
-    ret = null_alloc(size);
+  if (size == 0) {
+    /* Avoid zero-length malloc(); on non-POSIX systems, the behavior is
+     * not dependable.  And on POSIX systems, malloc(3) might still return
+     * a "unique pointer" for a zero-length allocation (or NULL).
+     *
+     * Either way, a zero-length allocation request here means that someone
+     * is doing something they should not be doing.
+     */
+    null_alloc();
+  }
+
+  res = malloc(size);
+  if (res == NULL) {
+    null_alloc();
+  }
 
-  return ret;
+  return res;
 }
 
 /* Grab a completely new block from the system pool.  Relies on malloc()
@@ -553,7 +547,12 @@ static void *alloc_pool(struct pool_rec *p, size_t reqsz, int exact) {
   char *new_first_avail;
 
   if (reqsz == 0) {
-    /* Don't try to allocate memory of zero length. */
+    /* Don't try to allocate memory of zero length.
+     *
+     * This should NOT happen normally; if it does, by returning NULL we
+     * almost guarantee a null pointer dereference.
+     */
+    errno = EINVAL;
     return NULL;
   }
 
diff --git a/src/support.c b/src/support.c
index 44e6ff9..2341dce 100644
--- a/src/support.c
+++ b/src/support.c
@@ -2,7 +2,7 @@
  * ProFTPD - FTP server daemon
  * Copyright (c) 1997, 1998 Public Flood Software
  * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver at tos.net>
- * Copyright (c) 2001-2013 The ProFTPD Project team
+ * Copyright (c) 2001-2015 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
@@ -26,8 +26,6 @@
 
 /* Various basic support routines for ProFTPD, used by all modules
  * and not specific to one or another.
- *
- * $Id: support.c,v 1.120 2013-08-07 16:35:27 castaglia Exp $
  */
 
 #include "conf.h"
@@ -654,7 +652,7 @@ void pr_getopt_reset(void) {
     defined(FREEBSD7) || defined(FREEBSD8) || defined(FREEBSD9) || \
     defined(FREEBSD10) || \
     defined(DARWIN7) || defined(DARWIN8) || defined(DARWIN9) || \
-    defined(DARWIN10) || defined(DARWIN11)
+    defined(DARWIN10) || defined(DARWIN11) || defined(DARWIN12)
   optreset = 1;
   opterr = 1;
   optind = 1;
diff --git a/tests/api/fsio.c b/tests/api/fsio.c
index 5efb21b..3b4b360 100644
--- a/tests/api/fsio.c
+++ b/tests/api/fsio.c
@@ -172,6 +172,22 @@ START_TEST (fs_dircat_test) {
   fail_unless(res == 0, "Failed to concatenate two empty paths");
   fail_unless(strcmp(buf, ok) == 0, "Expected concatenated dir '%s', got '%s'",
     ok, buf);
+
+  a = "/foo";
+  b = "";
+  ok = "/foo/";
+  res = pr_fs_dircat(buf, sizeof(buf)-1, a, b);
+  fail_unless(res == 0, "Failed to concatenate two empty paths");
+  fail_unless(strcmp(buf, ok) == 0, "Expected concatenated dir '%s', got '%s'",
+    ok, buf);
+
+  a = "";
+  b = "/bar";
+  ok = "/bar/";
+  res = pr_fs_dircat(buf, sizeof(buf)-1, a, b);
+  fail_unless(res == 0, "Failed to concatenate two empty paths");
+  fail_unless(strcmp(buf, ok) == 0, "Expected concatenated dir '%s', got '%s'",
+    ok, buf);
 }
 END_TEST
 
diff --git a/tests/api/str.c b/tests/api/str.c
index fc9ecf0..a05de2c 100644
--- a/tests/api/str.c
+++ b/tests/api/str.c
@@ -230,10 +230,11 @@ START_TEST (sreplace_enospc_test) {
   char *fmt = NULL, *res;
   size_t bufsz = 8192;
 
-  fmt = palloc(p, bufsz);
+  fmt = palloc(p, bufsz + 1);
   memset(fmt, ' ', bufsz);
   fmt[bufsz-2] = '%';
   fmt[bufsz-1] = 'a';
+  fmt[bufsz] = '\0';
 
   res = sreplace(p, fmt, "%a", "foo", NULL);
   fail_unless(res == NULL, "Failed to reject too-long buffer");
diff --git a/utils/Makefile.in b/utils/Makefile.in
index 527277f..c3d705e 100644
--- a/utils/Makefile.in
+++ b/utils/Makefile.in
@@ -25,5 +25,5 @@ clean:
 	rm -f *.o
 
 depend:
-	makedepend $(CPPFLAGS) -Y *.c 2>/dev/null
-	makedepend $(CPPFLAGS) -Y -fMakefile.in *.c 2>/dev/null
+	$(MAKEDEPEND) $(CPPFLAGS) *.c 2>/dev/null
+	$(MAKEDEPEND) $(CPPFLAGS) -fMakefile.in *.c 2>/dev/null

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-proftpd/proftpd-dfsg.git



More information about the Pkg-proftpd-maintainers mailing list