[Git][debian-proftpd-team/proftpd][master] Add a bunch of fixes from upstream.
Hilmar Preuße (@hilmar-guest)
gitlab at salsa.debian.org
Fri Feb 18 23:13:20 GMT 2022
Hilmar Preuße pushed to branch master at Debian ProFTPD Team / proftpd
Commits:
fbc2d571 by Hilmar Preusse at 2022-02-19T00:12:53+01:00
Add a bunch of fixes from upstream.
- - - - -
7 changed files:
- debian/changelog
- + debian/patches/bug_4467
- debian/patches/series
- + debian/patches/upstream_1322
- + debian/patches/upstream_1325
- + debian/patches/upstream_1346
- + debian/patches/upstream_long_AuthGroupFile_lines
Changes:
=====================================
debian/changelog
=====================================
@@ -7,6 +7,12 @@ proftpd-dfsg (1.3.7c+dfsg-2) UNRELEASED; urgency=medium
- Rules-Requires-Root: binary
- More small stuff
* Fix syntax values in proftpd.conf example (Closes: #1002467)
+ * Add a bunch of fixes from upstream:
+ + upstream_1322
+ + upstream_1325
+ + upstream_1346
+ + upstream_long_AuthGroupFile_lines
+ + bug_4467
[ Debian Janitor ]
* Remove constraints unnecessary since buster:
=====================================
debian/patches/bug_4467
=====================================
@@ -0,0 +1,34 @@
+From ed101eed833bdb53d6bcecdb4448040bd4ce9b5f Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Sat, 29 Jan 2022 11:20:14 -0800
+Subject: [PATCH] Backport of fix for Bug#4467 to the 1.3.7 branch.
+
+---
+ NEWS | 2 ++
+ modules/mod_xfer.c | 5 ++---
+ 2 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/modules/mod_xfer.c b/modules/mod_xfer.c
+index 9348118c00..b7fcf74e94 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-2020 The ProFTPD Project team
++ * Copyright (c) 2001-2022 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
+@@ -1014,9 +1014,8 @@ static void stor_abort(pool *p) {
+ }
+ }
+ }
+- }
+
+- if (session.xfer.path != NULL) {
++ } else if (session.xfer.path != NULL) {
+ if (delete_stores != NULL &&
+ *delete_stores == TRUE) {
+ pr_log_debug(DEBUG5, "removing aborted file '%s'", session.xfer.path);
=====================================
debian/patches/series
=====================================
@@ -19,3 +19,8 @@ proftpd-mysql-password-backend.diff
#2eadd82f392573235432a9cb60266f6472d08884.diff
#upstream_1284
spelling_error_in_contrib_mod_tls.c
+upstream_1322
+upstream_1325
+upstream_1346
+upstream_long_AuthGroupFile_lines
+bug_4467
=====================================
debian/patches/upstream_1322
=====================================
@@ -0,0 +1,154 @@
+From e428a379fc97db8400a497045a57a69468666901 Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Sat, 4 Sep 2021 10:19:01 -0700
+Subject: [PATCH] Issue #1321: When processing very long lines from an
+ `AuthGroupFile`, allocate larger buffers in smaller increments, as
+ `fgetgrent(3)` does.
+
+---
+ modules/mod_auth_file.c | 11 ++-
+ tests/t/lib/ProFTPD/TestSuite/Utils.pm | 4 +-
+ .../ProFTPD/Tests/Modules/mod_auth_file.pm | 80 +++++++++++++++++++
+ 3 files changed, 92 insertions(+), 3 deletions(-)
+
+--- proftpd.orig/modules/mod_auth_file.c
++++ proftpd/modules/mod_auth_file.c
+@@ -332,6 +332,9 @@
+ static char *af_getgrentline(char **buf, int *buflen, pr_fh_t *fh,
+ unsigned int *lineno) {
+ char *cp = *buf;
++ int original_buflen;
++
++ original_buflen = *buflen;
+
+ while (pr_fsio_gets(cp, (*buflen) - (cp - *buf), fh) != NULL) {
+ pr_signals_handle();
+@@ -343,8 +346,12 @@
+ return *buf;
+ }
+
+- /* No -- allocate a larger buffer, doubling buflen. */
+- *buflen += *buflen;
++ /* No -- allocate a larger buffer. Note that doubling the buflen
++ * each time may cause issues; fgetgrent(3) would increment the
++ * allocated buffer by the original buffer length each time. So we
++ * do the same (Issue #1321).
++ */
++ *buflen += original_buflen;
+
+ {
+ char *new_buf;
+--- proftpd.orig/tests/t/lib/ProFTPD/TestSuite/Utils.pm
++++ proftpd/tests/t/lib/ProFTPD/TestSuite/Utils.pm
+@@ -1210,6 +1210,8 @@
+ my $gid = shift;
+ $gid = 500 unless defined($gid);
+ my $home_dir = shift;
++ my $groups = shift;
++ $groups = $user unless defined($groups);
+
+ my $config_file = "$tmpdir/$name.conf";
+ my $pid_file = File::Spec->rel2abs("$tmpdir/$name.pid");
+@@ -1238,7 +1240,7 @@
+
+ auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
+ '/bin/bash');
+- auth_group_write($auth_group_file, $group, $gid, $user);
++ auth_group_write($auth_group_file, $group, $gid, $groups);
+
+ my $setup = {
+ auth_user_file => $auth_user_file,
+--- proftpd.orig/tests/t/lib/ProFTPD/Tests/Modules/mod_auth_file.pm
++++ proftpd/tests/t/lib/ProFTPD/Tests/Modules/mod_auth_file.pm
+@@ -122,6 +122,10 @@
+ test_class => [qw(bug forking)],
+ },
+
++ auth_file_line_too_long_issue1321 => {
++ order => ++$order,
++ test_class => [qw(bug forking)],
++ },
+ };
+
+ sub new {
+@@ -2271,4 +2275,80 @@
+ unlink($log_file);
+ }
+
++sub auth_file_line_too_long_issue1321 {
++ my $self = shift;
++ my $tmpdir = $self->{tmpdir};
++
++ # For Issue #1321, we create a very long AuthGroupFile entry with many
++ # group names.
++
++ my $groups = 'proftpd';
++ for (my $i = 0; $i < 200; $i++) {
++ $groups .= ",quite.long.example.group.$i";
++ }
++
++ my $setup = test_setup($tmpdir, 'authfile', undef, undef, undef, undef, undef,
++ undef, $groups);
++
++ my $config = {
++ PidFile => $setup->{pid_file},
++ ScoreboardFile => $setup->{scoreboard_file},
++ SystemLog => $setup->{log_file},
++
++ AuthUserFile => $setup->{auth_user_file},
++ AuthGroupFile => $setup->{auth_group_file},
++
++ IfModules => {
++ 'mod_delay.c' => {
++ DelayEngine => 'off',
++ },
++ },
++ };
++
++ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++ $config);
++
++ # 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 {
++ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
++ $client->login($setup->{user}, $setup->{passwd});
++ $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;
=====================================
debian/patches/upstream_1325
=====================================
@@ -0,0 +1,569 @@
+From 6e3d8debc14f1b532fae27994120f591b39fb183 Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Sat, 11 Sep 2021 14:22:31 -0700
+Subject: [PATCH] Backport fix for Issue #1325 to the 1.3.7 branch.
+
+---
+ NEWS | 1 +
+ modules/mod_ls.c | 6 +-
+ tests/t/lib/ProFTPD/Tests/Commands/NLST.pm | 453 +++++++++++++++++++--
+ 3 files changed, 412 insertions(+), 48 deletions(-)
+
+diff --git a/modules/mod_ls.c b/modules/mod_ls.c
+index 45a3187bd9..c7a44c450a 100644
+--- a/modules/mod_ls.c
++++ b/modules/mod_ls.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
++ * Copyright (c) 2001-2021 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
+@@ -3163,10 +3163,6 @@ MODRET ls_nlst(cmd_rec *cmd) {
+ p = *path;
+ path++;
+
+- if (*p == '.' && (!opt_A || is_dotdir(p))) {
+- continue;
+- }
+-
+ pr_fs_clear_cache2(p);
+ if (pr_fsio_stat(p, &st) == 0) {
+ /* If it's a directory... */
+diff --git a/tests/t/lib/ProFTPD/Tests/Commands/NLST.pm b/tests/t/lib/ProFTPD/Tests/Commands/NLST.pm
+index 7fa5c84177..eac38ea24d 100644
+--- a/tests/t/lib/ProFTPD/Tests/Commands/NLST.pm
++++ b/tests/t/lib/ProFTPD/Tests/Commands/NLST.pm
+@@ -157,6 +157,16 @@ my $TESTS = {
+ test_class => [qw(forking rootprivs)],
+ },
+
++ nlst_glob_with_rel_path_issue1325 => {
++ order => ++$order,
++ test_class => [qw(bug forking)],
++ },
++
++ nlst_glob_with_rel_path_dotdir_issue1325 => {
++ order => ++$order,
++ test_class => [qw(bug forking)],
++ },
++
+ };
+
+ sub new {
+@@ -3616,22 +3626,7 @@ sub nlst_rel_path_chrooted_bug2496 {
+ sub nlst_parent_dir_bug4011 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+-
+- my $config_file = "$tmpdir/cmds.conf";
+- my $pid_file = File::Spec->rel2abs("$tmpdir/cmds.pid");
+- my $scoreboard_file = File::Spec->rel2abs("$tmpdir/cmds.scoreboard");
+-
+- my $log_file = test_get_logfile();
+-
+- my $auth_user_file = File::Spec->rel2abs("$tmpdir/cmds.passwd");
+- my $auth_group_file = File::Spec->rel2abs("$tmpdir/cmds.group");
+-
+- my $user = 'proftpd';
+- my $passwd = 'test';
+- my $group = 'ftpd';
+- my $home_dir = File::Spec->rel2abs($tmpdir);
+- my $uid = 500;
+- my $gid = 500;
++ my $setup = test_setup($tmpdir, 'cmds');
+
+ my $sub_dir1 = File::Spec->rel2abs("$tmpdir/dir1");
+ my $sub_dir2 = File::Spec->rel2abs("$tmpdir/dir1/dir2");
+@@ -3662,26 +3657,22 @@ sub nlst_parent_dir_bug4011 {
+ # 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_dir1)) {
+- die("Can't set perms on $home_dir to 0755: $!");
++ unless (chmod(0755, $sub_dir1)) {
++ die("Can't set perms on $sub_dir1 to 0755: $!");
+ }
+
+- unless (chown($uid, $gid, $home_dir, $sub_dir1)) {
+- die("Can't set owner of $home_dir to $uid/$gid: $!");
++ unless (chown($setup->{uid}, $setup->{gid}, $sub_dir1)) {
++ die("Can't set owner of $sub_dir1 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 $config = {
+- PidFile => $pid_file,
+- ScoreboardFile => $scoreboard_file,
+- SystemLog => $log_file,
++ PidFile => $setup->{pid_file},
++ ScoreboardFile => $setup->{scoreboard_file},
++ SystemLog => $setup->{log_file},
+
+- AuthUserFile => $auth_user_file,
+- AuthGroupFile => $auth_group_file,
++ AuthUserFile => $setup->{auth_user_file},
++ AuthGroupFile => $setup->{auth_group_file},
+
+ IfModules => {
+ 'mod_delay.c' => {
+@@ -3690,7 +3681,8 @@ sub nlst_parent_dir_bug4011 {
+ },
+ };
+
+- 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
+@@ -3708,7 +3700,7 @@ sub nlst_parent_dir_bug4011 {
+ if ($pid) {
+ eval {
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
+- $client->login($user, $passwd);
++ $client->login($setup->{user}, $setup->{passwd});
+ $client->cwd("dir1");
+ $client->cwd("dir2");
+
+@@ -3722,6 +3714,12 @@ sub nlst_parent_dir_bug4011 {
+ $conn->read($buf, 8192, 25);
+ eval { $conn->close() };
+
++ $client->quit();
++
++ if ($ENV{TEST_VERBOSE}) {
++ print STDERR "# response:\n$buf\n";
++ }
++
+ # We have to be careful of the fact that readdir returns directory
+ # entries in an unordered fashion.
+ my $res = {};
+@@ -3749,7 +3747,6 @@ sub nlst_parent_dir_bug4011 {
+ die("Unexpected name '$mismatch' appeared in NLST data")
+ }
+ };
+-
+ if ($@) {
+ $ex = $@;
+ }
+@@ -3758,7 +3755,7 @@ sub nlst_parent_dir_bug4011 {
+ $wfh->flush();
+
+ } else {
+- eval { server_wait($config_file, $rfh) };
++ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+@@ -3768,18 +3765,10 @@ sub nlst_parent_dir_bug4011 {
+ }
+
+ # 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 nlst_opt_a_root_dir_bug4069 {
+@@ -4080,4 +4069,382 @@ sub nlst_opt_1_with_chroot {
+ unlink($log_file);
+ }
+
++sub nlst_glob_with_rel_path_issue1325 {
++ my $self = shift;
++ my $tmpdir = $self->{tmpdir};
++ my $setup = test_setup($tmpdir, 'cmds');
++
++ my $test_path = File::Spec->rel2abs("$tmpdir/test.d");
++ mkpath($test_path);
++
++ for (my $i = 0; $i < 10; $i++) {
++ my $test_file = File::Spec->rel2abs("$test_path/TEST000$i.dat");
++ if (open(my $fh, "> $test_file")) {
++ print $fh "Hello, World!\n";
++ unless (close($fh)) {
++ die("Can't write $test_file: $!");
++ }
++
++ } else {
++ die("Can't open $test_file: $!");
++ }
++ }
++
++ my $config = {
++ PidFile => $setup->{pid_file},
++ ScoreboardFile => $setup->{scoreboard_file},
++ SystemLog => $setup->{log_file},
++
++ AuthUserFile => $setup->{auth_user_file},
++ AuthGroupFile => $setup->{auth_group_file},
++
++ IfModules => {
++ 'mod_delay.c' => {
++ DelayEngine => 'off',
++ },
++ },
++ };
++
++ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++ $config);
++
++ # 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 {
++ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
++ $client->login($setup->{user}, $setup->{passwd});
++
++ my $conn = $client->nlst_raw('test.d/TEST????.dat');
++ unless ($conn) {
++ die("Failed to NLST: " . $client->response_code() . " " .
++ $client->response_msg());
++ }
++
++ my $buf;
++ $conn->read($buf, 8192, 25);
++ eval { $conn->close() };
++
++ if ($ENV{TEST_VERBOSE}) {
++ print STDERR "# response:\n$buf\n";
++ }
++
++ # We have to be careful of the fact that readdir returns directory
++ # entries in an unordered fashion.
++ my $res = {};
++ my $names = [split(/\n/, $buf)];
++ foreach my $name (@$names) {
++ $res->{$name} = 1;
++ }
++
++ $self->assert(scalar(@$names) > 0,
++ test_msg("Expected multiple names, got 0"));
++
++ my $expected = {
++ 'test.d/TEST0000.dat' => 1,
++ 'test.d/TEST0001.dat' => 1,
++ 'test.d/TEST0002.dat' => 1,
++ 'test.d/TEST0003.dat' => 1,
++ 'test.d/TEST0004.dat' => 1,
++ 'test.d/TEST0005.dat' => 1,
++ 'test.d/TEST0006.dat' => 1,
++ 'test.d/TEST0007.dat' => 1,
++ 'test.d/TEST0008.dat' => 1,
++ 'test.d/TEST0009.dat' => 1,
++ };
++
++ my $ok = 1;
++ my $mismatch;
++ foreach my $name (keys(%$res)) {
++ unless (defined($expected->{$name})) {
++ $mismatch = $name;
++ $ok = 0;
++ last;
++ }
++ }
++
++ unless ($ok) {
++ die("Unexpected name '$mismatch' appeared in NLST data")
++ }
++
++ # Now do it again, this time using an explicit relative path.
++
++ $conn = $client->nlst_raw('./test.d/TEST????.dat');
++ unless ($conn) {
++ die("Failed to NLST: " . $client->response_code() . " " .
++ $client->response_msg());
++ }
++
++ $buf = '';
++ $conn->read($buf, 8192, 25);
++ eval { $conn->close() };
++
++ if ($ENV{TEST_VERBOSE}) {
++ print STDERR "# response:\n$buf\n";
++ }
++
++ # We have to be careful of the fact that readdir returns directory
++ # entries in an unordered fashion.
++ $res = {};
++ $names = [split(/\n/, $buf)];
++ foreach my $name (@$names) {
++ $res->{$name} = 1;
++ }
++
++ $self->assert(scalar(@$names) > 0,
++ test_msg("Expected multiple names, got 0"));
++
++ $expected = {
++ './test.d/TEST0000.dat' => 1,
++ './test.d/TEST0001.dat' => 1,
++ './test.d/TEST0002.dat' => 1,
++ './test.d/TEST0003.dat' => 1,
++ './test.d/TEST0004.dat' => 1,
++ './test.d/TEST0005.dat' => 1,
++ './test.d/TEST0006.dat' => 1,
++ './test.d/TEST0007.dat' => 1,
++ './test.d/TEST0008.dat' => 1,
++ './test.d/TEST0009.dat' => 1,
++ };
++
++ $ok = 1;
++ $mismatch = '';
++ foreach my $name (keys(%$res)) {
++ unless (defined($expected->{$name})) {
++ $mismatch = $name;
++ $ok = 0;
++ last;
++ }
++ }
++
++ unless ($ok) {
++ die("Unexpected name '$mismatch' appeared in NLST data")
++ }
++
++ $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);
++}
++
++sub nlst_glob_with_rel_path_dotdir_issue1325 {
++ my $self = shift;
++ my $tmpdir = $self->{tmpdir};
++ my $setup = test_setup($tmpdir, 'cmds');
++
++ my $test_path = File::Spec->rel2abs("$tmpdir/.test.d");
++ mkpath($test_path);
++
++ for (my $i = 0; $i < 10; $i++) {
++ my $test_file = File::Spec->rel2abs("$test_path/TEST000$i.dat");
++ if (open(my $fh, "> $test_file")) {
++ print $fh "Hello, World!\n";
++ unless (close($fh)) {
++ die("Can't write $test_file: $!");
++ }
++
++ } else {
++ die("Can't open $test_file: $!");
++ }
++ }
++
++ my $config = {
++ PidFile => $setup->{pid_file},
++ ScoreboardFile => $setup->{scoreboard_file},
++ SystemLog => $setup->{log_file},
++
++ AuthUserFile => $setup->{auth_user_file},
++ AuthGroupFile => $setup->{auth_group_file},
++
++ IfModules => {
++ 'mod_delay.c' => {
++ DelayEngine => 'off',
++ },
++ },
++ };
++
++ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++ $config);
++
++ # 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 {
++ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
++ $client->login($setup->{user}, $setup->{passwd});
++
++ my $conn = $client->nlst_raw('.test.d/TEST????.dat');
++ unless ($conn) {
++ die("Failed to NLST: " . $client->response_code() . " " .
++ $client->response_msg());
++ }
++
++ my $buf;
++ $conn->read($buf, 8192, 25);
++ eval { $conn->close() };
++
++ if ($ENV{TEST_VERBOSE}) {
++ print STDERR "# response:\n$buf\n";
++ }
++
++ # We have to be careful of the fact that readdir returns directory
++ # entries in an unordered fashion.
++ my $res = {};
++ my $names = [split(/\n/, $buf)];
++ foreach my $name (@$names) {
++ $res->{$name} = 1;
++ }
++
++ $self->assert(scalar(@$names) > 0,
++ test_msg("Expected multiple names, got 0"));
++
++ my $expected = {
++ '.test.d/TEST0000.dat' => 1,
++ '.test.d/TEST0001.dat' => 1,
++ '.test.d/TEST0002.dat' => 1,
++ '.test.d/TEST0003.dat' => 1,
++ '.test.d/TEST0004.dat' => 1,
++ '.test.d/TEST0005.dat' => 1,
++ '.test.d/TEST0006.dat' => 1,
++ '.test.d/TEST0007.dat' => 1,
++ '.test.d/TEST0008.dat' => 1,
++ '.test.d/TEST0009.dat' => 1,
++ };
++
++ my $ok = 1;
++ my $mismatch;
++ foreach my $name (keys(%$res)) {
++ unless (defined($expected->{$name})) {
++ $mismatch = $name;
++ $ok = 0;
++ last;
++ }
++ }
++
++ unless ($ok) {
++ die("Unexpected name '$mismatch' appeared in NLST data")
++ }
++
++ # Now do it again, this time using an explicit relative path.
++
++ $conn = $client->nlst_raw('./.test.d/TEST????.dat');
++ unless ($conn) {
++ die("Failed to NLST: " . $client->response_code() . " " .
++ $client->response_msg());
++ }
++
++ $buf = '';
++ $conn->read($buf, 8192, 25);
++ eval { $conn->close() };
++
++ if ($ENV{TEST_VERBOSE}) {
++ print STDERR "# response:\n$buf\n";
++ }
++
++ # We have to be careful of the fact that readdir returns directory
++ # entries in an unordered fashion.
++ $res = {};
++ $names = [split(/\n/, $buf)];
++ foreach my $name (@$names) {
++ $res->{$name} = 1;
++ }
++
++ $self->assert(scalar(@$names) > 0,
++ test_msg("Expected multiple names, got 0"));
++
++ $expected = {
++ './.test.d/TEST0000.dat' => 1,
++ './.test.d/TEST0001.dat' => 1,
++ './.test.d/TEST0002.dat' => 1,
++ './.test.d/TEST0003.dat' => 1,
++ './.test.d/TEST0004.dat' => 1,
++ './.test.d/TEST0005.dat' => 1,
++ './.test.d/TEST0006.dat' => 1,
++ './.test.d/TEST0007.dat' => 1,
++ './.test.d/TEST0008.dat' => 1,
++ './.test.d/TEST0009.dat' => 1,
++ };
++
++ $ok = 1;
++ $mismatch = '';
++ foreach my $name (keys(%$res)) {
++ unless (defined($expected->{$name})) {
++ $mismatch = $name;
++ $ok = 0;
++ last;
++ }
++ }
++
++ unless ($ok) {
++ die("Unexpected name '$mismatch' appeared in NLST data")
++ }
++
++ $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;
=====================================
debian/patches/upstream_1346
=====================================
@@ -0,0 +1,441 @@
+From bb8116d21f7849bf36cc356de4d8cfec579eba64 Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Tue, 26 Oct 2021 19:10:59 -0700
+Subject: [PATCH] Issue #1346: When accept connections for passive data
+ transfers, and a class name has been configured for `AllowForeignAddress`,
+ check the connecting client against that class.
+
+---
+ src/inet.c | 90 +++++--
+ .../Tests/Config/AllowForeignAddress.pm | 255 +++++++++++++++++-
+ 2 files changed, 314 insertions(+), 31 deletions(-)
+
+diff --git a/src/inet.c b/src/inet.c
+index 33ce349aa3..81c1c99b1d 100644
+--- a/src/inet.c
++++ b/src/inet.c
+@@ -1522,9 +1522,9 @@ int pr_inet_accept_nowait(pool *p, conn_t *c) {
+ */
+ conn_t *pr_inet_accept(pool *p, conn_t *d, conn_t *c, int rfd, int wfd,
+ unsigned char resolve) {
++ config_rec *allow_foreign_addr_config = NULL;
+ conn_t *res = NULL;
+- unsigned char *foreign_addr = NULL;
+- int fd = -1, allow_foreign_address = FALSE;
++ int fd = -1;
+ pr_netaddr_t na;
+ socklen_t nalen;
+
+@@ -1540,13 +1540,10 @@ conn_t *pr_inet_accept(pool *p, conn_t *d, conn_t *c, int rfd, int wfd,
+ pr_netaddr_set_family(&na, pr_netaddr_get_family(c->remote_addr));
+ nalen = pr_netaddr_get_sockaddr_len(&na);
+
++ allow_foreign_addr_config = find_config(TOPLEVEL_CONF, CONF_PARAM,
++ "AllowForeignAddress", FALSE);
+ d->mode = CM_ACCEPT;
+
+- foreign_addr = get_param_ptr(TOPLEVEL_CONF, "AllowForeignAddress", FALSE);
+- if (foreign_addr != NULL) {
+- allow_foreign_address = *foreign_addr;
+- }
+-
+ /* A directive could enforce only IPv4 or IPv6 connections here, by
+ * actually using a sockaddr argument to accept(2), and checking the
+ * family of the connecting entity.
+@@ -1566,28 +1563,67 @@ conn_t *pr_inet_accept(pool *p, conn_t *d, conn_t *c, int rfd, int wfd,
+ break;
+ }
+
+- if (allow_foreign_address == FALSE) {
+- /* If foreign addresses (i.e. IP addresses that do not match the
+- * control connection's remote IP address) are not allowed, we
+- * need to see just what our remote address IS.
+- */
+- if (getpeername(fd, pr_netaddr_get_sockaddr(&na), &nalen) < 0) {
+- /* If getpeername(2) fails, should we still allow this connection?
+- * Caution (and the AllowForeignAddress setting say "no".
++ if (allow_foreign_addr_config != NULL) {
++ int allowed;
++
++ allowed = *((int *) allow_foreign_addr_config->argv[0]);
++ if (allowed != TRUE) {
++ /* If foreign addresses (i.e. IP addresses that do not match the
++ * control connection's remote IP address) are not allowed, we
++ * need to see just what our remote address IS.
+ */
+- pr_log_pri(PR_LOG_DEBUG, "rejecting passive connection; "
+- "failed to get address of remote peer: %s", strerror(errno));
+- (void) close(fd);
+- continue;
+- }
+
+- if (pr_netaddr_cmp(&na, c->remote_addr) != 0) {
+- pr_log_pri(PR_LOG_NOTICE, "SECURITY VIOLATION: Passive connection "
+- "from foreign IP address %s rejected (does not match client "
+- "IP address %s).", pr_netaddr_get_ipstr(&na),
+- pr_netaddr_get_ipstr(c->remote_addr));
+- (void) close(fd);
+- continue;
++ if (getpeername(fd, pr_netaddr_get_sockaddr(&na), &nalen) < 0) {
++ /* If getpeername(2) fails, should we still allow this connection?
++ * Caution (and the AllowForeignAddress setting) say "no".
++ */
++ pr_log_pri(PR_LOG_DEBUG, "rejecting passive connection; "
++ "failed to get address of remote peer: %s", strerror(errno));
++ (void) close(fd);
++ continue;
++ }
++
++ if (allowed == FALSE) {
++ if (pr_netaddr_cmp(&na, c->remote_addr) != 0) {
++ pr_log_pri(PR_LOG_NOTICE, "SECURITY VIOLATION: Passive connection "
++ "from foreign IP address %s rejected (does not match client "
++ "IP address %s).", pr_netaddr_get_ipstr(&na),
++ pr_netaddr_get_ipstr(c->remote_addr));
++
++ (void) close(fd);
++ d->mode = CM_ERROR;
++ d->xerrno = EACCES;
++
++ return NULL;
++ }
++
++ } else {
++ char *class_name;
++ const pr_class_t *cls;
++
++ class_name = allow_foreign_addr_config->argv[1];
++ cls = pr_class_find(class_name);
++ if (cls != NULL) {
++ if (pr_class_satisfied(p, cls, &na) != TRUE) {
++ pr_log_debug(DEBUG8, "<Class> '%s' not satisfied by foreign "
++ "address '%s'", class_name, pr_netaddr_get_ipstr(&na));
++
++ pr_log_pri(PR_LOG_NOTICE,
++ "SECURITY VIOLATION: Passive connection from foreign IP "
++ "address %s rejected (does not match <Class %s>).",
++ pr_netaddr_get_ipstr(&na), class_name);
++
++ (void) close(fd);
++ d->mode = CM_ERROR;
++ d->xerrno = EACCES;
++ return NULL;
++ }
++
++ } else {
++ pr_log_debug(DEBUG8, "<Class> '%s' not found for filtering "
++ "AllowForeignAddress", class_name);
++ }
++ }
+ }
+ }
+
+diff --git a/tests/t/lib/ProFTPD/Tests/Config/AllowForeignAddress.pm b/tests/t/lib/ProFTPD/Tests/Config/AllowForeignAddress.pm
+index cd64b5578d..0ee36cbb4e 100644
+--- a/tests/t/lib/ProFTPD/Tests/Config/AllowForeignAddress.pm
++++ b/tests/t/lib/ProFTPD/Tests/Config/AllowForeignAddress.pm
+@@ -26,11 +26,16 @@ my $TESTS = {
+ test_class => [qw(forking)],
+ },
+
+- fxp_denied_by_class => {
++ fxp_port_denied_by_class => {
+ order => ++$order,
+ test_class => [qw(forking)],
+ },
+
++ fxp_pasv_denied_by_class_issue1346 => {
++ order => ++$order,
++ test_class => [qw(bug forking)],
++ },
++
+ fxp_allowed => {
+ order => ++$order,
+ test_class => [qw(forking)],
+@@ -41,11 +46,16 @@ my $TESTS = {
+ test_class => [qw(forking)],
+ },
+
+- fxp_allowed_by_class => {
++ fxp_port_allowed_by_class => {
+ order => ++$order,
+ test_class => [qw(forking)],
+ },
+
++ fxp_pasv_allowed_by_class_issue1346 => {
++ order => ++$order,
++ test_class => [qw(bug forking)],
++ },
++
+ fxp_allowed_2gb => {
+ order => ++$order,
+ test_class => [qw(forking)],
+@@ -353,7 +363,7 @@ sub fxp_denied_eprt {
+ test_cleanup($setup->{log_file}, $ex);
+ }
+
+-sub fxp_denied_by_class {
++sub fxp_port_denied_by_class {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'config');
+@@ -519,6 +529,139 @@ EOC
+ test_cleanup($setup->{log_file}, $ex);
+ }
+
++sub fxp_pasv_denied_by_class_issue1346 {
++ my $self = shift;
++ my $tmpdir = $self->{tmpdir};
++ my $setup = test_setup($tmpdir, 'config');
++
++ my $class_name = 'allowed_fxp';
++
++ my $config = {
++ PidFile => $setup->{pid_file},
++ ScoreboardFile => $setup->{scoreboard_file},
++ SystemLog => $setup->{log_file},
++
++ AuthUserFile => $setup->{auth_user_file},
++ AuthGroupFile => $setup->{auth_group_file},
++
++ AllowForeignAddress => $class_name,
++
++ IfModules => {
++ 'mod_delay.c' => {
++ DelayEngine => 'off',
++ },
++ },
++ };
++
++ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++ $config);
++
++ if (open(my $fh, ">> $setup->{config_file}")) {
++ print $fh <<EOC;
++<Class $class_name>
++ From none
++</Class>
++EOC
++ unless (close($fh)) {
++ die("Can't write $setup->{config_file}: $!");
++ }
++
++ } else {
++ die("Can't open $setup->{config_file}: $!");
++ }
++
++ # Open pipes, for use between the parent and child processes. Specifically,
++ # the child will indicate when it's done with 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 {
++ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 3);
++ $client->login($setup->{user}, $setup->{passwd});
++
++ # Attemping a data transfer should fail, due to the AllowForeignAddress
++ # class restriction.
++
++ my $conn = $client->list_raw();
++ if ($conn) {
++ die("LIST succeeded unexpectedly");
++ }
++
++ my $resp_code = $client->response_code();
++ my $resp_msg = $client->response_msg();
++
++ my $expected = 425;
++ $self->assert($expected == $resp_code,
++ "Expected response code $expected, got $resp_code");
++
++ $expected = 'Unable to build data connection:';
++ $self->assert(qr/$expected/, $resp_msg,
++ "Expected response message '$expected', got '$resp_msg'");
++
++ $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);
++
++ eval {
++ if (open(my $fh, "< $setup->{log_file}")) {
++ my $ok = 0;
++
++ while (my $line = <$fh>) {
++ chomp($line);
++
++ if ($ENV{TEST_VERBOSE}) {
++ print STDERR "$line\n";
++ }
++
++ if ($line =~ /SECURITY VIOLATION: Passive connection from foreign IP address \S+ rejected \(does not match <Class \S+>\)/) {
++ $ok = 1;
++ last;
++ }
++ }
++
++ close($fh);
++
++ $self->assert($ok, "Did not see expected log messages");
++
++ } else {
++ die("Can't read $setup->{log_file}: $!");
++ }
++ };
++ if ($@) {
++ $ex = $@;
++ }
++
++ test_cleanup($setup->{log_file}, $ex);
++}
++
+ sub fxp_allowed {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+@@ -781,7 +924,7 @@ sub fxp_allowed_eprt {
+ test_cleanup($setup->{log_file}, $ex);
+ }
+
+-sub fxp_allowed_by_class {
++sub fxp_port_allowed_by_class {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'config');
+@@ -926,6 +1069,110 @@ EOC
+ test_cleanup($setup->{log_file}, $ex);
+ }
+
++sub fxp_pasv_allowed_by_class_issue1346 {
++ my $self = shift;
++ my $tmpdir = $self->{tmpdir};
++ my $setup = test_setup($tmpdir, 'config');
++
++ my $class_name = 'allowed_fxp';
++
++ my $config = {
++ PidFile => $setup->{pid_file},
++ ScoreboardFile => $setup->{scoreboard_file},
++ SystemLog => $setup->{log_file},
++ TraceLog => $setup->{log_file},
++ Trace => 'class:20 inet:20',
++
++ AuthUserFile => $setup->{auth_user_file},
++ AuthGroupFile => $setup->{auth_group_file},
++
++ AllowForeignAddress => $class_name,
++
++ IfModules => {
++ 'mod_delay.c' => {
++ DelayEngine => 'off',
++ },
++ },
++ };
++
++ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
++ $config);
++
++ if (open(my $fh, ">> $setup->{config_file}")) {
++ print $fh <<EOC;
++<Class $class_name>
++ From 127.0.0.0/8
++</Class>
++EOC
++ unless (close($fh)) {
++ die("Can't write $setup->{config_file}: $!");
++ }
++
++ } else {
++ die("Can't open $setup->{config_file}: $!");
++ }
++
++ # Open pipes, for use between the parent and child processes. Specifically,
++ # the child will indicate when it's done with 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 {
++ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 3);
++ $client->login($setup->{user}, $setup->{passwd});
++
++ # Attemping a data transfer should succeed, due to the AllowForeignAddress
++ # class restriction.
++ my $conn = $client->list_raw();
++ unless ($conn) {
++ die("Failed to LIST: " . $client->response_code() . " " .
++ $client->response_msg());
++ }
++
++ my $buf;
++ $conn->read($buf, 8192, 30);
++ eval { $conn->close() };
++
++ my ($resp_code, $resp_msg);
++ $resp_code = $client->response_code();
++ $resp_msg = $client->response_msg();
++
++ $self->assert_transfer_ok($resp_code, $resp_msg);
++ $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);
++}
++
+ sub fxp_allowed_2gb {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
=====================================
debian/patches/upstream_long_AuthGroupFile_lines
=====================================
@@ -0,0 +1,187 @@
+From 9aff151f6810b8f0e64cc9f7e15dd21ebecd4cc3 Mon Sep 17 00:00:00 2001
+From: TJ Saunders <tj at castaglia.org>
+Date: Fri, 28 Jan 2022 08:21:44 -0800
+Subject: [PATCH] Backporting buffering fixes when parsing long `AuthGroupFile`
+ lines.
+
+---
+ modules/mod_auth_file.c | 87 ++++++++++++++++++++++++++++++++---------
+ 1 file changed, 68 insertions(+), 19 deletions(-)
+
+diff --git a/modules/mod_auth_file.c b/modules/mod_auth_file.c
+index b1ccaa771..3530905f1 100644
+--- a/modules/mod_auth_file.c
++++ b/modules/mod_auth_file.c
+@@ -1,7 +1,7 @@
+ /*
+ * ProFTPD: mod_auth_file - file-based authentication module that supports
+ * restrictions on the file contents
+- * Copyright (c) 2002-2021 The ProFTPD Project team
++ * Copyright (c) 2002-2022 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
+@@ -325,24 +325,35 @@ static struct passwd *af_getpasswd(const char *buf, unsigned int lineno) {
+ #define NGRPFIELDS 4
+
+ static char *grpbuf = NULL;
++static size_t grpbufsz = 0;
+ static struct group grent;
+ static char *grpfields[NGRPFIELDS];
+ static char *members[MAXMEMBERS+1];
+
+-static char *af_getgrentline(char **buf, int *buflen, pr_fh_t *fh,
++static char *af_getgrentline(char **buf, size_t *bufsz, pr_fh_t *fh,
+ unsigned int *lineno) {
+- char *cp = *buf;
+- int original_buflen;
++ char *ptr, *res;
++ size_t original_bufsz, buflen;
+
+- original_buflen = *buflen;
++ original_bufsz = *bufsz;
++ buflen = *bufsz;
+
+- while (pr_fsio_gets(cp, (*buflen) - (cp - *buf), fh) != NULL) {
+- pr_signals_handle();
++ /* Try to keep our unfilled buffer zeroed out, so that strlen(3) et al
++ * work as expected.
++ */
++ memset(*buf, '\0', *bufsz);
+
+- (*lineno)++;
++ ptr = *buf;
++ res = pr_fsio_gets(ptr, buflen, fh);
++ while (res != NULL) {
++ pr_signals_handle();
+
+ /* Is this a full line? */
+- if (strchr(cp, '\n')) {
++ if (strchr(*buf, '\n') != NULL) {
++ pr_trace_msg(trace_channel, 25,
++ "found LF, returning line: '%s' (%lu bytes)", *buf,
++ (unsigned long) strlen(*buf));
++ (*lineno)++;
+ return *buf;
+ }
+
+@@ -351,26 +362,37 @@ static char *af_getgrentline(char **buf, int *buflen, pr_fh_t *fh,
+ * allocated buffer by the original buffer length each time. So we
+ * do the same (Issue #1321).
+ */
+- *buflen += original_buflen;
+-
+ {
++ size_t new_bufsz;
+ char *new_buf;
+
+- new_buf = realloc(*buf, *buflen);
++ pr_trace_msg(trace_channel, 25, "getgrentline() buffer (%lu bytes): "
++ "'%.*s'", (unsigned long) *bufsz, (int) *bufsz, *buf);
++
++ pr_trace_msg(trace_channel, 19,
++ "no LF found in group line, increasing buffer (%lu bytes) by %lu bytes",
++ (unsigned long) *bufsz, (unsigned long) original_bufsz);
++ new_bufsz = *bufsz + original_bufsz;
++
++ new_buf = realloc(*buf, new_bufsz);
+ if (new_buf == NULL) {
+ break;
+ }
+
++ ptr = new_buf + *bufsz;
+ *buf = new_buf;
++ *bufsz = new_bufsz;
++ buflen = original_bufsz;
++
++ memset(ptr, '\0', buflen);
+ }
+
+- cp = *buf + (cp - *buf);
+- cp = strchr(cp, '\0');
++ res = pr_fsio_gets(ptr, buflen, fh);
+ }
+
+ free(*buf);
+ *buf = NULL;
+- *buflen = 0;
++ *bufsz = 0;
+
+ return NULL;
+ }
+@@ -401,22 +423,29 @@ static struct group *af_getgrp(const char *buf, unsigned int lineno) {
+
+ i = strlen(buf) + 1;
+
+- if (!grpbuf) {
++ if (grpbuf == NULL) {
++ grpbufsz = i;
+ grpbuf = malloc(i);
+
+- } else {
++ } else if (grpbufsz < (size_t) i) {
+ char *new_buf;
+
++ pr_trace_msg(trace_channel, 19,
++ "parsing group line '%s' (%lu bytes), allocating %lu bytes via "
++ "realloc(3)", buf, (unsigned long) i, (unsigned long) i);
++
+ new_buf = realloc(grpbuf, i);
+ if (new_buf == NULL) {
+ return NULL;
+ }
+
+ grpbuf = new_buf;
++ grpbufsz = i;
+ }
+
+- if (!grpbuf)
++ if (grpbuf == NULL) {
+ return NULL;
++ }
+
+ sstrncpy(grpbuf, buf, i);
+
+@@ -524,7 +553,16 @@ static struct group *af_getgrent(pool *p) {
+
+ while (TRUE) {
+ char *cp = NULL, *buf = NULL;
+- int buflen = PR_TUNABLE_BUFFER_SIZE;
++ size_t buflen;
++
++ buflen = PR_TUNABLE_BUFFER_SIZE;
++
++ if (af_group_file->af_file_fh->fh_iosz > 0) {
++ /* This aligns our group(5) buffer with the preferred filesystem read
++ * block size.
++ */
++ buflen = af_group_file->af_file_fh->fh_iosz;
++ }
+
+ pr_signals_handle();
+
+@@ -533,6 +571,11 @@ static struct group *af_getgrent(pool *p) {
+ pr_log_pri(PR_LOG_ALERT, "Out of memory!");
+ _exit(1);
+ }
++
++ pr_trace_msg(trace_channel, 19,
++ "getgrent(3): allocated buffer %p (%lu bytes)", buf,
++ (unsigned long) buflen);
++
+ grp = NULL;
+
+ while (af_getgrentline(&buf, &buflen, af_group_file->af_file_fh,
+@@ -636,6 +679,12 @@ static int af_setgrent(pool *p) {
+ pbuf->remaining = pbuf->buflen;
+ }
+
++ if (grpbuf != NULL) {
++ free(grpbuf);
++ grpbuf = NULL;
++ }
++ grpbufsz = 0;
++
+ return 0;
+ }
+
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/fbc2d5716815378eca0bf62ec472643fd2c5f6e4
--
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/fbc2d5716815378eca0bf62ec472643fd2c5f6e4
You're receiving this email because of your account on salsa.debian.org.
More information about the Pkg-proftpd-maintainers
mailing list