[Git][debian-proftpd-team/proftpd][master] Add some patches from upstream. See debian/changelog.

Hilmar Preuße gitlab at salsa.debian.org
Thu Jul 30 22:59:33 BST 2020



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


Commits:
18c0faa2 by Hilmar Preusse at 2020-07-30T23:58:46+02:00
Add some patches from upstream. See debian/changelog.

- - - - -


5 changed files:

- debian/changelog
- debian/patches/series
- + debian/patches/upstream_1061
- + debian/patches/upstream_1063
- + debian/patches/upstream_1070


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,16 @@
+proftpd-dfsg (1.3.7a-2) UNRELEASED; urgency=medium
+
+  * Applied some patches pulled from upstream.
+    - upstream_1063: Avoid segfaults for TLSv1.3 data transfers in
+      our session tickey callback by checking the status before using
+      SSL_SESSION pointer.
+    - upstream_1070: Implement support for Redis 6.x AUTH semantics.
+    - upstream_1061: While investigating some reported issues with
+      Ed25519 keys and mod_sftp, I reproduced one segfault when verifying
+      such keys during publickey authentication.
+
+ -- Hilmar Preusse <hille42 at web.de>  Thu, 30 Jul 2020 23:16:38 +0200
+
 proftpd-dfsg (1.3.7a-1) unstable; urgency=medium
 
   New upstream release 1.3.7a:


=====================================
debian/patches/series
=====================================
@@ -11,3 +11,6 @@ odbc
 wrong-path-for-interpreter_perl.diff
 #cd9036f4ef7a05c107f0ffcb19a018b20267c531.patch
 #   proftpd-mysql-password-backend.patch
+upstream_1063
+upstream_1070
+upstream_1061


