Skip to content

Commit

Permalink
Issue #1043: Fix null pointer dereference for invalid SCP command by …
Browse files Browse the repository at this point in the history
…passing

the correct argument count to `getopt(3)`.
  • Loading branch information
Castaglia committed Jun 27, 2020
1 parent 9bed20d commit 5dabf56
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 2 deletions.
4 changes: 2 additions & 2 deletions contrib/mod_sftp/scp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2485,7 +2485,6 @@ int sftp_scp_set_params(pool *p, uint32_t channel_id, array_header *req) {
pr_getopt_reset();

reqargv = (char **) req->elts;

for (i = 0; i < req->nelts; i++) {
if (reqargv[i]) {
pr_trace_msg(trace_channel, 5, "reqargv[%u] = '%s'", i, reqargv[i]);
Expand All @@ -2501,7 +2500,7 @@ int sftp_scp_set_params(pool *p, uint32_t channel_id, array_header *req) {
scp_pool = make_sub_pool(sftp_pool);
pr_pool_tag(scp_pool, "SSH2 SCP Pool");

while ((optc = getopt(req->nelts, reqargv, opts)) != -1) {
while ((optc = getopt(req->nelts-1, reqargv, opts)) != -1) {
switch (optc) {
case 'd':
scp_opts |= SFTP_SCP_OPT_DIR;
Expand Down Expand Up @@ -2535,6 +2534,7 @@ int sftp_scp_set_params(pool *p, uint32_t channel_id, array_header *req) {
if (reqargv[optind] == NULL) {
(void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
"'scp' request provided no paths, ignoring");
errno = EINVAL;
return -1;
}

Expand Down
174 changes: 174 additions & 0 deletions tests/t/lib/ProFTPD/Tests/Modules/mod_sftp.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,11 @@ my $TESTS = {
test_class => [qw(forking inprogress scp ssh2)],
},

scp_ext_null_ptr_issue1043 => {
order => ++$order,
test_class => [qw(bug forking scp ssh2)],
},

scp_ext_upload_recursive_dir_bug3447 => {
order => ++$order,
test_class => [qw(bug forking scp ssh2)],
Expand Down Expand Up @@ -52771,6 +52776,175 @@ sub scp_upload_fifo_bug3313 {
unlink($log_file);
}

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

my $rsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_rsa_key');
my $dsa_host_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/ssh_host_dsa_key');

my $rsa_priv_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/test_rsa_key'); my $rsa_pub_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/test_rsa_key.pub');
my $rsa_rfc4716_key = File::Spec->rel2abs('t/etc/modules/mod_sftp/authorized_rsa_keys');

my $authorized_keys = File::Spec->rel2abs("$tmpdir/.authorized_keys");
unless (copy($rsa_rfc4716_key, $authorized_keys)) {
die("Can't copy $rsa_rfc4716_key to $authorized_keys: $!");
}

my $config = {
PidFile => $setup->{pid_file},
ScoreboardFile => $setup->{scoreboard_file},
SystemLog => $setup->{log_file},
TraceLog => $setup->{log_file},
Trace => 'DEFAULT:10 ssh2:20 scp:20',

AuthUserFile => $setup->{auth_user_file},
AuthGroupFile => $setup->{auth_group_file},

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

'mod_sftp.c' => [
"SFTPEngine on",
"SFTPLog $setup->{log_file}",
"SFTPHostKey $rsa_host_key",
"SFTPHostKey $dsa_host_key",
"SFTPAuthorizedUserKeys file:~/.authorized_keys",
],
},
};

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: $!");
}

require Net::SSH2;

my $ex;

# Ignore SIGPIPE
local $SIG{PIPE} = sub { };

# Fork child
$self->handle_sigchld();
defined(my $pid = fork()) or die("Can't fork: $!");
if ($pid) {
eval {
my @cmd = (
'ssh',
'-vv',
'-oBatchMode=yes',
'-oCheckHostIP=no',
"-oPort=$port",
"-oIdentityFile=$rsa_priv_key",
'-oPubkeyAuthentication=yes',
'-oStrictHostKeyChecking=no',
"$setup->{user}\@127.0.0.1",
'scp'
);

my $scp_rh = IO::Handle->new();
my $scp_wh = IO::Handle->new();
my $scp_eh = IO::Handle->new();

$scp_wh->autoflush(1);

sleep(1);

local $SIG{CHLD} = 'DEFAULT';

# Make sure that the perms on the priv key are what OpenSSH wants
unless (chmod(0400, $rsa_priv_key)) {
die("Can't set perms on $rsa_priv_key to 0400: $!");
}

if ($ENV{TEST_VERBOSE}) {
print STDERR "Executing: ", join(' ', @cmd), "\n";
}

my $scp_pid = open3($scp_wh, $scp_rh, $scp_eh, @cmd);
waitpid($scp_pid, 0);
my $exit_status = $?;

# Restore the perms on the priv key
unless (chmod(0644, $rsa_priv_key)) {
die("Can't set perms on $rsa_priv_key to 0644: $!");
}

my ($res, $errstr);
if ($exit_status >> 8 == 0) {
$errstr = join('', <$scp_eh>);
$res = 0;

} else {
$errstr = join('', <$scp_eh>);
if ($ENV{TEST_VERBOSE}) {
print STDERR "Stderr: $errstr\n";
}

$res = 1;
}

if ($res == 0) {
die("'scp' command succeeded unexpectedly");
}
};
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>) {
if ($line =~ /request provided no paths/) {
$ok = 1;
last;
}
}

close($fh);
$self->assert($ok, test_msg("Did not see expected SCP error in logs"));

} else {
die("Can't read $setup->{log_file}: $!");
}
};
if ($@) {
$ex = $@;
}

test_cleanup($setup->{log_file}, $ex);
}

sub scp_ext_upload_recursive_dir_bug3447 {
my $self = shift;
my $tmpdir = $self->{tmpdir};
Expand Down

0 comments on commit 5dabf56

Please sign in to comment.