On 22.07.19 23:48, Moritz Mühlenhoff wrote: > On Fri, Jul 19, 2019 at 08:35:19PM +0200, Hilmar Preuße wrote:
Hi, >> The patch from upstream applies nicely to our master branch (and would >> apply to the buster package too). I could upload the fix to Debian sid >> right now. Will you care about stable and oldstable? > > Can you please prepare updates for buster-security and stretch-security? > Attached are the debdiff files for stretch and buster. As expected the upstream patch applies nicely to the buster package. For the stretch package I had to adapt the patch, then it applied w/ a few tens lines offset. The code compiles fine, but I did not perform any functional test. How to proceed? Hilmar -- #206401 http://counter.li.org
diff -Nru proftpd-dfsg-1.3.5b/debian/changelog proftpd-dfsg-1.3.5b/debian/changelog --- proftpd-dfsg-1.3.5b/debian/changelog 2017-04-05 15:57:53.000000000 +0200 +++ proftpd-dfsg-1.3.5b/debian/changelog 2019-07-23 19:57:05.000000000 +0200 @@ -1,3 +1,9 @@ +proftpd-dfsg (1.3.5b-4+deb9u1) stretch-security; urgency=medium + + * Add adapted patch from upstream to address Bug#932453: CVE-2019-12815. + + -- Hilmar Preuße <hill...@web.de> Tue, 23 Jul 2019 19:57:05 +0200 + proftpd-dfsg (1.3.5b-4) unstable; urgency=medium * Added patch CVE-2017-7418 to add recursive handling of DefalutRoot path. diff -Nru proftpd-dfsg-1.3.5b/debian/patches/CVE-2019-12815.diff proftpd-dfsg-1.3.5b/debian/patches/CVE-2019-12815.diff --- proftpd-dfsg-1.3.5b/debian/patches/CVE-2019-12815.diff 1970-01-01 01:00:00.000000000 +0100 +++ proftpd-dfsg-1.3.5b/debian/patches/CVE-2019-12815.diff 2019-07-23 19:55:46.000000000 +0200 @@ -0,0 +1,367 @@ +From 71cd49ea82313f78d52a52d0c628a3770dc96608 Mon Sep 17 00:00:00 2001 +From: TJ Saunders <t...@castaglia.org> +Date: Wed, 17 Jul 2019 09:25:31 -0700 +Subject: [PATCH] Bug #4372: Ensure that mod_copy checks for <Limits> for its + SITE CPFR/CPTO commands. + +--- + contrib/mod_copy.c | 36 ++- + tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm | 253 +++++++++++++++++- + 2 files changed, 285 insertions(+), 4 deletions(-) + +Index: proftpd-dfsg-1.3.5b/contrib/mod_copy.c +=================================================================== +--- proftpd-dfsg-1.3.5b.orig/contrib/mod_copy.c ++++ proftpd-dfsg-1.3.5b/contrib/mod_copy.c +@@ -565,7 +565,7 @@ MODRET copy_copy(cmd_rec *cmd) { + MODRET copy_cpfr(cmd_rec *cmd) { + register unsigned int i; + int res; +- char *path = ""; ++ char *cmd_name, *path = ""; + unsigned char *authenticated = NULL; + + if (copy_engine == FALSE) { +@@ -596,6 +596,21 @@ MODRET copy_cpfr(cmd_rec *cmd) { + pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL); + } + ++ cmd_name = cmd->argv[0]; ++ pr_cmd_set_name(cmd, "SITE_CPFR"); ++ if (!dir_check(cmd->tmp_pool, cmd, G_READ, path, NULL)) { ++ int xerrno = EPERM; ++ ++ pr_cmd_set_name(cmd, cmd_name); ++ pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[3], ++ strerror(xerrno)); ++ ++ pr_cmd_set_errno(cmd, xerrno); ++ errno = xerrno; ++ return PR_ERROR(cmd); ++ } ++ pr_cmd_set_name(cmd, cmd_name); ++ + res = pr_filter_allow_path(CURRENT_CONF, path); + switch (res) { + case 0: +@@ -635,6 +650,7 @@ MODRET copy_cpfr(cmd_rec *cmd) { + MODRET copy_cpto(cmd_rec *cmd) { + register unsigned int i; + char *from, *to = ""; ++ char *cmd_name; + unsigned char *authenticated = NULL; + + if (copy_engine == FALSE) { +@@ -673,6 +689,20 @@ MODRET copy_cpto(cmd_rec *cmd) { + + to = dir_canonical_vpath(cmd->tmp_pool, to); + ++ cmd_name = cmd->argv[0]; ++ pr_cmd_set_name(cmd, "SITE_CPTO"); ++ if (!dir_check(cmd->tmp_pool, cmd, G_WRITE, to, NULL)) { ++ int xerrno = EPERM; ++ ++ pr_cmd_set_name(cmd, cmd_name); ++ pr_response_add_err(R_550, "%s: %s", to, strerror(xerrno)); ++ ++ pr_cmd_set_errno(cmd, xerrno); ++ errno = xerrno; ++ return PR_ERROR(cmd); ++ } ++ pr_cmd_set_name(cmd, cmd_name); ++ + if (copy_paths(cmd->tmp_pool, from, to) < 0) { + int xerrno = errno; + +@@ -751,7 +781,7 @@ static conftable copy_conftab[] = { + + static cmdtable copy_cmdtab[] = { + { CMD, C_SITE, G_WRITE, copy_copy, FALSE, FALSE, CL_MISC }, +- { CMD, C_SITE, G_DIRS, copy_cpfr, FALSE, FALSE, CL_MISC }, ++ { CMD, C_SITE, G_READ, copy_cpfr, FALSE, FALSE, CL_MISC }, + { CMD, C_SITE, G_WRITE, copy_cpto, FALSE, FALSE, CL_MISC }, + { POST_CMD, C_PASS, G_NONE, copy_post_pass, FALSE, FALSE }, + { LOG_CMD, C_SITE, G_NONE, copy_log_site, FALSE, FALSE }, +Index: proftpd-dfsg-1.3.5b/tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm +=================================================================== +--- proftpd-dfsg-1.3.5b.orig/tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm ++++ proftpd-dfsg-1.3.5b/tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm +@@ -111,6 +111,15 @@ my $TESTS = { + test_class => [qw(forking rootprivs)], + }, + ++ copy_cpfr_config_limit_read_bug4372 => { ++ order => ++$order, ++ test_class => [qw(bug forking)], ++ }, ++ ++ copy_cpto_config_limit_write_bug4372 => { ++ order => ++$order, ++ test_class => [qw(bug forking)], ++ }, + }; + + sub new { +@@ -3238,6 +3247,12 @@ sub copy_config_limit_bug3399 { + + my ($port, $config_user, $config_group) = config_write($config_file, $config); + ++ my $config_subdir = $sub_dir; ++ if ($^O eq 'darwin') { ++ # MacOSX hack ++ $config_subdir = '/private' . $sub_dir; ++ } ++ + if (open(my $fh, ">> $config_file")) { + print $fh <<EOC; + <Directory /> +@@ -3246,7 +3261,7 @@ sub copy_config_limit_bug3399 { + </Limit> + </Directory> + +-<Directory $sub_dir> ++<Directory $config_subdir> + <Limit WRITE> + AllowAll + </Limit> +@@ -3326,4 +3341,240 @@ EOC + unlink($log_file); + } + ++sub copy_cpfr_config_limit_read_bug4372 { ++ my $self = shift; ++ my $tmpdir = $self->{tmpdir}; ++ my $setup = test_setup($tmpdir, 'copy'); ++ ++ my $src_file = File::Spec->rel2abs("$tmpdir/foo.dat"); ++ if (open(my $fh, "> $src_file")) { ++ unless (close($fh)) { ++ die("Can't write $src_file: $!"); ++ } ++ ++ } else { ++ die("Can't open $src_file: $!"); ++ } ++ ++ my $config = { ++ PidFile => $setup->{pid_file}, ++ ScoreboardFile => $setup->{scoreboard_file}, ++ SystemLog => $setup->{log_file}, ++ TraceLog => $setup->{log_file}, ++ Trace => 'copy:20 timer:20', ++ ++ AuthUserFile => $setup->{auth_user_file}, ++ AuthGroupFile => $setup->{auth_group_file}, ++ TimeoutIdle => 3, ++ ++ 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; ++<Directory /> ++ <Limit READ> ++ DenyAll ++ </Limit> ++</Directory> ++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, 1); ++ $client->login($setup->{user}, $setup->{passwd}); ++ ++ eval { $client->site('CPFR', 'foo.dat') }; ++ unless ($@) { ++ die("SITE CPFR succeeded unexpectedly"); ++ } ++ ++ my $resp_code = $client->response_code(); ++ my $resp_msg = $client->response_msg(); ++ ++ my $expected = 550; ++ $self->assert($expected == $resp_code, ++ test_msg("Expected response code $expected, got $resp_code")); ++ ++ $expected = 'Operation not permitted'; ++ $self->assert(qr/$expected/, $resp_msg, ++ test_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, 30) }; ++ 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 copy_cpto_config_limit_write_bug4372 { ++ my $self = shift; ++ my $tmpdir = $self->{tmpdir}; ++ my $setup = test_setup($tmpdir, 'copy'); ++ ++ my $src_file = File::Spec->rel2abs("$tmpdir/foo.dat"); ++ if (open(my $fh, "> $src_file")) { ++ unless (close($fh)) { ++ die("Can't write $src_file: $!"); ++ } ++ ++ } else { ++ die("Can't open $src_file: $!"); ++ } ++ ++ my $dst_file = File::Spec->rel2abs("$tmpdir/bar.dat"); ++ ++ my $config = { ++ PidFile => $setup->{pid_file}, ++ ScoreboardFile => $setup->{scoreboard_file}, ++ SystemLog => $setup->{log_file}, ++ TraceLog => $setup->{log_file}, ++ Trace => 'copy:20 timer:20', ++ ++ AuthUserFile => $setup->{auth_user_file}, ++ AuthGroupFile => $setup->{auth_group_file}, ++ TimeoutIdle => 3, ++ ++ 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; ++<Directory /> ++ <Limit WRITE> ++ DenyAll ++ </Limit> ++</Directory> ++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, 1); ++ $client->login($setup->{user}, $setup->{passwd}); ++ ++ my ($resp_code, $resp_msg) = $client->site('CPFR', 'foo.dat'); ++ ++ my $expected = 350; ++ $self->assert($expected == $resp_code, ++ test_msg("Expected response code $expected, got $resp_code")); ++ ++ $expected = 'File or directory exists, ready for destination name'; ++ $self->assert($expected eq $resp_msg, ++ test_msg("Expected response message '$expected', got '$resp_msg'")); ++ ++ eval { $client->site('CPTO', 'bar.dat') }; ++ unless ($@) { ++ die('SITE CPTO succeeded unexpectedly'); ++ } ++ ++ my $resp_code = $client->response_code(); ++ my $resp_msg = $client->response_msg(); ++ ++ my $expected = 550; ++ $self->assert($expected == $resp_code, ++ test_msg("Expected response code $expected, got $resp_code")); ++ ++ $expected = 'Operation not permitted'; ++ $self->assert(qr/$expected/, $resp_msg, ++ test_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, 30) }; ++ if ($@) { ++ warn($@); ++ exit 1; ++ } ++ ++ exit 0; ++ } ++ ++ # Stop server ++ server_stop($setup->{pid_file}); ++ $self->assert_child_ok($pid); ++ ++ test_cleanup($setup->{log_file}, $ex); ++} ++ + 1; diff -Nru proftpd-dfsg-1.3.5b/debian/patches/series proftpd-dfsg-1.3.5b/debian/patches/series --- proftpd-dfsg-1.3.5b/debian/patches/series 2017-04-05 15:57:53.000000000 +0200 +++ proftpd-dfsg-1.3.5b/debian/patches/series 2019-07-23 19:57:05.000000000 +0200 @@ -15,3 +15,4 @@ reproducible_build not_read_whole_passwd_db CVE-2017-7418 +CVE-2019-12815.diff \ No newline at end of file
diff -Nru proftpd-dfsg-1.3.6/debian/changelog proftpd-dfsg-1.3.6/debian/changelog --- proftpd-dfsg-1.3.6/debian/changelog 2019-01-14 12:07:16.000000000 +0100 +++ proftpd-dfsg-1.3.6/debian/changelog 2019-07-23 20:20:14.000000000 +0200 @@ -1,3 +1,9 @@ +proftpd-dfsg (1.3.6-4+deb10u1) buster-security; urgency=medium + + * Add patch from upstream to address Bug#932453: CVE-2019-12815. + + -- Hilmar Preusse <hill...@web.de> Tue, 23 Jul 2019 20:20:14 +0200 + proftpd-dfsg (1.3.6-4) unstable; urgency=medium [ Hilmar Preuße ] diff -Nru proftpd-dfsg-1.3.6/debian/patches/CVE-2019-12815.patch proftpd-dfsg-1.3.6/debian/patches/CVE-2019-12815.patch --- proftpd-dfsg-1.3.6/debian/patches/CVE-2019-12815.patch 1970-01-01 01:00:00.000000000 +0100 +++ proftpd-dfsg-1.3.6/debian/patches/CVE-2019-12815.patch 2019-07-23 20:11:08.000000000 +0200 @@ -0,0 +1,376 @@ +From 71cd49ea82313f78d52a52d0c628a3770dc96608 Mon Sep 17 00:00:00 2001 +From: TJ Saunders <t...@castaglia.org> +Date: Wed, 17 Jul 2019 09:25:31 -0700 +Subject: [PATCH] Bug #4372: Ensure that mod_copy checks for <Limits> for its + SITE CPFR/CPTO commands. + +--- + contrib/mod_copy.c | 36 ++- + tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm | 253 +++++++++++++++++- + 2 files changed, 285 insertions(+), 4 deletions(-) + +diff --git a/contrib/mod_copy.c b/contrib/mod_copy.c +index 26b72a91d..c8672c40d 100644 +--- a/contrib/mod_copy.c ++++ b/contrib/mod_copy.c +@@ -1,7 +1,7 @@ + /* + * ProFTPD: mod_copy -- a module supporting copying of files on the server + * without transferring the data to the client and back +- * Copyright (c) 2009-2016 TJ Saunders ++ * Copyright (c) 2009-2019 TJ Saunders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -657,7 +657,7 @@ MODRET copy_copy(cmd_rec *cmd) { + MODRET copy_cpfr(cmd_rec *cmd) { + register unsigned int i; + int res; +- char *path = ""; ++ char *cmd_name, *path = ""; + unsigned char *authenticated = NULL; + + if (copy_engine == FALSE) { +@@ -705,6 +705,21 @@ MODRET copy_cpfr(cmd_rec *cmd) { + path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", decoded_path, NULL); + } + ++ cmd_name = cmd->argv[0]; ++ pr_cmd_set_name(cmd, "SITE_CPFR"); ++ if (!dir_check(cmd->tmp_pool, cmd, G_READ, path, NULL)) { ++ int xerrno = EPERM; ++ ++ pr_cmd_set_name(cmd, cmd_name); ++ pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[3], ++ strerror(xerrno)); ++ ++ pr_cmd_set_errno(cmd, xerrno); ++ errno = xerrno; ++ return PR_ERROR(cmd); ++ } ++ pr_cmd_set_name(cmd, cmd_name); ++ + res = pr_filter_allow_path(CURRENT_CONF, path); + switch (res) { + case 0: +@@ -758,6 +773,7 @@ MODRET copy_cpfr(cmd_rec *cmd) { + MODRET copy_cpto(cmd_rec *cmd) { + register unsigned int i; + const char *from, *to = ""; ++ char *cmd_name; + unsigned char *authenticated = NULL; + + if (copy_engine == FALSE) { +@@ -816,6 +832,20 @@ MODRET copy_cpto(cmd_rec *cmd) { + + to = dir_canonical_vpath(cmd->tmp_pool, to); + ++ cmd_name = cmd->argv[0]; ++ pr_cmd_set_name(cmd, "SITE_CPTO"); ++ if (!dir_check(cmd->tmp_pool, cmd, G_WRITE, to, NULL)) { ++ int xerrno = EPERM; ++ ++ pr_cmd_set_name(cmd, cmd_name); ++ pr_response_add_err(R_550, "%s: %s", to, strerror(xerrno)); ++ ++ pr_cmd_set_errno(cmd, xerrno); ++ errno = xerrno; ++ return PR_ERROR(cmd); ++ } ++ pr_cmd_set_name(cmd, cmd_name); ++ + if (copy_paths(cmd->tmp_pool, from, to) < 0) { + int xerrno = errno; + const char *err_code = R_550; +@@ -940,7 +970,7 @@ static conftable copy_conftab[] = { + + static cmdtable copy_cmdtab[] = { + { CMD, C_SITE, G_WRITE, copy_copy, FALSE, FALSE, CL_MISC }, +- { CMD, C_SITE, G_DIRS, copy_cpfr, FALSE, FALSE, CL_MISC }, ++ { CMD, C_SITE, G_READ, copy_cpfr, FALSE, FALSE, CL_MISC }, + { CMD, C_SITE, G_WRITE, copy_cpto, FALSE, FALSE, CL_MISC }, + { POST_CMD, C_PASS, G_NONE, copy_post_pass, FALSE, FALSE }, + { LOG_CMD, C_SITE, G_NONE, copy_log_site, FALSE, FALSE }, +diff --git a/tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm b/tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm +index 778bff839..2018e71bc 100644 +--- a/tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm ++++ b/tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm +@@ -121,6 +121,15 @@ my $TESTS = { + test_class => [qw(bug forking)], + }, + ++ copy_cpfr_config_limit_read_bug4372 => { ++ order => ++$order, ++ test_class => [qw(bug forking)], ++ }, ++ ++ copy_cpto_config_limit_write_bug4372 => { ++ order => ++$order, ++ test_class => [qw(bug forking)], ++ }, + }; + + sub new { +@@ -3248,6 +3257,12 @@ sub copy_config_limit_bug3399 { + + my ($port, $config_user, $config_group) = config_write($config_file, $config); + ++ my $config_subdir = $sub_dir; ++ if ($^O eq 'darwin') { ++ # MacOSX hack ++ $config_subdir = '/private' . $sub_dir; ++ } ++ + if (open(my $fh, ">> $config_file")) { + print $fh <<EOC; + <Directory /> +@@ -3256,7 +3271,7 @@ sub copy_config_limit_bug3399 { + </Limit> + </Directory> + +-<Directory $sub_dir> ++<Directory $config_subdir> + <Limit WRITE> + AllowAll + </Limit> +@@ -3652,4 +3667,240 @@ sub copy_cpto_timeout_bug4263 { + test_cleanup($setup->{log_file}, $ex); + } + ++sub copy_cpfr_config_limit_read_bug4372 { ++ my $self = shift; ++ my $tmpdir = $self->{tmpdir}; ++ my $setup = test_setup($tmpdir, 'copy'); ++ ++ my $src_file = File::Spec->rel2abs("$tmpdir/foo.dat"); ++ if (open(my $fh, "> $src_file")) { ++ unless (close($fh)) { ++ die("Can't write $src_file: $!"); ++ } ++ ++ } else { ++ die("Can't open $src_file: $!"); ++ } ++ ++ my $config = { ++ PidFile => $setup->{pid_file}, ++ ScoreboardFile => $setup->{scoreboard_file}, ++ SystemLog => $setup->{log_file}, ++ TraceLog => $setup->{log_file}, ++ Trace => 'copy:20 timer:20', ++ ++ AuthUserFile => $setup->{auth_user_file}, ++ AuthGroupFile => $setup->{auth_group_file}, ++ TimeoutIdle => 3, ++ ++ 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; ++<Directory /> ++ <Limit READ> ++ DenyAll ++ </Limit> ++</Directory> ++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, 1); ++ $client->login($setup->{user}, $setup->{passwd}); ++ ++ eval { $client->site('CPFR', 'foo.dat') }; ++ unless ($@) { ++ die("SITE CPFR succeeded unexpectedly"); ++ } ++ ++ my $resp_code = $client->response_code(); ++ my $resp_msg = $client->response_msg(); ++ ++ my $expected = 550; ++ $self->assert($expected == $resp_code, ++ test_msg("Expected response code $expected, got $resp_code")); ++ ++ $expected = 'Operation not permitted'; ++ $self->assert(qr/$expected/, $resp_msg, ++ test_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, 30) }; ++ 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 copy_cpto_config_limit_write_bug4372 { ++ my $self = shift; ++ my $tmpdir = $self->{tmpdir}; ++ my $setup = test_setup($tmpdir, 'copy'); ++ ++ my $src_file = File::Spec->rel2abs("$tmpdir/foo.dat"); ++ if (open(my $fh, "> $src_file")) { ++ unless (close($fh)) { ++ die("Can't write $src_file: $!"); ++ } ++ ++ } else { ++ die("Can't open $src_file: $!"); ++ } ++ ++ my $dst_file = File::Spec->rel2abs("$tmpdir/bar.dat"); ++ ++ my $config = { ++ PidFile => $setup->{pid_file}, ++ ScoreboardFile => $setup->{scoreboard_file}, ++ SystemLog => $setup->{log_file}, ++ TraceLog => $setup->{log_file}, ++ Trace => 'copy:20 timer:20', ++ ++ AuthUserFile => $setup->{auth_user_file}, ++ AuthGroupFile => $setup->{auth_group_file}, ++ TimeoutIdle => 3, ++ ++ 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; ++<Directory /> ++ <Limit WRITE> ++ DenyAll ++ </Limit> ++</Directory> ++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, 1); ++ $client->login($setup->{user}, $setup->{passwd}); ++ ++ my ($resp_code, $resp_msg) = $client->site('CPFR', 'foo.dat'); ++ ++ my $expected = 350; ++ $self->assert($expected == $resp_code, ++ test_msg("Expected response code $expected, got $resp_code")); ++ ++ $expected = 'File or directory exists, ready for destination name'; ++ $self->assert($expected eq $resp_msg, ++ test_msg("Expected response message '$expected', got '$resp_msg'")); ++ ++ eval { $client->site('CPTO', 'bar.dat') }; ++ unless ($@) { ++ die('SITE CPTO succeeded unexpectedly'); ++ } ++ ++ my $resp_code = $client->response_code(); ++ my $resp_msg = $client->response_msg(); ++ ++ my $expected = 550; ++ $self->assert($expected == $resp_code, ++ test_msg("Expected response code $expected, got $resp_code")); ++ ++ $expected = 'Operation not permitted'; ++ $self->assert(qr/$expected/, $resp_msg, ++ test_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, 30) }; ++ if ($@) { ++ warn($@); ++ exit 1; ++ } ++ ++ exit 0; ++ } ++ ++ # Stop server ++ server_stop($setup->{pid_file}); ++ $self->assert_child_ok($pid); ++ ++ test_cleanup($setup->{log_file}, $ex); ++} ++ + 1; diff -Nru proftpd-dfsg-1.3.6/debian/patches/series proftpd-dfsg-1.3.6/debian/patches/series --- proftpd-dfsg-1.3.6/debian/patches/series 2019-01-14 12:07:16.000000000 +0100 +++ proftpd-dfsg-1.3.6/debian/patches/series 2019-07-23 20:18:01.000000000 +0200 @@ -17,3 +17,4 @@ upstream_4356 wrong-path-for-interpreter_perl.diff github_pr_594 +CVE-2019-12815.patch
signature.asc
Description: OpenPGP digital signature