mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-07-28 01:41:48 +03:00
This is leaking memory allocated in process_open(), which is stored in the handles list in the sftpserver session. Given that the data is provided by the use callbacks, we can not universally free them on our side, but we should, in the long term, introduce some way for the implementers to free outstanding handles that were not closed by misbehaving clients. Signed-off-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
1312 lines
37 KiB
C
1312 lines
37 KiB
C
/*
|
|
* This file is part of the SSH Library
|
|
*
|
|
* Copyright (c) 2018 by Red Hat, Inc.
|
|
*
|
|
* Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
|
|
*
|
|
* The SSH Library is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* The SSH Library is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
* License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with the SSH Library; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
* MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "libssh/sftp.h"
|
|
|
|
#define LIBSSH_STATIC
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <pwd.h>
|
|
|
|
#ifdef HAVE_VALGRIND_VALGRIND_H
|
|
#include <valgrind/valgrind.h>
|
|
#endif
|
|
|
|
#include "libssh/buffer.h"
|
|
#include "libssh/libssh.h"
|
|
#include "libssh/priv.h"
|
|
#include "libssh/session.h"
|
|
#include "libssh/sftp_priv.h"
|
|
#include "torture.h"
|
|
#include "torture_key.h"
|
|
|
|
#include "test_server.h"
|
|
#include "default_cb.h"
|
|
|
|
#define TORTURE_KNOWN_HOSTS_FILE "libssh_torture_knownhosts"
|
|
|
|
const char template[] = "temp_dir_XXXXXX";
|
|
|
|
struct test_server_st {
|
|
struct torture_state *state;
|
|
struct server_state_st *ss;
|
|
char *cwd;
|
|
char *temp_dir;
|
|
};
|
|
|
|
void sftp_handle_session_cb(ssh_event event,
|
|
ssh_session session,
|
|
struct server_state_st *state);
|
|
|
|
static void free_test_server_state(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
|
|
torture_free_state(tss->state);
|
|
SAFE_FREE(tss);
|
|
}
|
|
|
|
static int setup_default_server(void **state)
|
|
{
|
|
struct torture_state *s;
|
|
struct server_state_st *ss;
|
|
struct test_server_st *tss;
|
|
|
|
char ed25519_hostkey[1024] = {0};
|
|
char rsa_hostkey[1024];
|
|
char ecdsa_hostkey[1024];
|
|
//char trusted_ca_pubkey[1024];
|
|
|
|
char sshd_path[1024];
|
|
char log_file[1024];
|
|
int rc;
|
|
|
|
char pid_str[1024];
|
|
|
|
pid_t pid;
|
|
|
|
assert_non_null(state);
|
|
|
|
tss = (struct test_server_st*)calloc(1, sizeof(struct test_server_st));
|
|
assert_non_null(tss);
|
|
|
|
torture_setup_socket_dir((void **)&s);
|
|
assert_non_null(s->socket_dir);
|
|
|
|
/* Set the default interface for the server */
|
|
setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1);
|
|
setenv("PAM_WRAPPER", "1", 1);
|
|
|
|
snprintf(sshd_path,
|
|
sizeof(sshd_path),
|
|
"%s/sshd",
|
|
s->socket_dir);
|
|
|
|
rc = mkdir(sshd_path, 0755);
|
|
assert_return_code(rc, errno);
|
|
|
|
snprintf(log_file,
|
|
sizeof(log_file),
|
|
"%s/sshd/log",
|
|
s->socket_dir);
|
|
|
|
snprintf(ed25519_hostkey,
|
|
sizeof(ed25519_hostkey),
|
|
"%s/sshd/ssh_host_ed25519_key",
|
|
s->socket_dir);
|
|
torture_write_file(ed25519_hostkey,
|
|
torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0));
|
|
|
|
snprintf(rsa_hostkey,
|
|
sizeof(rsa_hostkey),
|
|
"%s/sshd/ssh_host_rsa_key",
|
|
s->socket_dir);
|
|
torture_write_file(rsa_hostkey, torture_get_testkey(SSH_KEYTYPE_RSA, 0));
|
|
|
|
snprintf(ecdsa_hostkey,
|
|
sizeof(ecdsa_hostkey),
|
|
"%s/sshd/ssh_host_ecdsa_key",
|
|
s->socket_dir);
|
|
torture_write_file(ecdsa_hostkey,
|
|
torture_get_testkey(SSH_KEYTYPE_ECDSA_P521, 0));
|
|
|
|
/* Create default server state */
|
|
ss = (struct server_state_st *)calloc(1, sizeof(struct server_state_st));
|
|
assert_non_null(ss);
|
|
|
|
ss->address = strdup("127.0.0.10");
|
|
assert_non_null(ss->address);
|
|
|
|
ss->port = 22;
|
|
|
|
ss->ecdsa_key = strdup(ecdsa_hostkey);
|
|
assert_non_null(ss->ecdsa_key);
|
|
|
|
ss->ed25519_key = strdup(ed25519_hostkey);
|
|
assert_non_null(ss->ed25519_key);
|
|
|
|
ss->rsa_key = strdup(rsa_hostkey);
|
|
assert_non_null(ss->rsa_key);
|
|
|
|
ss->host_key = NULL;
|
|
|
|
/* Use default username and password (set in default_handle_session_cb) */
|
|
ss->expected_username = NULL;
|
|
ss->expected_password = NULL;
|
|
|
|
/* not to mix up the client and server messages */
|
|
ss->verbosity = torture_libssh_verbosity();
|
|
ss->log_file = strdup(log_file);
|
|
|
|
ss->auth_methods = SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY;
|
|
|
|
#ifdef WITH_PCAP
|
|
ss->with_pcap = 1;
|
|
ss->pcap_file = strdup(s->pcap_file);
|
|
assert_non_null(ss->pcap_file);
|
|
#endif
|
|
|
|
/* TODO make configurable */
|
|
ss->max_tries = 3;
|
|
ss->error = 0;
|
|
|
|
tss->state = s;
|
|
tss->ss = ss;
|
|
|
|
/* Use the default session handling function */
|
|
ss->handle_session = sftp_handle_session_cb;
|
|
assert_non_null(ss->handle_session);
|
|
|
|
/* Do not use global configuration */
|
|
ss->parse_global_config = false;
|
|
|
|
/* Start the server using the default values */
|
|
pid = fork_run_server(ss, free_test_server_state, &tss);
|
|
if (pid < 0) {
|
|
fail();
|
|
}
|
|
|
|
snprintf(pid_str, sizeof(pid_str), "%d", pid);
|
|
|
|
torture_write_file(s->srv_pidfile, (const char *)pid_str);
|
|
|
|
setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1);
|
|
unsetenv("PAM_WRAPPER");
|
|
|
|
/* Wait until the sshd is ready to accept connections */
|
|
rc = torture_wait_for_daemon(5);
|
|
assert_int_equal(rc, 0);
|
|
|
|
*state = tss;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int teardown_default_server(void **state)
|
|
{
|
|
struct torture_state *s;
|
|
struct server_state_st *ss;
|
|
struct test_server_st *tss;
|
|
|
|
tss = *state;
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
ss = tss->ss;
|
|
assert_non_null(ss);
|
|
|
|
/* This function can be reused */
|
|
torture_teardown_sshd_server((void **)&s);
|
|
|
|
free_server_state(tss->ss);
|
|
SAFE_FREE(tss->ss);
|
|
SAFE_FREE(tss);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int session_setup(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s;
|
|
int verbosity = torture_libssh_verbosity();
|
|
char template2[] = "/tmp/ssh_torture_XXXXXX";
|
|
char *cwd = NULL;
|
|
char *tmp_dir = NULL;
|
|
char *p = NULL;
|
|
bool b = false;
|
|
int rc;
|
|
|
|
assert_non_null(tss);
|
|
|
|
/* Make sure we do not test the agent */
|
|
unsetenv("SSH_AUTH_SOCK");
|
|
|
|
cwd = torture_get_current_working_dir();
|
|
assert_non_null(cwd);
|
|
|
|
tmp_dir = torture_make_temp_dir(template);
|
|
p = mkdtemp(template2);
|
|
assert_non_null(p);
|
|
assert_non_null(tmp_dir);
|
|
|
|
tss->cwd = cwd;
|
|
tss->temp_dir = tmp_dir;
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
s->ssh.session = ssh_new();
|
|
assert_non_null(s->ssh.session);
|
|
|
|
s->ssh.tsftp = (struct torture_sftp*)calloc(1, sizeof(struct torture_sftp));
|
|
assert_non_null(s->ssh.tsftp);
|
|
s->ssh.tsftp->testdir = strdup(p);
|
|
assert_non_null(s->ssh.tsftp->testdir);
|
|
|
|
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
|
assert_ssh_return_code(s->ssh.session, rc);
|
|
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
|
|
assert_ssh_return_code(s->ssh.session, rc);
|
|
/* Make sure no other configuration options from system will get used */
|
|
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
|
|
assert_ssh_return_code(s->ssh.session, rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int session_setup_sftp(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s = NULL;
|
|
struct torture_sftp *tsftp = NULL;
|
|
ssh_session session = NULL;
|
|
sftp_session sftp = NULL;
|
|
int rc;
|
|
|
|
assert_non_null(tss);
|
|
|
|
rc = session_setup(state);
|
|
assert_int_equal(rc, 0);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
session = s->ssh.session;
|
|
assert_non_null(session);
|
|
|
|
rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
rc = ssh_connect(session);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
rc = ssh_userauth_none(session, NULL);
|
|
/* This request should return a SSH_REQUEST_DENIED error */
|
|
if (rc == SSH_AUTH_ERROR) {
|
|
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
|
|
}
|
|
rc = ssh_userauth_list(session, NULL);
|
|
assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
|
|
|
|
rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
|
|
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
|
|
|
ssh_get_issue_banner(session);
|
|
|
|
/* init sftp session */
|
|
tsftp = s->ssh.tsftp;
|
|
|
|
sftp = sftp_new(session);
|
|
assert_non_null(sftp);
|
|
tsftp->sftp = sftp;
|
|
|
|
rc = sftp_init(sftp);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int session_teardown(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s;
|
|
int rc = 0;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
SAFE_FREE(s->ssh.tsftp->testdir);
|
|
sftp_free(s->ssh.tsftp->sftp);
|
|
SAFE_FREE(s->ssh.tsftp);
|
|
|
|
ssh_disconnect(s->ssh.session);
|
|
ssh_free(s->ssh.session);
|
|
|
|
rc = torture_change_dir(tss->cwd);
|
|
assert_int_equal(rc, 0);
|
|
|
|
rc = torture_rmdirs(tss->temp_dir);
|
|
assert_int_equal(rc, 0);
|
|
|
|
SAFE_FREE(tss->temp_dir);
|
|
SAFE_FREE(tss->cwd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void torture_server_establish_sftp(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s;
|
|
struct torture_sftp *tsftp;
|
|
ssh_session session;
|
|
sftp_session sftp;
|
|
int rc;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
session = s->ssh.session;
|
|
assert_non_null(session);
|
|
|
|
/* TODO: implement proper pam authentication in callback */
|
|
/* Using the default user for the server */
|
|
rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
rc = ssh_connect(session);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
rc = ssh_userauth_none(session, NULL);
|
|
/* This request should return a SSH_REQUEST_DENIED error */
|
|
if (rc == SSH_AUTH_ERROR) {
|
|
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
|
|
}
|
|
rc = ssh_userauth_list(session, NULL);
|
|
assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
|
|
|
|
/* TODO: implement proper pam authentication in callback */
|
|
/* Using the default password for the server */
|
|
rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
|
|
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
|
|
|
ssh_get_issue_banner(session);
|
|
|
|
/* init sftp session */
|
|
tsftp = s->ssh.tsftp;
|
|
|
|
printf("in establish before sftp_new\n");
|
|
sftp = sftp_new(session);
|
|
assert_non_null(sftp);
|
|
|
|
rc = sftp_init(sftp);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
tsftp->sftp = sftp;
|
|
}
|
|
|
|
static void torture_server_test_sftp_function(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s;
|
|
struct torture_sftp *tsftp;
|
|
ssh_session session;
|
|
sftp_session sftp;
|
|
int rc;
|
|
char *rv_str;
|
|
sftp_dir dir;
|
|
|
|
char data[65535] = {0};
|
|
sftp_file source;
|
|
sftp_file to;
|
|
int read_len;
|
|
int write_len;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
session = s->ssh.session;
|
|
assert_non_null(session);
|
|
|
|
rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
rc = ssh_connect(session);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
rc = ssh_userauth_none(session, NULL);
|
|
/* This request should return a SSH_REQUEST_DENIED error */
|
|
if (rc == SSH_AUTH_ERROR) {
|
|
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
|
|
}
|
|
rc = ssh_userauth_list(session, NULL);
|
|
assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
|
|
|
|
/* TODO: implement proper pam authentication in callback */
|
|
/* Using the default password for the server */
|
|
rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
|
|
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
|
|
|
/* init sftp session */
|
|
tsftp = s->ssh.tsftp;
|
|
sftp = sftp_new(session);
|
|
assert_non_null(sftp);
|
|
tsftp->sftp = sftp;
|
|
|
|
rc = sftp_init(sftp);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
/* symbol link */
|
|
rc = sftp_symlink(sftp, "/tmp/this_is_the_link", "/tmp/sftp_symlink_test");
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
rv_str = sftp_readlink(sftp, "/tmp/sftp_symlink_test");
|
|
assert_non_null(rv_str);
|
|
ssh_string_free_char(rv_str);
|
|
|
|
rc = sftp_unlink(sftp, "/tmp/sftp_symlink_test");
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
/* open and close dir */
|
|
dir = sftp_opendir(sftp, "./");
|
|
assert_non_null(dir);
|
|
|
|
rc = sftp_closedir(dir);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
/* file read and write */
|
|
source = sftp_open(sftp, "/usr/bin/ssh", O_RDONLY, 0);
|
|
assert_non_null(source);
|
|
|
|
to = sftp_open(sftp, "ssh-copy", O_WRONLY | O_CREAT, 0700);
|
|
assert_non_null(to);
|
|
|
|
read_len = sftp_read(source, data, 4096);
|
|
write_len = sftp_write(to, data, read_len);
|
|
assert_int_equal(write_len, read_len);
|
|
|
|
rc = sftp_close(source);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
rc = sftp_close(to);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
rc = sftp_unlink(sftp, "ssh-copy");
|
|
assert_int_equal(rc, SSH_OK);
|
|
}
|
|
|
|
static void torture_server_sftp_open_read_write(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s = NULL;
|
|
struct torture_sftp *tsftp = NULL;
|
|
sftp_session sftp = NULL;
|
|
ssh_session session = NULL;
|
|
sftp_attributes a = NULL;
|
|
sftp_file new_file = NULL;
|
|
char tmp_file[PATH_MAX] = {0};
|
|
char data[10] = "0123456789";
|
|
char read_data[10] = {0};
|
|
struct stat sb;
|
|
int rc, write_len, read_len;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
session = s->ssh.session;
|
|
assert_non_null(session);
|
|
|
|
tsftp = s->ssh.tsftp;
|
|
assert_non_null(tsftp);
|
|
|
|
sftp = tsftp->sftp;
|
|
assert_non_null(sftp);
|
|
|
|
snprintf(tmp_file, sizeof(tmp_file), "%s/newfile", tss->temp_dir);
|
|
|
|
/*
|
|
* Create a new file
|
|
*/
|
|
new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_CREAT, 0751);
|
|
assert_non_null(new_file);
|
|
|
|
/* Write should work ok */
|
|
write_len = sftp_write(new_file, data, sizeof(data));
|
|
assert_int_equal(write_len, sizeof(data));
|
|
|
|
/* Reading should fail */
|
|
read_len = sftp_read(new_file, read_data, sizeof(read_data));
|
|
assert_int_equal(read_len, SSH_ERROR);
|
|
|
|
rc = sftp_close(new_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* Verify locally the mode is correct */
|
|
rc = stat(tmp_file, &sb);
|
|
assert_int_equal(rc, 0);
|
|
assert_int_equal(sb.st_mode, S_IFREG | 0751);
|
|
assert_int_equal(sb.st_size, sizeof(data)); /* 10b written */
|
|
|
|
/* Remote stat */
|
|
a = sftp_stat(sftp, tmp_file);
|
|
assert_non_null(a);
|
|
assert_int_equal(a->permissions, S_IFREG | 0751);
|
|
assert_int_equal(a->size, sizeof(data)); /* 10b written */
|
|
assert_int_equal(a->type, SSH_FILEXFER_TYPE_REGULAR);
|
|
sftp_attributes_free(a);
|
|
|
|
/*
|
|
* Now that file exists and contains some data, lets try O_TRUNC,
|
|
* mode is ignored
|
|
*/
|
|
new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_TRUNC, 0);
|
|
assert_non_null(new_file);
|
|
|
|
/* Verify that the existing data in the file has been truncated */
|
|
a = sftp_stat(sftp, tmp_file);
|
|
assert_non_null(a);
|
|
assert_int_equal(a->size, 0); /* No content due to truncation */
|
|
sftp_attributes_free(a);
|
|
|
|
/* Write should work ok */
|
|
write_len = sftp_write(new_file, data, sizeof(data));
|
|
assert_int_equal(write_len, sizeof(data));
|
|
|
|
/* Reading should fail */
|
|
read_len = sftp_read(new_file, read_data, sizeof(read_data));
|
|
assert_int_equal(read_len, SSH_ERROR);
|
|
|
|
rc = sftp_close(new_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/*
|
|
* Now, lets try O_APPEND, mode is ignored
|
|
*/
|
|
new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_APPEND, 0);
|
|
assert_non_null(new_file);
|
|
|
|
/* fstat is not implemented */
|
|
a = sftp_fstat(new_file);
|
|
assert_null(a);
|
|
|
|
/* Write should work ok */
|
|
write_len = sftp_write(new_file, data, sizeof(data));
|
|
assert_int_equal(write_len, sizeof(data));
|
|
|
|
/* Reading should fail */
|
|
read_len = sftp_read(new_file, read_data, sizeof(read_data));
|
|
assert_int_equal(read_len, SSH_ERROR);
|
|
|
|
rc = sftp_close(new_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/*
|
|
* Now, lets try read+write, mode is ignored
|
|
*/
|
|
new_file = sftp_open(sftp, tmp_file, O_RDWR, 0);
|
|
assert_non_null(new_file);
|
|
|
|
/* Reading should work */
|
|
read_len = sftp_read(new_file, read_data, sizeof(read_data));
|
|
assert_int_equal(read_len, sizeof(read_data));
|
|
assert_int_equal(sizeof(read_data), sizeof(data)); /* sanity */
|
|
assert_memory_equal(read_data, data, sizeof(data));
|
|
|
|
rc = sftp_seek(new_file, 20);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* Write should work also ok */
|
|
write_len = sftp_write(new_file, data, sizeof(data));
|
|
assert_int_equal(write_len, sizeof(data));
|
|
|
|
rc = sftp_close(new_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* Remove the file */
|
|
rc = sftp_unlink(sftp, tmp_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* again: the file does not exist anymore so we should fail now */
|
|
rc = sftp_unlink(sftp, tmp_file);
|
|
assert_int_equal(rc, SSH_ERROR);
|
|
|
|
/*
|
|
* Now, lets try read+write+create
|
|
*/
|
|
new_file = sftp_open(sftp, tmp_file, O_RDWR | O_CREAT, 0700);
|
|
assert_non_null(new_file);
|
|
|
|
/* Reading should not fail but return no data */
|
|
read_len = sftp_read(new_file, read_data, sizeof(read_data));
|
|
assert_int_equal(read_len, 0);
|
|
|
|
rc = sftp_close(new_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* be nice */
|
|
rc = sftp_unlink(sftp, tmp_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* null flags should be invalid */
|
|
/* but there is no way in libssh client to force null flags so skip this
|
|
new_file = sftp_open(sftp, tmp_file, 0, 0700);
|
|
assert_null(new_file);
|
|
*/
|
|
|
|
/* Only O_CREAT is invalid on file which does not exist. Read is implicit */
|
|
new_file = sftp_open(sftp, tmp_file, O_CREAT, 0700);
|
|
assert_null(new_file);
|
|
}
|
|
|
|
static void torture_server_sftp_mkdir(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s;
|
|
struct torture_sftp *tsftp;
|
|
sftp_session sftp;
|
|
ssh_session session;
|
|
sftp_file new_file = NULL;
|
|
char tmp_dir[PATH_MAX] = {0};
|
|
char tmp_file[PATH_MAX] = {0};
|
|
sftp_attributes a = NULL;
|
|
struct stat sb;
|
|
int rc;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
session = s->ssh.session;
|
|
assert_non_null(session);
|
|
|
|
tsftp = s->ssh.tsftp;
|
|
assert_non_null(tsftp);
|
|
|
|
sftp = tsftp->sftp;
|
|
assert_non_null(sftp);
|
|
|
|
snprintf(tmp_dir, sizeof(tmp_dir), "%s/newdir", tss->temp_dir);
|
|
|
|
/* create a test dir */
|
|
rc = sftp_mkdir(sftp, tmp_dir, 0751);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* try the same path again -- we should get an error */
|
|
rc = sftp_mkdir(sftp, tmp_dir, 0751);
|
|
assert_int_equal(rc, SSH_ERROR);
|
|
|
|
/* Verify locally the mode is correct */
|
|
rc = stat(tmp_dir, &sb);
|
|
assert_int_equal(rc, 0);
|
|
assert_int_equal(sb.st_mode, S_IFDIR | 0751);
|
|
|
|
/* Remote stat */
|
|
a = sftp_stat(sftp, tmp_dir);
|
|
assert_non_null(a);
|
|
assert_int_equal(a->permissions, S_IFDIR | 0751);
|
|
assert_int_equal(a->type, SSH_FILEXFER_TYPE_DIRECTORY);
|
|
sftp_attributes_free(a);
|
|
|
|
snprintf(tmp_file, sizeof(tmp_file), "%s/newdir/newfile", tss->temp_dir);
|
|
|
|
/* create a file in there */
|
|
new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_CREAT, 0700);
|
|
assert_non_null(new_file);
|
|
rc = sftp_close(new_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* remove of non-empty directory fails */
|
|
rc = sftp_rmdir(sftp, tmp_dir);
|
|
assert_int_equal(rc, SSH_ERROR);
|
|
|
|
/* Unlink can not remove directory either */
|
|
rc = sftp_unlink(sftp, tmp_dir);
|
|
assert_int_equal(rc, SSH_ERROR);
|
|
|
|
/* Remove the file */
|
|
rc = sftp_unlink(sftp, tmp_file);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
/* Now it should work */
|
|
rc = sftp_rmdir(sftp, tmp_dir);
|
|
assert_ssh_return_code(session, rc);
|
|
}
|
|
|
|
static void torture_server_sftp_realpath(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s;
|
|
struct torture_sftp *tsftp;
|
|
sftp_session sftp;
|
|
ssh_session session;
|
|
char path[PATH_MAX] = {0};
|
|
char exp_path[PATH_MAX] = {0};
|
|
char *new_path = NULL;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
session = s->ssh.session;
|
|
assert_non_null(session);
|
|
|
|
tsftp = s->ssh.tsftp;
|
|
assert_non_null(tsftp);
|
|
|
|
sftp = tsftp->sftp;
|
|
assert_non_null(sftp);
|
|
|
|
/* first try with the empty string, which should be equivalent to CWD */
|
|
new_path = sftp_canonicalize_path(sftp, path);
|
|
assert_non_null(new_path);
|
|
assert_string_equal(new_path, tss->cwd);
|
|
ssh_string_free_char(new_path);
|
|
|
|
/* now, lets try some more complicated paths relative to the CWD */
|
|
snprintf(path, sizeof(path), "%s/.././%s",
|
|
tss->temp_dir, tss->temp_dir);
|
|
new_path = sftp_canonicalize_path(sftp, path);
|
|
assert_non_null(new_path);
|
|
snprintf(exp_path, sizeof(exp_path), "%s/%s",
|
|
tss->cwd, tss->temp_dir);
|
|
assert_string_equal(new_path, exp_path);
|
|
ssh_string_free_char(new_path);
|
|
|
|
/* and this one does not exists, which is an error */
|
|
snprintf(path, sizeof(path), "%s/.././%s/nodir",
|
|
tss->temp_dir, tss->temp_dir);
|
|
new_path = sftp_canonicalize_path(sftp, path);
|
|
assert_null(new_path);
|
|
ssh_string_free_char(new_path);
|
|
}
|
|
|
|
static void torture_server_sftp_symlink(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s;
|
|
struct torture_sftp *tsftp;
|
|
sftp_session sftp;
|
|
ssh_session session;
|
|
sftp_file new_file = NULL;
|
|
char tmp_dir[PATH_MAX] = {0};
|
|
char tmp_file[PATH_MAX] = {0};
|
|
char path[PATH_MAX] = {0};
|
|
char abs_path[PATH_MAX] = {0};
|
|
char data[42] = "012345678901234567890123456789012345678901";
|
|
char *new_path = NULL;
|
|
sftp_attributes a = NULL;
|
|
sftp_dir dir;
|
|
int write_len, num_files = 0;
|
|
int rc;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
session = s->ssh.session;
|
|
assert_non_null(session);
|
|
|
|
tsftp = s->ssh.tsftp;
|
|
assert_non_null(tsftp);
|
|
|
|
sftp = tsftp->sftp;
|
|
assert_non_null(sftp);
|
|
|
|
/* create a test dir */
|
|
snprintf(tmp_dir, sizeof(tmp_dir), "%s/newdir", tss->temp_dir);
|
|
rc = sftp_mkdir(sftp, tmp_dir, 0751);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* create a file in there */
|
|
snprintf(tmp_file, sizeof(tmp_file), "%s/%s/newdir/newfile",
|
|
tss->cwd, tss->temp_dir);
|
|
new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_CREAT, 0700);
|
|
assert_non_null(new_file);
|
|
write_len = sftp_write(new_file, data, sizeof(data));
|
|
assert_int_equal(write_len, sizeof(data));
|
|
rc = sftp_close(new_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* now, lets create a (relative) symlink to the new file */
|
|
snprintf(path, sizeof(path), "%s/newdir/linkname", tss->temp_dir);
|
|
rc = sftp_symlink(sftp, tmp_file, path);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* when the destination exists, it should fail */
|
|
rc = sftp_symlink(sftp, tmp_dir, tmp_file);
|
|
assert_int_equal(rc, SSH_ERROR);
|
|
|
|
/* now, there are different versions of stat that follow symlinks or not */
|
|
/* lstat should not follow the symlink and show information about the link
|
|
* itself */
|
|
a = sftp_lstat(sftp, path);
|
|
assert_non_null(a);
|
|
assert_int_not_equal(a->size, sizeof(data));
|
|
assert_int_equal(a->type, SSH_FILEXFER_TYPE_SYMLINK);
|
|
sftp_attributes_free(a);
|
|
|
|
/* readlink should give us more information about the target of the symlink
|
|
*/
|
|
new_path = sftp_readlink(sftp, path);
|
|
assert_non_null(new_path);
|
|
snprintf(abs_path, sizeof(abs_path), "%s/%s/newdir/newfile",
|
|
tss->cwd, tss->temp_dir);
|
|
assert_string_equal(new_path, abs_path);
|
|
ssh_string_free_char(new_path);
|
|
|
|
/* stat should follow the symlink and show information about the link
|
|
* target */
|
|
a = sftp_stat(sftp, path);
|
|
assert_non_null(a);
|
|
assert_int_equal(a->size, sizeof(data));
|
|
assert_int_equal(a->permissions, S_IFREG | 0700);
|
|
assert_int_equal(a->type, SSH_FILEXFER_TYPE_REGULAR);
|
|
sftp_attributes_free(a);
|
|
|
|
/* on non-existing path, they fail */
|
|
a = sftp_lstat(sftp, "non-existing");
|
|
assert_null(a);
|
|
a = sftp_stat(sftp, "non-existing");
|
|
assert_null(a);
|
|
|
|
/**** readdir ****/
|
|
dir = sftp_opendir(sftp, tmp_dir);
|
|
assert_non_null(dir);
|
|
while ((a = sftp_readdir(sftp, dir))) {
|
|
if (strcmp(a->name, ".") != 0 &&
|
|
strcmp(a->name, "..") != 0 &&
|
|
strcmp(a->name, "newfile") != 0 &&
|
|
strcmp(a->name, "linkname") != 0) {
|
|
/* There is a file we did not create */
|
|
assert_true(false);
|
|
}
|
|
|
|
num_files++;
|
|
sftp_attributes_free(a);
|
|
}
|
|
assert_int_equal(num_files, 4);
|
|
rc = sftp_dir_eof(dir);
|
|
assert_int_equal(rc, 1);
|
|
rc = sftp_closedir(dir);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* now, remove the target of the link, the stat should not handle that,
|
|
* while lstat should keep working */
|
|
rc = sftp_unlink(sftp, tmp_file);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
a = sftp_lstat(sftp, path);
|
|
assert_non_null(a);
|
|
assert_int_not_equal(a->size, sizeof(data));
|
|
sftp_attributes_free(a);
|
|
|
|
a = sftp_stat(sftp, path);
|
|
assert_null(a);
|
|
|
|
/* readlink works ok on broken symlinks */
|
|
new_path = sftp_readlink(sftp, path);
|
|
assert_non_null(new_path);
|
|
snprintf(abs_path, sizeof(abs_path), "%s/%s/newdir/newfile",
|
|
tss->cwd, tss->temp_dir);
|
|
assert_string_equal(new_path, abs_path);
|
|
ssh_string_free_char(new_path);
|
|
|
|
/* readlink should fail on directories */
|
|
new_path = sftp_readlink(sftp, tmp_dir);
|
|
assert_null(new_path);
|
|
/* readlink should fail on or on non-existing files */
|
|
new_path = sftp_readlink(sftp, tmp_file);
|
|
assert_null(new_path);
|
|
|
|
/* Clean up symlink */
|
|
rc = sftp_unlink(sftp, path);
|
|
assert_int_equal(rc, SSH_OK);
|
|
/* Clean up temporary directory */
|
|
rc = sftp_rmdir(sftp, tmp_dir);
|
|
assert_int_equal(rc, SSH_OK);
|
|
}
|
|
|
|
static void torture_server_sftp_extended(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s;
|
|
struct torture_sftp *tsftp;
|
|
sftp_session sftp;
|
|
ssh_session session;
|
|
sftp_file new_file = NULL;
|
|
char tmp_dir[PATH_MAX] = {0};
|
|
char tmp_file[PATH_MAX] = {0};
|
|
sftp_statvfs_t st = NULL;
|
|
int rc;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
session = s->ssh.session;
|
|
assert_non_null(session);
|
|
|
|
tsftp = s->ssh.tsftp;
|
|
assert_non_null(tsftp);
|
|
|
|
sftp = tsftp->sftp;
|
|
assert_non_null(sftp);
|
|
|
|
/* create a test dir */
|
|
snprintf(tmp_dir, sizeof(tmp_dir), "%s/newdir", tss->temp_dir);
|
|
rc = sftp_mkdir(sftp, tmp_dir, 0751);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* create a file in there */
|
|
snprintf(tmp_file, sizeof(tmp_file), "%s/%s/newdir/newfile",
|
|
tss->cwd, tss->temp_dir);
|
|
new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_CREAT, 0700);
|
|
assert_non_null(new_file);
|
|
|
|
/* extended fstatvsf is not advertised nor supported now but calling this
|
|
* message will keep hanging the server. The extension protocol says that
|
|
* the clients can not request extension that are not supported by the
|
|
* server so before doing this, we should use sftp_extension_supported()
|
|
* anyway */
|
|
/* st = sftp_fstatvfs(new_file);
|
|
assert_null(st); */
|
|
|
|
/* close */
|
|
rc = sftp_close(new_file);
|
|
assert_ssh_return_code(session, rc);
|
|
|
|
/* extended statvsf */
|
|
st = sftp_statvfs(sftp, tmp_file);
|
|
assert_non_null(st);
|
|
/* probably hard to check more */
|
|
sftp_statvfs_free(st);
|
|
|
|
/* Clean up temporary directory */
|
|
rc = sftp_unlink(sftp, tmp_file);
|
|
assert_int_equal(rc, SSH_OK);
|
|
rc = sftp_rmdir(sftp, tmp_dir);
|
|
assert_int_equal(rc, SSH_OK);
|
|
}
|
|
|
|
static void
|
|
torture_server_sftp_setstat(void **state)
|
|
{
|
|
|
|
char name[128] = {0};
|
|
char data[10] = "0123456789";
|
|
int rc;
|
|
size_t len;
|
|
int atime = 10676, mtime = 13467;
|
|
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP;
|
|
|
|
struct passwd *pwd = NULL;
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s = NULL;
|
|
struct torture_sftp *tsftp = NULL;
|
|
struct sftp_attributes_struct attr;
|
|
sftp_attributes tmp_attr = NULL;
|
|
|
|
sftp_session sftp = NULL;
|
|
ssh_session session = NULL;
|
|
sftp_file new_file = NULL;
|
|
|
|
pwd = getpwnam("alice");
|
|
assert_non_null(pwd);
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
session = s->ssh.session;
|
|
assert_non_null(session);
|
|
|
|
tsftp = s->ssh.tsftp;
|
|
assert_non_null(tsftp);
|
|
|
|
sftp = tsftp->sftp;
|
|
assert_non_null(sftp);
|
|
assert_non_null(tsftp->testdir);
|
|
snprintf(name, sizeof(name), "%s/server_setstat_test", tsftp->testdir);
|
|
new_file = sftp_open(sftp, name, O_WRONLY | O_CREAT, 0700);
|
|
assert_non_null(new_file);
|
|
len = sftp_write(new_file, data, sizeof(data));
|
|
assert_int_equal(len, sizeof(data));
|
|
rc = sftp_close(new_file);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
ZERO_STRUCT(attr);
|
|
attr.flags = SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS |
|
|
SSH_FILEXFER_ATTR_UIDGID | SSH_FILEXFER_ATTR_ACMODTIME;
|
|
|
|
attr.size = len;
|
|
attr.uid = pwd->pw_uid;
|
|
attr.gid = pwd->pw_gid;
|
|
attr.permissions = mode;
|
|
attr.atime = atime;
|
|
attr.mtime = mtime;
|
|
|
|
rc = sftp_setstat(sftp, name, &attr);
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
assert_int_equal(rc, SSH_OK);
|
|
|
|
tmp_attr = sftp_stat(sftp, name);
|
|
assert_non_null(tmp_attr);
|
|
|
|
assert_int_equal(tmp_attr->uid, pwd->pw_uid);
|
|
assert_int_equal(tmp_attr->gid, pwd->pw_gid);
|
|
|
|
assert_int_equal(len, tmp_attr->size);
|
|
assert_int_equal(tmp_attr->permissions & ACCESSPERMS, mode);
|
|
assert_int_equal(tmp_attr->mtime, mtime);
|
|
assert_int_equal(tmp_attr->atime, atime);
|
|
|
|
/*negative tests*/
|
|
rc = sftp_setstat(sftp, "not existing", &attr);
|
|
assert_int_equal(rc, SSH_ERROR);
|
|
sftp_unlink(sftp, name);
|
|
sftp_attributes_free(tmp_attr);
|
|
}
|
|
|
|
/* The max number of handles is 256 in sftpserver.h -- keep in sync! */
|
|
#define SFTP_HANDLES 256
|
|
static void torture_server_sftp_handles_exhaustion(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s = NULL;
|
|
struct torture_sftp *tsftp = NULL;
|
|
char name[128] = {0};
|
|
sftp_file handle, handles[SFTP_HANDLES] = {0};
|
|
sftp_session sftp = NULL;
|
|
int rc;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
tsftp = s->ssh.tsftp;
|
|
assert_non_null(tsftp);
|
|
|
|
sftp = tsftp->sftp;
|
|
assert_non_null(sftp);
|
|
|
|
/* Occupy all handles */
|
|
for (int i = 0; i < SFTP_HANDLES; i++) {
|
|
snprintf(name, sizeof(name), "%s/fn%d", tsftp->testdir, i);
|
|
handles[i] = sftp_open(sftp, name, O_WRONLY | O_CREAT, 0700);
|
|
assert_non_null(handles[i]);
|
|
}
|
|
|
|
/* Next handle should fail, but not crash or OOB */
|
|
snprintf(name, sizeof(name), "%s/failfn", tsftp->testdir);
|
|
handle = sftp_open(sftp, name, O_WRONLY | O_CREAT, 0700);
|
|
assert_null(handle);
|
|
|
|
/* cleanup */
|
|
for (int i = 0; i < SFTP_HANDLES; i++) {
|
|
snprintf(name, sizeof(name), "%s/fn%d", tsftp->testdir, i);
|
|
rc = sftp_close(handles[i]);
|
|
assert_int_equal(rc, SSH_OK);
|
|
}
|
|
}
|
|
|
|
static void torture_server_sftp_handle_overrun(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s = NULL;
|
|
struct torture_sftp *tsftp = NULL;
|
|
char name[128] = {0};
|
|
sftp_session sftp = NULL;
|
|
sftp_file handle = NULL;
|
|
ssh_buffer buffer = NULL;
|
|
uint32_t id, bad_handle = SFTP_HANDLES, bad_handle_len = 4;
|
|
sftp_message msg = NULL;
|
|
sftp_status_message status = NULL;
|
|
int rc;
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
tsftp = s->ssh.tsftp;
|
|
assert_non_null(tsftp);
|
|
|
|
sftp = tsftp->sftp;
|
|
assert_non_null(sftp);
|
|
|
|
/* Initialize the sftp handles by opening first file */
|
|
snprintf(name, sizeof(name), "%s/file", tsftp->testdir);
|
|
handle = sftp_open(sftp, name, O_WRONLY | O_CREAT, 0700);
|
|
assert_non_null(handle);
|
|
|
|
/* Craft an malicious SFTP packet trying to access handle 256
|
|
* (SFTP_HANDLES) */
|
|
buffer = ssh_buffer_new();
|
|
id = sftp_get_new_id(sftp);
|
|
assert_non_null(buffer);
|
|
|
|
rc = ssh_buffer_pack(buffer,
|
|
"ddPqd",
|
|
id,
|
|
(uint32_t)bad_handle_len,
|
|
(size_t)bad_handle_len,
|
|
&bad_handle, /* a 32b int as ssh_string */
|
|
(uint64_t)0,
|
|
(uint32_t)1024);
|
|
assert_int_equal(rc, SSH_OK);
|
|
rc = sftp_packet_write(sftp, SSH_FXP_READ, buffer);
|
|
SSH_BUFFER_FREE(buffer);
|
|
assert_int_equal(rc, 29);
|
|
|
|
rc = sftp_recv_response_msg(sftp, id, true, &msg);
|
|
assert_int_equal(rc, SSH_OK);
|
|
assert_int_equal(msg->packet_type, SSH_FXP_STATUS);
|
|
status = parse_status_msg(msg);
|
|
sftp_message_free(msg);
|
|
assert_int_equal(status->status, SSH_FX_INVALID_HANDLE);
|
|
status_msg_free(status);
|
|
|
|
rc = sftp_close(handle);
|
|
assert_int_equal(rc, SSH_OK);
|
|
}
|
|
|
|
static void torture_server_sftp_payload_overrun(void **state)
|
|
{
|
|
struct test_server_st *tss = *state;
|
|
struct torture_state *s = NULL;
|
|
struct torture_sftp *tsftp = NULL;
|
|
char name[128] = {0};
|
|
sftp_session sftp = NULL;
|
|
sftp_file handle = NULL;
|
|
ssh_buffer buffer = NULL;
|
|
uint32_t id, bad_payload_len = 0x7ffffffc;
|
|
int rc;
|
|
|
|
#ifdef HAVE_VALGRIND_VALGRIND_H
|
|
if (RUNNING_ON_VALGRIND) {
|
|
/* This malformed message does not crash the server, but keeps waiting
|
|
* for more data as announced in the payloiad length so the opened FD on
|
|
* the server side is leaking when the server terminates.
|
|
* Given that the custom sftp server could store anything into the
|
|
* handles, it should take care of cleaning up the outstanding handles,
|
|
* but this is something to solve in the future. Now just skipping the
|
|
* test.
|
|
*/
|
|
skip();
|
|
}
|
|
#endif /* HAVE_VALGRIND_VALGRIND_H */
|
|
|
|
assert_non_null(tss);
|
|
|
|
s = tss->state;
|
|
assert_non_null(s);
|
|
|
|
tsftp = s->ssh.tsftp;
|
|
assert_non_null(tsftp);
|
|
|
|
sftp = tsftp->sftp;
|
|
assert_non_null(sftp);
|
|
|
|
/* Open a file for writing */
|
|
snprintf(name, sizeof(name), "%s/file", tsftp->testdir);
|
|
handle = sftp_open(sftp, name, O_WRONLY | O_CREAT, 0700);
|
|
assert_non_null(handle);
|
|
|
|
/* Craft an malicious SFTP packet trying to write to the file with
|
|
* payload_length overrun */
|
|
buffer = ssh_buffer_new();
|
|
id = sftp_get_new_id(sftp);
|
|
assert_non_null(buffer);
|
|
|
|
rc = ssh_buffer_pack(buffer,
|
|
"dbdSqd",
|
|
bad_payload_len,
|
|
SSH_FXP_WRITE,
|
|
id,
|
|
handle->handle,
|
|
(uint64_t)0,
|
|
(uint32_t)0);
|
|
assert_int_equal(rc, SSH_OK);
|
|
rc = ssh_channel_write(sftp->channel,
|
|
ssh_buffer_get(buffer),
|
|
ssh_buffer_get_len(buffer));
|
|
assert_int_equal(rc, 29);
|
|
SSH_BUFFER_FREE(buffer);
|
|
|
|
/* We do not get answer for this malformed packet -- just kill the
|
|
* connection */
|
|
ssh_string_free(handle->handle);
|
|
free(handle);
|
|
}
|
|
|
|
int torture_run_tests(void) {
|
|
int rc;
|
|
struct CMUnitTest tests[] = {
|
|
cmocka_unit_test_setup_teardown(torture_server_establish_sftp,
|
|
session_setup,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_test_sftp_function,
|
|
session_setup,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_sftp_open_read_write,
|
|
session_setup_sftp,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_sftp_mkdir,
|
|
session_setup_sftp,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_sftp_realpath,
|
|
session_setup_sftp,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_sftp_symlink,
|
|
session_setup_sftp,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_sftp_extended,
|
|
session_setup_sftp,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_sftp_setstat,
|
|
session_setup_sftp,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_sftp_handles_exhaustion,
|
|
session_setup_sftp,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_sftp_handle_overrun,
|
|
session_setup_sftp,
|
|
session_teardown),
|
|
cmocka_unit_test_setup_teardown(torture_server_sftp_payload_overrun,
|
|
session_setup_sftp,
|
|
session_teardown),
|
|
};
|
|
|
|
ssh_init();
|
|
|
|
torture_filter_tests(tests);
|
|
rc = cmocka_run_group_tests(tests,
|
|
setup_default_server,
|
|
teardown_default_server);
|
|
|
|
ssh_finalize();
|
|
|
|
return rc;
|
|
}
|