mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-09 18:04:25 +09:00
sftp: add sftp api for sftpserver
Signed-off-by: tatataeki <shengzeyu19_98@163.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org> Reviewed-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
This commit is contained in:
@@ -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 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
|
#ifdef WITH_SERVER
|
||||||
/**
|
/**
|
||||||
* @brief Create a new sftp server session.
|
* @brief Create a new sftp server session.
|
||||||
|
|||||||
@@ -457,5 +457,10 @@ LIBSSH_4_9_0 # Released
|
|||||||
ssh_session_set_disconnect_message;
|
ssh_session_set_disconnect_message;
|
||||||
ssh_userauth_publickey_auto_get_current_identity;
|
ssh_userauth_publickey_auto_get_current_identity;
|
||||||
ssh_vlog;
|
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;
|
} LIBSSH_4_8_1;
|
||||||
|
|
||||||
|
|||||||
130
src/sftp.c
130
src/sftp.c
@@ -353,6 +353,107 @@ void sftp_server_free(sftp_session sftp)
|
|||||||
|
|
||||||
SAFE_FREE(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 */
|
#endif /* WITH_SERVER */
|
||||||
|
|
||||||
void sftp_free(sftp_session sftp)
|
void sftp_free(sftp_session sftp)
|
||||||
@@ -387,6 +488,35 @@ void sftp_free(sftp_session sftp)
|
|||||||
SAFE_FREE(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)
|
int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload)
|
||||||
{
|
{
|
||||||
uint8_t header[5] = {0};
|
uint8_t header[5] = {0};
|
||||||
|
|||||||
265
src/sftpserver.c
265
src/sftpserver.c
@@ -249,6 +249,240 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) {
|
|||||||
return msg;
|
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 */
|
/* Send an sftp client message. Can be used in cas of proxying */
|
||||||
int sftp_send_client_message(sftp_session sftp, sftp_client_message msg){
|
int sftp_send_client_message(sftp_session sftp, sftp_client_message msg){
|
||||||
return sftp_packet_write(sftp, msg->type, msg->complete_message);
|
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;
|
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.
|
* This function will return you a new handle to give the client.
|
||||||
* the function accepts an info that can be retrieved later with
|
* the function accepts an info that can be retrieved later with
|
||||||
|
|||||||
Reference in New Issue
Block a user