From 329d53a1094c599d927d9034d6b1617ea1a07f27 Mon Sep 17 00:00:00 2001 From: tatataeki Date: Tue, 6 Sep 2022 15:50:41 +0800 Subject: [PATCH] tests: add sftp server test Signed-off-by: tatataeki Reviewed-by: Andreas Schneider Reviewed-by: Anderson Toshiyuki Sasaki --- tests/server/CMakeLists.txt | 1 + tests/server/test_server/CMakeLists.txt | 3 +- tests/server/test_server/default_cb.h | 2 + tests/server/test_server/sftpserver_cb.c | 1338 ++++++++++++++++++++++ tests/server/torture_sftpserver.c | 482 ++++++++ 5 files changed, 1825 insertions(+), 1 deletion(-) create mode 100644 tests/server/test_server/sftpserver_cb.c create mode 100644 tests/server/torture_sftpserver.c diff --git a/tests/server/CMakeLists.txt b/tests/server/CMakeLists.txt index 96457cd2..f741e95a 100644 --- a/tests/server/CMakeLists.txt +++ b/tests/server/CMakeLists.txt @@ -11,6 +11,7 @@ set(LIBSSH_SERVER_TESTS torture_server_auth_kbdint torture_server_config torture_server_algorithms + torture_sftpserver ) include_directories(${libssh_SOURCE_DIR}/include diff --git a/tests/server/test_server/CMakeLists.txt b/tests/server/test_server/CMakeLists.txt index 2f5a128d..f1453d43 100644 --- a/tests/server/test_server/CMakeLists.txt +++ b/tests/server/test_server/CMakeLists.txt @@ -10,7 +10,8 @@ set(server_SRCS add_library(testserver STATIC test_server.c - default_cb.c) + default_cb.c + sftpserver_cb.c) set(LIBSSH_SERVER_TESTS # torture_server_kbdint diff --git a/tests/server/test_server/default_cb.h b/tests/server/test_server/default_cb.h index ecfd6515..529c71e1 100644 --- a/tests/server/test_server/default_cb.h +++ b/tests/server/test_server/default_cb.h @@ -24,6 +24,7 @@ #include "config.h" #include +#include #include #define SSHD_DEFAULT_USER "libssh" @@ -70,6 +71,7 @@ struct channel_data_st { void *server_state; /* This pointer is useful to set data for custom callbacks */ void *extra_data; + sftp_session sftp; }; /* A userdata struct for session. */ diff --git a/tests/server/test_server/sftpserver_cb.c b/tests/server/test_server/sftpserver_cb.c new file mode 100644 index 00000000..ae6c497d --- /dev/null +++ b/tests/server/test_server/sftpserver_cb.c @@ -0,0 +1,1338 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2018 by Red Hat, Inc. + * + * Author: Anderson Toshiyuki Sasaki + * + * 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 "test_server.h" +#include "default_cb.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBUTIL_H +#include +#endif +#ifdef HAVE_PTY_H +#include +#endif +#ifdef HAVE_UTMP_H +#include +#endif +#ifdef HAVE_UTIL_H +#include +#endif + +/* below are for sftp */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define BUF_SIZE 1048576 +#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR) + +#define MAX_HANDLE_NUM 10 +#define MAX_ENTRIES_NUM_IN_PACKET 50 +#define MAX_LONG_NAME_LEN 150 + +#define SSH_SFTP_CALLBACK(name) \ + static int name (sftp_client_message message) + +typedef int (*client_message_callback) (sftp_client_message message); + +struct message_handler{ + const char *name; + const char *extended_name; + u_int type; + + client_message_callback cb; +}; + +struct sftp_handle { + uint8_t type; + int fd; + DIR *dirp; + char *name; + void *session_id; +}; + +enum handle_type { + NULL_HANDLE, + DIR_HANDLE, + FILE_HANDLE +}; + +SSH_SFTP_CALLBACK(process_unsupposed); +SSH_SFTP_CALLBACK(process_open); +SSH_SFTP_CALLBACK(process_read); +SSH_SFTP_CALLBACK(process_write); +SSH_SFTP_CALLBACK(process_close); +SSH_SFTP_CALLBACK(process_opendir); +SSH_SFTP_CALLBACK(process_readdir); +SSH_SFTP_CALLBACK(process_rmdir); +SSH_SFTP_CALLBACK(process_mkdir); +SSH_SFTP_CALLBACK(process_lstat); +SSH_SFTP_CALLBACK(process_readlink); +SSH_SFTP_CALLBACK(process_symlink); +SSH_SFTP_CALLBACK(process_remove); +SSH_SFTP_CALLBACK(process_extended_statvfs); + +const struct message_handler message_handlers[] = { + { "open", NULL, SSH_FXP_OPEN, process_open}, + { "close", NULL, SSH_FXP_CLOSE, process_close}, + { "read", NULL, SSH_FXP_READ, process_read}, + { "write", NULL, SSH_FXP_WRITE, process_write}, + { "lstat", NULL, SSH_FXP_LSTAT, process_lstat}, + { "fstat", NULL, SSH_FXP_FSTAT, process_unsupposed}, + { "setstat", NULL, SSH_FXP_SETSTAT, process_unsupposed}, + { "fsetstat", NULL, SSH_FXP_FSETSTAT, process_unsupposed}, + { "opendir", NULL, SSH_FXP_OPENDIR, process_opendir}, + { "readdir", NULL, SSH_FXP_READDIR, process_readdir}, + { "remove", NULL, SSH_FXP_REMOVE, process_remove}, + { "mkdir", NULL, SSH_FXP_MKDIR, process_mkdir}, + { "rmdir", NULL, SSH_FXP_RMDIR, process_rmdir}, + { "realpath", NULL, SSH_FXP_REALPATH, process_unsupposed}, + { "stat", NULL, SSH_FXP_STAT, process_unsupposed}, + { "rename", NULL, SSH_FXP_RENAME, process_unsupposed}, + { "readlink", NULL, SSH_FXP_READLINK, process_readlink}, + { "symlink", NULL, SSH_FXP_SYMLINK, process_symlink}, + { "init", NULL, SSH_FXP_INIT, sftp_process_init_packet}, + { NULL, NULL, 0, NULL} +}; + +const struct message_handler extended_handlers[] = { + /* here are some extended type handlers */ + { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs}, + { NULL, NULL, 0, NULL} +}; + +struct sftp_handle s_handle_table[MAX_HANDLE_NUM]; + +void init_handle_table(int handle_num) { + int obj_size = sizeof(struct sftp_handle); + memset(s_handle_table, 0, obj_size*handle_num); +} + +static void reinit_single_handle(struct sftp_handle* handle) { + handle->type = NULL_HANDLE; + handle->session_id = NULL; + handle->dirp = NULL; + handle->name = NULL; + handle->fd = -1; +} + +static int handle_is_ok(int i, int type) { + return i >= 0 && (u_int)i < MAX_HANDLE_NUM && s_handle_table[i].type == type; +} + +static int handle_close(int handle_ind) { + int ret = SSH_ERROR; + + if (handle_is_ok(handle_ind, FILE_HANDLE)) { + close(s_handle_table[handle_ind].fd); + ret = SSH_OK; + } else if (handle_is_ok(handle_ind, DIR_HANDLE)) { + closedir((DIR *)s_handle_table[handle_ind].dirp); + ret = SSH_OK; + } else if (handle_is_ok(handle_ind, NULL_HANDLE)) { + ret = SSH_OK; + } + + if (s_handle_table[handle_ind].name != NULL) { + free(s_handle_table[handle_ind].name); + s_handle_table[handle_ind].name = NULL; + } + + return ret; +} + +static int handle_close_by_pointer(struct sftp_handle* handle) { + if(handle->type == NULL_HANDLE) + return -1; + + if(handle->fd > 0) + close(handle->fd); + + if(handle->dirp!=NULL) + closedir(handle->dirp); + + if(handle->name!=NULL){ + free(handle->name); + handle->name = NULL; + } + + return 0; +} + +static void free_handles(int handle_num) { + + for(int i = 0; i < handle_num; i++) { + handle_close(i); + //reinit this handle + reinit_single_handle(&s_handle_table[i]); + } + return; +} + +static int add_handle(int type, void *dirp, int fd, const char *name, void *session_id) { + int ret = SSH_ERROR; + if(dirp == NULL && fd < 0){ + return ret; + } + + for(int i = 0; iname!=NULL) + ret = handle->name; + + return ret; +} + +static const char* ssh_str_error(int u_errno) { + switch (u_errno) + { + case SSH_FX_NO_SUCH_FILE: + return "No such file"; + case SSH_FX_PERMISSION_DENIED: + return "Permission denied"; + case SSH_FX_BAD_MESSAGE: + return "Bad message"; + case SSH_FX_OP_UNSUPPORTED: + return "Operation not supported"; + default: + return "Operation failed"; + } + return "Operation failed"; +} + +static int unix_errno_to_ssh_stat(int u_errno) { + int ret = SSH_OK; + switch (u_errno) + { + case 0: + break; + case ENOENT: + case ENOTDIR: + case EBADF: + case ELOOP: + { + ret = SSH_FX_NO_SUCH_FILE; + break; + } + case EPERM: + case EACCES: + case EFAULT: + { + ret = SSH_FX_PERMISSION_DENIED; + break; + } + case ENAMETOOLONG: + case EINVAL: + { + ret = SSH_FX_BAD_MESSAGE; + break; + } + case ENOSYS: + { + ret = SSH_FX_OP_UNSUPPORTED; + break; + } + default: + { + ret = SSH_FX_FAILURE; + break; + } + } + + return ret; +} + +static void stat_to_filexfer_attrib(const struct stat* z_st, struct sftp_attributes_struct* z_attr) +{ + z_attr->flags = 0; + z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_SIZE; + z_attr->size = z_st->st_size; + + z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_UIDGID; + z_attr->uid = z_st->st_uid; + z_attr->gid = z_st->st_gid; + + z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS; + z_attr->permissions = z_st->st_mode; + + z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_ACMODTIME; + z_attr->atime = z_st->st_atime; + z_attr->mtime = z_st->st_mtime; +} + +static void clear_filexfer_attrib(struct sftp_attributes_struct* z_attr) +{ + z_attr->flags = 0; + z_attr->size = 0; + z_attr->uid = 0; + z_attr->gid = 0; + z_attr->permissions = 0; + z_attr->atime = 0; + z_attr->mtime = 0; +} + +static int readdir_long_name(char* z_file_name, struct stat* z_st, char* z_long_name) +{ + char tmpbuf[MAX_LONG_NAME_LEN]; + char time[50]; + char* ptr = z_long_name; + int mode = z_st->st_mode; + + *ptr = '\0'; + + switch(mode & S_IFMT) + { + case S_IFDIR: + { + *ptr++ = 'd'; + break; + } + default: + { + *ptr++ = '-'; + break; + } + } + + /* user */ + if(mode & 0400) + *ptr++ = 'r'; + else + *ptr++ ='-'; + + if(mode & 0200) + *ptr++ = 'w'; + else + *ptr++ = '-'; + + if(mode & 0100) { + if(mode & S_ISUID) + *ptr++ = 's'; + else + *ptr++ = 'x'; + } else + *ptr++ = '-'; + + /* group */ + if(mode & 040) + *ptr++ = 'r'; + else + *ptr++ = '-'; + if(mode & 020) + *ptr++ = 'w'; + else + *ptr++ ='-'; + if(mode & 010) + *ptr++ = 'x'; + else + *ptr++ = '-'; + + /* other */ + if(mode & 04) + *ptr++ = 'r'; + else + *ptr++ = '-'; + if(mode & 02) + *ptr++ = 'w'; + else + *ptr++ = '-'; + if(mode & 01) + *ptr++ = 'x'; + else + *ptr++ = '-'; + + *ptr++ = ' '; + *ptr = '\0'; + + snprintf(tmpbuf, sizeof(tmpbuf),"%3d %d %d %d", (int)z_st->st_nlink, + (int)z_st->st_uid, (int)z_st->st_gid, (int)z_st->st_size); + strcat(z_long_name, tmpbuf); + + ctime_r(&z_st->st_mtime, time); + if((ptr = strchr(time,'\n'))) + { + *ptr = '\0'; + } + snprintf(tmpbuf,sizeof(tmpbuf)," %s %s", time + 4, z_file_name); + strcat(z_long_name, tmpbuf); + + return SSH_OK; +} + +static int process_open(sftp_client_message client_msg) { + int ret = SSH_OK; + const char *filename = sftp_client_message_get_filename(client_msg); + uint32_t msg_flag = sftp_client_message_get_flags(client_msg); + int file_flag; + int fd = -1; + int handle_ind = -1; + int status; + + if (( (msg_flag&(uint32_t)SSH_FXF_READ) == SSH_FXF_READ) && + ( (msg_flag&(uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE)) { + file_flag = O_RDWR; //file must exist + if ((msg_flag & (uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT) + file_flag |= O_CREAT; + } else if ( (msg_flag&(uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE ){ + file_flag = O_WRONLY; + if ( (msg_flag&(uint32_t)SSH_FXF_APPEND) == SSH_FXF_APPEND ) + file_flag |= O_APPEND; + if ( (msg_flag&(uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT ) + file_flag |= O_CREAT; + } else if ( (msg_flag&(uint32_t)SSH_FXF_READ) == SSH_FXF_READ ){ + file_flag = O_RDONLY; + } + + fd = open(filename, file_flag, 0600); + if(fd == -1){ + status = unix_errno_to_ssh_stat(errno); + printf("error open file with error: %d\n", errno); + sftp_reply_status(client_msg, status, "Write error"); + ret = SSH_ERROR; + return ret; + } + + handle_ind = add_handle(FILE_HANDLE, NULL, fd, filename, client_msg->sftp); + if(handle_ind >= 0){ + void *handle_ptr = &s_handle_table[handle_ind]; + ssh_string handle_s = sftp_handle_alloc(client_msg->sftp, handle_ptr); + sftp_reply_handle(client_msg, handle_s); + ssh_string_free(handle_s); + ret = SSH_OK; + }else { + close(fd); + printf("opening file failed"); + sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available"); + } + + return ret; +} + +static int process_read(sftp_client_message client_msg) { + int ret = SSH_OK; + struct sftp_handle *client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp, client_msg->handle); + uint32_t readn; + int fd; + char *buffer; + int rv; + + if (client_handle == NULL || client_handle->session_id != client_msg->sftp) { + printf("get wrong handle from msg\n"); + sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL); + return SSH_ERROR; + } + + fd = client_handle->fd; + + if (fd < 0) { + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + printf("error reading file fd: %d\n", fd); + ret = SSH_ERROR; + return ret; + } + rv = lseek(fd, client_msg->offset, SEEK_SET); + if (rv == -1) { + sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL); + printf("error seeking file fd: %d at offset: %ld\n", fd, client_msg->offset); + ret = SSH_ERROR; + return ret; + } + + buffer = malloc((client_msg->len)*sizeof(char)); + readn = read(fd, buffer, client_msg->len); + + if(readn > 0){ + sftp_reply_data(client_msg, buffer, readn); + }else if(readn == 0) { + sftp_reply_status(client_msg, SSH_FX_EOF, "EOF encountered"); + }else { + sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL); + printf("read file error!\n"); + } + return ret; +} + +static int process_write(sftp_client_message client_msg) { + int ret = SSH_OK; + struct sftp_handle *client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp, client_msg->handle); + uint32_t writen; + int fd; + const char *msg_data; + uint32_t len; + int rv; + + if (client_handle == NULL || client_handle->session_id != client_msg->sftp) { + printf("get wrong handle from msg\n"); + sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL); + return SSH_ERROR; + } + + fd = client_handle->fd; + + if (fd < 0) { + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + printf("write file fd error!\n"); + ret = SSH_ERROR; + return ret; + } + + msg_data = ssh_string_get_char(client_msg->data); + len = ssh_string_len(client_msg->data); + + rv = lseek(fd, client_msg->offset, SEEK_SET); + if (rv == -1) { + sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL); + printf("error seeking file at offset: %ld\n", client_msg->offset); + } + writen = write(fd, msg_data, len); + if(writen == len) { + sftp_reply_status(client_msg, SSH_FX_OK, NULL); + }else { + sftp_reply_status(client_msg, SSH_FX_FAILURE, "Write error"); + } + + return ret; +} + +static int process_close(sftp_client_message client_msg) { + int ret = SSH_OK; + struct sftp_handle *client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp, client_msg->handle); + + if (client_handle == NULL) { + printf("get wrong handle from msg\n"); + return SSH_ERROR; + } + + ret = handle_close_by_pointer(client_handle); + if (ret == SSH_OK) { + reinit_single_handle(client_handle); + sftp_reply_status(client_msg, SSH_FX_OK, NULL); + } else { + printf("closing file failed\n"); + sftp_reply_status(client_msg, SSH_FX_BAD_MESSAGE, "Invalid handle"); + } + + return ret; +} + +static int process_opendir(sftp_client_message client_msg) { + int ret = SSH_OK; + DIR *dir = NULL; + const char *dir_name = sftp_client_message_get_filename(client_msg); + int handle_ind = -1; + + dir = opendir(dir_name); + if (dir == NULL) { + sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "No such directory"); + return SSH_ERROR; + } + + handle_ind = add_handle(DIR_HANDLE, dir, -1, dir_name, client_msg->sftp); + if (handle_ind >= 0) { + ssh_string handle_s = sftp_handle_alloc(client_msg->sftp, &s_handle_table[handle_ind]); + sftp_reply_handle(client_msg, handle_s); + ssh_string_free(handle_s); + ret = SSH_OK; + } else { + closedir(dir); + sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available"); + } + + return ret; +} + +static int process_readdir(sftp_client_message client_msg) { + int ret = SSH_OK; + struct sftp_handle *client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp, client_msg->handle); + int entries = 0; + struct dirent *dentry; + DIR *dir = NULL; + + char long_path[PATH_MAX]; + int path_length; + char *handle_name; + + if (client_handle == NULL || client_handle->session_id != client_msg->sftp) { + printf("get wrong handle from msg\n"); + sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL); + return SSH_ERROR; + } + + dir = client_handle->dirp; + if (dir == NULL) { + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + printf("read dir handle error!\n"); + ret = SSH_ERROR; + return ret; + } + + ret = SSH_OK; + handle_name = get_handle_name(client_handle); + if (handle_name == NULL) { + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + return SSH_ERROR; + } + strcpy(long_path, handle_name); + strcat(long_path, "/"); + path_length = (int)strlen(long_path); + + for(int i=0; i < MAX_ENTRIES_NUM_IN_PACKET; i++) { + dentry = readdir(dir); + + if (dentry!=NULL) { + struct sftp_attributes_struct attr; + struct stat st; + char long_name[MAX_LONG_NAME_LEN]; + + strcpy(&long_path[path_length], dentry->d_name); + + if(lstat(long_path, &st) == 0) { + stat_to_filexfer_attrib(&st, &attr); + } + else { + clear_filexfer_attrib(&attr); + } + + if(readdir_long_name(dentry->d_name, &st, long_name) == 0){ + sftp_reply_names_add(client_msg, dentry->d_name, long_name, &attr); + } else { + printf("readdir long name error\n"); + } + + entries++; + } else { + break; + } + } + + if (entries > 0) { + ret = sftp_reply_names(client_msg); + } else { + sftp_reply_status(client_msg, SSH_FX_EOF, NULL); + } + + return ret; +} + +static int process_mkdir(sftp_client_message client_msg) { + int ret = SSH_OK; + const char *filename = sftp_client_message_get_filename(client_msg); + uint32_t msg_flags = client_msg->flags; + uint32_t permission = client_msg->attr->permissions; + uint32_t mode = (msg_flags & (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS) ? permission & (uint32_t)07777 : 0777; + int status = SSH_FX_OK; + int rv; + + if (filename==NULL) { + sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error"); + return SSH_ERROR; + } + + rv = mkdir(filename, mode); + if (rv < 0) { + status = unix_errno_to_ssh_stat(errno); + ret = SSH_ERROR; + } + + sftp_reply_status(client_msg, status, NULL); + + return ret; +} + +static int process_rmdir(sftp_client_message client_msg) { + int ret = SSH_OK; + const char *filename = sftp_client_message_get_filename(client_msg); + int status = SSH_FX_OK; + int rv; + + if (filename==NULL) { + sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error"); + return SSH_ERROR; + } + + rv = rmdir(filename); + if (rv < 0) { + status = unix_errno_to_ssh_stat(errno); + ret = SSH_ERROR; + } + + sftp_reply_status(client_msg, status, NULL); + + return ret; +} + +static int process_lstat(sftp_client_message client_msg) { + int ret = SSH_OK; + const char *filename = sftp_client_message_get_filename(client_msg); + struct sftp_attributes_struct attr; + struct stat st; + int status = SSH_FX_OK; + int rv; + + if (filename==NULL) { + sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error"); + return SSH_ERROR; + } + + rv = lstat(filename, &st); + if (rv < 0) { + status = unix_errno_to_ssh_stat(errno); + sftp_reply_status(client_msg, status, NULL); + ret = SSH_ERROR; + } else { + stat_to_filexfer_attrib(&st, &attr); + sftp_reply_attr(client_msg, &attr); + } + + return ret; +} + +static int process_readlink(sftp_client_message client_msg) { + int ret = SSH_OK; + const char *filename = sftp_client_message_get_filename(client_msg); + char buf[PATH_MAX]; + int len = -1; + const char *err_msg; + int status = SSH_FX_OK; + + if (filename==NULL) { + sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error"); + return SSH_ERROR; + } + + len = readlink(filename, buf, sizeof(buf) - 1); + if (len < 0) { + printf("read link error with reason: %d\n", errno); + status = unix_errno_to_ssh_stat(errno); + err_msg = ssh_str_error(status); + sftp_reply_status(client_msg, status, err_msg); + ret = SSH_ERROR; + } else { + buf[len] = '\0'; + sftp_reply_name(client_msg, buf, NULL); + } + + return ret; +} + +static int process_symlink(sftp_client_message client_msg) { + int ret = SSH_OK; + const char *destpath = sftp_client_message_get_filename(client_msg); + const char *srcpath = ssh_string_get_char(client_msg->data); + int status = SSH_FX_OK; + int rv; + // printf("try to create link with src: %s and dest: %s \n", srcpath, destpath); + + if (srcpath == NULL || destpath == NULL) { + sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error"); + return SSH_ERROR; + } + + rv = symlink(srcpath, destpath); + if (rv < 0) { + status = unix_errno_to_ssh_stat(errno); + printf("error symlink with error: %d\n", errno); + sftp_reply_status(client_msg, status, "Write error"); + ret = SSH_ERROR; + } else { + sftp_reply_status(client_msg, SSH_FX_OK, "write success"); + } + + return ret; +} + +static int process_remove(sftp_client_message client_msg) { + int ret = SSH_OK; + const char *filename = sftp_client_message_get_filename(client_msg); + int rv; + int status = SSH_FX_OK; + + rv = unlink(filename); + if (rv < 0) { + printf("unlink error with reason: %d\n", errno); + status = unix_errno_to_ssh_stat(errno); + ret = SSH_ERROR; + } + + sftp_reply_status(client_msg, status, NULL); + + return ret; +} + +static int process_unsupposed(sftp_client_message client_msg) { + int ret = SSH_OK; + sftp_reply_status(client_msg, SSH_FX_OP_UNSUPPORTED, "Operation not supported"); + printf("Message type %d not implemented\n", sftp_client_message_get_type(client_msg)); + return ret; +} + +static int process_extended_statvfs(sftp_client_message client_msg) { + int ret = SSH_OK; + const char *path = sftp_client_message_get_filename(client_msg); + struct statvfs st; + int status; + int rv; + + rv = statvfs(path, &st); + if (rv == 0) { + sftp_statvfs_t sftp_statvfs; + u_int64_t flag; + + sftp_statvfs = calloc(1, sizeof(struct sftp_statvfs_struct)); + if (sftp_statvfs == NULL) { + goto error; + } + + flag = (st.f_flag & ST_RDONLY) ? SSH_FXE_STATVFS_ST_RDONLY : 0; + flag |= (st.f_flag & ST_NOSUID) ? SSH_FXE_STATVFS_ST_NOSUID : 0; + + sftp_statvfs->f_bsize = st.f_bsize; + sftp_statvfs->f_frsize = st.f_frsize; + sftp_statvfs->f_blocks = st.f_blocks; + sftp_statvfs->f_bfree = st.f_bfree; + sftp_statvfs->f_bavail = st.f_bavail; + sftp_statvfs->f_files = st.f_files; + sftp_statvfs->f_ffree = st.f_ffree; + sftp_statvfs->f_favail = st.f_favail; + sftp_statvfs->f_fsid = st.f_fsid; + sftp_statvfs->f_flag = flag; + sftp_statvfs->f_namemax = st.f_namemax; + + if(sftp_reply_statvfs(client_msg, sftp_statvfs) == 0) + return ret; + } + +error: + status = unix_errno_to_ssh_stat(errno); + ret = SSH_ERROR; + sftp_reply_status(client_msg, status, NULL); + + printf("statvfs send failed!\n"); + return ret; +} + +static int process_extended(sftp_client_message sftp_msg) { + int status = SSH_ERROR; + + const char *subtype = sftp_msg->submessage; + client_message_callback handler = NULL; + for(int i=0;extended_handlers[i].cb!=NULL;i++){ + if (strcmp(subtype, extended_handlers[i].extended_name) == 0) { + handler = extended_handlers[i].cb; + break; + } + } + if (handler!=NULL) { + status = handler(sftp_msg); + return status; + } + + sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED, "Extended Operation not supported"); + printf("Extended Message type %s not implemented\n", subtype); + return SSH_OK; + + return status; +} + +static int dispatch_sftp_request(sftp_client_message sftp_msg) { + int status = SSH_ERROR; + client_message_callback handler = NULL; + u_int sft_msg_type = sftp_client_message_get_type(sftp_msg); + + for(int i=0;message_handlers[i].cb!=NULL;i++){ + if (sft_msg_type == message_handlers[i].type) { + handler = message_handlers[i].cb; + break; + } + } + if (handler!=NULL) + status = handler(sftp_msg); + else + goto not_implemented; + + return status; + +not_implemented: + sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED, "Operation not supported"); + printf("Message type %d not implemented\n", sft_msg_type); + return SSH_OK; +} + +static int process_client_message(sftp_client_message client_msg) { + int status = SSH_OK; + if (client_msg == NULL) { + return SSH_ERROR; + } + + switch(client_msg->type) { + case SSH_FXP_EXTENDED: + status = process_extended(client_msg); + break; + default: + status = dispatch_sftp_request(client_msg); + } + + // status = dispatch_sftp_request(client_msg); + if (status!=SSH_OK) + printf("error occur in process client message!\n"); + + return status; +} + +/* TODO implement proper pam authentication cb */ +static int sftp_auth_password_cb(UNUSED_PARAM(ssh_session session), + const char *user, + const char *password, + void *userdata) +{ + bool known_user = false; + bool valid_password = false; + + struct session_data_st *sdata; + + sdata = (struct session_data_st *)userdata; + + if (sdata == NULL) { + fprintf(stderr, "Error: NULL userdata\n"); + goto null_userdata; + } + + if (sdata->username == NULL) { + fprintf(stderr, "Error: expected username not set\n"); + goto denied; + } + + if (sdata->password == NULL) { + fprintf(stderr, "Error: expected password not set\n"); + goto denied; + } + + printf("Password authentication of user %s\n", user); + + known_user = !(strcmp(user, sdata->username)); + valid_password = !(strcmp(password, sdata->password)); + + if (known_user && valid_password) { + sdata->authenticated = 1; + sdata->auth_attempts = 0; + printf("Authenticated\n"); + return SSH_AUTH_SUCCESS; + } + +denied: + sdata->auth_attempts++; +null_userdata: + return SSH_AUTH_DENIED; +} + + +static int sftp_channel_data_cb(UNUSED_PARAM(ssh_session session), + UNUSED_PARAM(ssh_channel channel), + void *data, + uint32_t len, + UNUSED_PARAM(int is_stderr), + void *userdata) +{ + struct channel_data_st *cdata = (struct channel_data_st *) userdata; + sftp_session sftp = cdata->sftp; + sftp_client_message msg; + int decode_len; + int rc; + + decode_len = sftp_decode_channel_data_to_packet(sftp, data); + if(decode_len == -1) + return -1; + + msg = sftp_get_client_message_from_packet(cdata->sftp); + rc = process_client_message(msg); + sftp_client_message_free(msg); + if(rc != SSH_OK) + printf("process sftp failed!\n"); + + return decode_len; +} + +static int sftp_channel_subsystem_request_cb(ssh_session session, + ssh_channel channel, + const char *subsystem, + void *userdata) +{ + if (strcmp(subsystem, "sftp") == 0) { + struct channel_data_st* cdata = (struct channel_data_st*) userdata; + + /* initialize sftp session and file handler */ + cdata->sftp = sftp_server_new(session, channel); + init_handle_table(MAX_HANDLE_NUM); + + return SSH_OK; + } + return SSH_ERROR; +} + +static int sftp_channel_write_wontblock_cb(UNUSED_PARAM(ssh_session session), + UNUSED_PARAM(ssh_channel channel), + UNUSED_PARAM(size_t bytes), + UNUSED_PARAM(void *userdata)) +{ + /* TODO */ + + return 0; +} + +static ssh_channel sftp_channel_new_session_cb(ssh_session session, void *userdata) +{ + struct session_data_st *sdata = NULL; + ssh_channel chan = NULL; + + sdata = (struct session_data_st *)userdata; + + if (sdata == NULL) { + fprintf(stderr, "NULL userdata"); + goto end; + } + + chan = ssh_channel_new(session); + if (chan == NULL) { + fprintf(stderr, "Error creating channel: %s\n", + ssh_get_error(session)); + goto end; + } + + sdata->channel = chan; + +end: + return chan; +} + +#ifdef WITH_PCAP +static void set_pcap(struct session_data_st *sdata, + ssh_session session, + char *pcap_file) +{ + int rc = 0; + + if (sdata == NULL) { + return; + } + + if (pcap_file == NULL) { + return; + } + + sdata->pcap = ssh_pcap_file_new(); + if (sdata->pcap == NULL) { + return; + } + + rc = ssh_pcap_file_open(sdata->pcap, pcap_file); + if (rc == SSH_ERROR) { + fprintf(stderr, "Error opening pcap file\n"); + ssh_pcap_file_free(sdata->pcap); + sdata->pcap = NULL; + return; + } + ssh_set_pcap_file(session, sdata->pcap); +} + +static void cleanup_pcap(struct session_data_st *sdata) +{ + if (sdata == NULL) { + return; + } + + if (sdata->pcap == NULL) { + return; + } + + ssh_pcap_file_free(sdata->pcap); + sdata->pcap = NULL; +} +#endif + + +/* The caller is responsible to set the userdata to be provided to the callback + * The caller is responsible to free the allocated structure + * */ +struct ssh_server_callbacks_struct *get_sftp_server_cb(void) +{ + + struct ssh_server_callbacks_struct *cb; + + cb = (struct ssh_server_callbacks_struct *)calloc(1, + sizeof(struct ssh_server_callbacks_struct)); + + if (cb == NULL) { + fprintf(stderr, "Out of memory\n"); + goto end; + } + + cb->auth_password_function = sftp_auth_password_cb; + cb->channel_open_request_session_function = sftp_channel_new_session_cb; + +end: + return cb; +} + +/* The caller is responsible to set the userdata to be provided to the callback + * The caller is responsible to free the allocated structure + * */ +struct ssh_channel_callbacks_struct *get_sftp_channel_cb(void) +{ + struct ssh_channel_callbacks_struct *cb; + + cb = (struct ssh_channel_callbacks_struct *)calloc(1, + sizeof(struct ssh_channel_callbacks_struct)); + if (cb == NULL) { + fprintf(stderr, "Out of memory\n"); + goto end; + } + + cb->channel_subsystem_request_function = sftp_channel_subsystem_request_cb; + cb->channel_data_function = sftp_channel_data_cb; + +end: + return cb; +}; + +void sftp_handle_session_cb(ssh_event event, + ssh_session session, + struct server_state_st *state) +{ + int n; + int rc = 0; + + /* Structure for storing the pty size. */ + struct winsize wsize = { + .ws_row = 0, + .ws_col = 0, + .ws_xpixel = 0, + .ws_ypixel = 0 + }; + + /* Our struct holding information about the channel. */ + struct channel_data_st cdata = { + .pid = 0, + .pty_master = -1, + .pty_slave = -1, + .child_stdin = -1, + .child_stdout = -1, + .child_stderr = -1, + .event = NULL, + .winsize = &wsize, + .sftp = NULL + }; + + /* Our struct holding information about the session. */ + struct session_data_st sdata = { + .channel = NULL, + .auth_attempts = 0, + .authenticated = 0, + .username = SSHD_DEFAULT_USER, + .password = SSHD_DEFAULT_PASSWORD + }; + + struct ssh_channel_callbacks_struct *channel_cb = NULL; + struct ssh_server_callbacks_struct *server_cb = NULL; + + if (state == NULL) { + fprintf(stderr, "NULL server state provided\n"); + goto end; + } + + /* If callbacks were provided use them. Otherwise, use default callbacks */ + if (state->server_cb != NULL) { + /* This is a macro, it does not return a value */ + ssh_callbacks_init(state->server_cb); + + rc = ssh_set_server_callbacks(session, state->server_cb); + if (rc) { + goto end; + } + } else { + server_cb = get_sftp_server_cb(); + if (server_cb == NULL) { + goto end; + } + + server_cb->userdata = &sdata; + + /* This is a macro, it does not return a value */ + ssh_callbacks_init(server_cb); + + rc = ssh_set_server_callbacks(session, server_cb); + if (rc) { + goto end; + } + } + + sdata.server_state = (void *)state; + cdata.server_state = (void *)state; + +#ifdef WITH_PCAP + set_pcap(&sdata, session, state->pcap_file); +#endif + + if (state->expected_username != NULL) { + sdata.username = state->expected_username; + } + + if (state->expected_password != NULL) { + sdata.password = state->expected_password; + } + + if (ssh_handle_key_exchange(session) != SSH_OK) { + fprintf(stderr, "%s\n", ssh_get_error(session)); + return; + } + + /* Set the supported authentication methods */ + if (state->auth_methods) { + ssh_set_auth_methods(session, state->auth_methods); + } else { + ssh_set_auth_methods(session, + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_PUBLICKEY); + } + + ssh_event_add_session(event, session); + + n = 0; + while (sdata.authenticated == 0 || sdata.channel == NULL) { + /* If the user has used up all attempts, or if he hasn't been able to + * authenticate in 10 seconds (n * 100ms), disconnect. */ + if (sdata.auth_attempts >= state->max_tries || n >= 100) { + return; + } + + if (ssh_event_dopoll(event, 100) == SSH_ERROR) { + fprintf(stderr, "do_poll error: %s\n", ssh_get_error(session)); + return; + } + n++; + } + + /* TODO check return values */ + if (state->channel_cb != NULL) { + ssh_callbacks_init(state->channel_cb); + + rc = ssh_set_channel_callbacks(sdata.channel, state->channel_cb); + if (rc) { + goto end; + } + } else { + channel_cb = get_sftp_channel_cb(); + if (channel_cb == NULL) { + goto end; + } + + channel_cb->userdata = &cdata; + + ssh_callbacks_init(channel_cb); + rc = ssh_set_channel_callbacks(sdata.channel, channel_cb); + if (rc) { + goto end; + } + } + + do { + /* Poll the main event which takes care of the session, the channel and + * even our child process's stdout/stderr (once it's started). */ + if (ssh_event_dopoll(event, -1) == SSH_ERROR) { + ssh_channel_close(sdata.channel); + } + + /* If child process's stdout/stderr has been registered with the event, + * or the child process hasn't started yet, continue. */ + if (cdata.event != NULL || cdata.pid == 0) { + continue; + } + + } while(ssh_channel_is_open(sdata.channel) && + (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0)); + + free_handles(MAX_HANDLE_NUM); + + ssh_channel_send_eof(sdata.channel); + ssh_channel_close(sdata.channel); + + /* Wait up to 5 seconds for the client to terminate the session. */ + for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) { + ssh_event_dopoll(event, 100); + } + +end: +#ifdef WITH_PCAP + cleanup_pcap(&sdata); +#endif + if (channel_cb != NULL) { + free(channel_cb); + } + if (server_cb != NULL) { + free(server_cb); + } + return; +} diff --git a/tests/server/torture_sftpserver.c b/tests/server/torture_sftpserver.c new file mode 100644 index 00000000..263a6f50 --- /dev/null +++ b/tests/server/torture_sftpserver.c @@ -0,0 +1,482 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2018 by Red Hat, Inc. + * + * Author: Anderson Toshiyuki Sasaki + * + * 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" + +#define LIBSSH_STATIC + +#include +#include +#include +#include + +#include "torture.h" +#include "torture_key.h" +#include "libssh/libssh.h" +#include "libssh/priv.h" +#include "libssh/session.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 int setup_default_server(void **state) +{ + struct torture_state *s; + struct server_state_st *ss; + struct test_server_st *tss; +#ifdef HAVE_DSA + char dsa_hostkey[1024]; +#endif /* HAVE_DSA */ + + char ed25519_hostkey[1024] = {0}; + char rsa_hostkey[1024]; + char ecdsa_hostkey[1024]; + //char trusted_ca_pubkey[1024]; + + char sshd_path[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(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)); + +#ifdef HAVE_DSA + snprintf(dsa_hostkey, + sizeof(dsa_hostkey), + "%s/sshd/ssh_host_dsa_key", + s->socket_dir); + torture_write_file(dsa_hostkey, torture_get_testkey(SSH_KEYTYPE_DSS, 0)); +#endif /* HAVE_DSA */ + + 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)); + + // sftp_server = getenv("TORTURE_SFTP_SERVER"); + // if (sftp_server == NULL) { + // for (i = 0; i < sftp_sl_size; i++) { + // sftp_server = sftp_server_locations[i]; + // rc = lstat(sftp_server, &sb); + // if (rc == 0) { + // break; + // } + // } + // } + // assert_non_null(sftp_server); + + /* 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); + +#ifdef HAVE_DSA + ss->dsa_key = strdup(dsa_hostkey); + assert_non_null(ss->dsa_key); +#endif /* HAVE_DSA */ + + ss->ed25519_key = strdup(ed25519_hostkey); + assert_non_null(ed25519_hostkey); + + 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; + + ss->verbosity = torture_libssh_verbosity(); + + 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; + + /* 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); + 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); + + /* TODO properly wait for the server (use ping approach) */ + /* Wait 200ms */ + usleep(200 * 1000); + + tss->state = s; + tss->ss = ss; + + *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 *cwd = NULL; + char *tmp_dir = 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); + 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); + + 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_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); + + 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; + sftp = tsftp->sftp; + + 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); +} + + +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 fichier; + 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); + + rv_str = ssh_get_issue_banner(session); + + /* init sftp session */ + tsftp = s->ssh.tsftp; + sftp = tsftp->sftp; + sftp = sftp_new(session); + assert_non_null(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); + + 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 */ + fichier = sftp_open(sftp, "/usr/bin/ssh", O_RDONLY, 0); + assert_non_null(fichier); + + to = sftp_open(sftp, "ssh-copy", O_WRONLY | O_CREAT, 0700); + assert_non_null(fichier); + + read_len = sftp_read(fichier, data, 4096); + write_len = sftp_write(to, data, read_len); + assert_int_equal(write_len, read_len); + + rc = sftp_close(fichier); + 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); +} + +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), + }; + + ssh_init(); + + torture_filter_tests(tests); + rc = cmocka_run_group_tests(tests, + setup_default_server, + teardown_default_server); + + ssh_finalize(); + + return rc; +}