1
0
mirror of https://git.libssh.org/projects/libssh.git synced 2025-12-12 15:41:16 +03:00
Files
libssh/src/sftp_common.c
Liu Husong 2d3b7e07af fix: sftp_packet_read stuck in an infinite loop in blocking mode
Signed-off-by: Liu Husong <huliu@janestreet.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2024-07-01 13:22:35 +00:00

933 lines
26 KiB
C

/*
* sftp_common.c - Secure FTP functions which are private and are used
* internally by other sftp api functions spread across
* various source files.
*
* This file is part of the SSH Library
*
* Copyright (c) 2005-2008 by Aris Adamantiadis
* Copyright (c) 2008-2018 by Andreas Schneider <asn@cryptomilk.org>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <ctype.h>
#include "libssh/sftp.h"
#include "libssh/sftp_priv.h"
#include "libssh/buffer.h"
#include "libssh/session.h"
#include "libssh/bytearray.h"
#ifdef WITH_SFTP
/* Buffer size maximum is 256M */
#define SFTP_PACKET_SIZE_MAX 0x10000000
sftp_packet sftp_packet_read(sftp_session sftp)
{
uint8_t tmpbuf[4];
uint8_t *buffer = NULL;
sftp_packet packet = sftp->read_packet;
uint32_t size;
int nread;
bool is_eof;
int rc;
packet->sftp = sftp;
/*
* If the packet has a payload, then just reinit the buffer, otherwise
* allocate a new one.
*/
if (packet->payload != NULL) {
rc = ssh_buffer_reinit(packet->payload);
if (rc != 0) {
ssh_set_error_oom(sftp->session);
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
} else {
packet->payload = ssh_buffer_new();
if (packet->payload == NULL) {
ssh_set_error_oom(sftp->session);
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
}
nread = 0;
do {
int s;
/* read from channel until 4 bytes have been read or an error occurs */
s = ssh_channel_read(sftp->channel, tmpbuf + nread, 4 - nread, 0);
if (s < 0) {
goto error;
} else if (s == 0) {
is_eof = ssh_channel_is_eof(sftp->channel);
if (is_eof) {
ssh_set_error(sftp->session,
SSH_FATAL,
"Received EOF while reading sftp packet size");
sftp_set_error(sftp, SSH_FX_EOF);
goto error;
} else {
ssh_set_error(sftp->session,
SSH_FATAL,
"Timeout while reading sftp packet size");
sftp_set_error(sftp, SSH_FX_FAILURE);
goto error;
}
} else {
nread += s;
}
} while (nread < 4);
size = PULL_BE_U32(tmpbuf, 0);
if (size == 0 || size > SFTP_PACKET_SIZE_MAX) {
ssh_set_error(sftp->session, SSH_FATAL, "Invalid sftp packet size!");
sftp_set_error(sftp, SSH_FX_FAILURE);
goto error;
}
do {
nread = ssh_channel_read(sftp->channel, tmpbuf, 1, 0);
if (nread < 0) {
goto error;
} else if (nread == 0) {
is_eof = ssh_channel_is_eof(sftp->channel);
if (is_eof) {
ssh_set_error(sftp->session,
SSH_FATAL,
"Received EOF while reading sftp packet type");
sftp_set_error(sftp, SSH_FX_EOF);
goto error;
} else {
ssh_set_error(sftp->session,
SSH_FATAL,
"Timeout while reading sftp packet type");
sftp_set_error(sftp, SSH_FX_FAILURE);
goto error;
}
}
} while (nread < 1);
packet->type = tmpbuf[0];
/* Remove the packet type size */
size -= sizeof(uint8_t);
/* Allocate the receive buffer from payload */
buffer = ssh_buffer_allocate(packet->payload, size);
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
sftp_set_error(sftp, SSH_FX_FAILURE);
goto error;
}
while (size > 0 && size < SFTP_PACKET_SIZE_MAX) {
nread = ssh_channel_read(sftp->channel, buffer, size, 0);
if (nread < 0) {
/* TODO: check if there are cases where an error needs to be set here */
goto error;
}
if (nread > 0) {
buffer += nread;
size -= nread;
} else { /* nread == 0 */
/* Retry the reading unless the remote was closed */
is_eof = ssh_channel_is_eof(sftp->channel);
if (is_eof) {
ssh_set_error(sftp->session,
SSH_REQUEST_DENIED,
"Received EOF while reading sftp packet");
sftp_set_error(sftp, SSH_FX_EOF);
goto error;
} else {
ssh_set_error(sftp->session,
SSH_FATAL,
"Timeout while reading sftp packet");
sftp_set_error(sftp, SSH_FX_FAILURE);
goto error;
}
}
}
return packet;
error:
ssh_buffer_reinit(packet->payload);
return NULL;
}
int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload)
{
uint8_t header[5] = {0};
uint32_t payload_size;
int size;
int rc;
/* Add size of type */
payload_size = ssh_buffer_get_len(payload) + sizeof(uint8_t);
PUSH_BE_U32(header, 0, payload_size);
PUSH_BE_U8(header, 4, type);
rc = ssh_buffer_prepend_data(payload, header, sizeof(header));
if (rc < 0) {
ssh_set_error_oom(sftp->session);
sftp_set_error(sftp, SSH_FX_FAILURE);
return -1;
}
size = ssh_channel_write(sftp->channel,
ssh_buffer_get(payload),
ssh_buffer_get_len(payload));
if (size < 0) {
sftp_set_error(sftp, SSH_FX_FAILURE);
return -1;
}
if ((uint32_t)size != ssh_buffer_get_len(payload)) {
SSH_LOG(SSH_LOG_PACKET,
"Had to write %" PRIu32 " bytes, wrote only %d",
ssh_buffer_get_len(payload),
size);
}
return size;
}
void sftp_packet_free(sftp_packet packet)
{
if (packet == NULL) {
return;
}
SSH_BUFFER_FREE(packet->payload);
free(packet);
}
int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr)
{
uint32_t flags = (attr ? attr->flags : 0);
int rc;
flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID |
SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
rc = ssh_buffer_pack(buffer, "d", flags);
if (rc != SSH_OK) {
return -1;
}
if (attr != NULL) {
if (flags & SSH_FILEXFER_ATTR_SIZE) {
rc = ssh_buffer_pack(buffer, "q", attr->size);
if (rc != SSH_OK) {
return -1;
}
}
if (flags & SSH_FILEXFER_ATTR_UIDGID) {
rc = ssh_buffer_pack(buffer, "dd", attr->uid, attr->gid);
if (rc != SSH_OK) {
return -1;
}
}
if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
rc = ssh_buffer_pack(buffer, "d", attr->permissions);
if (rc != SSH_OK) {
return -1;
}
}
if (flags & SSH_FILEXFER_ATTR_ACMODTIME) {
rc = ssh_buffer_pack(buffer, "dd", attr->atime, attr->mtime);
if (rc != SSH_OK) {
return -1;
}
}
}
return 0;
}
/*
* Parse the attributes from a payload from some messages. It is coded on
* baselines from the protocol version 4.
* This code is more or less dead but maybe we will need it in the future.
*/
static sftp_attributes sftp_parse_attr_4(sftp_session sftp,
ssh_buffer buf,
int expectnames)
{
sftp_attributes attr = NULL;
ssh_string owner = NULL;
ssh_string group = NULL;
uint32_t flags = 0;
int ok = 0;
/* unused member variable */
(void) expectnames;
attr = calloc(1, sizeof(struct sftp_attributes_struct));
if (attr == NULL) {
ssh_set_error_oom(sftp->session);
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
/* This isn't really a loop, but it is like a try..catch.. */
do {
if (ssh_buffer_get_u32(buf, &flags) != 4) {
break;
}
flags = ntohl(flags);
attr->flags = flags;
if (flags & SSH_FILEXFER_ATTR_SIZE) {
if (ssh_buffer_get_u64(buf, &attr->size) != 8) {
break;
}
attr->size = ntohll(attr->size);
}
if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) {
owner = ssh_buffer_get_ssh_string(buf);
if (owner == NULL) {
break;
}
attr->owner = ssh_string_to_char(owner);
SSH_STRING_FREE(owner);
if (attr->owner == NULL) {
break;
}
group = ssh_buffer_get_ssh_string(buf);
if (group == NULL) {
break;
}
attr->group = ssh_string_to_char(group);
SSH_STRING_FREE(group);
if (attr->group == NULL) {
break;
}
}
if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
if (ssh_buffer_get_u32(buf, &attr->permissions) != 4) {
break;
}
attr->permissions = ntohl(attr->permissions);
/* FIXME on windows! */
switch (attr->permissions & SSH_S_IFMT) {
case SSH_S_IFSOCK:
case SSH_S_IFBLK:
case SSH_S_IFCHR:
case SSH_S_IFIFO:
attr->type = SSH_FILEXFER_TYPE_SPECIAL;
break;
case SSH_S_IFLNK:
attr->type = SSH_FILEXFER_TYPE_SYMLINK;
break;
case SSH_S_IFREG:
attr->type = SSH_FILEXFER_TYPE_REGULAR;
break;
case SSH_S_IFDIR:
attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
break;
default:
attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
break;
}
}
if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
if (ssh_buffer_get_u64(buf, &attr->atime64) != 8) {
break;
}
attr->atime64 = ntohll(attr->atime64);
if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
if (ssh_buffer_get_u32(buf, &attr->atime_nseconds) != 4) {
break;
}
attr->atime_nseconds = ntohl(attr->atime_nseconds);
}
}
if (flags & SSH_FILEXFER_ATTR_CREATETIME) {
if (ssh_buffer_get_u64(buf, &attr->createtime) != 8) {
break;
}
attr->createtime = ntohll(attr->createtime);
if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
if (ssh_buffer_get_u32(buf, &attr->createtime_nseconds) != 4) {
break;
}
attr->createtime_nseconds = ntohl(attr->createtime_nseconds);
}
}
if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) {
if (ssh_buffer_get_u64(buf, &attr->mtime64) != 8) {
break;
}
attr->mtime64 = ntohll(attr->mtime64);
if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
if (ssh_buffer_get_u32(buf, &attr->mtime_nseconds) != 4) {
break;
}
attr->mtime_nseconds = ntohl(attr->mtime_nseconds);
}
}
if (flags & SSH_FILEXFER_ATTR_ACL) {
if ((attr->acl = ssh_buffer_get_ssh_string(buf)) == NULL) {
break;
}
}
if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
if (ssh_buffer_get_u32(buf,&attr->extended_count) != 4) {
break;
}
attr->extended_count = ntohl(attr->extended_count);
while (attr->extended_count &&
(attr->extended_type = ssh_buffer_get_ssh_string(buf)) &&
(attr->extended_data = ssh_buffer_get_ssh_string(buf))) {
attr->extended_count--;
}
if (attr->extended_count) {
break;
}
}
ok = 1;
} while (0);
if (ok == 0) {
/* break issued somewhere */
SSH_STRING_FREE(attr->acl);
SSH_STRING_FREE(attr->extended_type);
SSH_STRING_FREE(attr->extended_data);
SAFE_FREE(attr->owner);
SAFE_FREE(attr->group);
SAFE_FREE(attr);
ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
return NULL;
}
return attr;
}
enum sftp_longname_field_e {
SFTP_LONGNAME_PERM = 0,
SFTP_LONGNAME_FIXME,
SFTP_LONGNAME_OWNER,
SFTP_LONGNAME_GROUP,
SFTP_LONGNAME_SIZE,
SFTP_LONGNAME_DATE,
SFTP_LONGNAME_TIME,
SFTP_LONGNAME_NAME,
};
static char * sftp_parse_longname(const char *longname,
enum sftp_longname_field_e longname_field)
{
const char *p, *q;
size_t len, field = 0;
p = longname;
/*
* Find the beginning of the field which is specified
* by sftp_longname_field_e.
*/
while (field != longname_field) {
if (isspace(*p)) {
field++;
p++;
while (*p && isspace(*p)) {
p++;
}
} else {
p++;
}
}
q = p;
while (! isspace(*q)) {
q++;
}
len = q - p;
return strndup(p, len);
}
/* sftp version 0-3 code. It is different from the v4 */
/* maybe a paste of the draft is better than the code */
/*
uint32 flags
uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
string extended_type
string extended_data
... more extended data (extended_type - extended_data pairs),
so that number of pairs equals extended_count */
static sftp_attributes sftp_parse_attr_3(sftp_session sftp,
ssh_buffer buf,
int expectname)
{
sftp_attributes attr;
int rc;
attr = calloc(1, sizeof(struct sftp_attributes_struct));
if (attr == NULL) {
ssh_set_error_oom(sftp->session);
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
if (expectname) {
rc = ssh_buffer_unpack(buf, "ss",
&attr->name,
&attr->longname);
if (rc != SSH_OK){
goto error;
}
SSH_LOG(SSH_LOG_DEBUG, "Name: %s", attr->name);
/* Set owner and group if we talk to openssh and have the longname */
if (ssh_get_openssh_version(sftp->session)) {
attr->owner = sftp_parse_longname(attr->longname,
SFTP_LONGNAME_OWNER);
if (attr->owner == NULL) {
goto error;
}
attr->group = sftp_parse_longname(attr->longname,
SFTP_LONGNAME_GROUP);
if (attr->group == NULL) {
goto error;
}
}
}
rc = ssh_buffer_unpack(buf, "d", &attr->flags);
if (rc != SSH_OK){
goto error;
}
SSH_LOG(SSH_LOG_DEBUG,
"Flags: %.8" PRIx32 "\n", attr->flags);
if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
rc = ssh_buffer_unpack(buf, "q", &attr->size);
if(rc != SSH_OK) {
goto error;
}
SSH_LOG(SSH_LOG_DEBUG,
"Size: %" PRIu64 "\n",
(uint64_t) attr->size);
}
if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {
rc = ssh_buffer_unpack(buf, "dd",
&attr->uid,
&attr->gid);
if (rc != SSH_OK) {
goto error;
}
}
if (attr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
rc = ssh_buffer_unpack(buf, "d", &attr->permissions);
if (rc != SSH_OK) {
goto error;
}
switch (attr->permissions & SSH_S_IFMT) {
case SSH_S_IFSOCK:
case SSH_S_IFBLK:
case SSH_S_IFCHR:
case SSH_S_IFIFO:
attr->type = SSH_FILEXFER_TYPE_SPECIAL;
break;
case SSH_S_IFLNK:
attr->type = SSH_FILEXFER_TYPE_SYMLINK;
break;
case SSH_S_IFREG:
attr->type = SSH_FILEXFER_TYPE_REGULAR;
break;
case SSH_S_IFDIR:
attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
break;
default:
attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
break;
}
}
if (attr->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
rc = ssh_buffer_unpack(buf, "dd",
&attr->atime,
&attr->mtime);
if (rc != SSH_OK) {
goto error;
}
}
if (attr->flags & SSH_FILEXFER_ATTR_EXTENDED) {
rc = ssh_buffer_unpack(buf, "d", &attr->extended_count);
if (rc != SSH_OK) {
goto error;
}
if (attr->extended_count > 0) {
rc = ssh_buffer_unpack(buf, "ss",
&attr->extended_type,
&attr->extended_data);
if (rc != SSH_OK) {
goto error;
}
attr->extended_count--;
}
/* just ignore the remaining extensions */
while (attr->extended_count > 0) {
ssh_string tmp1,tmp2;
rc = ssh_buffer_unpack(buf, "SS", &tmp1, &tmp2);
if (rc != SSH_OK){
goto error;
}
SAFE_FREE(tmp1);
SAFE_FREE(tmp2);
attr->extended_count--;
}
}
return attr;
error:
SSH_STRING_FREE(attr->extended_type);
SSH_STRING_FREE(attr->extended_data);
SAFE_FREE(attr->name);
SAFE_FREE(attr->longname);
SAFE_FREE(attr->owner);
SAFE_FREE(attr->group);
SAFE_FREE(attr);
ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
sftp_attributes sftp_parse_attr(sftp_session session,
ssh_buffer buf,
int expectname)
{
switch (session->version) {
case 4:
return sftp_parse_attr_4(session, buf, expectname);
case 3:
case 2:
case 1:
case 0:
return sftp_parse_attr_3(session, buf, expectname);
default:
ssh_set_error(session->session, SSH_FATAL,
"Version %d unsupported by client",
session->server_version);
return NULL;
}
return NULL;
}
void sftp_set_error(sftp_session sftp, int errnum)
{
if (sftp != NULL) {
sftp->errnum = errnum;
}
}
void sftp_message_free(sftp_message msg)
{
if (msg == NULL) {
return;
}
SSH_BUFFER_FREE(msg->payload);
SAFE_FREE(msg);
}
static sftp_request_queue request_queue_new(sftp_message msg)
{
sftp_request_queue queue = NULL;
queue = calloc(1, sizeof(struct sftp_request_queue_struct));
if (queue == NULL) {
ssh_set_error_oom(msg->sftp->session);
sftp_set_error(msg->sftp, SSH_FX_FAILURE);
return NULL;
}
queue->message = msg;
return queue;
}
static void request_queue_free(sftp_request_queue queue)
{
if (queue == NULL) {
return;
}
ZERO_STRUCTP(queue);
SAFE_FREE(queue);
}
static int
sftp_enqueue(sftp_session sftp, sftp_message msg)
{
sftp_request_queue queue = NULL;
sftp_request_queue ptr;
queue = request_queue_new(msg);
if (queue == NULL) {
return -1;
}
SSH_LOG(SSH_LOG_PACKET,
"Queued msg id %" PRIu32 " type %d",
msg->id, msg->packet_type);
if(sftp->queue == NULL) {
sftp->queue = queue;
} else {
ptr = sftp->queue;
while(ptr->next) {
ptr=ptr->next; /* find end of linked list */
}
ptr->next = queue; /* add it on bottom */
}
return 0;
}
/*
* Pulls a message from the queue based on the ID.
* Returns NULL if no message has been found.
*/
sftp_message sftp_dequeue(sftp_session sftp, uint32_t id)
{
sftp_request_queue prev = NULL;
sftp_request_queue queue;
sftp_message msg;
if(sftp->queue == NULL) {
return NULL;
}
queue = sftp->queue;
while (queue) {
if (queue->message->id == id) {
/* remove from queue */
if (prev == NULL) {
sftp->queue = queue->next;
} else {
prev->next = queue->next;
}
msg = queue->message;
request_queue_free(queue);
SSH_LOG(SSH_LOG_PACKET,
"Dequeued msg id %" PRIu32 " type %d",
msg->id,
msg->packet_type);
return msg;
}
prev = queue;
queue = queue->next;
}
return NULL;
}
static sftp_message sftp_get_message(sftp_packet packet)
{
sftp_session sftp = packet->sftp;
sftp_message msg = NULL;
int rc;
switch (packet->type) {
case SSH_FXP_STATUS:
case SSH_FXP_HANDLE:
case SSH_FXP_DATA:
case SSH_FXP_ATTRS:
case SSH_FXP_NAME:
case SSH_FXP_EXTENDED_REPLY:
break;
default:
ssh_set_error(packet->sftp->session,
SSH_FATAL,
"Unknown packet type %d",
packet->type);
sftp_set_error(packet->sftp, SSH_FX_FAILURE);
return NULL;
}
msg = calloc(1, sizeof(struct sftp_message_struct));
if (msg == NULL) {
ssh_set_error_oom(sftp->session);
sftp_set_error(packet->sftp, SSH_FX_FAILURE);
return NULL;
}
msg->sftp = packet->sftp;
msg->packet_type = packet->type;
/* Move the payload from the packet to the message */
msg->payload = packet->payload;
packet->payload = NULL;
rc = ssh_buffer_unpack(msg->payload, "d", &msg->id);
if (rc != SSH_OK) {
ssh_set_error(packet->sftp->session, SSH_FATAL,
"Invalid packet %d: no ID", packet->type);
sftp_message_free(msg);
sftp_set_error(packet->sftp, SSH_FX_FAILURE);
return NULL;
}
SSH_LOG(SSH_LOG_PACKET,
"Packet with id %" PRIu32 " type %d",
msg->id,
msg->packet_type);
return msg;
}
int sftp_read_and_dispatch(sftp_session sftp)
{
sftp_packet packet = NULL;
sftp_message msg = NULL;
packet = sftp_packet_read(sftp);
if (packet == NULL) {
/* something nasty happened reading the packet */
return -1;
}
msg = sftp_get_message(packet);
if (msg == NULL) {
return -1;
}
if (sftp_enqueue(sftp, msg) < 0) {
sftp_message_free(msg);
return -1;
}
return 0;
}
sftp_status_message parse_status_msg(sftp_message msg)
{
sftp_status_message status = NULL;
int rc;
if (msg->packet_type != SSH_FXP_STATUS) {
ssh_set_error(msg->sftp->session, SSH_FATAL,
"Not a ssh_fxp_status message passed in!");
sftp_set_error(msg->sftp, SSH_FX_BAD_MESSAGE);
return NULL;
}
status = calloc(1, sizeof(struct sftp_status_message_struct));
if (status == NULL) {
ssh_set_error_oom(msg->sftp->session);
sftp_set_error(msg->sftp, SSH_FX_FAILURE);
return NULL;
}
status->id = msg->id;
rc = ssh_buffer_unpack(msg->payload, "d",
&status->status);
if (rc != SSH_OK) {
SAFE_FREE(status);
ssh_set_error(msg->sftp->session, SSH_FATAL,
"Invalid SSH_FXP_STATUS message");
sftp_set_error(msg->sftp, SSH_FX_FAILURE);
return NULL;
}
rc = ssh_buffer_unpack(msg->payload, "ss",
&status->errormsg,
&status->langmsg);
if (rc != SSH_OK && msg->sftp->version >= 3) {
/* These are mandatory from version 3 */
SAFE_FREE(status);
ssh_set_error(msg->sftp->session, SSH_FATAL,
"Invalid SSH_FXP_STATUS message");
sftp_set_error(msg->sftp, SSH_FX_FAILURE);
return NULL;
}
if (status->errormsg == NULL)
status->errormsg = strdup("No error message in packet");
if (status->langmsg == NULL)
status->langmsg = strdup("");
if (status->errormsg == NULL || status->langmsg == NULL) {
ssh_set_error_oom(msg->sftp->session);
sftp_set_error(msg->sftp, SSH_FX_FAILURE);
status_msg_free(status);
return NULL;
}
return status;
}
void status_msg_free(sftp_status_message status)
{
if (status == NULL) {
return;
}
SAFE_FREE(status->errormsg);
SAFE_FREE(status->langmsg);
SAFE_FREE(status);
}
#endif /* WITH_SFTP */