Skip to content

Commit

Permalink
Issue #158: Ensure that we have regression tests asserting that `mod_…
Browse files Browse the repository at this point in the history
…proxy` properly implements the `AllowForeignAddress` checks, and rejects `PORT`, `EPRT` commands with foreign addresses.
  • Loading branch information
Castaglia committed Jun 25, 2022
1 parent e658fc9 commit e497bca
Showing 1 changed file with 287 additions and 0 deletions.
287 changes: 287 additions & 0 deletions t/lib/ProFTPD/Tests/Modules/mod_proxy.pm
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ my $TESTS = {
test_class => [qw(forking reverse)],
},

proxy_reverse_port_allowforeignaddress_false => {
order => ++$order,
test_class => [qw(forking reverse)],
},

proxy_reverse_epsv => {
order => ++$order,
test_class => [qw(forking reverse)],
Expand All @@ -133,6 +138,11 @@ my $TESTS = {
test_class => [qw(feature_ipv6 forking reverse)],
},

proxy_reverse_eprt_allowforeignaddress_false => {
order => ++$order,
test_class => [qw(forking reverse)],
},

proxy_reverse_retr_pasv_ascii => {
order => ++$order,
test_class => [qw(forking reverse)],
Expand Down Expand Up @@ -3859,6 +3869,148 @@ EOC
unlink($log_file);
}

sub proxy_reverse_port_allowforeignaddress_false {
my $self = shift;
my $tmpdir = $self->{tmpdir};
my $setup = test_setup($tmpdir, 'proxy');

my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
$vhost_port += 12;

my $proxy_config = get_reverse_proxy_config($tmpdir, $setup->{log_file},
$vhost_port);

my $timeout_idle = 10;

my $config = {
PidFile => $setup->{pid_file},
ScoreboardFile => $setup->{scoreboard_file},
SystemLog => $setup->{log_file},
TraceLog => $setup->{log_file},
Trace => 'DEFAULT:10 event:0 lock:0 scoreboard:0 signal:0 proxy:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20',

AuthUserFile => $setup->{auth_user_file},
AuthGroupFile => $setup->{auth_group_file},
AuthOrder => 'mod_auth_file.c',

SocketBindTight => 'on',
TimeoutIdle => $timeout_idle,

IfModules => {
'mod_proxy.c' => $proxy_config,

'mod_delay.c' => {
DelayEngine => 'off',
},
},

Limit => {
LOGIN => {
DenyUser => $setup->{user},
},
},

};

my ($port, $config_user, $config_group) = config_write($setup->{config_file},
$config);

if (open(my $fh, ">> $setup->{config_file}")) {
print $fh <<EOC;
<VirtualHost 127.0.0.1>
Port $vhost_port
ServerName "Real Server"

AuthUserFile $setup->{auth_user_file}
AuthGroupFile $setup->{auth_group_file}
AuthOrder mod_auth_file.c

AllowOverride off
TimeoutIdle $timeout_idle

TransferLog none
WtmpLog off
</VirtualHost>
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 {
sleep(1);
my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 1, 1);
$client->login($setup->{user}, $setup->{passwd});

# IP address, to trigger the AllowForeignAddress restriction.
my $port_addr = '1,2,3,4,192,6';
eval { $client->port($port_addr) };
unless ($@) {
die("PORT $port_addr succeeded unexpectedly");
}

my $resp_code = $client->response_code();
my $resp_msg = $client->response_msg();

my $expected = 500;
$self->assert($expected == $resp_code,
test_msg("Expected response code $expected, got $resp_code"));

$expected = 'Illegal PORT command';
$self->assert($expected eq $resp_msg,
test_msg("Expected response message '$expected', got '$resp_msg'"));

($resp_code, $resp_msg) = $client->quit();

$expected = 221;
$self->assert($expected == $resp_code,
test_msg("Expected response code $expected, got $resp_code"));

$expected = 'Goodbye.';
$self->assert($expected eq $resp_msg,
test_msg("Expected response message '$expected', got '$resp_msg'"));
};
if ($@) {
$ex = $@;
}

$wfh->print("done\n");
$wfh->flush();

} else {
eval { server_wait($setup->{config_file}, $rfh, $timeout_idle + 2) };
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 proxy_reverse_epsv {
my $self = shift;
my $tmpdir = $self->{tmpdir};
Expand Down Expand Up @@ -4353,6 +4505,141 @@ EOC
unlink($log_file);
}

sub proxy_reverse_eprt_allowforeignaddress_false {
my $self = shift;
my $tmpdir = $self->{tmpdir};
my $setup = test_setup($tmpdir, 'proxy');

my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
$vhost_port += 12;

my $proxy_config = get_reverse_proxy_config($tmpdir, $setup->{log_file},
$vhost_port);

my $timeout_idle = 10;

my $config = {
PidFile => $setup->{pid_file},
ScoreboardFile => $setup->{scoreboard_file},
SystemLog => $setup->{log_file},
TraceLog => $setup->{log_file},
Trace => 'DEFAULT:10 event:0 lock:0 scoreboard:0 signal:0 proxy:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20',

AuthUserFile => $setup->{auth_user_file},
AuthGroupFile => $setup->{auth_group_file},
AuthOrder => 'mod_auth_file.c',

SocketBindTight => 'on',
TimeoutIdle => $timeout_idle,

IfModules => {
'mod_proxy.c' => $proxy_config,

'mod_delay.c' => {
DelayEngine => 'off',
},
},

Limit => {
LOGIN => {
DenyUser => $setup->{user},
},
},

};

my ($port, $config_user, $config_group) = config_write($setup->{config_file},
$config);

if (open(my $fh, ">> $setup->{config_file}")) {
print $fh <<EOC;
<VirtualHost 127.0.0.1>
Port $vhost_port
ServerName "Real Server"

AuthUserFile $setup->{auth_user_file}
AuthGroupFile $setup->{auth_group_file}
AuthOrder mod_auth_file.c

AllowOverride off
TimeoutIdle $timeout_idle

TransferLog none
WtmpLog off
</VirtualHost>
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 {
sleep(1);
my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
$client->login($setup->{user}, $setup->{passwd});

# Deliberately provide an IP address that does not match our source
# IP address, to trigger the AllowForeignAddress restriction.
my $eprt_addr = '|1|1.2.3.4|49158|';
eval { $client->eprt($eprt_addr) };
unless ($@) {
die("EPRT $eprt_addr succeeded unexpectedly");
}

my $resp_code = $client->response_code();
my $resp_msg = $client->response_msg();

my $expected = 500;
$self->assert($expected == $resp_code,
"Expected response code $expected, got $resp_code");

$expected = 'Illegal EPRT command';
$self->assert($expected eq $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, $timeout_idle + 2) };
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 proxy_reverse_retr_pasv_ascii {
my $self = shift;
my $tmpdir = $self->{tmpdir};
Expand Down

0 comments on commit e497bca

Please sign in to comment.