1
1

sftp_open: clean up, better check of input data

The clang-analyzer report made it look into this function and
I've went through it to remove a potential use of an
uninitialized variable and I also added some validation of input
data received from the server.

In general, lots of more code in this file need to validate the
input before assuming it is correct: there are servers out there
that have bugs or just have another idea of how to do the SFTP
protocol.
Этот коммит содержится в:
Daniel Stenberg 2010-04-27 23:59:55 +02:00
родитель c28fa65424
Коммит 77efca961d
2 изменённых файлов: 89 добавлений и 72 удалений

Просмотреть файл

@ -554,7 +554,7 @@ struct _LIBSSH2_SFTP_HANDLE
unsigned char request_packet[SFTP_HANDLE_MAXLEN + 25]; unsigned char request_packet[SFTP_HANDLE_MAXLEN + 25];
char handle[SFTP_HANDLE_MAXLEN]; char handle[SFTP_HANDLE_MAXLEN];
int handle_len; size_t handle_len;
char handle_type; char handle_type;

Просмотреть файл

@ -821,18 +821,15 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
LIBSSH2_SFTP_ATTRIBUTES attrs = { LIBSSH2_SFTP_ATTRIBUTES attrs = {
LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0 LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0
}; };
size_t data_len; unsigned char *s;
unsigned char *data, *s;
static const unsigned char fopen_responses[2] =
{ SSH_FXP_HANDLE, SSH_FXP_STATUS };
int rc; int rc;
int open_file = (open_type == LIBSSH2_SFTP_OPENFILE)?1:0;
if (sftp->open_state == libssh2_NB_state_idle) { if (sftp->open_state == libssh2_NB_state_idle) {
/* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) + /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) +
flags(4) */ flags(4) */
sftp->open_packet_len = filename_len + 13 + sftp->open_packet_len = filename_len + 13 +
((open_type == (open_file? (4 + sftp_attrsize(&attrs)) : 0);
LIBSSH2_SFTP_OPENFILE) ? (4 + sftp_attrsize(&attrs)) : 0);
s = sftp->open_packet = LIBSSH2_ALLOC(session, sftp->open_packet_len); s = sftp->open_packet = LIBSSH2_ALLOC(session, sftp->open_packet_len);
if (!sftp->open_packet) { if (!sftp->open_packet) {
@ -843,25 +840,22 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
} }
/* Filetype in SFTP 3 and earlier */ /* Filetype in SFTP 3 and earlier */
attrs.permissions = mode | attrs.permissions = mode |
((open_type == (open_file ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE :
LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE :
LIBSSH2_SFTP_ATTR_PFILETYPE_DIR); LIBSSH2_SFTP_ATTR_PFILETYPE_DIR);
_libssh2_store_u32(&s, sftp->open_packet_len - 4); _libssh2_store_u32(&s, sftp->open_packet_len - 4);
*(s++) = (open_type == *(s++) = open_file? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
LIBSSH2_SFTP_OPENFILE) ? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
sftp->open_request_id = sftp->request_id++; sftp->open_request_id = sftp->request_id++;
_libssh2_store_u32(&s, sftp->open_request_id); _libssh2_store_u32(&s, sftp->open_request_id);
_libssh2_store_str(&s, filename, filename_len); _libssh2_store_str(&s, filename, filename_len);
if (open_type == LIBSSH2_SFTP_OPENFILE) { if (open_file) {
_libssh2_store_u32(&s, flags); _libssh2_store_u32(&s, flags);
s += sftp_attr2bin(s, &attrs); s += sftp_attr2bin(s, &attrs);
} }
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Sending %s open request", _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Sending %s open request",
(open_type == open_file? "file" : "directory");
LIBSSH2_SFTP_OPENFILE) ? "file" : "directory");
sftp->open_state = libssh2_NB_state_created; sftp->open_state = libssh2_NB_state_created;
} }
@ -892,6 +886,10 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
} }
if (sftp->open_state == libssh2_NB_state_sent) { if (sftp->open_state == libssh2_NB_state_sent) {
size_t data_len;
unsigned char *data;
static const unsigned char fopen_responses[2] =
{ SSH_FXP_HANDLE, SSH_FXP_STATUS };
rc = sftp_packet_requirev(sftp, 2, fopen_responses, rc = sftp_packet_requirev(sftp, 2, fopen_responses,
sftp->open_request_id, &data, sftp->open_request_id, &data,
&data_len); &data_len);
@ -900,29 +898,37 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
"Would block waiting for status message"); "Would block waiting for status message");
return NULL; return NULL;
} }
else if (rc) {
_libssh2_error(session, rc, "Timeout waiting for status message");
sftp->open_state = libssh2_NB_state_idle; sftp->open_state = libssh2_NB_state_idle;
if (rc) {
_libssh2_error(session, rc, "Timeout waiting for status message");
return NULL; return NULL;
} }
}
sftp->open_state = libssh2_NB_state_idle; /* OPEN can basically get STATUS or HANDLE back, where HANDLE implies
a fine response while STATUS means error. It seems though that at
/* OPEN can basically get STATUS or HANDLE back, where HANDLE implies a times we get an SSH_FX_OK back in a STATUS, followed the "real"
fine response while STATUS means error. It seems though that at times HANDLE so we need to properly deal with that. */
we get an SSH_FX_OK back in a STATUS, followed the "real" HANDLE so
we need to properly deal with that. */
if (data[0] == SSH_FXP_STATUS) { if (data[0] == SSH_FXP_STATUS) {
int badness = 1; int badness = 1;
if(data_len < 9) {
_libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
"Too small FXP_STATUS");
LIBSSH2_FREE(session, data);
return NULL;
}
sftp->last_errno = _libssh2_ntohu32(data + 5); sftp->last_errno = _libssh2_ntohu32(data + 5);
if(LIBSSH2_FX_OK == sftp->last_errno) { if(LIBSSH2_FX_OK == sftp->last_errno) {
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "got HANDLE FXOK!"); _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "got HANDLE FXOK!");
LIBSSH2_FREE(session, data);
/* silly situation, but check for a HANDLE */ /* silly situation, but check for a HANDLE */
rc = sftp_packet_require(sftp, SSH_FXP_HANDLE, rc = sftp_packet_require(sftp, SSH_FXP_HANDLE,
sftp->open_request_id, &data, &data_len); sftp->open_request_id, &data,
&data_len);
if(rc == LIBSSH2_ERROR_EAGAIN) { if(rc == LIBSSH2_ERROR_EAGAIN) {
/* go back to sent state and wait for something else */ /* go back to sent state and wait for something else */
sftp->open_state = libssh2_NB_state_sent; sftp->open_state = libssh2_NB_state_sent;
@ -943,6 +949,13 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
} }
} }
if(data_len < 10) {
_libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
"Too small FXP_HANDLE");
LIBSSH2_FREE(session, data);
return NULL;
}
fp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE)); fp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE));
if (!fp) { if (!fp) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC, _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
@ -951,18 +964,20 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
return NULL; return NULL;
} }
memset(fp, 0, sizeof(LIBSSH2_SFTP_HANDLE)); memset(fp, 0, sizeof(LIBSSH2_SFTP_HANDLE));
fp->handle_type = fp->handle_type = open_file ? LIBSSH2_SFTP_HANDLE_FILE :
(open_type ==
LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_HANDLE_FILE :
LIBSSH2_SFTP_HANDLE_DIR; LIBSSH2_SFTP_HANDLE_DIR;
fp->handle_len = _libssh2_ntohu32(data + 5); fp->handle_len = _libssh2_ntohu32(data + 5);
if (fp->handle_len > SFTP_HANDLE_MAXLEN) { if (fp->handle_len > SFTP_HANDLE_MAXLEN)
/* SFTP doesn't allow handles longer than 256 characters */ /* SFTP doesn't allow handles longer than 256 characters */
fp->handle_len = SFTP_HANDLE_MAXLEN; fp->handle_len = SFTP_HANDLE_MAXLEN;
}
if(fp->handle_len > (data_len - 9))
/* do not reach beyond the end of the data we got */
fp->handle_len = data_len - 9;
memcpy(fp->handle, data + 9, fp->handle_len); memcpy(fp->handle, data + 9, fp->handle_len);
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
/* add this file handle to the list kept in the sftp session */ /* add this file handle to the list kept in the sftp session */
@ -975,6 +990,8 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Open command successful"); _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Open command successful");
return fp; return fp;
} }
return NULL;
}
/* libssh2_sftp_open_ex /* libssh2_sftp_open_ex
*/ */