aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAnderson Toshiyuki Sasaki <ansasaki@redhat.com>2019-10-25 13:27:48 +0200
committerAndreas Schneider <asn@cryptomilk.org>2019-12-09 16:08:03 +0100
commit27bcac68457bf0aa4b296ec79bd11bfd13015636 (patch)
tree1781ddb7f995f40e45dd4502b94d96fa24b9852a /tests
parente00ef9635a487f408e723540b1cbb3b6872af9ca (diff)
downloadlibssh-27bcac68457bf0aa4b296ec79bd11bfd13015636.tar.gz
libssh-27bcac68457bf0aa4b296ec79bd11bfd13015636.tar.xz
libssh-27bcac68457bf0aa4b296ec79bd11bfd13015636.zip
CVE-2019-14889: tests: Add tests for SCP client
Fixes T181 Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/client/CMakeLists.txt8
-rw-r--r--tests/client/torture_scp.c497
2 files changed, 505 insertions, 0 deletions
diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt
index 0998e4aa..70b5de3e 100644
--- a/tests/client/CMakeLists.txt
+++ b/tests/client/CMakeLists.txt
@@ -17,11 +17,19 @@ set(LIBSSH_CLIENT_TESTS
torture_request_env
torture_client_global_requests)
+find_program(SCP_EXECUTABLE NAMES scp)
+if (SCP_EXECUTABLE)
+ set(LIBSSH_CLIENT_TESTS
+ ${LIBSSH_CLIENT_TESTS}
+ torture_scp)
+endif()
+
if (DEFAULT_C_NO_DEPRECATION_FLAGS)
set_source_files_properties(torture_knownhosts.c
PROPERTIES
COMPILE_FLAGS ${DEFAULT_C_NO_DEPRECATION_FLAGS})
endif()
+
if (WITH_SFTP)
if (WITH_BENCHMARKS)
set(SFTP_BENCHMARK_TESTS
diff --git a/tests/client/torture_scp.c b/tests/client/torture_scp.c
new file mode 100644
index 00000000..b20ed34a
--- /dev/null
+++ b/tests/client/torture_scp.c
@@ -0,0 +1,497 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2019 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.
+ */
+
+#define LIBSSH_STATIC
+
+#include "config.h"
+
+#include "torture.h"
+#include "libssh/scp.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <errno.h>
+
+#define BUF_SIZE 1024
+
+#define TEMPLATE BINARYDIR "/tests/home/alice/temp_dir_XXXXXX"
+
+struct scp_st {
+ struct torture_state *s;
+ char *tmp_dir;
+ char *tmp_dir_basename;
+};
+
+static int sshd_setup(void **state)
+{
+ struct scp_st *ts = NULL;
+ struct torture_state *s = NULL;
+
+ ts = (struct scp_st *)calloc(1, sizeof(struct scp_st));
+ assert_non_null(ts);
+
+ torture_setup_sshd_server((void **)&s, false);
+ assert_non_null(s);
+
+ ts->s = s;
+
+ *state = ts;
+
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ struct scp_st *ts = NULL;
+
+ ts = *((struct scp_st **)state);
+ assert_non_null(ts);
+ assert_non_null(ts->s);
+
+ torture_teardown_sshd_server((void **)&(ts->s));
+
+ SAFE_FREE(ts);
+
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct scp_st *ts = NULL;
+ struct torture_state *s = NULL;
+
+ char *tmp_dir = NULL;
+ char *tmp_dir_basename = NULL;
+
+ struct passwd *pwd;
+
+ int rc;
+
+ assert_non_null(state);
+
+ ts = *state;
+
+ assert_non_null(ts);
+ assert_non_null(ts->s);
+
+ s = ts->s;
+
+ /* Create temporary directory for alice */
+ tmp_dir = torture_make_temp_dir(TEMPLATE);
+ assert_non_null(tmp_dir);
+ ts->tmp_dir = tmp_dir;
+
+ tmp_dir_basename = ssh_basename(tmp_dir);
+ assert_non_null(tmp_dir_basename);
+ ts->tmp_dir_basename = tmp_dir_basename;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ rc = setuid(pwd->pw_uid);
+ assert_return_code(rc, errno);
+
+ s->ssh.session = torture_ssh_session(s,
+ TORTURE_SSH_SERVER,
+ NULL,
+ TORTURE_SSH_USER_ALICE,
+ NULL);
+ assert_non_null(s->ssh.session);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct scp_st *ts = NULL;
+ struct torture_state *s = NULL;
+
+ assert_non_null(state);
+ ts = *((struct scp_st **)state);
+
+ assert_non_null(ts->s);
+ s = ts->s;
+
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ assert_non_null(ts->tmp_dir);
+ torture_rmdirs(ts->tmp_dir);
+
+ SAFE_FREE(ts->tmp_dir);
+ SAFE_FREE(ts->tmp_dir_basename);
+
+ return 0;
+}
+
+static void torture_scp_upload(void **state)
+{
+ struct scp_st *ts = NULL;
+ struct torture_state *s = NULL;
+
+ ssh_session session = NULL;
+ ssh_scp scp = NULL;
+
+ char expected_a[BUF_SIZE];
+ char buf[BUF_SIZE];
+ FILE *file = NULL;
+
+ int rc;
+
+ assert_non_null(state);
+ ts = *state;
+
+ assert_non_null(ts->s);
+ s = ts->s;
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ assert_non_null(ts->tmp_dir_basename);
+ assert_non_null(ts->tmp_dir);
+
+ /* Upload file "a" to alice's temp dir */
+
+ /* When writing the file_name must be the directory name */
+ scp = ssh_scp_new(session, SSH_SCP_WRITE, ts->tmp_dir_basename);
+ assert_non_null(scp);
+
+ rc = ssh_scp_init(scp);
+ assert_ssh_return_code(session, rc);
+
+ /* Init buffer content to be written */
+ memset(expected_a, 'A', BUF_SIZE);
+
+ /* For ssh_scp_push_file(), the file_name is the name of the file without
+ * path */
+ rc = ssh_scp_push_file(scp, "a", BUF_SIZE, 0644);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_scp_write(scp, expected_a, BUF_SIZE);
+ assert_ssh_return_code(session, rc);
+
+ /* Cleanup */
+ ssh_scp_close(scp);
+ ssh_scp_free(scp);
+
+ /* Open file and check content */
+ snprintf(buf, BUF_SIZE, "%s/a", ts->tmp_dir);
+
+ file = fopen(buf, "r");
+ assert_non_null(file);
+
+ fread(buf, BUF_SIZE, 1, file);
+ assert_memory_equal(buf, expected_a, BUF_SIZE);
+
+ fclose(file);
+}
+
+static void torture_scp_upload_recursive(void **state)
+{
+ struct scp_st *ts = NULL;
+ struct torture_state *s = NULL;
+
+ ssh_session session = NULL;
+ ssh_scp scp = NULL;
+
+ char expected_b[BUF_SIZE];
+ char buf[BUF_SIZE];
+
+ FILE *file = NULL;
+
+ int rc;
+
+ assert_non_null(state);
+ ts = *state;
+
+ assert_non_null(ts->s);
+ s = ts->s;
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ assert_non_null(ts->tmp_dir_basename);
+ assert_non_null(ts->tmp_dir);
+
+ /* Upload directory "test_dir" containing file "b" to alice's temp dir */
+
+ /* When writing the file_name must be the directory name */
+ scp = ssh_scp_new(session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE,
+ ts->tmp_dir_basename);
+ assert_non_null(scp);
+
+ rc = ssh_scp_init(scp);
+ assert_ssh_return_code(session, rc);
+
+ /* Push directory where the new file will be copied */
+ rc = ssh_scp_push_directory(scp, "test_dir", 0755);
+ assert_ssh_return_code(session, rc);
+
+ memset(expected_b, 'B', BUF_SIZE);
+
+ /* For ssh_scp_push_file(), the file_name is the name of the file without
+ * path */
+ rc = ssh_scp_push_file(scp, "b", BUF_SIZE, 0644);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_scp_write(scp, expected_b, BUF_SIZE);
+ assert_ssh_return_code(session, rc);
+
+ /* Leave the directory */
+ rc = ssh_scp_leave_directory(scp);
+ assert_ssh_return_code(session, rc);
+
+ /* Cleanup */
+ ssh_scp_close(scp);
+ ssh_scp_free(scp);
+
+ /* Open file and check content */
+ snprintf(buf, BUF_SIZE, "%s/test_dir/b", ts->tmp_dir);
+
+ file = fopen(buf, "r");
+ assert_non_null(file);
+
+ fread(buf, BUF_SIZE, 1, file);
+ assert_memory_equal(buf, expected_b, BUF_SIZE);
+
+ fclose(file);
+}
+
+static void torture_scp_download(void **state)
+{
+ struct scp_st *ts = NULL;
+ struct torture_state *s = NULL;
+
+ ssh_session session = NULL;
+ ssh_scp scp = NULL;
+
+ char expected_a[BUF_SIZE];
+ char buf[BUF_SIZE];
+ const char *remote_file = NULL;
+
+ FILE *file = NULL;
+ int fd = 0;
+
+ size_t size;
+
+ int mode;
+ int rc;
+
+ assert_non_null(state);
+
+ ts = *state;
+
+ assert_non_null(ts);
+ assert_non_null(ts->s);
+
+ s = ts->s;
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ assert_non_null(ts->tmp_dir_basename);
+ assert_non_null(ts->tmp_dir);
+
+ /* Create file "a" for alice */
+ memset(expected_a, 'A', BUF_SIZE);
+
+ snprintf(buf, BUF_SIZE, "%s/a", ts->tmp_dir);
+
+ fd = open(buf, O_WRONLY | O_CREAT, 0644);
+ assert_true(fd > 0);
+
+ file = fdopen(fd, "w");
+ assert_non_null(file);
+
+ size = fwrite(expected_a, 1, BUF_SIZE, file);
+ assert_int_equal(size, BUF_SIZE);
+ fclose(file);
+
+ /* Construct the file path */
+ snprintf(buf, BUF_SIZE, "%s/a", ts->tmp_dir_basename);
+
+ /* When reading, the location is the file path */
+ scp = ssh_scp_new(session, SSH_SCP_READ, buf);
+ assert_non_null(scp);
+
+ rc = ssh_scp_init(scp);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_scp_pull_request(scp);
+ assert_int_equal(rc, SSH_SCP_REQUEST_NEWFILE);
+
+ size = ssh_scp_request_get_size(scp);
+ assert_int_equal(size, BUF_SIZE);
+
+ mode = ssh_scp_request_get_permissions(scp);
+ assert_int_equal(mode, 0644);
+
+ remote_file = ssh_scp_request_get_filename(scp);
+ assert_non_null(remote_file);
+ assert_string_equal(remote_file, "a");
+
+ rc = ssh_scp_accept_request(scp);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_scp_read(scp, buf, BUF_SIZE);
+ assert_int_equal(rc, size);
+
+ assert_memory_equal(expected_a, buf, BUF_SIZE);
+
+ /* Cleanup */
+ ssh_scp_close(scp);
+ ssh_scp_free(scp);
+}
+
+static void torture_scp_download_recursive(void **state)
+{
+ struct scp_st *ts = NULL;
+ struct torture_state *s = NULL;
+
+ ssh_session session = NULL;
+ ssh_scp scp = NULL;
+
+ char expected_b[BUF_SIZE];
+ char buf[BUF_SIZE];
+ const char *remote_file = NULL;
+ FILE *file = NULL;
+ int fd = 0;
+
+ size_t size;
+
+ int mode;
+ int rc;
+
+ assert_non_null(state);
+ ts = *state;
+
+ assert_non_null(ts->s);
+ s = ts->s;
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ assert_non_null(ts->tmp_dir_basename);
+ assert_non_null(ts->tmp_dir);
+
+ /* Create file "b" for alice */
+ memset(expected_b, 'B', BUF_SIZE);
+
+ snprintf(buf, BUF_SIZE, "%s/b", ts->tmp_dir);
+
+ fd = open(buf, O_WRONLY | O_CREAT, 0644);
+ assert_true(fd > 0);
+
+ file = fdopen(fd, "w");
+ assert_non_null(file);
+
+ size = fwrite(expected_b, 1, BUF_SIZE, file);
+ assert_int_equal(size, BUF_SIZE);
+ fclose(file);
+
+ /* Copy the directory containing the file "b" */
+ scp = ssh_scp_new(session, SSH_SCP_READ | SSH_SCP_RECURSIVE,
+ ts->tmp_dir_basename);
+ assert_non_null(scp);
+
+ rc = ssh_scp_init(scp);
+ assert_ssh_return_code(session, rc);
+
+ /* Receive the directory */
+ rc = ssh_scp_pull_request(scp);
+ assert_int_equal(rc, SSH_SCP_REQUEST_NEWDIR);
+
+ mode = ssh_scp_request_get_permissions(scp);
+ assert_int_equal(mode, 0700);
+
+ remote_file = ssh_scp_request_get_filename(scp);
+ assert_non_null(remote_file);
+ assert_string_equal(remote_file, ts->tmp_dir_basename);
+
+ rc = ssh_scp_accept_request(scp);
+ assert_ssh_return_code(session, rc);
+
+ /* Receive the file "b" */
+ rc = ssh_scp_pull_request(scp);
+ assert_int_equal(rc, SSH_SCP_REQUEST_NEWFILE);
+
+ size = ssh_scp_request_get_size(scp);
+ assert_int_equal(size, BUF_SIZE);
+
+ mode = ssh_scp_request_get_permissions(scp);
+ assert_int_equal(mode, 0644);
+
+ remote_file = ssh_scp_request_get_filename(scp);
+ assert_non_null(remote_file);
+ assert_string_equal(remote_file, "b");
+
+ rc = ssh_scp_accept_request(scp);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_scp_read(scp, buf, BUF_SIZE);
+ assert_int_equal(rc, size);
+
+ /* Check if the content was the expected */
+ assert_memory_equal(expected_b, buf, BUF_SIZE);
+
+ /* Receive end of directory */
+ rc = ssh_scp_pull_request(scp);
+ assert_int_equal(rc, SSH_SCP_REQUEST_ENDDIR);
+
+ /* Receive end of communication */
+ rc = ssh_scp_pull_request(scp);
+ assert_int_equal(rc, SSH_SCP_REQUEST_EOF);
+
+ /* Cleanup */
+ ssh_scp_close(scp);
+ ssh_scp_free(scp);
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_scp_upload,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_scp_upload_recursive,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_scp_download,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_scp_download_recursive,
+ session_setup,
+ session_teardown)
+ };
+
+ ssh_init();
+
+ torture_filter_tests(tests);
+ rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
+ ssh_finalize();
+
+ return rc;
+}