=====================================
debian/patches/upstream_1061
=====================================
@@ -0,0 +1,50 @@
+From 1117b003fa25c2e29e3a679ba3a1a3b92b5a4587 Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Sat, 25 Jul 2020 16:51:26 -0700
+Subject: [PATCH] Issue #1061: While investigating some reported issues with
+ Ed25519 keys and mod_sftp, I reproduced one segfault when verifying such keys
+ during publickey authentication.
+
+---
+ contrib/mod_sftp/kex.c  | 2 +-
+ contrib/mod_sftp/keys.c | 7 +++++--
+ 2 files changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/contrib/mod_sftp/kex.c b/contrib/mod_sftp/kex.c
+index 17e853fe2..59c76f3c0 100644
+--- a/contrib/mod_sftp/kex.c
++++ b/contrib/mod_sftp/kex.c
+@@ -2869,7 +2869,7 @@ static int write_ext_info_server_sig_algs(struct ssh2_packet *pkt, char **buf,
+   char *sig_algs = "";
+ 
+ #if defined(PR_USE_SODIUM)
+-  sig_algs = pstrcat(pkt->pool, sig_algs, *sig_algs ? "," : "", "ssh-ec25519",
++  sig_algs = pstrcat(pkt->pool, sig_algs, *sig_algs ? "," : "", "ssh-ed25519",
+     NULL);
+ #endif /* PR_USE_SODIUM */
+ 
+diff --git a/contrib/mod_sftp/keys.c b/contrib/mod_sftp/keys.c
+index b2e03f722..c94a3b201 100644
+--- a/contrib/mod_sftp/keys.c
++++ b/contrib/mod_sftp/keys.c
+@@ -4453,7 +4453,7 @@ const unsigned char *sftp_keys_sign_data(pool *p,
+ 
+ int sftp_keys_verify_pubkey_type(pool *p, unsigned char *pubkey_data,
+     uint32_t pubkey_len, enum sftp_key_type_e pubkey_type) {
+-  EVP_PKEY *pkey;
++  EVP_PKEY *pkey = NULL;
+   int res = FALSE;
+   uint32_t len;
+ 
+@@ -4542,7 +4542,10 @@ int sftp_keys_verify_pubkey_type(pool *p, unsigned char *pubkey_data,
+       break;
+   }
+ 
+-  EVP_PKEY_free(pkey);
++  if (pkey != NULL) {
++    EVP_PKEY_free(pkey);
++  }
++
+   return res;
+ }
+ 


=====================================
debian/patches/upstream_1063
=====================================
@@ -0,0 +1,45 @@
+From adf43dd4ddaab0332e74abc86bbcef9cf27ee54a Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Sat, 25 Jul 2020 11:10:07 -0700
+Subject: [PATCH] Issue #1063: Avoid segfaults for TLSv1.3 data transfers in
+ our session tickey callback by checking the status before using SSL_SESSION
+ pointer.
+
+---
+ contrib/mod_tls.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+diff --git a/contrib/mod_tls.c b/contrib/mod_tls.c
+index c4e3a2f18..3cab789c9 100644
+--- a/contrib/mod_tls.c
++++ b/contrib/mod_tls.c
+@@ -6833,14 +6833,23 @@ static SSL_TICKET_RETURN tls_decrypt_session_ticket_data_upload_cb(SSL *ssl,
+     SSL_SESSION *ssl_session, const unsigned char *key_name, size_t key_namelen,
+     SSL_TICKET_STATUS status, void *user_data) {
+   SSL_TICKET_RETURN res;
+-  int ssl_version, renew_tickets = TRUE;
++  int renew_tickets = TRUE;
+ 
+-  ssl_version = SSL_SESSION_get_protocol_version(ssl_session);
++  /* Avoid using the given SSL_SESSION pointer unless the status indicates that
++   * that pointer is valid (Issue #1063).
++   */
++
++  if (status != SSL_TICKET_EMPTY &&
++      status != SSL_TICKET_NO_DECRYPT) {
++    int ssl_version;
++
++    ssl_version = SSL_SESSION_get_protocol_version(ssl_session);
+ # if defined(TLS1_3_VERSION)
+-  if (ssl_version == TLS1_3_VERSION) {
+-    pr_trace_msg(trace_channel, 29,
+-      "suppressing renewal of TLSv1.3 tickets for data transfers");
+-    renew_tickets = FALSE;
++    if (ssl_version == TLS1_3_VERSION) {
++      pr_trace_msg(trace_channel, 29,
++        "suppressing renewal of TLSv1.3 tickets for data transfers");
++      renew_tickets = FALSE;
++    }
+   }
+ # endif /* TLS1_3_VERSION */
+ 


=====================================
debian/patches/upstream_1070
=====================================
@@ -0,0 +1,705 @@
+From 3fe77bd7088af1e0f5822e12555a4f4221301dbe Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Sun, 26 Jul 2020 13:38:42 -0700
+Subject: [PATCH] Issue #1070: Implement support for Redis 6.x AUTH semantics.
+
+---
+ include/redis.h    |  10 ++-
+ src/redis.c        | 128 +++++++++++++++++++++++++--
+ tests/api/env.c    |   8 +-
+ tests/api/redis.c  | 210 ++++++++++++++++++++++++++++++++++++++-------
+ tests/api/sets.c   |  24 +++---
+ tests/api/str.c    |   8 +-
+ tests/api/timers.c |   2 +-
+ 7 files changed, 334 insertions(+), 56 deletions(-)
+
+diff --git a/include/redis.h b/include/redis.h
+index 27f71e454..eae519bfb 100644
+--- a/include/redis.h
++++ b/include/redis.h
+@@ -1,6 +1,6 @@
+ /*
+  * ProFTPD - FTP server daemon
+- * Copyright (c) 2017 The ProFTPD Project team
++ * Copyright (c) 2017-2020 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
+@@ -55,8 +55,14 @@ int pr_redis_conn_destroy(pr_redis_t *redis);
+ int pr_redis_conn_set_namespace(pr_redis_t *redis, module *m,
+   const void *prefix, size_t prefixsz);
+ 
++/* Redis server version. */
++int pr_redis_conn_get_version(pr_redis_t *redis, unsigned int *major_version,
++  unsigned int *minor_version, unsigned int *patch_version);
++
+ /* Authenticate to a password-protected Redis server. */
+ int pr_redis_auth(pr_redis_t *redis, const char *password);
++int pr_redis_auth2(pr_redis_t *redis, const char *username,
++  const char *password);
+ 
+ /* Select the database used by the Redis server. */
+ int pr_redis_select(pr_redis_t *redis, const char *db_idx);
+@@ -304,6 +310,8 @@ int pr_redis_sentinel_get_masters(pool *p, pr_redis_t *redis,
+ /* For internal use only */
+ int redis_set_server(const char *server, int port, unsigned long flags,
+   const char *password, const char *db_idx);
++int redis_set_server2(const char *server, int port, unsigned long flags,
++  const char *username, const char *password, const char *db_idx);
+ int redis_set_sentinels(array_header *sentinels, const char *name);
+ int redis_set_timeouts(unsigned long connect_millis, unsigned long io_millis);
+ 
+diff --git a/src/redis.c b/src/redis.c
+index 5bd99623f..cca05bd3f 100644
+--- a/src/redis.c
++++ b/src/redis.c
+@@ -49,6 +49,11 @@ struct redis_rec {
+    */
+   unsigned int refcount;
+ 
++  /* Redis server version. */
++  unsigned int major_version;
++  unsigned int minor_version;
++  unsigned int patch_version;
++
+   /* Table mapping modules to their namespaces */
+   pr_table_t *namespace_tab;
+ };
+@@ -59,6 +64,7 @@ static const char *redis_sentinel_master = NULL;
+ static const char *redis_server = NULL;
+ static int redis_port = -1;
+ static unsigned long redis_flags = 0UL;
++static const char *redis_username = NULL;
+ static const char *redis_password = NULL;
+ static const char *redis_db_idx = NULL;
+ 
+@@ -69,6 +75,8 @@ static unsigned long redis_io_millis = 500;
+ 
+ static const char *trace_channel = "redis";
+ 
++static const char *get_reply_type(int reply_type);
++
+ static void millis2timeval(struct timeval *tv, unsigned long millis) {
+   tv->tv_sec = (millis / 1000);
+   tv->tv_usec = (millis - (tv->tv_sec * 1000)) * 1000;
+@@ -194,6 +202,48 @@ static int ping_server(pr_redis_t *redis) {
+   return 0;
+ }
+ 
++static void parse_redis_version(pr_redis_t *redis, redisReply *info) {
++  pool *tmp_pool;
++  unsigned int major, minor, patch;
++  char *text, *version_text;
++
++  if (info->type != REDIS_REPLY_STRING) {
++    pr_trace_msg(trace_channel, 1, "expected STRING reply for INFO, got %s",
++      get_reply_type(info->type));
++    return;
++  }
++
++  tmp_pool = make_sub_pool(redis->pool);
++  pr_pool_tag(tmp_pool, "Redis version parsing pool");
++  text = pstrndup(tmp_pool, info->str, info->len);
++
++  /* Scan the entire INFO string for "redis_version:N.N.N". */
++
++  version_text = strstr(text, "redis_version:");
++  if (version_text == NULL) {
++    pr_trace_msg(trace_channel, 1, "no `redis_version` found in INFO reply");
++    destroy_pool(tmp_pool);
++    return;
++  }
++
++  if (sscanf(version_text, "redis_version:%u.%u.%u", &major, &minor,
++      &patch) == 3) {
++    redis->major_version = major;
++    redis->minor_version = minor;
++    redis->patch_version = patch;
++
++    pr_trace_msg(trace_channel, 9,
++      "parsed Redis version %u (major), %u (minor), %u (patch) out of INFO",
++      redis->major_version, redis->minor_version, redis->patch_version);
++
++  } else {
++    pr_trace_msg(trace_channel, 1, "failed to scan Redis version '%s'",
++      version_text);
++  }
++
++  destroy_pool(tmp_pool);
++}
++
+ static int stat_server(pr_redis_t *redis, const char *section) {
+   const char *cmd;
+   redisReply *reply;
+@@ -214,6 +264,18 @@ static int stat_server(pr_redis_t *redis, const char *section) {
+       (unsigned long) reply->len);
+   }
+ 
++  if (redis->major_version == 0 &&
++      (strcmp(section, "server") == 0 || strcmp(section, "") == 0)) {
++    /* We are particularly interested in the Redis server version; we key
++     * off of this version to detect when to change our command semantics,
++     * such as for AUTH.
++     *
++     * Thus we parse the server version out of the "server" info, unless
++     * the version is already known.
++     */
++    parse_redis_version(redis, reply);
++  }
++
+   freeReplyObject(reply);
+   return 0;
+ }
+@@ -508,7 +570,13 @@ pr_redis_t *pr_redis_conn_new(pool *p, module *m, unsigned long flags) {
+   }
+ 
+   if (redis_password != NULL) {
+-    res = pr_redis_auth(redis, redis_password);
++    if (redis_username != NULL) {
++      res = pr_redis_auth2(redis, redis_username, redis_password);
++
++    } else {
++      res = pr_redis_auth(redis, redis_password);
++    }
++
+     if (res < 0) {
+       xerrno = errno;
+ 
+@@ -682,6 +750,36 @@ int pr_redis_conn_set_namespace(pr_redis_t *redis, module *m,
+   return 0;
+ }
+ 
++int pr_redis_conn_get_version(pr_redis_t *redis, unsigned int *major_version,
++    unsigned int *minor_version, unsigned int *patch_version) {
++
++  if (redis == NULL) {
++    errno = EINVAL;
++    return -1;
++  }
++
++  if (major_version == NULL &&
++      minor_version == NULL &&
++      patch_version == NULL) {
++    errno = EINVAL;
++    return -1;
++  }
++
++  if (major_version != NULL) {
++    *major_version = redis->major_version;
++  }
++
++  if (minor_version != NULL) {
++    *minor_version = redis->minor_version;
++  }
++
++  if (patch_version != NULL) {
++    *patch_version = redis->patch_version;
++  }
++
++  return 0;
++}
++
+ int pr_redis_add(pr_redis_t *redis, module *m, const char *key, void *value,
+     size_t valuesz, time_t expires) {
+   int res;
+@@ -2003,12 +2101,14 @@ int pr_redis_command(pr_redis_t *redis, const array_header *args,
+   return 0;
+ }
+ 
+-int pr_redis_auth(pr_redis_t *redis, const char *password) {
++int pr_redis_auth2(pr_redis_t *redis, const char *username,
++    const char *password) {
+   const char *cmd;
+   pool *tmp_pool;
+   redisReply *reply;
+ 
+   if (redis == NULL ||
++      username == NULL ||
+       password == NULL) {
+     errno = EINVAL;
+     return -1;
+@@ -2019,7 +2119,15 @@ int pr_redis_auth(pr_redis_t *redis, const char *password) {
+ 
+   cmd = "AUTH";
+   pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
+-  reply = redisCommand(redis->ctx, "%s %s", cmd, password);
++
++  /* Redis 6.x changed the AUTH semantics, now requiring a username. */
++  if (redis->major_version >= 6) {
++    reply = redisCommand(redis->ctx, "%s %s %s", cmd, username, password);
++
++  } else {
++    reply = redisCommand(redis->ctx, "%s %s", cmd, password);
++  }
++
+   reply = handle_reply(redis, cmd, reply);
+   if (reply == NULL) {
+     pr_trace_msg(trace_channel, 2,
+@@ -2053,6 +2161,10 @@ int pr_redis_auth(pr_redis_t *redis, const char *password) {
+   return 0;
+ }
+ 
++int pr_redis_auth(pr_redis_t *redis, const char *password) {
++  return pr_redis_auth2(redis, "default", password);
++}
++
+ int pr_redis_select(pr_redis_t *redis, const char *db_idx) {
+   const char *cmd;
+   pool *tmp_pool;
+@@ -5632,8 +5744,8 @@ int pr_redis_sentinel_get_masters(pool *p, pr_redis_t *redis,
+   return res;
+ }
+ 
+-int redis_set_server(const char *server, int port, unsigned long flags,
+-    const char *password, const char *db_idx) {
++int redis_set_server2(const char *server, int port, unsigned long flags,
++    const char *username, const char *password, const char *db_idx) {
+ 
+   if (server == NULL) {
+     /* By using a port of -2 specifically, we can use this function to
+@@ -5649,12 +5761,18 @@ int redis_set_server(const char *server, int port, unsigned long flags,
+   redis_server = server;
+   redis_port = port;
+   redis_flags = flags;
++  redis_username = username;
+   redis_password = password;
+   redis_db_idx = db_idx;
+ 
+   return 0;
+ }
+ 
++int redis_set_server(const char *server, int port, unsigned long flags,
++    const char *password, const char *db_idx) {
++  return redis_set_server2(server, port, flags, "default", password, db_idx);
++}
++
+ int redis_set_sentinels(array_header *sentinels, const char *name) {
+ 
+   if (sentinels != NULL &&
+diff --git a/tests/api/env.c b/tests/api/env.c
+index ad126385e..b839f2ea4 100644
+--- a/tests/api/env.c
++++ b/tests/api/env.c
+@@ -1,6 +1,6 @@
+ /*
+  * ProFTPD - FTP server testsuite
+- * Copyright (c) 2008-2015 The ProFTPD Project team
++ * Copyright (c) 2008-2020 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
+@@ -61,11 +61,13 @@ START_TEST (env_get_test) {
+   pr_env_unset(p, key);
+ 
+   res = pr_env_get(p, key);
+-  fail_unless(res == NULL);
++  fail_unless(res == NULL, "Unexpectedly found value '%s' for key '%s'",
++    res, key);
+ 
+   /* XXX PATH should always be set in the environment, right? */
+   res = pr_env_get(p, "PATH");
+-  fail_unless(res != NULL);
++  fail_unless(res != NULL, "Failed to get value for 'PATH': %s",
++    strerror(errno));
+ 
+ #else
+   res = pr_env_get(p, key);
+diff --git a/tests/api/redis.c b/tests/api/redis.c
+index 7e95893ee..e44d5a4bd 100644
+--- a/tests/api/redis.c
++++ b/tests/api/redis.c
+@@ -222,11 +222,44 @@ START_TEST (redis_conn_set_namespace_test) {
+ }
+ END_TEST
+ 
++START_TEST (redis_conn_get_version_test) {
++  int res;
++  pr_redis_t *redis;
++  unsigned int major = 0, minor = 0, patch = 0;
++
++  mark_point();
++  res = pr_redis_conn_get_version(NULL, NULL, NULL, NULL);
++  fail_unless(res < 0, "Failed to handle null redis");
++  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
++    strerror(errno), errno);
++
++  mark_point();
++  redis = pr_redis_conn_new(p, NULL, 0);
++  fail_unless(redis != NULL, "Failed to open connection to Redis: %s",
++    strerror(errno));
++
++  mark_point();
++  res = pr_redis_conn_get_version(redis, NULL, NULL, NULL);
++  fail_unless(res < 0, "Failed to handle null version arguments");
++  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
++    strerror(errno), errno);
++
++  mark_point();
++  res = pr_redis_conn_get_version(redis, &major, &minor, &patch);
++  fail_unless(res == 0, "Failed to get Redis version: %s", strerror(errno));
++
++  mark_point();
++  res = pr_redis_conn_destroy(redis);
++  fail_unless(res == TRUE, "Failed to close redis: %s", strerror(errno));
++}
++END_TEST
++
+ START_TEST (redis_conn_auth_test) {
+   int res;
+   pr_redis_t *redis;
+   const char *text;
+   array_header *args;
++  unsigned int major_version = 0;
+ 
+   mark_point();
+   res = pr_redis_auth(NULL, NULL);
+@@ -245,52 +278,167 @@ START_TEST (redis_conn_auth_test) {
+   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
+     strerror(errno), errno);
+ 
+-  text = "password";
+-
+   /* What happens if we try to AUTH to a non-password-protected Redis?
+    * Answer: Redis returns an error indicating that no password is required.
++   *
++   * Note that this behavior changed with Redis 6.x.  In particular, any
++   * "AUTH default ..." command automatically succeeds with Redis 6.x,
++   * regardless of the actual password given.  Sigh.
+    */
++
++  mark_point();
++  res = pr_redis_conn_get_version(redis, &major_version, NULL, NULL);
++  fail_unless(res == 0, "Failed to get Redis version: %s", strerror(errno));
++
+   mark_point();
++  text = "password";
+   res = pr_redis_auth(redis, text);
+-  fail_unless(res < 0, "Failed to handle lack of need for authentication");
++
++  if (major_version < 6) {
++    fail_unless(res < 0, "Failed to handle lack of need for authentication");
++    fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
++      strerror(errno), errno);
++
++    /* Use CONFIG SET to require a password. */
++    args = make_array(p, 0, sizeof(char *));
++    *((char **) push_array(args)) = pstrdup(p, "CONFIG");
++    *((char **) push_array(args)) = pstrdup(p, "SET");
++    *((char **) push_array(args)) = pstrdup(p, "requirepass");
++    *((char **) push_array(args)) = pstrdup(p, text);
++
++    mark_point();
++    res = pr_redis_command(redis, args, PR_REDIS_REPLY_TYPE_STATUS);
++    fail_unless(res == 0, "Failed to enable authentication: %s",
++      strerror(errno));
++
++    args = make_array(p, 0, sizeof(char *));
++    *((char **) push_array(args)) = pstrdup(p, "TIME");
++
++    mark_point();
++    res = pr_redis_command(redis, args, PR_REDIS_REPLY_TYPE_ARRAY);
++    fail_unless(res < 0, "Failed to handle required authentication");
++    fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
++      strerror(errno), errno);
++
++    mark_point();
++    res = pr_redis_auth(redis, text);
++    fail_unless(res == 0, "Failed to authenticate client: %s", strerror(errno));
++
++    /* Don't forget to remove the password. */
++    args = make_array(p, 0, sizeof(char *));
++    *((char **) push_array(args)) = pstrdup(p, "CONFIG");
++    *((char **) push_array(args)) = pstrdup(p, "SET");
++    *((char **) push_array(args)) = pstrdup(p, "requirepass");
++    *((char **) push_array(args)) = pstrdup(p, "");
++
++    mark_point();
++    res = pr_redis_command(redis, args, PR_REDIS_REPLY_TYPE_STATUS);
++    fail_unless(res == 0, "Failed to remove password authentication: %s",
++      strerror(errno));
++
++  } else {
++    fail_unless(res == 0, "Failed to handle AUTH command: %s",
++      strerror(errno));
++  }
++
++  mark_point();
++  res = pr_redis_conn_destroy(redis);
++  fail_unless(res == TRUE, "Failed to close redis: %s", strerror(errno));
++}
++END_TEST
++
++START_TEST (redis_conn_auth2_test) {
++  int res;
++  pr_redis_t *redis;
++  const char *username, *password;
++  array_header *args;
++  unsigned int major_version = 0;
++
++  mark_point();
++  res = pr_redis_auth2(NULL, NULL, NULL);
++  fail_unless(res < 0, "Failed to handle null redis");
+   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
+     strerror(errno), errno);
+ 
+-  /* Use CONFIG SET to require a password. */
+-  args = make_array(p, 0, sizeof(char *));
+-  *((char **) push_array(args)) = pstrdup(p, "CONFIG");
+-  *((char **) push_array(args)) = pstrdup(p, "SET");
+-  *((char **) push_array(args)) = pstrdup(p, "requirepass");
+-  *((char **) push_array(args)) = pstrdup(p, text);
++  mark_point();
++  redis = pr_redis_conn_new(p, NULL, 0);
++  fail_unless(redis != NULL, "Failed to open connection to Redis: %s",
++    strerror(errno));
+ 
+   mark_point();
+-  res = pr_redis_command(redis, args, PR_REDIS_REPLY_TYPE_STATUS);
+-  fail_unless(res == 0, "Failed to enable authentication: %s", strerror(errno));
++  res = pr_redis_auth2(redis, NULL, NULL);
++  fail_unless(res < 0, "Failed to handle null username");
++  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
++    strerror(errno), errno);
+ 
+-  args = make_array(p, 0, sizeof(char *));
+-  *((char **) push_array(args)) = pstrdup(p, "TIME");
++  /* Note: Do NOT use "default" as the initial username; that name has
++   * specific semantics for Redis 6.x and later.
++   */
++  username = "foobar";
+ 
+   mark_point();
+-  res = pr_redis_command(redis, args, PR_REDIS_REPLY_TYPE_ARRAY);
+-  fail_unless(res < 0, "Failed to handle required authentication");
++  res = pr_redis_auth2(redis, username, NULL);
++  fail_unless(res < 0, "Failed to handle null password");
+   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
+     strerror(errno), errno);
+ 
+-  mark_point();
+-  res = pr_redis_auth(redis, text);
+-  fail_unless(res == 0, "Failed to authenticate client: %s", strerror(errno));
++  /* What happens if we try to AUTH to a non-password-protected Redis?
++   * Answer: Redis returns an error indicating that no password is required.
++   *
++   * Note that this behavior changed with Redis 6.x.  In particular, any
++   * "AUTH default ..." command automatically succeeds with Redis 6.x,
++   * regardless of the actual password given.  Sigh.
++   */
+ 
+-  /* Don't forget to remove the password. */
+-  args = make_array(p, 0, sizeof(char *));
+-  *((char **) push_array(args)) = pstrdup(p, "CONFIG");
+-  *((char **) push_array(args)) = pstrdup(p, "SET");
+-  *((char **) push_array(args)) = pstrdup(p, "requirepass");
+-  *((char **) push_array(args)) = pstrdup(p, "");
++  mark_point();
++  res = pr_redis_conn_get_version(redis, &major_version, NULL, NULL);
++  fail_unless(res == 0, "Failed to get Redis version: %s", strerror(errno));
+ 
+   mark_point();
+-  res = pr_redis_command(redis, args, PR_REDIS_REPLY_TYPE_STATUS);
+-  fail_unless(res == 0, "Failed to remove password authentication: %s",
+-    strerror(errno));
++  password = "password";
++  res = pr_redis_auth2(redis, username, password);
++  fail_unless(res < 0, "Failed to handle lack of need for authentication");
++  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
++    strerror(errno), errno);
++
++  if (major_version < 6) {
++    /* Use CONFIG SET to require a password. */
++    args = make_array(p, 0, sizeof(char *));
++    *((char **) push_array(args)) = pstrdup(p, "CONFIG");
++    *((char **) push_array(args)) = pstrdup(p, "SET");
++    *((char **) push_array(args)) = pstrdup(p, "requirepass");
++    *((char **) push_array(args)) = pstrdup(p, password);
++
++    mark_point();
++    res = pr_redis_command(redis, args, PR_REDIS_REPLY_TYPE_STATUS);
++    fail_unless(res == 0, "Failed to enable authentication: %s",
++      strerror(errno));
++
++    args = make_array(p, 0, sizeof(char *));
++    *((char **) push_array(args)) = pstrdup(p, "TIME");
++
++    mark_point();
++    res = pr_redis_command(redis, args, PR_REDIS_REPLY_TYPE_ARRAY);
++    fail_unless(res < 0, "Failed to handle required authentication");
++    fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
++      strerror(errno), errno);
++
++    mark_point();
++    res = pr_redis_auth2(redis, username, password);
++    fail_unless(res == 0, "Failed to authenticate client: %s", strerror(errno));
++
++    /* Don't forget to remove the password. */
++    args = make_array(p, 0, sizeof(char *));
++    *((char **) push_array(args)) = pstrdup(p, "CONFIG");
++    *((char **) push_array(args)) = pstrdup(p, "SET");
++    *((char **) push_array(args)) = pstrdup(p, "requirepass");
++    *((char **) push_array(args)) = pstrdup(p, "");
++
++    mark_point();
++    res = pr_redis_command(redis, args, PR_REDIS_REPLY_TYPE_STATUS);
++    fail_unless(res == 0, "Failed to remove password authentication: %s",
++      strerror(errno));
++  }
+ 
+   mark_point();
+   res = pr_redis_conn_destroy(redis);
+@@ -1987,7 +2135,7 @@ START_TEST (redis_hash_keys_test) {
+   mark_point();
+   res = pr_redis_hash_keys(p, redis, &m, key, &fields);
+   fail_unless(res == 0, "Failed to handle existing fields: %s", strerror(errno));
+-  fail_unless(fields != NULL);
++  fail_unless(fields != NULL, "Failed to get hash fields");
+   fail_unless(fields->nelts == 2, "Expected 2, got %u", fields->nelts);
+ 
+   (void) pr_redis_remove(redis, &m, key);
+@@ -2074,7 +2222,7 @@ START_TEST (redis_hash_values_test) {
+   mark_point();
+   res = pr_redis_hash_values(p, redis, &m, key, &values);
+   fail_unless(res == 0, "Failed to handle existing values: %s", strerror(errno));
+-  fail_unless(values != NULL);
++  fail_unless(values != NULL, "Failed to get hash values");
+   fail_unless(values->nelts == 2, "Expected 2, got %u", values->nelts);
+ 
+   (void) pr_redis_remove(redis, &m, key);
+@@ -2161,7 +2309,7 @@ START_TEST (redis_hash_getall_test) {
+   mark_point();
+   res = pr_redis_hash_getall(p, redis, &m, key, &hash);
+   fail_unless(res == 0, "Failed to handle existing fields: %s", strerror(errno));
+-  fail_unless(hash != NULL);
++  fail_unless(hash != NULL, "Failed to get hash");
+   res = pr_table_count(hash);
+   fail_unless(res == 2, "Expected 2, got %d", res);
+ 
+@@ -4743,7 +4891,9 @@ Suite *tests_get_redis_suite(void) {
+   tcase_add_test(testcase, redis_conn_new_test);
+   tcase_add_test(testcase, redis_conn_get_test);
+   tcase_add_test(testcase, redis_conn_set_namespace_test);
++  tcase_add_test(testcase, redis_conn_get_version_test);
+   tcase_add_test(testcase, redis_conn_auth_test);
++  tcase_add_test(testcase, redis_conn_auth2_test);
+   tcase_add_test(testcase, redis_conn_select_test);
+   tcase_add_test(testcase, redis_conn_reconnect_test);
+   tcase_add_test(testcase, redis_command_test);
+diff --git a/tests/api/sets.c b/tests/api/sets.c
+index 9f3deafc2..9459454f2 100644
+--- a/tests/api/sets.c
++++ b/tests/api/sets.c
+@@ -1,6 +1,6 @@
+ /*
+  * ProFTPD - FTP server testsuite
+- * Copyright (c) 2008-2011 The ProFTPD Project team
++ * Copyright (c) 2008-2020 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
+@@ -97,20 +97,20 @@ START_TEST (set_create_test) {
+   fail_unless(errno == EPERM, "Failed to set errno to EPERM");
+ 
+   res = xaset_create(p, NULL);
+-  fail_unless(res != NULL);
++  fail_unless(res != NULL, "Expected non-null result");
+   fail_unless(res->pool == p, "Expected %p, got %p", p, res->pool);
+ 
+   permanent_pool = make_sub_pool(p);
+ 
+   res = xaset_create(NULL, NULL);
+-  fail_unless(res != NULL);
++  fail_unless(res != NULL, "Expected non-null result");
+   fail_unless(res->pool == permanent_pool, "Expected %p, got %p",
+     permanent_pool, res->pool);
+   fail_unless(res->xas_compare == NULL, "Expected NULL, got %p",
+     res->xas_compare);
+ 
+   res = xaset_create(p, (XASET_COMPARE) item_cmp);
+-  fail_unless(res != NULL);
++  fail_unless(res != NULL, "Expected non-null result");
+   fail_unless(res->pool == p, "Expected %p, got %p", p, res->pool);
+   fail_unless(res->xas_compare == (XASET_COMPARE) item_cmp,
+     "Expected %p, got %p", item_cmp, res->xas_compare);
+@@ -355,12 +355,12 @@ START_TEST (set_remove_test) {
+   fail_unless(res == 0, "Failed to add item2");
+ 
+   member = (xasetmember_t *) item1;
+-  fail_unless(member->next == NULL);
+-  fail_unless(member->prev != NULL);
++  fail_unless(member->next == NULL, "Expected member->next to be null");
++  fail_unless(member->prev != NULL, "Expected member->prev to not be null");
+ 
+   member = (xasetmember_t *) item2;
+-  fail_unless(member->next != NULL);
+-  fail_unless(member->prev == NULL);
++  fail_unless(member->next != NULL, "Expected member->next to not be null");
++  fail_unless(member->prev == NULL, "Expected member->prev to be null");
+ 
+   member = set->xas_list;
+   fail_unless(member == (xasetmember_t *) item2,
+@@ -371,8 +371,8 @@ START_TEST (set_remove_test) {
+     strerror(errno));
+ 
+   member = (xasetmember_t *) item2;
+-  fail_unless(member->next == NULL);
+-  fail_unless(member->prev == NULL);
++  fail_unless(member->next == NULL, "Expected member->next to be null");
++  fail_unless(member->prev == NULL, "Expected member->prev to be null");
+ 
+   member = set->xas_list;
+   fail_unless(member == (xasetmember_t *) item1,
+@@ -383,8 +383,8 @@ START_TEST (set_remove_test) {
+     strerror(errno));
+ 
+   member = (xasetmember_t *) item1;
+-  fail_unless(member->next == NULL);
+-  fail_unless(member->prev == NULL);
++  fail_unless(member->next == NULL, "Expected member->next to be null");
++  fail_unless(member->prev == NULL, "Expected member->prev to be null");
+ 
+   member = set->xas_list;
+   fail_unless(member == NULL, "Expected list to be empty, got %p", member);
+diff --git a/tests/api/str.c b/tests/api/str.c
+index 9dce95820..050f5c563 100644
+--- a/tests/api/str.c
++++ b/tests/api/str.c
+@@ -1539,10 +1539,10 @@ START_TEST (uid2str_test) {
+   const char *res;
+ 
+   res = pr_uid2str(NULL, (uid_t) 1);
+-  fail_unless(strcmp(res, "1") == 0);
++  fail_unless(strcmp(res, "1") == 0, "Expected '1', got '%s'", res);
+ 
+   res = pr_uid2str(NULL, (uid_t) -1);
+-  fail_unless(strcmp(res, "-1") == 0);
++  fail_unless(strcmp(res, "-1") == 0, "Expected '-1', got '%s'", res);
+ }
+ END_TEST
+ 
+@@ -1550,10 +1550,10 @@ START_TEST (gid2str_test) {
+   const char *res;
+ 
+   res = pr_gid2str(NULL, (gid_t) 1);
+-  fail_unless(strcmp(res, "1") == 0);
++  fail_unless(strcmp(res, "1") == 0, "Expected '1', got '%s'", res);
+ 
+   res = pr_gid2str(NULL, (gid_t) -1);
+-  fail_unless(strcmp(res, "-1") == 0);
++  fail_unless(strcmp(res, "-1") == 0, "Expected '-1', got '%s'", res);
+ }
+ END_TEST
+ 
+diff --git a/tests/api/timers.c b/tests/api/timers.c
+index 99b6348e9..0616a7989 100644
+--- a/tests/api/timers.c
++++ b/tests/api/timers.c
+@@ -157,7 +157,7 @@ START_TEST (timer_remove_test) {
+   int res;
+ 
+   res = pr_timer_remove(0, NULL);
+-  fail_unless(res == 0);
++  fail_unless(res == 0, "Failed to remove timer: %s", strerror(errno));
+ 
+   res = pr_timer_add(1, 0, NULL, timers_test_cb, "test");
+   fail_unless(res == 0, "Failed to add timer (%d): %s", res, strerror(errno));



View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/18c0faa2d7acbe48fb47bc16461b7b2c7bb97fe8

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




More information about the Pkg-proftpd-maintainers mailing list