From f8bfb5a7a1e5b1a0a910c128e827a1391ddde452 Mon Sep 17 00:00:00 2001 From: tatataeki Date: Thu, 11 Aug 2022 10:21:04 +0800 Subject: [PATCH] sftp: add sftp api for sftpserver Signed-off-by: tatataeki Reviewed-by: Andreas Schneider Reviewed-by: Anderson Toshiyuki Sasaki --- include/libssh/sftp.h | 10 ++ src/libssh.map | 5 + src/sftp.c | 130 +++++++++++++++++++++ src/sftpserver.c | 265 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 410 insertions(+) diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h index 767cff76..b75fcf6a 100644 --- a/include/libssh/sftp.h +++ b/include/libssh/sftp.h @@ -864,6 +864,16 @@ LIBSSH_API char *sftp_canonicalize_path(sftp_session sftp, const char *path); */ LIBSSH_API int sftp_server_version(sftp_session sftp); +LIBSSH_API sftp_session sftp_from_session(ssh_session session, ssh_channel channel); + +LIBSSH_API int sftp_decode_channel_data_to_packet(sftp_session sftp, void *data); + +LIBSSH_API sftp_client_message sftp_get_client_message_from_packet(sftp_session sftp); + +LIBSSH_API int sftp_process_init_packet(sftp_client_message client_msg); + +LIBSSH_API int sftp_reply_statvfs(sftp_client_message msg, sftp_statvfs_t st); + #ifdef WITH_SERVER /** * @brief Create a new sftp server session. diff --git a/src/libssh.map b/src/libssh.map index eeb625c5..f9fc2ee7 100644 --- a/src/libssh.map +++ b/src/libssh.map @@ -457,5 +457,10 @@ LIBSSH_4_9_0 # Released ssh_session_set_disconnect_message; ssh_userauth_publickey_auto_get_current_identity; ssh_vlog; + sftp_from_session; + sftp_get_client_message_from_packet; + sftp_process_init_packet; + sftp_decode_channel_data_to_packet; + sftp_reply_statvfs; } LIBSSH_4_8_1; diff --git a/src/sftp.c b/src/sftp.c index c98fe655..928aa199 100644 --- a/src/sftp.c +++ b/src/sftp.c @@ -353,6 +353,107 @@ void sftp_server_free(sftp_session sftp) SAFE_FREE(sftp); } + +sftp_session sftp_from_session(ssh_session session, ssh_channel channel) +{ + sftp_session sftp; + + if (session == NULL) { + return NULL; + } + + sftp = calloc(1, sizeof(struct sftp_session_struct)); + if (sftp == NULL) { + ssh_set_error_oom(session); + + return NULL; + } + + sftp->ext = sftp_ext_new(); + if (sftp->ext == NULL) { + ssh_set_error_oom(session); + goto error; + } + + sftp->read_packet = calloc(1, sizeof(struct sftp_packet_struct)); + if (sftp->read_packet == NULL) { + ssh_set_error_oom(session); + goto error; + } + + sftp->read_packet->payload = ssh_buffer_new(); + if (sftp->read_packet->payload == NULL) { + ssh_set_error_oom(session); + goto error; + } + + sftp->session = session; + sftp->channel = channel; + + return sftp; +error: + if (sftp->ext != NULL) { + sftp_ext_free(sftp->ext); + } + if (sftp->channel != NULL) { + ssh_channel_free(sftp->channel); + } + if (sftp->read_packet != NULL) { + if (sftp->read_packet->payload != NULL) { + SSH_BUFFER_FREE(sftp->read_packet->payload); + } + SAFE_FREE(sftp->read_packet); + } + SAFE_FREE(sftp); + return NULL; +} + +int sftp_process_init_packet(sftp_client_message client_msg) { + int ret = SSH_OK; + sftp_session sftp = client_msg->sftp; + ssh_session session = sftp->session; + int version; + ssh_buffer reply; + int rc; + + version = sftp->client_version; + reply = ssh_buffer_new(); + if (reply == NULL) { + ssh_set_error_oom(session); + return -1; + } + + rc = ssh_buffer_pack(reply, "dssssss", + LIBSFTP_VERSION, + "posix-rename@openssh.com", + "1", + "hardlink@openssh.com", + "1", + "statvfs@openssh.com", + "2"); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + SSH_BUFFER_FREE(reply); + return -1; + } + + if (sftp_packet_write(sftp, SSH_FXP_VERSION, reply) < 0) { + SSH_BUFFER_FREE(reply); + return -1; + } + SSH_BUFFER_FREE(reply); + + SSH_LOG(SSH_LOG_PROTOCOL, "Server version sent"); + + if (version > LIBSFTP_VERSION) { + sftp->version = LIBSFTP_VERSION; + } else { + sftp->version = (int)version; + } + + return ret; +} + #endif /* WITH_SERVER */ void sftp_free(sftp_session sftp) @@ -387,6 +488,35 @@ void sftp_free(sftp_session sftp) SAFE_FREE(sftp); } +int sftp_decode_channel_data_to_packet(sftp_session sftp, void *data) { + sftp_packet packet = sftp->read_packet; + int nread; + int payload_len; + int offset; + int to_read; + + if(packet->sftp == NULL) + packet->sftp = sftp; + + packet->type = *((uint8_t *)data + sizeof(int)); + payload_len = PULL_BE_U32(data, 0); + + /* We should check the legality of payload length */ + if(payload_len > MAX_PACKET_LEN || payload_len < 0) + return SSH_ERROR; + + offset = sizeof(int)+sizeof(uint8_t); + to_read = payload_len - sizeof(uint8_t); + ssh_buffer_add_data(packet->payload, (void*)((uint8_t *)data + offset), payload_len-sizeof(uint8_t)); + nread = ssh_buffer_get_len(packet->payload); + + /* We should check if we copy the whole data */ + if(nread != to_read) + return SSH_ERROR; + + return payload_len + sizeof(int); +} + int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload) { uint8_t header[5] = {0}; diff --git a/src/sftpserver.c b/src/sftpserver.c index 9117f155..98d92dc5 100644 --- a/src/sftpserver.c +++ b/src/sftpserver.c @@ -249,6 +249,240 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { return msg; } +sftp_client_message sftp_get_client_message_from_packet(sftp_session sftp) { + ssh_session session = sftp->session; + sftp_packet packet; + sftp_client_message msg; + ssh_buffer payload; + int rc; + int version; + + msg = malloc(sizeof (struct sftp_client_message_struct)); + if (msg == NULL) { + ssh_set_error_oom(session); + return NULL; + } + ZERO_STRUCTP(msg); + + packet = sftp->read_packet; + if (packet == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + + payload = packet->payload; + msg->type = packet->type; + msg->sftp = sftp; + + /* take a copy of the whole packet */ + msg->complete_message = ssh_buffer_new(); + if (msg->complete_message == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + + rc = ssh_buffer_add_data(msg->complete_message, + ssh_buffer_get(payload), + ssh_buffer_get_len(payload)); + if (rc < 0) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + + if(msg->type!=SSH_FXP_INIT) + ssh_buffer_get_u32(payload, &msg->id); + + switch(msg->type) { + case SSH_FXP_INIT: + rc = ssh_buffer_unpack(payload, + "d", + &version); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + printf("unpack init failed!\n"); + return NULL; + } + version = ntohl(version); + sftp->client_version = (int)version; + break; + case SSH_FXP_CLOSE: + case SSH_FXP_READDIR: + msg->handle = ssh_buffer_get_ssh_string(payload); + if (msg->handle == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_READ: + rc = ssh_buffer_unpack(payload, + "Sqd", + &msg->handle, + &msg->offset, + &msg->len); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_WRITE: + rc = ssh_buffer_unpack(payload, + "SqS", + &msg->handle, + &msg->offset, + &msg->data); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_REMOVE: + case SSH_FXP_RMDIR: + case SSH_FXP_OPENDIR: + case SSH_FXP_READLINK: + case SSH_FXP_REALPATH: + rc = ssh_buffer_unpack(payload, + "s", + &msg->filename); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_RENAME: + case SSH_FXP_SYMLINK: + rc = ssh_buffer_unpack(payload, + "sS", + &msg->filename, + &msg->data); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_MKDIR: + case SSH_FXP_SETSTAT: + rc = ssh_buffer_unpack(payload, + "s", + &msg->filename); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->attr = sftp_parse_attr(sftp, payload, 0); + if (msg->attr == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_FSETSTAT: + msg->handle = ssh_buffer_get_ssh_string(payload); + if (msg->handle == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->attr = sftp_parse_attr(sftp, payload, 0); + if (msg->attr == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_LSTAT: + case SSH_FXP_STAT: + rc = ssh_buffer_unpack(payload, + "s", + &msg->filename); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + if(sftp->version > 3) { + ssh_buffer_unpack(payload, "d", &msg->flags); + } + break; + case SSH_FXP_OPEN: + rc = ssh_buffer_unpack(payload, + "sd", + &msg->filename, + &msg->flags); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->attr = sftp_parse_attr(sftp, payload, 0); + if (msg->attr == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_FSTAT: + rc = ssh_buffer_unpack(payload, + "S", + &msg->handle); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_EXTENDED: + rc = ssh_buffer_unpack(payload, + "s", + &msg->submessage); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + + if (strcmp(msg->submessage, "hardlink@openssh.com") == 0 || + strcmp(msg->submessage, "posix-rename@openssh.com") == 0) { + rc = ssh_buffer_unpack(payload, + "sS", + &msg->filename, + &msg->data); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + } else if (strcmp(msg->submessage, "statvfs@openssh.com") == 0 ){ + rc = ssh_buffer_unpack(payload, + "s", + &msg->filename); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + } + break; + default: + ssh_set_error(sftp->session, SSH_FATAL, + "Received unhandled sftp message %d", msg->type); + sftp_client_message_free(msg); + return NULL; + } + + return msg; +} + + /* Send an sftp client message. Can be used in cas of proxying */ int sftp_send_client_message(sftp_session sftp, sftp_client_message msg){ return sftp_packet_write(sftp, msg->type, msg->complete_message); @@ -485,6 +719,37 @@ int sftp_reply_data(sftp_client_message msg, const void *data, int len) { return 0; } +/* zeyu added */ +int sftp_reply_statvfs(sftp_client_message msg, sftp_statvfs_t st) { + ssh_buffer out; + + out = ssh_buffer_new(); + if (out == NULL) { + return -1; + } + + if (ssh_buffer_add_u32(out, msg->id) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_bsize)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_frsize)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_blocks)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_bfree)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_bavail)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_files)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_ffree)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_favail)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_fsid)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_flag)) < 0 || + ssh_buffer_add_u64(out, ntohll(st->f_namemax)) < 0 || + sftp_packet_write(msg->sftp, SSH_FXP_EXTENDED_REPLY, out) < 0) { + SSH_BUFFER_FREE(out); + return -1; + } + SSH_BUFFER_FREE(out); + + return 0; +} + + /* * This function will return you a new handle to give the client. * the function accepts an info that can be retrieved later with