[Git][debian-proftpd-team/proftpd][upstream] New upstream version 1.3.7c+dfsg
Hilmar Preuße (@hilmar-guest)
gitlab at salsa.debian.org
Mon Aug 30 09:41:21 BST 2021
Hilmar Preuße pushed to branch upstream at Debian ProFTPD Team / proftpd
Commits:
34847869 by Hilmar Preusse at 2021-08-30T10:29:22+02:00
New upstream version 1.3.7c+dfsg
- - - - -
16 changed files:
- + .github/workflows/rpm.yml
- NEWS
- README.md
- RELEASE_NOTES
- contrib/dist/rpm/proftpd.spec
- contrib/mod_radius.c
- contrib/mod_tls.c
- include/regexp.h
- include/version.h
- modules/mod_auth_file.c
- src/bindings.c
- src/dirtree.c
- src/regexp.c
- tests/api/regexp.c
- tests/t/lib/ProFTPD/Tests/Modules/mod_rewrite.pm
- tests/t/lib/ProFTPD/Tests/Modules/mod_tls.pm
Changes:
=====================================
.github/workflows/rpm.yml
=====================================
@@ -0,0 +1,92 @@
+name: RPM
+
+on:
+ push:
+ branches:
+ - master
+ - 1.3.7
+ pull_request:
+ branches:
+ - master
+ - 1.3.7
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ container:
+ - centos:7
+ - centos:8
+
+ container: ${{ matrix.container }}
+
+ steps:
+ - name: Checkout source code
+ uses: actions/checkout at v2
+
+ - name: Configure Centos 7 repos
+ if: ${{ matrix.container == 'centos:7' }}
+ run: |
+ # Need to add other repos for e.g. libsodium
+ yum install -y dnf-plugins-core epel-release
+ # for mod_wrap
+ yum install -y libnsl2-devel tcp_wrappers-devel
+
+ - name: Configure Centos 8 repos
+ if: ${{ matrix.container == 'centos:8' }}
+ run: |
+ # Need to add other repos for e.g. libsodium
+ yum install -y dnf-plugins-core epel-release
+ yum config-manager --set-enabled powertools
+ # for mod_wrap
+ yum install -y libnsl2-devel https://pkgs.dyn.su/el8/extras/x86_64/tcp_wrappers-libs-7.6-77.el8.x86_64.rpm https://pkgs.dyn.su/el8/extras/x86_64/tcp_wrappers-devel-7.6-77.el8.x86_64.rpm
+
+ - name: Install packages
+ run: |
+ # for builds
+ yum install -y bash gcc make imake
+ # for rpm builds
+ yum install -y rpm-build rpmlint redhat-rpm-config
+ # for dependencies
+ yum install -y GeoIP-devel
+ yum install -y gettext
+ yum install -y hiredis-devel
+ yum install -y libacl-devel
+ yum install -y libcap-devel
+ yum install -y libmemcached-devel
+ yum install -y libsodium-devel
+ yum install -y mysql-devel
+ yum install -y ncurses-devel
+ yum install -y openldap-devel cyrus-sasl-devel
+ yum install -y openssl-devel
+ yum install -y pam-devel
+ yum install -y pcre-devel
+ yum install -y postgresql-devel
+ yum install -y sqlite-devel
+ yum install -y zlib-devel
+
+ - name: Generate RPM spec
+ run: |
+ ./configure
+ make
+ # To properly name the tarball later with the version suffix, we need
+ # to query the built `proftpd` binary for its version.
+ echo "release_version=$(./proftpd -v | cut -f3 -d' ')" >> $GITHUB_ENV
+ make dist
+
+ - name: Check RPM spec
+ run: |
+ rpmlint proftpd.spec
+
+ - name: Build release tarball
+ run: |
+ cd ..
+ mv proftpd "proftpd-${{ env.release_version }}"
+ tar zcf "/tmp/proftpd-${{ env.release_version }}.tar.gz" "proftpd-${{ env.release_version }}"
+ mv "proftpd-${{ env.release_version }}" proftpd
+
+ - name: Build RPM from release tarball
+ run: |
+ rpmbuild -tb -vvv --with everything "/tmp/proftpd-${{ env.release_version }}.tar.gz"
=====================================
NEWS
=====================================
@@ -15,6 +15,18 @@
where `N' is the issue number.
-----------------------------------------------------------------------------
+1.3.7c - Released 29-Aug-2021
+--------------------------------
+- Issue 1273 - Improve mod_tls log messages for unsupported older TLS protocol
+ requests.
+- Issue 1284 - Fix memory disclosure to RADIUS servers by mod_radius.
+- Issue 1282 - Properly handle <VirtualHost> sections that use interface/device
+ names.
+- Issue 1300 - PCRE expressions with capture groups are not being handled
+ properly.
+- Issue 1307 - AuthUserFile permissions check fails during SIGHUP, causing
+ ProFTPD to stop.
+
1.3.7b - Released 13-Jun-2021
--------------------------------
- Issue 1063 - FTPS data transfers using TLSv1.3 might segfault when session
=====================================
README.md
=====================================
@@ -7,7 +7,7 @@
[![Coverage Status](https://coveralls.io/repos/proftpd/proftpd/badge.svg?branch=master&service=github)](https://coveralls.io/github/proftpd/proftpd?branch=master)
[![Coverity Scan Status](https://scan.coverity.com/projects/198/badge.svg)](https://scan.coverity.com/projects/198)
[![C/C++ Language Grade](https://img.shields.io/lgtm/grade/cpp/g/proftpd/proftpd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/proftpd/proftpd/context:cpp)
-[![Release](https://img.shields.io/badge/release-1.3.7-brightgreen)](https://github.com/proftpd/proftpd/releases/latest)
+[![Release](https://img.shields.io/badge/release-1.3.7b-brightgreen)](https://github.com/proftpd/proftpd/releases/latest)
[![License](https://img.shields.io/badge/license-GPL-brightgreen.svg)](https://img.shields.io/badge/license-GPL-brightgreen.svg)
## Introduction
=====================================
RELEASE_NOTES
=====================================
@@ -6,6 +6,15 @@ 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.7c
+---------
+
+ + Fix memory disclosure to RADIUS servers by mod_radius (Issue #1284).
+
+ + PCRE expressions with capture groups were not being handled properly
+ (Issue #1300).
+
+
1.3.7b
---------
=====================================
contrib/dist/rpm/proftpd.spec
=====================================
@@ -53,7 +53,7 @@
# RHEL5 and clones don't have suitably recent versions of pcre/libmemcached
# so use --with rhel5 to inhibit those features when using --with everything
-%global proftpd_version 1.3.7b
+%global proftpd_version 1.3.7c
# rc_version should be incremented for each RC release, and reset back to 1
# AFTER each stable release.
@@ -61,7 +61,7 @@
# release_version should be incremented for each maint release, and reset back
# to 1 BEFORE starting new release cycle.
-%global release_version 4
+%global release_version 5
%if %(echo %{proftpd_version} | grep rc >/dev/null 2>&1 && echo 1 || echo 0)
%global rpm_version %(echo %{proftpd_version} | sed -e 's/rc.*//')
=====================================
contrib/mod_radius.c
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD: mod_radius -- a module for RADIUS authentication and accounting
- * Copyright (c) 2001-2020 TJ Saunders
+ * Copyright (c) 2001-2021 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
@@ -2319,21 +2319,28 @@ static void radius_add_passwd(radius_packet_t *packet, unsigned char type,
pwlen = strlen((const char *) passwd);
+ /* Clear the buffers. */
+ memset(pwhash, '\0', sizeof(pwhash));
+
if (pwlen == 0) {
pwlen = RADIUS_PASSWD_LEN;
- } if ((pwlen & (RADIUS_PASSWD_LEN - 1)) != 0) {
+ } else if ((pwlen & (RADIUS_PASSWD_LEN - 1)) != 0) {
+ /* pwlen is not a multiple of RADIUS_PASSWD_LEN, need to prepare a proper
+ * buffer.
+ */
+ memcpy(pwhash, passwd, pwlen);
/* Round up the length. */
pwlen += (RADIUS_PASSWD_LEN - 1);
/* Truncate the length, as necessary. */
pwlen &= ~(RADIUS_PASSWD_LEN - 1);
- }
- /* Clear the buffers. */
- memset(pwhash, '\0', sizeof(pwhash));
- memcpy(pwhash, passwd, pwlen);
+ } else {
+ /* pwlen is a multiple of RADIUS_PASSWD_LEN, we can just use it. */
+ memcpy(pwhash, passwd, pwlen);
+ }
/* Find the password attribute. */
attrib = radius_get_attrib(packet, RADIUS_PASSWORD);
=====================================
contrib/mod_tls.c
=====================================
@@ -7597,7 +7597,7 @@ static int tls_accept(conn_t *conn, unsigned char on_data) {
ssl_opts = SSL_get_options(ssl);
-#ifdef SSL_OP_NO_SSLv2
+#if SSL_OP_NO_SSLv2
if (ssl_opts & SSL_OP_NO_SSLv2) {
proto_str = pstrcat(tmp_pool, proto_str, *proto_str ? ", " : "",
"SSLv2", NULL);
@@ -7655,6 +7655,60 @@ static int tls_accept(conn_t *conn, unsigned char on_data) {
break;
}
+#if defined(SSL_R_VERSION_TOO_LOW)
+ case SSL_R_VERSION_TOO_LOW: {
+ int client_version;
+
+ client_version = SSL_client_version(ssl);
+ switch (client_version) {
+# if defined(SSL3_VERSION) && defined(OPENSSL_NO_SSL3)
+ case SSL3_VERSION:
+ tls_log("%s: %s lacks support for client requested TLS "
+ "protocol version: %s", msg, OPENSSL_VERSION_TEXT,
+ SSL_get_version(ssl));
+ break;
+# endif /* SSLv3 and OPENSSL_NO_SSL3 */
+
+# if defined(TLS1_VERSION) && defined(OPENSSL_NO_TLS1)
+ case TLS1_VERSION:
+ tls_log("%s: %s lacks support for client requested TLS "
+ "protocol version: %s", msg, OPENSSL_VERSION_TEXT,
+ SSL_get_version(ssl));
+ break;
+# endif /* TLSv1 and OPENSSL_NO_TLS1 */
+
+# if defined(TLS1_1_VERSION) && defined(OPENSSL_NO_TLS1_1)
+ case TLS1_1_VERSION:
+ tls_log("%s: %s lacks support for client requested TLS "
+ "protocol version: %s", msg, OPENSSL_VERSION_TEXT,
+ SSL_get_version(ssl));
+ break;
+# endif /* TLSv1.1 and OPENSSL_NO_TLS1_1 */
+
+# if defined(TLS1_2_VERSION) && defined(OPENSSL_NO_TLS1_2)
+ case TLS1_2_VERSION:
+ tls_log("%s: %s lacks support for client requested TLS "
+ "protocol version: %s", msg, OPENSSL_VERSION_TEXT,
+ SSL_get_version(ssl));
+ break;
+# endif /* TLSv1.2 and OPENSSL_NO_TLS1_2 */
+
+# if defined(TLS1_3_VERSION) && defined(OPENSSL_NO_TLS1_3)
+ case TLS1_3_VERSION:
+ tls_log("%s: %s lacks support for client requested TLS "
+ "protocol version: %s", msg, OPENSSL_VERSION_TEXT,
+ SSL_get_version(ssl));
+ break;
+# endif /* TLSv1.3 and OPENSSL_NO_TLS1_3 */
+
+ default:
+ tls_log("%s: perhaps client requested unsupported TLS protocol "
+ "version: %s", msg, SSL_get_version(ssl));
+ }
+ break;
+ }
+#endif /* SSL_R_VERSION_TOO_LOW */
+
default:
break;
}
=====================================
include/regexp.h
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - FTP server daemon
- * Copyright (c) 2001-2016 The ProFTPD Project team
+ * Copyright (c) 2001-2021 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
@@ -32,7 +32,7 @@
* code.
*/
-#ifdef PR_USE_PCRE
+#if defined(PR_USE_PCRE)
# include <pcre.h>
# include <pcreposix.h>
=====================================
include/version.h
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - FTP server daemon
- * Copyright (c) 2020 The ProFTPD Project team
+ * Copyright (c) 2020-2021 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
@@ -28,8 +28,8 @@
#include "buildstamp.h"
/* Application version (in various forms) */
-#define PROFTPD_VERSION_NUMBER 0x0001030707
-#define PROFTPD_VERSION_TEXT "1.3.7b"
+#define PROFTPD_VERSION_NUMBER 0x0001030708
+#define PROFTPD_VERSION_TEXT "1.3.7c"
/* Module API version */
#define PR_MODULE_API_VERSION 0x20
@@ -39,6 +39,6 @@ unsigned long pr_version_get_number(void);
const char *pr_version_get_str(void);
/* PR_STATUS is reported by --version-status -- don't ask why */
-#define PR_STATUS "(git)"
+#define PR_STATUS "(maint)"
#endif /* PR_VERSION_H */
=====================================
modules/mod_auth_file.c
=====================================
@@ -1422,14 +1422,22 @@ MODRET set_authgroupfile(cmd_rec *cmd) {
}
if (!(auth_file_opts & AUTH_FILE_OPT_INSECURE_PERMS)) {
+ int res, xerrno;
+
/* Make sure the configured file has the correct permissions. Note that
* AuthGroupFiles, unlike AuthUserFiles, do not contain any sensitive
* information, and can thus be world-readable.
*/
flags = PR_AUTH_FILE_FL_ALLOW_WORLD_READABLE;
- if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) {
+
+ PRIVS_ROOT
+ res = af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags);
+ xerrno = errno;
+ PRIVS_RELINQUISH
+
+ if (res < 0) {
CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
- "unable to use ", path, ": ", strerror(errno), NULL));
+ "unable to use ", path, ": ", strerror(xerrno), NULL));
}
}
@@ -1546,12 +1554,20 @@ MODRET set_authuserfile(cmd_rec *cmd) {
}
if (!(auth_file_opts & AUTH_FILE_OPT_INSECURE_PERMS)) {
+ int res, xerrno;
+
/* Make sure the configured file has the correct permissions. Note that
* AuthUserFiles, unlike AuthGroupFiles, DO contain any sensitive
* information, and thus CANNOT be world-readable.
*/
flags = 0;
- if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) {
+
+ PRIVS_ROOT
+ res = af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags);
+ xerrno = errno;
+ PRIVS_RELINQUISH
+
+ if (res < 0) {
CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
"unable to use ", path, ": ", strerror(errno), NULL));
}
=====================================
src/bindings.c
=====================================
@@ -31,12 +31,25 @@ extern xaset_t *server_list;
extern server_rec *main_server;
static pr_ipbind_t *ipbind_table[PR_BINDINGS_TABLE_SIZE];
+static int ipbind_table_initialized = FALSE;
+
static pool *binding_pool = NULL;
static pr_ipbind_t *ipbind_default_server = NULL,
*ipbind_localhost_server = NULL;
static const char *trace_channel = "binding";
+static void trace_ipbind_table(void);
+
+static void init_ipbind_table(void) {
+ if (ipbind_table_initialized == TRUE) {
+ return;
+ }
+
+ memset(ipbind_table, 0, sizeof(ipbind_table));
+ ipbind_table_initialized = TRUE;
+}
+
/* Server cleanup callback function */
static void server_cleanup_cb(void *conn) {
*((conn_t **) conn) = NULL;
@@ -425,6 +438,9 @@ int pr_ipbind_create(server_rec *server, const pr_netaddr_t *addr,
return -1;
}
+ /* Ensure the ipbind table has been initialized. */
+ init_ipbind_table();
+
i = ipbind_hash_addr(addr);
pr_trace_msg(trace_channel, 29, "hashed address '%s' to index %u",
pr_netaddr_get_ipstr(addr), i);
@@ -540,6 +556,9 @@ pr_ipbind_t *pr_ipbind_find(const pr_netaddr_t *addr, unsigned int port,
return NULL;
}
+ /* Ensure the ipbind table has been initialized. */
+ init_ipbind_table();
+
i = ipbind_hash_addr(addr);
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
@@ -1147,6 +1166,7 @@ void free_bindings(void) {
}
memset(ipbind_table, 0, sizeof(ipbind_table));
+ ipbind_table_initialized = FALSE;
/* Mark all listening conns as "unclaimed"; any that remaining unclaimed
* after init_bindings() can be closed.
@@ -1378,7 +1398,8 @@ static int init_standalone_bindings(void) {
server_rec *serv = NULL;
unsigned char *default_server = NULL, is_default = FALSE;
- memset(ipbind_table, 0, sizeof(ipbind_table));
+ /* Ensure the ipbind table has been initialized. */
+ init_ipbind_table();
/* If a port is set to zero, the address/port is not bound to a socket
* at all.
=====================================
src/dirtree.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-2020 The ProFTPD Project team
+ * Copyright (c) 2001-2021 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
@@ -2545,7 +2545,12 @@ int fixup_servers(xaset_t *list) {
}
} else {
- s->addr = pr_netaddr_get_addr(s->pool, s->ServerAddress, NULL);
+ int flags = PR_NETADDR_GET_ADDR_FL_INCL_DEVICE;
+
+ /* Make sure we properly handle a ServerAddress that is an
+ * interface/device name here (Issue #1282).
+ */
+ s->addr = pr_netaddr_get_addr2(s->pool, s->ServerAddress, NULL, flags);
}
if (s->addr == NULL) {
=====================================
src/regexp.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-2020 The ProFTPD Project team
+ * Copyright (c) 2001-2021 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
@@ -30,7 +30,7 @@
#ifdef PR_USE_REGEX
-#ifdef PR_USE_PCRE
+#if defined(PR_USE_PCRE)
#include <pcre.h>
struct regexp_rec {
@@ -77,7 +77,7 @@ static array_header *regexp_list = NULL;
static const char *trace_channel = "regexp";
static void regexp_free(pr_regex_t *pre) {
-#ifdef PR_USE_PCRE
+#if defined(PR_USE_PCRE)
if (pre->pcre != NULL) {
# if defined(HAVE_PCRE_PCRE_FREE_STUDY)
pcre_free_study(pre->pcre_extra);
@@ -176,7 +176,7 @@ void pr_regexp_free(module *m, pr_regex_t *pre) {
}
}
-#ifdef PR_USE_PCRE
+#if defined(PR_USE_PCRE)
static int regexp_compile_pcre(pr_regex_t *pre, const char *pattern,
int flags) {
int err_offset, study_flags = 0;
@@ -237,6 +237,11 @@ int pr_regexp_compile_posix(pr_regex_t *pre, const char *pattern, int flags) {
pattern);
pre->pattern = pstrdup(pre->regex_pool, pattern);
+#if defined(REG_EXTENDED)
+ /* Enable modern ("extended") POSIX regular expressions by default. */
+ flags |= REG_EXTENDED;
+#endif /* REG_EXTENDED */
+
pre->re = pcalloc(pre->regex_pool, sizeof(regex_t));
res = regcomp(pre->re, pattern, flags);
@@ -244,7 +249,7 @@ int pr_regexp_compile_posix(pr_regex_t *pre, const char *pattern, int flags) {
}
int pr_regexp_compile(pr_regex_t *pre, const char *pattern, int flags) {
-#ifdef PR_USE_PCRE
+#if defined(PR_USE_PCRE)
int pcre_flags = 0;
/* Provide a simple mapping of POSIX regcomp(3) flags to
@@ -271,7 +276,7 @@ size_t pr_regexp_error(int errcode, const pr_regex_t *pre, char *buf,
return 0;
}
-#ifdef PR_USE_PCRE
+#if defined(PR_USE_PCRE)
if (pre->pcre_errstr != NULL) {
sstrncpy(buf, pre->pcre_errstr, bufsz);
return strlen(pre->pcre_errstr) + 1;
@@ -301,130 +306,184 @@ const char *pr_regexp_get_pattern(const pr_regex_t *pre) {
return pre->pattern;
}
-#ifdef PR_USE_PCRE
-static int regexp_exec_pcre(pr_regex_t *pre, const char *str,
+#if defined(PR_USE_PCRE)
+static int regexp_exec_pcre(pr_regex_t *pre, const char *text,
size_t nmatches, regmatch_t *matches, int flags, unsigned long match_limit,
unsigned long match_limit_recursion) {
+ int res, ovector_count = 0, *ovector = NULL;
+ size_t text_len;
+ pool *tmp_pool = NULL;
- if (pre->pcre != NULL) {
- int res;
- size_t str_len;
+ if (pre->pcre == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
- str_len = strlen(str);
+ text_len = strlen(text);
- /* Use the default match limits, if set and if the caller did not
- * explicitly provide limits.
- */
- if (match_limit == 0) {
- match_limit = pcre_match_limit;
- }
+ /* Use the default match limits, if set and if the caller did not
+ * explicitly provide limits.
+ */
+ if (match_limit == 0) {
+ match_limit = pcre_match_limit;
+ }
- if (match_limit_recursion == 0) {
- match_limit_recursion = pcre_match_limit_recursion;
+ if (match_limit_recursion == 0) {
+ match_limit_recursion = pcre_match_limit_recursion;
+ }
+
+ if (match_limit > 0) {
+ if (pre->pcre_extra == NULL) {
+ pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra));
}
- if (match_limit > 0) {
- if (pre->pcre_extra == NULL) {
- pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra));
- }
+ pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT;
+ pre->pcre_extra->match_limit = match_limit;
+ }
- pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT;
- pre->pcre_extra->match_limit = match_limit;
+ if (match_limit_recursion > 0) {
+ if (pre->pcre_extra == NULL) {
+ pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra));
}
- if (match_limit_recursion > 0) {
- if (pre->pcre_extra == NULL) {
- pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra));
- }
+ pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
+ pre->pcre_extra->match_limit_recursion = match_limit_recursion;
+ }
+
+ if (nmatches > 0 &&
+ matches != NULL) {
+ tmp_pool = make_sub_pool(pre->regex_pool);
+ pr_pool_tag(tmp_pool, "regexp tmp pool");
+
+ ovector_count = nmatches;
+ ovector = pcalloc(tmp_pool, sizeof(int) * nmatches * 3);
+ }
- pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
- pre->pcre_extra->match_limit_recursion = match_limit_recursion;
+ pr_trace_msg(trace_channel, 9,
+ "executing PCRE regex '%s' against subject '%s'",
+ pr_regexp_get_pattern(pre), text);
+ res = pcre_exec(pre->pcre, pre->pcre_extra, text, text_len, 0, flags,
+ ovector, ovector_count);
+
+ if (res < 0) {
+ if (tmp_pool != NULL) {
+ destroy_pool(tmp_pool);
}
- pr_trace_msg(trace_channel, 9,
- "executing PCRE regex '%s' against subject '%s'",
- pr_regexp_get_pattern(pre), str);
- res = pcre_exec(pre->pcre, pre->pcre_extra, str, str_len, 0, flags,
- NULL, 0);
-
- if (res < 0) {
- if (pr_trace_get_level(trace_channel) >= 9) {
- const char *reason = "unknown";
-
- switch (res) {
- case PCRE_ERROR_NOMATCH:
- reason = "subject did not match pattern";
- break;
-
- case PCRE_ERROR_NULL:
- reason = "null regex or subject";
- break;
-
- case PCRE_ERROR_BADOPTION:
- reason = "unsupported options bit";
- break;
-
- case PCRE_ERROR_BADMAGIC:
- reason = "bad magic number in regex";
- break;
-
- case PCRE_ERROR_UNKNOWN_OPCODE:
- case PCRE_ERROR_INTERNAL:
- reason = "internal PCRE error or corrupted regex";
- break;
-
- case PCRE_ERROR_NOMEMORY:
- reason = "not enough memory for backreferences";
- break;
-
- case PCRE_ERROR_MATCHLIMIT:
- reason = "match limit reached/exceeded";
- break;
-
- case PCRE_ERROR_RECURSIONLIMIT:
- reason = "match limit recursion reached/exceeded";
- break;
-
- case PCRE_ERROR_BADUTF8:
- reason = "invalid UTF8 subject used";
- break;
-
- case PCRE_ERROR_PARTIAL:
- reason = "subject matched only partially; PCRE_PARTIAL flag not used";
- break;
- }
-
- pr_trace_msg(trace_channel, 9,
- "PCRE regex '%s' failed to match subject '%s': %s",
- pr_regexp_get_pattern(pre), str, reason);
-
- } else {
- pr_trace_msg(trace_channel, 9,
- "PCRE regex '%s' successfully matched subject '%s'",
- pr_regexp_get_pattern(pre), str);
+ if (pr_trace_get_level(trace_channel) >= 9) {
+ const char *reason = "unknown";
+
+ switch (res) {
+ case PCRE_ERROR_NOMATCH:
+ reason = "subject did not match pattern";
+ break;
+
+ case PCRE_ERROR_NULL:
+ reason = "null regex or subject";
+ break;
+
+ case PCRE_ERROR_BADOPTION:
+ reason = "unsupported options bit";
+ break;
+
+ case PCRE_ERROR_BADMAGIC:
+ reason = "bad magic number in regex";
+ break;
+
+ case PCRE_ERROR_UNKNOWN_OPCODE:
+ case PCRE_ERROR_INTERNAL:
+ reason = "internal PCRE error or corrupted regex";
+ break;
+
+ case PCRE_ERROR_NOMEMORY:
+ reason = "not enough memory for backreferences";
+ break;
+
+ case PCRE_ERROR_MATCHLIMIT:
+ reason = "match limit reached/exceeded";
+ break;
+
+ case PCRE_ERROR_RECURSIONLIMIT:
+ reason = "match limit recursion reached/exceeded";
+ break;
+
+ case PCRE_ERROR_BADUTF8:
+ reason = "invalid UTF8 subject used";
+ break;
+
+ case PCRE_ERROR_PARTIAL:
+ reason = "subject matched only partially; PCRE_PARTIAL flag not used";
+ break;
}
+
+ pr_trace_msg(trace_channel, 9,
+ "PCRE regex '%s' failed to match subject '%s': %s",
+ pr_regexp_get_pattern(pre), text, reason);
}
return res;
}
- errno = EINVAL;
- return -1;
+ pr_trace_msg(trace_channel, 9,
+ "PCRE regex '%s' successfully matched subject '%s'",
+ pr_regexp_get_pattern(pre), text);
+
+ if (ovector_count > 0) {
+ /* Populate the provided POSIX regmatch_t array with the PCRE data. */
+ register unsigned int i;
+
+ for (i = 0; i < res; i++) {
+ matches[i].rm_so = ovector[i * 2];
+ matches[i].rm_eo = ovector[(i * 2) + 1];
+ }
+
+ /* Ensure the remaining items are set to proper defaults as well. */
+ for (; i < nmatches; i++) {
+ matches[i].rm_so = matches[i].rm_eo = -1;
+ }
+ }
+
+ destroy_pool(tmp_pool);
+
+ if (matches != NULL &&
+ pr_trace_get_level(trace_channel) >= 20) {
+ register unsigned int i;
+
+ for (i = 0; i < nmatches; i++) {
+ int match_len;
+ const char *match_text;
+
+ if (matches[i].rm_so == -1 ||
+ matches[i].rm_eo == -1) {
+ break;
+ }
+
+ match_text = &(text[matches[i].rm_so]);
+ match_len = matches[i].rm_eo - matches[i].rm_so;
+
+ pr_trace_msg(trace_channel, 20,
+ "PCRE regex '%s' match #%u: %.*s (start %ld, len %d)",
+ pr_regexp_get_pattern(pre), i, (int) match_len, match_text,
+ (long) matches[i].rm_so, match_len);
+ }
+ }
+
+ return 0;
}
#endif /* PR_USE_PCRE */
-static int regexp_exec_posix(pr_regex_t *pre, const char *str,
+static int regexp_exec_posix(pr_regex_t *pre, const char *text,
size_t nmatches, regmatch_t *matches, int flags) {
int res;
pr_trace_msg(trace_channel, 9,
"executing POSIX regex '%s' against subject '%s'",
- pr_regexp_get_pattern(pre), str);
- res = regexec(pre->re, str, nmatches, matches, flags);
+ pr_regexp_get_pattern(pre), text);
+ res = regexec(pre->re, text, nmatches, matches, flags);
if (res == 0) {
pr_trace_msg(trace_channel, 9,
"POSIX regex '%s' successfully matched subject '%s'",
- pr_regexp_get_pattern(pre), str);
+ pr_regexp_get_pattern(pre), text);
if (matches != NULL &&
pr_trace_get_level(trace_channel) >= 20) {
@@ -439,7 +498,7 @@ static int regexp_exec_posix(pr_regex_t *pre, const char *str,
break;
}
- match_text = &(str[matches[i].rm_so]);
+ match_text = &(text[matches[i].rm_so]);
match_len = matches[i].rm_eo - matches[i].rm_so;
pr_trace_msg(trace_channel, 20,
@@ -450,43 +509,41 @@ static int regexp_exec_posix(pr_regex_t *pre, const char *str,
}
} else {
- const char *reason = "unknown";
-
if (pr_trace_get_level(trace_channel) >= 9) {
- switch (res) {
- case REG_NOMATCH:
- reason = "subject did not match pattern";
- break;
- }
- }
+ const char *reason = "subject did not match pattern";
- pr_trace_msg(trace_channel, 9,
- "POSIX regex '%s' failed to match subject '%s': %s",
- pr_regexp_get_pattern(pre), str, reason);
+ /* NOTE: Expectation of `res` values here are mixed when PCRE
+ * support, and the <pcreposix.h> header, are involved.
+ */
+
+ pr_trace_msg(trace_channel, 9,
+ "POSIX regex '%s' failed to match subject '%s': %s (%d)",
+ pr_regexp_get_pattern(pre), text, reason, res);
+ }
}
return res;
}
-int pr_regexp_exec(pr_regex_t *pre, const char *str, size_t nmatches,
+int pr_regexp_exec(pr_regex_t *pre, const char *text, size_t nmatches,
regmatch_t *matches, int flags, unsigned long match_limit,
unsigned long match_limit_recursion) {
int res;
if (pre == NULL ||
- str == NULL) {
+ text == NULL) {
errno = EINVAL;
return -1;
}
-#ifdef PR_USE_PCRE
+#if defined(PR_USE_PCRE)
if (pre->pcre != NULL) {
- return regexp_exec_pcre(pre, str, nmatches, matches, flags, match_limit,
+ return regexp_exec_pcre(pre, text, nmatches, matches, flags, match_limit,
match_limit_recursion);
}
#endif /* PR_USE_PCRE */
- res = regexp_exec_posix(pre, str, nmatches, matches, flags);
+ res = regexp_exec_posix(pre, text, nmatches, matches, flags);
/* Make sure that we return a negative value to indicate a failed match;
* PCRE already does this.
@@ -501,7 +558,7 @@ int pr_regexp_exec(pr_regex_t *pre, const char *str, size_t nmatches,
int pr_regexp_set_limits(unsigned long match_limit,
unsigned long match_limit_recursion) {
-#ifdef PR_USE_PCRE
+#if defined(PR_USE_PCRE)
pcre_match_limit = match_limit;
pcre_match_limit_recursion = match_limit_recursion;
#endif
@@ -521,7 +578,7 @@ void init_regexp(void) {
pr_event_register(NULL, "core.restart", regexp_restart_ev, NULL);
pr_event_register(NULL, "core.exit", regexp_exit_ev, NULL);
-#ifdef PR_USE_PCRE
+#if defined(PR_USE_PCRE)
pr_log_debug(DEBUG2, "using PCRE %s", pcre_version());
#endif /* PR_USE_PCRE */
}
=====================================
tests/api/regexp.c
=====================================
@@ -242,7 +242,7 @@ START_TEST (regexp_exec_test) {
pre = pr_regexp_alloc(NULL);
pattern = "^foo";
- res = pr_regexp_compile(pre, pattern, 0);
+ res = pr_regexp_compile(pre, pattern, REG_ICASE);
fail_unless(res == 0, "Failed to compile regex pattern '%s'", pattern);
res = pr_regexp_exec(pre, NULL, 0, NULL, 0, 0, 0);
@@ -256,8 +256,11 @@ START_TEST (regexp_exec_test) {
res = pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0);
fail_unless(res == 0, "Failed to match string");
- pr_regexp_free(NULL, pre);
+ str = "FOOBAR";
+ res = pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0);
+ fail_unless(res == 0, "Failed to match string");
+ pr_regexp_free(NULL, pre);
pre = pr_regexp_alloc(NULL);
pattern = "^foo";
@@ -271,13 +274,122 @@ START_TEST (regexp_exec_test) {
res = pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0);
fail_unless(res != 0, "Matched string unexpectedly");
+ str = "foobar";
+ res = pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0);
+ fail_unless(res == 0, "Failed to match string");
+
+#if !defined(PR_USE_PCRE)
+ /* Note that when PCRE support is used, behavior of POSIX matching may be
+ * surprising; I suspect it relates to the overrides in <pcreposix.h>.
+ */
str = "FOOBAR";
res = pr_regexp_exec(pre, str, 0, NULL, 0, 0, 0);
fail_unless(res == 0, "Failed to match string");
+#endif /* PR_USE_PCRE */
+
+ pr_regexp_free(NULL, pre);
+}
+END_TEST
+
+#if !defined(PR_USE_PCRE)
+START_TEST (regexp_capture_posix_test) {
+ register unsigned int i;
+ pr_regex_t *pre = NULL;
+ int captured = FALSE, res;
+ char *pattern, *str;
+ size_t nmatches;
+ regmatch_t *matches;
+
+ pre = pr_regexp_alloc(NULL);
+
+ pattern = "(.*)";
+ res = pr_regexp_compile_posix(pre, pattern, 0);
+ fail_unless(res == 0, "Failed to compile regex pattern '%s'", pattern);
+
+ nmatches = 10;
+ matches = pcalloc(p, sizeof(regmatch_t) * nmatches);
+
+ str = "foobar";
+ res = pr_regexp_exec(pre, str, nmatches, matches, 0, 0, 0);
+ fail_unless(res == 0, "Failed to match string");
+
+ for (i = 0; i < nmatches; i++) {
+ int match_len;
+ const char *match_text;
+
+ if (matches[i].rm_so == -1 ||
+ matches[i].rm_eo == -1) {
+ break;
+ }
+
+ match_text = &(str[matches[i].rm_so]);
+ match_len = matches[i].rm_eo - matches[i].rm_so;
+
+ fail_unless(strcmp(match_text, str) == 0,
+ "Expected matched text '%s', got '%s'", str, match_text);
+ fail_unless(match_len == 6,
+ "Expected match text len 6, got %d", match_len);
+
+ captured = TRUE;
+ }
+
+ fail_unless(captured == TRUE,
+ "POSIX regex failed to capture expected groups");
+
+ pr_regexp_free(NULL, pre);
+}
+END_TEST
+#endif /* PR_USE_PCRE */
+
+#if defined(PR_USE_PCRE)
+START_TEST (regexp_capture_pcre_test) {
+ register unsigned int i;
+ pr_regex_t *pre = NULL;
+ int captured = FALSE, res;
+ char *pattern, *str;
+ size_t nmatches;
+ regmatch_t *matches;
+
+ pre = pr_regexp_alloc(NULL);
+
+ pattern = "(.*)";
+ res = pr_regexp_compile(pre, pattern, 0);
+ fail_unless(res == 0, "Failed to compile regex pattern '%s'", pattern);
+
+ nmatches = 10;
+ matches = pcalloc(p, sizeof(regmatch_t) * nmatches);
+
+ str = "foobar";
+ res = pr_regexp_exec(pre, str, nmatches, matches, 0, 0, 0);
+ fail_unless(res == 0, "Failed to match string");
+
+ for (i = 0; i < nmatches; i++) {
+ int match_len;
+ const char *match_text;
+
+ if (matches[i].rm_so == -1 ||
+ matches[i].rm_eo == -1) {
+ break;
+ }
+
+ match_text = &(str[matches[i].rm_so]);
+ match_len = matches[i].rm_eo - matches[i].rm_so;
+
+ fail_unless(strcmp(match_text, str) == 0,
+ "Expected matched text '%s', got '%s' (i = %u)", str, match_text, i);
+ fail_unless(match_len == 6,
+ "Expected match text len 6, got %d (i = %u)", match_len, i);
+
+ captured = TRUE;
+ }
+
+ fail_unless(captured == TRUE,
+ "PCRE regex failed to capture expected groups");
pr_regexp_free(NULL, pre);
}
END_TEST
+#endif /* PR_USE_PCRE */
START_TEST (regexp_cleanup_test) {
pr_regex_t *pre, *pre2, *pre3;
@@ -329,6 +441,12 @@ Suite *tests_get_regexp_suite(void) {
tcase_add_test(testcase, regexp_compile_test);
tcase_add_test(testcase, regexp_compile_posix_test);
tcase_add_test(testcase, regexp_exec_test);
+#if !defined(PR_USE_PCRE)
+ tcase_add_test(testcase, regexp_capture_posix_test);
+#endif /* !PR_USE_PCRE */
+#if defined(PR_USE_PCRE)
+ tcase_add_test(testcase, regexp_capture_pcre_test);
+#endif /* PR_USE_PCRE */
tcase_add_test(testcase, regexp_get_pattern_test);
tcase_add_test(testcase, regexp_set_limits_test);
tcase_add_test(testcase, regexp_cleanup_test);
=====================================
tests/t/lib/ProFTPD/Tests/Modules/mod_rewrite.pm
=====================================
@@ -213,6 +213,11 @@ my $TESTS = {
test_class => [qw(bug feature_pcre forking rootprivs)],
},
+ rewrite_using_pcre_issue1300 => {
+ order => ++$order,
+ test_class => [qw(bug feature_pcre forking)],
+ },
+
};
sub new {
@@ -2966,23 +2971,8 @@ sub rewrite_cond_nc_flags {
sub rewrite_map_fifo_bug3611 {
my $self = shift;
my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'rewrite');
- my $config_file = "$tmpdir/rewrite.conf";
- my $pid_file = File::Spec->rel2abs("$tmpdir/rewrite.pid");
- my $scoreboard_file = File::Spec->rel2abs("$tmpdir/rewrite.scoreboard");
-
- my $log_file = test_get_logfile();
-
- my $auth_user_file = File::Spec->rel2abs("$tmpdir/rewrite.passwd");
- my $auth_group_file = File::Spec->rel2abs("$tmpdir/rewrite.group");
-
- my $user = 'proftpd';
- my $passwd = 'test';
- my $group = 'ftpd';
- my $home_dir = File::Spec->rel2abs($tmpdir);
- my $uid = 500;
- my $gid = 500;
-
my $sub_dir = File::Spec->rel2abs("$tmpdir/tmp");
mkpath($sub_dir);
@@ -3003,37 +2993,33 @@ sub rewrite_map_fifo_bug3611 {
# Make sure that, if we're running as root, that the home directory has
# permissions/privs set for the account we create
if ($< == 0) {
- unless (chmod(0755, $home_dir, $sub_dir)) {
- die("Can't set perms on $home_dir to 0755: $!");
+ unless (chmod(0755, $sub_dir)) {
+ die("Can't set perms on $sub_dir to 0755: $!");
}
- unless (chown($uid, $gid, $home_dir, $sub_dir)) {
- die("Can't set owner of $home_dir to $uid/$gid: $!");
+ unless (chown($setup->{uid}, $setup->{gid}, $sub_dir)) {
+ die("Can't set owner of $sub_dir to $setup->{uid}/$setup->{gid}: $!");
}
}
- auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
- '/bin/bash');
- auth_group_write($auth_group_file, $group, $gid, $user);
-
my $fifo_script = File::Spec->rel2abs('t/etc/modules/mod_rewrite/reverse.pl');
- my $fifo = File::Spec->rel2abs("$home_dir/test.fifo");
+ my $fifo = File::Spec->rel2abs("$setup->{home_dir}/test.fifo");
unless (POSIX::mkfifo($fifo, 0666)) {
die("Can't create fifo $fifo: $!");
}
- my $fifo_pidfile = File::Spec->rel2abs("$home_dir/fifo.pid");
+ my $fifo_pidfile = File::Spec->rel2abs("$setup->{home_dir}/fifo.pid");
my $config = {
- PidFile => $pid_file,
- ScoreboardFile => $scoreboard_file,
- SystemLog => $log_file,
- TraceLog => $log_file,
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
Trace => 'DEFAULT:10',
- AuthUserFile => $auth_user_file,
- AuthGroupFile => $auth_group_file,
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
IfModules => {
'mod_delay.c' => {
@@ -3042,7 +3028,7 @@ sub rewrite_map_fifo_bug3611 {
'mod_rewrite.c' => [
'RewriteEngine on',
- "RewriteLog $log_file",
+ "RewriteLog $setup->{log_file}",
'RewriteCondition %m !PASS',
"RewriteMap reverse fifo:$fifo",
@@ -3051,7 +3037,8 @@ sub rewrite_map_fifo_bug3611 {
},
};
- my ($port, $config_user, $config_group) = config_write($config_file, $config);
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
# Open pipes, for use between the parent and child processes. Specifically,
# the child will indicate when it's done with its test by writing a message
@@ -3068,26 +3055,24 @@ sub rewrite_map_fifo_bug3611 {
defined(my $pid = fork()) or die("Can't fork: $!");
if ($pid) {
eval {
+ # Allow for server startup
+ sleep(1);
+
my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
- my $name = join('', reverse(split//, $user));
- $client->login($name, $passwd);
+ my $name = join('', reverse(split//, $setup->{user}));
+ $client->login($name, $setup->{passwd});
$client->type('binary');
# Send the path in reverse; the rewrite FIFO should reverse
# everything.
- my ($resp_code, $resp_msg);
-
my $path = join('', reverse(split(//, $test_file)));
- ($resp_code, $resp_msg) = $client->stat($path);
+ my ($resp_code, $resp_msg) = $client->stat($path);
- my $expected;
-
- $expected = 211;
+ my $expected = 213;
$self->assert($expected == $resp_code,
- test_msg("Expected $expected, got $resp_code"));
+ test_msg("Expected response code $expected, got $resp_code"));
};
-
if ($@) {
$ex = $@;
}
@@ -3097,9 +3082,9 @@ sub rewrite_map_fifo_bug3611 {
} else {
# Start the FIFO script
- `$fifo_script --verbose --fifo $fifo --pidfile $fifo_pidfile >> $log_file 2>&1 &`;
+ `$fifo_script --verbose --fifo $fifo --pidfile $fifo_pidfile >> $setup->{log_file} 2>&1 &`;
- eval { server_wait($config_file, $rfh) };
+ eval { server_wait($setup->{config_file}, $rfh) };
if ($@) {
warn($@);
@@ -3125,9 +3110,6 @@ sub rewrite_map_fifo_bug3611 {
exit 0;
}
- # Stop server
- server_stop($pid_file);
-
if (open(my $fh, "< $fifo_pidfile")) {
my $fifo_pid = <$fh>;
chomp($fifo_pid);
@@ -3136,16 +3118,11 @@ sub rewrite_map_fifo_bug3611 {
close($fh);
}
+ # Stop server
+ server_stop($setup->{pid_file});
$self->assert_child_ok($pid);
- if ($ex) {
- test_append_logfile($log_file, $ex);
- unlink($log_file);
-
- die($ex);
- }
-
- unlink($log_file);
+ test_cleanup($setup->{log_file}, $ex);
}
sub rewrite_rule_replaceall_backslash_with_slash {
@@ -3439,23 +3416,8 @@ sub rewrite_map_max_replace_bug3721 {
sub rewrite_cond_time_var_bug3673 {
my $self = shift;
my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'rewrite');
- my $config_file = "$tmpdir/rewrite.conf";
- my $pid_file = File::Spec->rel2abs("$tmpdir/rewrite.pid");
- my $scoreboard_file = File::Spec->rel2abs("$tmpdir/rewrite.scoreboard");
-
- my $log_file = test_get_logfile();
-
- my $auth_user_file = File::Spec->rel2abs("$tmpdir/rewrite.passwd");
- my $auth_group_file = File::Spec->rel2abs("$tmpdir/rewrite.group");
-
- my $user = 'proftpd';
- my $passwd = 'test';
- my $group = 'ftpd';
- my $home_dir = File::Spec->rel2abs($tmpdir);
- my $uid = 500;
- my $gid = 500;
-
my $sub_dir = File::Spec->rel2abs("$tmpdir/tmp");
mkpath($sub_dir);
@@ -3476,34 +3438,32 @@ sub rewrite_cond_time_var_bug3673 {
# Make sure that, if we're running as root, that the home directory has
# permissions/privs set for the account we create
if ($< == 0) {
- unless (chmod(0755, $home_dir, $sub_dir)) {
- die("Can't set perms on $home_dir to 0755: $!");
+ unless (chmod(0755, $sub_dir)) {
+ die("Can't set perms on $sub_dir to 0755: $!");
}
- unless (chown($uid, $gid, $home_dir, $sub_dir)) {
- die("Can't set owner of $home_dir to $uid/$gid: $!");
+ unless (chown($setup->{uid}, $setup->{gid}, $sub_dir)) {
+ die("Can't set owner of $sub_dir to $setup->{uid}/$setup->{gid}: $!");
}
}
- auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
- '/bin/bash');
- auth_group_write($auth_group_file, $group, $gid, $user);
-
require DateTime;
my $dt = DateTime->now();
$dt->set_time_zone('America/Los_Angeles');
- $dt->subtract(seconds => 3);
+ $dt->add(seconds => 30);
my $timestamp = ($dt->ymd('') . $dt->hms(''));
my $config = {
- PidFile => $pid_file,
- ScoreboardFile => $scoreboard_file,
- SystemLog => $log_file,
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'regexp:20 rewrite:20',
- AuthUserFile => $auth_user_file,
- AuthGroupFile => $auth_group_file,
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
IfModules => {
'mod_delay.c' => {
@@ -3512,7 +3472,7 @@ sub rewrite_cond_time_var_bug3673 {
'mod_rewrite.c' => [
'RewriteEngine on',
- "RewriteLog $log_file",
+ "RewriteLog $setup->{log_file}",
'RewriteMap replace int:replaceall',
@@ -3523,7 +3483,8 @@ sub rewrite_cond_time_var_bug3673 {
},
};
- my ($port, $config_user, $config_group) = config_write($config_file, $config);
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
# Open pipes, for use between the parent and child processes. Specifically,
# the child will indicate when it's done with its test by writing a message
@@ -3540,27 +3501,26 @@ sub rewrite_cond_time_var_bug3673 {
defined(my $pid = fork()) or die("Can't fork: $!");
if ($pid) {
eval {
- my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
-
- my ($resp_code, $resp_msg);
+ # Allow for server startup
+ sleep(1);
- $client->login($user, $passwd);
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
+ $client->login($setup->{user}, $setup->{passwd});
$client->type('binary');
# Send the path with spaces; the rewrite rules should handle it
- ($resp_code, $resp_msg) = $client->size("tmp/test file here.txt");
-
- my $expected;
+ my ($resp_code, $resp_msg) = $client->size("tmp/test file here.txt");
- $expected = 213;
+ my $expected = 213;
$self->assert($expected == $resp_code,
- test_msg("Expected $expected, got $resp_code"));
+ test_msg("Expected response code $expected, got $resp_code"));
$expected = '14';
$self->assert($expected eq $resp_msg,
- test_msg("Expected '$expected', got '$resp_msg'"));
- };
+ test_msg("Expected response message '$expected', got '$resp_msg'"));
+ $client->quit();
+ };
if ($@) {
$ex = $@;
}
@@ -3569,7 +3529,7 @@ sub rewrite_cond_time_var_bug3673 {
$wfh->flush();
} else {
- eval { server_wait($config_file, $rfh) };
+ eval { server_wait($setup->{config_file}, $rfh) };
if ($@) {
warn($@);
exit 1;
@@ -3579,18 +3539,10 @@ sub rewrite_cond_time_var_bug3673 {
}
# Stop server
- server_stop($pid_file);
-
+ server_stop($setup->{pid_file});
$self->assert_child_ok($pid);
- if ($ex) {
- test_append_logfile($log_file, $ex);
- unlink($log_file);
-
- die($ex);
- }
-
- unlink($log_file);
+ test_cleanup($setup->{log_file}, $ex);
}
sub rewrite_cond_time_year_var_bug3673 {
@@ -5996,4 +5948,97 @@ sub rewrite_using_pcre_bug4017 {
unlink($log_file);
}
+sub rewrite_using_pcre_issue1300 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'rewrite');
+
+ my $test_dir = File::Spec->rel2abs("$tmpdir/folder1/folder2");
+ mkpath($test_dir);
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'regexp:20 rewrite:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ DefaultChdir => '~',
+
+ DenyFilter => '\*.*/',
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+
+ 'mod_rewrite.c' => [
+ 'RewriteEngine on',
+ "RewriteLog $setup->{log_file}",
+ 'RewriteMap replace int:replaceall',
+
+ 'RewriteCondition %m RETR|SIZE|LIST|CWD',
+ 'RewriteRule (.*) "${replace:!$1!\\\\!/}"',
+ ],
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ # Allow server to start up
+ sleep(1);
+
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
+ $client->login($setup->{user}, $setup->{passwd});
+
+ my ($resp_code, $resp_msg) = $client->cwd('folder1\folder2');
+
+ my $expected = 250;
+ $self->assert($expected == $resp_code,
+ test_msg("Expected response code $expected, got $resp_code"));
+
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
1;
=====================================
tests/t/lib/ProFTPD/Tests/Modules/mod_tls.pm
=====================================
@@ -9,6 +9,7 @@ use File::Copy;
use File::Path qw(mkpath);
use File::Spec;
use IO::Handle;
+use IPC::Open3;
use Socket;
use ProFTPD::TestSuite::FTP;
@@ -474,6 +475,10 @@ my $TESTS = {
test_class => [qw(bug forking)],
},
+ tls_old_protocols_issue1273 => {
+ order => ++$order,
+ test_class => [qw(bug forking)],
+ },
};
sub new {
@@ -13489,4 +13494,175 @@ sub tls_fxp_issue618 {
test_cleanup($setup->{log_file}, $ex);
}
+sub tls_old_protocols_issue1273 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'tls');
+
+ my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem');
+ my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem');
+
+ my $tls_opts = 'NoSessionReuseRequired UseImplicitSSL';
+ if ($ENV{TEST_VERBOSE}) {
+ $tls_opts .= ' EnableDiags';
+ }
+
+ my $timeout_idle = 15;
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'command:20 response:20 data:20 netio:20 tls:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+
+ AllowForeignAddress => 'on',
+ AllowOverwrite => 'on',
+ TimeoutIdle => $timeout_idle,
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+
+ 'mod_tls.c' => {
+ TLSEngine => 'on',
+ TLSLog => $setup->{log_file},
+ TLSProtocol => 'SSLv23',
+ TLSRequired => 'on',
+ TLSRSACertificateFile => $cert_file,
+ TLSCACertificateFile => $ca_file,
+ TLSOptions => $tls_opts,
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ sleep(2);
+
+ # We use an older OpenSSL version for the older protocols.
+ # Allow server to start up
+ my $openssl = '/Users/tj/local/openssl-0.9.8d/bin/openssl';
+
+ # Explicitly use SSLv3, which has been disabled by default in
+ # OpenSSL-1.1.x; see:
+ # https://github.com/openssl/openssl/issues/4989
+
+ my @cmd = (
+ $openssl,
+ 's_client',
+ '-connect',
+ "127.0.0.1:$port",
+ '-ssl3',
+ );
+
+ my $tls_rh = IO::Handle->new();
+ my $tls_wh = IO::Handle->new();
+ my $tls_eh = IO::Handle->new();
+
+ $tls_wh->autoflush(1);
+
+ local $SIG{CHLD} = 'DEFAULT';
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "Executing: ", join(' ', @cmd), "\n";
+ }
+
+ my $tls_pid = open3($tls_wh, $tls_rh, $tls_eh, @cmd);
+ print $tls_wh "quit\n";
+ waitpid($tls_pid, 0);
+
+ my ($res, $cipher_str, $err_str, $out_str);
+ if ($? >> 8) {
+ $err_str = join('', <$tls_eh>);
+ $res = 0;
+
+ } else {
+ my $output = [<$tls_rh>];
+
+ if ($ENV{TEST_VERBOSE}) {
+ $out_str = join('', @$output);
+ print STDERR "Stdout: $out_str\n";
+
+ $err_str = join('', <$tls_eh>);
+ print STDERR "Stderr: $err_str\n";
+ }
+
+ $res = 1;
+ }
+
+ unless ($res) {
+ die("Can't talk to server: $err_str");
+ }
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh, $timeout_idle + 5) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+ test_cleanup($setup->{log_file}, $ex) if $ex;
+
+ eval {
+ if (open(my $fh, "< $setup->{log_file}")) {
+ my $seen = 0;
+
+ while (my $line = <$fh>) {
+ chomp($line);
+
+ if ($line =~ /OpenSSL.*?lacks support for client requested/) {
+ $seen = 1;
+ last;
+ }
+ }
+
+ close($fh);
+
+ $self->assert($seen, test_msg("Did not see expected log message"));
+
+ } else {
+ die("Can't read $setup->{log_file}: $!");
+ }
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
1;
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/348478693ccb37d8a3c0f35172bb037826cd7acc
--
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/348478693ccb37d8a3c0f35172bb037826cd7acc
You're receiving this email because of your account on salsa.debian.org.
More information about the Pkg-proftpd-maintainers
mailing list