diff --git a/examples/sample_sftpserver.c b/examples/sample_sftpserver.c index a7eb7963..3437f544 100644 --- a/examples/sample_sftpserver.c +++ b/examples/sample_sftpserver.c @@ -16,6 +16,7 @@ The goal is to show the API in action. #include #include #include +#include #include #ifdef HAVE_ARGP_H @@ -63,990 +64,6 @@ The goal is to show the API in action. #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 350 - -#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]; - -static void init_handle_table(void) -{ - int obj_size = sizeof(struct sftp_handle); - memset(s_handle_table, 0, obj_size * MAX_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(uint8_t i, int type) -{ - return i < MAX_HANDLE_NUM && s_handle_table[i].type == type; -} - -static int handle_close(uint8_t 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(void) -{ - uint8_t i; - for (i = 0; i < MAX_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 = -1; - uint8_t i; - if (dirp == NULL && fd < 0) - { - return ret; - } - - for (i = 0; i < MAX_HANDLE_NUM; i++) - { - if (s_handle_table[i].type == NULL_HANDLE) - { - s_handle_table[i].type = type; - s_handle_table[i].session_id = session_id; - s_handle_table[i].fd = fd; - s_handle_table[i].dirp = dirp; - s_handle_table[i].name = malloc((strlen(name) + 1) * sizeof(char)); - strcpy(s_handle_table[i].name, name); - - ret = i; - break; - } - } - - if (ret == SSH_ERROR) - printf("no other space for new handle\n"); - - return ret; -} - -static char *get_handle_name(struct sftp_handle *handle) -{ - if (handle != NULL && handle->name != NULL) - return handle->name; - - return NULL; -} - -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 | (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) -{ - 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; - } - else - { - printf("undefined message flag\n"); - sftp_reply_status(client_msg, SSH_FX_FAILURE, "Flag error"); - return SSH_ERROR; - } - - fd = open(filename, file_flag, 0600); - if (fd == -1) - { - status = unix_errno_to_ssh_stat(errno); - printf("error opening file with error: %d\n", errno); - sftp_reply_status(client_msg, status, "Write error"); - return SSH_ERROR; - } - - 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); - } - else - { - close(fd); - printf("opening file failed"); - sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available"); - } - - return SSH_OK; -} - -static int process_read(sftp_client_message client_msg) -{ - 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("got 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); - return SSH_ERROR; - } - 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: %" PRIu64 "\n", fd, client_msg->offset); - return SSH_ERROR; - } - - buffer = malloc(client_msg->len); - 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 SSH_ERROR; - } - - free(buffer); - return SSH_OK; -} - -static int process_write(sftp_client_message client_msg) -{ - struct sftp_handle *client_handle = (struct sftp_handle *)sftp_handle(client_msg->sftp, client_msg->handle); - int written; - 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"); - return SSH_ERROR; - } - - 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: %" PRIu64 "\n", client_msg->offset); - } - written = write(fd, msg_data, len); - if (written == (int)len) - { - sftp_reply_status(client_msg, SSH_FX_OK, NULL); - } - else if (written == -1) - { - sftp_reply_status(client_msg, SSH_FX_FAILURE, "Write error"); - } - else - { - sftp_reply_status(client_msg, SSH_FX_FAILURE, "Partial write"); - } - - return SSH_OK; -} - -static int process_close(sftp_client_message client_msg) -{ - struct sftp_handle *client_handle = (struct sftp_handle *)sftp_handle(client_msg->sftp, client_msg->handle); - int ret; - - if (client_handle == NULL) - { - printf("get wrong handle from msg\n"); - return SSH_ERROR; - } - - ret = handle_close_by_pointer(client_handle); - reinit_single_handle(client_handle); - if (ret == SSH_OK) - { - 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 SSH_OK; -} - -static int process_opendir(sftp_client_message client_msg) -{ - 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); - } - else - { - closedir(dir); - sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available"); - } - - return SSH_OK; -} - -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 srclen; - 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"); - return SSH_ERROR; - } - - handle_name = get_handle_name(client_handle); - if (handle_name == NULL) - { - sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); - return SSH_ERROR; - } - - srclen = strlen(handle_name); - if (srclen + 2 >= PATH_MAX) - { - printf("handle string length exceed max length!\n"); - sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); - return SSH_ERROR; - } - - 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]; - - if (strlen(dentry->d_name) + srclen + 1 >= PATH_MAX) - { - printf("handle string length exceed max length!\n"); - sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); - return SSH_ERROR; - } - snprintf(long_path, PATH_MAX, "%s/%s", handle_name, 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) -{ - 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 SSH_OK; -} - -static int process_extended_statvfs(sftp_client_message client_msg) -{ - 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) - { - 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; - - rv = sftp_reply_statvfs(client_msg, sftp_statvfs); - free(sftp_statvfs); - if (rv == 0) - { - return SSH_OK; - } - } - } - - status = unix_errno_to_ssh_stat(errno); - sftp_reply_status(client_msg, status, NULL); - - printf("statvfs send failed!\n"); - return SSH_ERROR; -} - -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; -} - -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 - { - 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; - } - - return status; -} - -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); - } - - if (status != SSH_OK) - printf("error occur in process client message!\n"); - - return status; -} - static void set_default_keys(ssh_bind sshbind, int rsa_already_set, int dsa_already_set, @@ -1229,50 +246,13 @@ struct session_data_struct int authenticated; }; -static int data_function(ssh_session session, ssh_channel channel, void *data, - uint32_t len, int is_stderr, void *userdata) -{ - struct channel_data_struct *cdata = (struct channel_data_struct *)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(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 subsystem_request(ssh_session session, ssh_channel channel, - const char *subsystem, void *userdata) -{ - /* subsystem requests behave simillarly to exec requests. */ - if (strcmp(subsystem, "sftp") == 0) - { - struct channel_data_struct *cdata = (struct channel_data_struct *)userdata; - - /* initialize sftp session and file handler */ - cdata->sftp = sftp_server_new(session, channel); - init_handle_table(); - - return SSH_OK; - } - return SSH_ERROR; -} - static int auth_password(ssh_session session, const char *user, const char *pass, void *userdata) { struct session_data_struct *sdata = (struct session_data_struct *)userdata; + (void)session; + if (strcmp(user, USER) == 0 && strcmp(pass, PASS) == 0) { sdata->authenticated = 1; @@ -1291,6 +271,9 @@ static int auth_publickey(ssh_session session, { struct session_data_struct *sdata = (struct session_data_struct *)userdata; + (void)session; + (void)user; + if (signature_state == SSH_PUBLICKEY_STATE_NONE) { return SSH_AUTH_SUCCESS; @@ -1360,9 +343,10 @@ static void handle_session(ssh_event event, ssh_session session) }; struct ssh_channel_callbacks_struct channel_cb = { - .userdata = &cdata, - .channel_data_function = data_function, - .channel_subsystem_request_function = subsystem_request}; + .userdata = &(cdata.sftp), + .channel_data_function = sftp_channel_default_data_callback, + .channel_subsystem_request_function = sftp_channel_default_subsystem_request, + }; struct ssh_server_callbacks_struct server_cb = { .userdata = &sdata, @@ -1424,8 +408,6 @@ static void handle_session(ssh_event event, ssh_session session) * closes the channel */ } while (ssh_channel_is_open(sdata.channel)); - sftp_server_free_handles(); - ssh_channel_send_eof(sdata.channel); ssh_channel_close(sdata.channel); @@ -1438,6 +420,8 @@ static void handle_session(ssh_event event, ssh_session session) /* SIGCHLD handler for cleaning up dead children. */ static void sigchld_handler(int signo) { + (void)signo; + while (waitpid(-1, NULL, WNOHANG) > 0) ; } diff --git a/include/libssh/CMakeLists.txt b/include/libssh/CMakeLists.txt index 83e7b9f8..93445680 100644 --- a/include/libssh/CMakeLists.txt +++ b/include/libssh/CMakeLists.txt @@ -20,6 +20,13 @@ if (WITH_SERVER) ${libssh_HDRS} server.h ) + + if (WITH_SFTP) + set(libssh_HDRS + ${libssh_HDRS} + sftpserver.h + ) + endif (WITH_SFTP) endif (WITH_SERVER) install( diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h index d168a174..f4dd6b74 100644 --- a/include/libssh/sftp.h +++ b/include/libssh/sftp.h @@ -899,7 +899,7 @@ LIBSSH_API void sftp_server_free(sftp_session sftp); * * @param data The pointer to the data buffer of channel. * - * @return Length of data deocded. + * @return Length of data decoded. */ LIBSSH_API int sftp_decode_channel_data_to_packet(sftp_session sftp, void *data); @@ -927,7 +927,7 @@ LIBSSH_API int sftp_process_init_packet(sftp_client_message client_msg); * @brief Handle the statvfs request, return information the mounted file system. * * @param msg The sftp client message. - * + * * @param st The statvfs state of target file. * * @return 0 on success, < 0 on error with ssh and sftp error set. diff --git a/include/libssh/sftpserver.h b/include/libssh/sftpserver.h new file mode 100644 index 00000000..d7ed6e49 --- /dev/null +++ b/include/libssh/sftpserver.h @@ -0,0 +1,73 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2022 Zeyu Sheng + * Copyright (c) 2023 Red Hat, Inc. + * + * Authors: Jakub Jelen + * + * This 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. + * + * This 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SFTP_SERVER_H +#define SFTP_SERVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +/** + * @defgroup libssh_sftp_server The libssh SFTP server API + * + * @brief SFTP server handling functions + * + * TODO + * + * @{ + */ + +#define SSH_SFTP_CALLBACK(name) \ + static int name(sftp_client_message message) + +typedef int (*sftp_server_message_callback)(sftp_client_message message); + +struct sftp_message_handler +{ + const char *name; + const char *extended_name; + uint8_t type; + + sftp_server_message_callback cb; +}; + +LIBSSH_API int sftp_channel_default_subsystem_request(ssh_session session, + ssh_channel channel, + const char *subsystem, + void *userdata); +LIBSSH_API int sftp_channel_default_data_callback(ssh_session session, + ssh_channel channel, + void *data, + uint32_t len, + int is_stderr, + void *userdata); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* SFTP_SERVER_H */ diff --git a/src/sftpserver.c b/src/sftpserver.c index 29c5d032..2b974905 100644 --- a/src/sftpserver.c +++ b/src/sftpserver.c @@ -3,7 +3,8 @@ * * This file is part of the SSH Library * - * Copyright (c) 2005 by Aris Adamantiadis + * Copyright (c) 2005 Aris Adamantiadis + * Copyright (c) 2022 Zeyu Sheng * * 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 @@ -23,16 +24,23 @@ #include "config.h" -#include #ifndef _WIN32 #include #include +#include +#include #endif +#include +#include +#include +#include +#include #include "libssh/libssh.h" #include "libssh/sftp.h" #include "libssh/sftp_priv.h" +#include "libssh/sftpserver.h" #include "libssh/ssh2.h" #include "libssh/priv.h" #include "libssh/buffer.h" @@ -40,6 +48,9 @@ #define SFTP_HANDLES 256 +#define MAX_ENTRIES_NUM_IN_PACKET 50 +#define MAX_LONG_NAME_LEN 350 + static sftp_client_message sftp_make_client_message(sftp_session sftp, sftp_packet packet) { @@ -641,3 +652,920 @@ void sftp_handle_remove(sftp_session sftp, void *handle) { } } } + +/* Default SFTP handlers */ +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 | (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; +} + +#ifndef _WIN32 +/* internal */ +enum sftp_handle_type +{ + SFTP_NULL_HANDLE, + SFTP_DIR_HANDLE, + SFTP_FILE_HANDLE +}; + +struct sftp_handle +{ + enum sftp_handle_type type; + int fd; + DIR *dirp; + char *name; +}; + +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 sftp_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 sftp_message_handler extended_handlers[] = { + /* here are some extended type handlers */ + {"statvfs", "statvfs@openssh.com", 0, process_extended_statvfs}, + {NULL, NULL, 0, NULL}, +}; + +static int +process_open(sftp_client_message client_msg) +{ + const char *filename = sftp_client_message_get_filename(client_msg); + uint32_t msg_flag = sftp_client_message_get_flags(client_msg); + ssh_string handle_s = NULL; + struct sftp_handle *h = NULL; + int file_flag; + int fd = -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; + } else { + SSH_LOG(SSH_LOG_PROTOCOL, "undefined message flag: %d", errno); + sftp_reply_status(client_msg, SSH_FX_FAILURE, "Flag error"); + return SSH_ERROR; + } + + fd = open(filename, file_flag, 0600); + if (fd == -1) { + status = unix_errno_to_ssh_stat(errno); + SSH_LOG(SSH_LOG_PROTOCOL, "error open file with error: %d", errno); + sftp_reply_status(client_msg, status, "Write error"); + return SSH_ERROR; + } + + h = calloc(1, sizeof (struct sftp_handle)); + if (h == NULL) { + SSH_LOG(SSH_LOG_PROTOCOL, "failed to allocate a new handle"); + sftp_reply_status(client_msg, SSH_FX_FAILURE, + "Failed to allocate new handle"); + return SSH_ERROR; + } + h->fd = fd; + h->type = SFTP_FILE_HANDLE; + handle_s = sftp_handle_alloc(client_msg->sftp, h); + if (handle_s != NULL) { + sftp_reply_handle(client_msg, handle_s); + ssh_string_free(handle_s); + } else { + SSH_LOG(SSH_LOG_PROTOCOL, "opening file failed: %d", errno); + close(fd); + sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available"); + } + + return SSH_OK; +} + +static int +process_read(sftp_client_message client_msg) +{ + sftp_session sftp = client_msg->sftp; + ssh_string handle = client_msg->handle; + struct sftp_handle *h = NULL; + uint32_t readn; + int fd = -1; + char *buffer = NULL; + int rv; + + h = sftp_handle(sftp, handle); + if (h->type == SFTP_FILE_HANDLE) { + fd = h->fd; + } + + if (fd < 0) { + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + SSH_LOG(SSH_LOG_PROTOCOL, "error reading file fd: %d", fd); + return SSH_ERROR; + } + rv = lseek(fd, client_msg->offset, SEEK_SET); + if (rv == -1) { + sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL); + SSH_LOG(SSH_LOG_PROTOCOL, + "error seeking file fd: %d at offset: %" PRIu64, + fd, + client_msg->offset); + return SSH_ERROR; + } + + buffer = malloc(client_msg->len); + 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); + SSH_LOG(SSH_LOG_PROTOCOL, "read file error!"); + free(buffer); + return SSH_ERROR; + } + + free(buffer); + return SSH_OK; +} + +static int +process_write(sftp_client_message client_msg) +{ + sftp_session sftp = client_msg->sftp; + ssh_string handle = client_msg->handle; + struct sftp_handle *h = NULL; + int written; + int fd = -1; + const char *msg_data = NULL; + uint32_t len; + int rv; + + h = sftp_handle(sftp, handle); + if (h->type == SFTP_FILE_HANDLE) { + fd = h->fd; + } + if (fd < 0) { + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + SSH_LOG(SSH_LOG_PROTOCOL, "write file fd error!"); + return SSH_ERROR; + } + + 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); + SSH_LOG(SSH_LOG_PROTOCOL, "error seeking file at offset: %" PRIu64, + client_msg->offset); + } + written = write(fd, msg_data, len); + if (written == (int)len) { + sftp_reply_status(client_msg, SSH_FX_OK, NULL); + } else if (written == -1) { + sftp_reply_status(client_msg, SSH_FX_FAILURE, "Write error"); + } else { + sftp_reply_status(client_msg, SSH_FX_FAILURE, "Partial write"); + } + + return SSH_OK; +} + +static int +process_close(sftp_client_message client_msg) +{ + sftp_session sftp = client_msg->sftp; + ssh_string handle = client_msg->handle; + struct sftp_handle *h = NULL; + int ret; + + h = sftp_handle(sftp, handle); + if (h->type == SFTP_FILE_HANDLE) { + int fd = h->fd; + close(fd); + ret = SSH_OK; + } else if (h->type == SFTP_DIR_HANDLE) { + DIR *dir = h->dirp; + closedir(dir); + ret = SSH_OK; + } else { + ret = SSH_ERROR; + } + SAFE_FREE(h->name); + sftp_handle_remove(sftp, h); + SAFE_FREE(h); + + if (ret == SSH_OK) { + sftp_reply_status(client_msg, SSH_FX_OK, NULL); + } else { + SSH_LOG(SSH_LOG_PROTOCOL, "closing file failed"); + sftp_reply_status(client_msg, SSH_FX_BAD_MESSAGE, "Invalid handle"); + } + + return SSH_OK; +} + +static int +process_opendir(sftp_client_message client_msg) +{ + DIR *dir = NULL; + const char *dir_name = sftp_client_message_get_filename(client_msg); + ssh_string handle_s = NULL; + struct sftp_handle *h = NULL; + + dir = opendir(dir_name); + if (dir == NULL) { + sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "No such directory"); + return SSH_ERROR; + } + + h = calloc(1, sizeof (struct sftp_handle)); + if (h == NULL) { + SSH_LOG(SSH_LOG_PROTOCOL, "failed to allocate a new handle"); + sftp_reply_status(client_msg, SSH_FX_FAILURE, + "Failed to allocate new handle"); + return SSH_ERROR; + } + h->dirp = dir; + h->name = strdup(dir_name); + h->type = SFTP_DIR_HANDLE; + handle_s = sftp_handle_alloc(client_msg->sftp, h); + + if (handle_s != NULL) { + sftp_reply_handle(client_msg, handle_s); + ssh_string_free(handle_s); + } else { + closedir(dir); + sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available"); + } + + return SSH_OK; +} + +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_readdir(sftp_client_message client_msg) +{ + sftp_session sftp = client_msg->sftp; + ssh_string handle = client_msg->handle; + struct sftp_handle *h = NULL; + int ret = SSH_OK; + int entries = 0; + struct dirent *dentry = NULL; + DIR *dir = NULL; + char long_path[PATH_MAX]; + int srclen; + const char *handle_name = NULL; + + h = sftp_handle(sftp, client_msg->handle); + if (h->type == SFTP_DIR_HANDLE) { + dir = h->dirp; + handle_name = h->name; + } + if (dir == NULL) { + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + SSH_LOG(SSH_LOG_PROTOCOL, "read dir handle error!"); + return SSH_ERROR; + } + + if (handle_name == NULL) { + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + return SSH_ERROR; + } + + srclen = strlen(handle_name); + if (srclen + 2 >= PATH_MAX) { + SSH_LOG(SSH_LOG_PROTOCOL, "handle string length exceed max length!"); + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + return SSH_ERROR; + } + + 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]; + + if (strlen(dentry->d_name) + srclen + 1 >= PATH_MAX) { + SSH_LOG(SSH_LOG_PROTOCOL, + "handle string length exceed max length!"); + sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL); + return SSH_ERROR; + } + snprintf(long_path, PATH_MAX, "%s/%s", handle_name, 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) { + SSH_LOG(SSH_LOG_PROTOCOL, "read link error with reason: %d", 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; + + SSH_LOG(SSH_LOG_PROTOCOL, "try to create link with src: %s and dest: %s", + 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); + SSH_LOG(SSH_LOG_PROTOCOL, "error symlink with error: %d", 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) { + SSH_LOG(SSH_LOG_PROTOCOL, "unlink error with reason: %d", 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) +{ + sftp_reply_status(client_msg, SSH_FX_OP_UNSUPPORTED, + "Operation not supported"); + SSH_LOG(SSH_LOG_PROTOCOL, "Message type %d not implemented", + sftp_client_message_get_type(client_msg)); + return SSH_OK; +} + +static int +process_extended_statvfs(sftp_client_message client_msg) +{ + 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) { + 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; + + rv = sftp_reply_statvfs(client_msg, sftp_statvfs); + free(sftp_statvfs); + if (rv == 0) { + return SSH_OK; + } + } + } + + status = unix_errno_to_ssh_stat(errno); + sftp_reply_status(client_msg, status, NULL); + + printf("statvfs send failed!\n"); + return SSH_ERROR; +} + +static int +process_extended(sftp_client_message sftp_msg) +{ + int status = SSH_ERROR; + const char *subtype = sftp_msg->submessage; + sftp_server_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"); + SSH_LOG(SSH_LOG_PROTOCOL, "Extended Message type %s not implemented", + subtype); + return SSH_OK; +} + +static int +dispatch_sftp_request(sftp_client_message sftp_msg) +{ + int status = SSH_ERROR; + sftp_server_message_callback handler = NULL; + uint8_t type = sftp_client_message_get_type(sftp_msg); + + for (int i = 0; message_handlers[i].cb != NULL; i++) { + if (type == message_handlers[i].type) { + handler = message_handlers[i].cb; + break; + } + } + + if (handler != NULL) { + status = handler(sftp_msg); + } else { + sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED, + "Operation not supported"); + SSH_LOG(SSH_LOG_PROTOCOL, "Message type %u not implemented", type); + return SSH_OK; + } + + return status; +} + +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); + } + + if (status != SSH_OK) + SSH_LOG(SSH_LOG_PROTOCOL, "error occur in process client message!"); + + return status; +} + +/** + * @brief Default subsystem request handler for SFTP subsystem + * + * @param[in] session The ssh session + * @param[in] channel The existing ssh channel + * @param[in] subsystem The subsystem name. Only "sftp" is handled + * @param[out] userdata The pointer to sftp_session which will get the + * resulting SFTP session + * + * @return SSH_OK when the SFTP server was successfully initialized, SSH_ERROR + * otherwise. + */ +int +sftp_channel_default_subsystem_request(ssh_session session, + ssh_channel channel, + const char *subsystem, + void *userdata) +{ + if (strcmp(subsystem, "sftp") == 0) { + sftp_session *sftp = (sftp_session *)userdata; + + /* initialize sftp session and file handler */ + *sftp = sftp_server_new(session, channel); + if (*sftp == NULL) { + return SSH_ERROR; + } + + return SSH_OK; + } + return SSH_ERROR; +} + +/** + * @brief Default data callback for sftp server + * + * @param[in] session The ssh session + * @param[in] channel The ssh channel with SFTP session opened + * @param[in] data The data to be processed. + * @param[in] len The length of input data to be processed + * @param[in] is_stderr Unused channel flag for stderr flagging + * @param[in] userdata The pointer to sftp_session + * + * @return number of bytes processed, -1 when error occurs. + */ +int +sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session), + UNUSED_PARAM(ssh_channel channel), + void *data, + UNUSED_PARAM(uint32_t len), + UNUSED_PARAM(int is_stderr), + void *userdata) +{ + sftp_session *sftpp = (sftp_session *)userdata; + sftp_session sftp = NULL; + sftp_client_message msg; + int decode_len; + int rc; + + if (sftpp == NULL) { + SSH_LOG(SSH_LOG_WARNING, "NULL userdata passed to callback"); + return -1; + } + sftp = *sftpp; + + decode_len = sftp_decode_channel_data_to_packet(sftp, data); + if (decode_len == -1) + return -1; + + msg = sftp_get_client_message_from_packet(sftp); + rc = process_client_message(msg); + sftp_client_message_free(msg); + if (rc != SSH_OK) + SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!"); + + return decode_len; +} +#else +/* Not available on Windows for now */ +int +sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session), + UNUSED_PARAM(ssh_channel channel), + UNUSED_PARAM(void *data), + UNUSED_PARAM(uint32_t len), + UNUSED_PARAM(int is_stderr), + UNUSED_PARAM(void *userdata)) +{ + return -1; +} + +int +sftp_channel_default_subsystem_request(UNUSED_PARAM(ssh_session session), + UNUSED_PARAM(ssh_channel channel), + UNUSED_PARAM(const char *subsystem), + UNUSED_PARAM(void *userdata)) +{ + return SSH_ERROR; +} +#endif diff --git a/tests/server/test_server/sftpserver_cb.c b/tests/server/test_server/sftpserver_cb.c index 7ffa8a38..49c28863 100644 --- a/tests/server/test_server/sftpserver_cb.c +++ b/tests/server/test_server/sftpserver_cb.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -69,906 +70,6 @@ #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 300 - -#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; - - SSH_LOG(SSH_LOG_PROTOCOL, "try to open file: %s", filename); - - 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 { - file_flag = O_RDONLY; - } - - fd = open(filename, file_flag, 0600); - if (fd == -1) { - status = unix_errno_to_ssh_stat(errno); - SSH_LOG(SSH_LOG_PROTOCOL, "error open file with error: %d", 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); - SSH_LOG(SSH_LOG_PROTOCOL, "opening file failed", errno); - 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; - uint32_t readn; - int fd; - char *buffer; - int rv; - - client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp, - client_msg->handle); - - SSH_LOG(SSH_LOG_PROTOCOL, "try to read a file from handle"); - - if (client_handle == NULL || client_handle->session_id != client_msg->sftp) { - SSH_LOG(SSH_LOG_PROTOCOL, "got wrong handle from msg"); - 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); - SSH_LOG(SSH_LOG_PROTOCOL, "error reading file fd: %d", 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); - SSH_LOG(SSH_LOG_PROTOCOL, "error seeking file fd: %d at offset: %ld", - 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); - SSH_LOG(SSH_LOG_PROTOCOL, "read file error!"); - } - return ret; -} - -static int process_write(sftp_client_message client_msg) -{ - int ret = SSH_OK; - struct sftp_handle *client_handle; - uint32_t writen; - int fd; - const char *msg_data; - uint32_t len; - int rv; - - client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp, - client_msg->handle); - - SSH_LOG(SSH_LOG_PROTOCOL, "try to write a file from handle"); - - if (client_handle == NULL || client_handle->session_id != client_msg->sftp) { - SSH_LOG(SSH_LOG_PROTOCOL, "got wrong handle from msg"); - 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); - SSH_LOG(SSH_LOG_PROTOCOL, "write file fd error!"); - 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); - SSH_LOG(SSH_LOG_PROTOCOL, "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; - - client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp, - client_msg->handle); - - SSH_LOG(SSH_LOG_PROTOCOL, "try to close 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 { - SSH_LOG(SSH_LOG_PROTOCOL, "closing file failed"); - 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; - - SSH_LOG(SSH_LOG_PROTOCOL, "try to open dir: %s", dir_name); - - 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; - } - - SSH_LOG(SSH_LOG_PROTOCOL, "read link : %s", filename); - - len = readlink(filename, buf, sizeof(buf) - 1); - if (len < 0) { - status = unix_errno_to_ssh_stat(errno); - err_msg = ssh_str_error(status); - SSH_LOG(SSH_LOG_PROTOCOL, "read link error with reason: %s", err_msg); - 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; - - SSH_LOG(SSH_LOG_PROTOCOL, "try to create link with src: %s and dest: %s", - 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); - SSH_LOG(SSH_LOG_PROTOCOL, "error symlink with error: %s\n", - strerror(errno)); - sftp_reply_status(client_msg, status, strerror(errno)); - 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; - - SSH_LOG(SSH_LOG_PROTOCOL, "try to remove: %s ", filename); - - rv = unlink(filename); - if (rv < 0) { - SSH_LOG(SSH_LOG_PROTOCOL, "unlink error with reason: %s ", - strerror(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), @@ -1016,60 +117,6 @@ 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; @@ -1178,8 +225,8 @@ struct ssh_channel_callbacks_struct *get_sftp_channel_cb(void) goto end; } - cb->channel_subsystem_request_function = sftp_channel_subsystem_request_cb; - cb->channel_data_function = sftp_channel_data_cb; + cb->channel_data_function = sftp_channel_default_data_callback; + cb->channel_subsystem_request_function = sftp_channel_default_subsystem_request; end: return cb; @@ -1309,7 +356,7 @@ void sftp_handle_session_cb(ssh_event event, goto end; } - channel_cb->userdata = &cdata; + channel_cb->userdata = &(cdata.sftp); ssh_callbacks_init(channel_cb); rc = ssh_set_channel_callbacks(sdata.channel, channel_cb);