2007-02-02 16:21:20 +00:00
|
|
|
/* Copyright (C) 2007 The Written Word, Inc. All rights reserved.
|
2010-04-15 01:12:22 +02:00
|
|
|
* Copyright (C) 2009-2010 by Daniel Stenberg
|
2007-02-02 16:21:20 +00:00
|
|
|
* Author: Daniel Stenberg <daniel@haxx.se>
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms,
|
|
|
|
* with or without modification, are permitted provided
|
|
|
|
* that the following conditions are met:
|
|
|
|
*
|
|
|
|
* Redistributions of source code must retain the above
|
|
|
|
* copyright notice, this list of conditions and the
|
|
|
|
* following disclaimer.
|
|
|
|
*
|
|
|
|
* Redistributions in binary form must reproduce the above
|
|
|
|
* copyright notice, this list of conditions and the following
|
|
|
|
* disclaimer in the documentation and/or other materials
|
|
|
|
* provided with the distribution.
|
|
|
|
*
|
|
|
|
* Neither the name of the copyright holder nor the names
|
|
|
|
* of any other contributors may be used to endorse or
|
|
|
|
* promote products derived from this software without
|
|
|
|
* specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
|
|
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
|
|
* OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* This file handles reading and writing to the SECSH transport layer. RFC4253.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "libssh2_priv.h"
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2010-01-15 22:58:44 +01:00
|
|
|
#include <ctype.h>
|
2010-11-13 02:06:41 +01:00
|
|
|
#ifdef LIBSSH2DEBUG
|
|
|
|
#include <stdio.h>
|
|
|
|
#endif
|
2007-02-02 16:21:20 +00:00
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
#include "transport.h"
|
2010-12-15 09:52:18 +01:00
|
|
|
#include "mac.h"
|
2009-03-26 15:41:14 +00:00
|
|
|
|
2010-04-16 00:18:51 +02:00
|
|
|
#define MAX_BLOCKSIZE 32 /* MUST fit biggest crypto block size we use/get */
|
2015-09-06 14:02:44 +00:00
|
|
|
#define MAX_MACSIZE 64 /* MUST fit biggest MAC length we support */
|
2007-02-02 16:21:20 +00:00
|
|
|
|
|
|
|
#ifdef LIBSSH2DEBUG
|
|
|
|
#define UNPRINTABLE_CHAR '.'
|
2007-08-06 20:48:04 +00:00
|
|
|
static void
|
|
|
|
debugdump(LIBSSH2_SESSION * session,
|
2010-10-23 00:11:59 +02:00
|
|
|
const char *desc, const unsigned char *ptr, size_t size)
|
2007-02-02 16:21:20 +00:00
|
|
|
{
|
2007-08-06 20:48:04 +00:00
|
|
|
size_t i;
|
|
|
|
size_t c;
|
|
|
|
unsigned int width = 0x10;
|
2010-01-15 22:58:44 +01:00
|
|
|
char buffer[256]; /* Must be enough for width*4 + about 30 or so */
|
|
|
|
size_t used;
|
|
|
|
static const char* hex_chars = "0123456789ABCDEF";
|
2007-02-02 16:21:20 +00:00
|
|
|
|
2009-12-08 08:52:03 +01:00
|
|
|
if (!(session->showmask & LIBSSH2_TRACE_TRANS)) {
|
2007-08-06 20:48:04 +00:00
|
|
|
/* not asked for, bail out */
|
|
|
|
return;
|
|
|
|
}
|
2007-02-02 23:23:36 +00:00
|
|
|
|
2010-01-15 22:58:44 +01:00
|
|
|
used = snprintf(buffer, sizeof(buffer), "=> %s (%d bytes)\n",
|
|
|
|
desc, (int) size);
|
|
|
|
if (session->tracehandler)
|
2010-04-16 00:18:51 +02:00
|
|
|
(session->tracehandler)(session, session->tracehandler_context,
|
|
|
|
buffer, used);
|
2010-01-15 22:58:44 +01:00
|
|
|
else
|
2010-11-13 02:06:41 +01:00
|
|
|
fprintf(stderr, "%s", buffer);
|
2007-02-02 16:21:20 +00:00
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
for(i = 0; i < size; i += width) {
|
2007-02-02 16:21:20 +00:00
|
|
|
|
2010-01-15 22:58:44 +01:00
|
|
|
used = snprintf(buffer, sizeof(buffer), "%04lx: ", (long)i);
|
2007-02-02 16:21:20 +00:00
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
/* hex not disabled, show it */
|
|
|
|
for(c = 0; c < width; c++) {
|
2010-01-15 22:58:44 +01:00
|
|
|
if (i + c < size) {
|
|
|
|
buffer[used++] = hex_chars[(ptr[i+c] >> 4) & 0xF];
|
|
|
|
buffer[used++] = hex_chars[ptr[i+c] & 0xF];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
buffer[used++] = ' ';
|
|
|
|
buffer[used++] = ' ';
|
|
|
|
}
|
|
|
|
|
2010-02-01 11:40:11 +01:00
|
|
|
buffer[used++] = ' ';
|
|
|
|
if ((width/2) - 1 == c)
|
|
|
|
buffer[used++] = ' ';
|
2007-08-06 20:48:04 +00:00
|
|
|
}
|
2007-02-02 16:21:20 +00:00
|
|
|
|
2010-01-15 22:58:44 +01:00
|
|
|
buffer[used++] = ':';
|
|
|
|
buffer[used++] = ' ';
|
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
for(c = 0; (c < width) && (i + c < size); c++) {
|
2010-01-15 22:58:44 +01:00
|
|
|
buffer[used++] = isprint(ptr[i + c]) ?
|
|
|
|
ptr[i + c] : UNPRINTABLE_CHAR;
|
2007-08-06 20:48:04 +00:00
|
|
|
}
|
2010-01-15 22:58:44 +01:00
|
|
|
buffer[used++] = '\n';
|
|
|
|
buffer[used] = 0;
|
|
|
|
|
|
|
|
if (session->tracehandler)
|
2010-04-16 00:18:51 +02:00
|
|
|
(session->tracehandler)(session, session->tracehandler_context,
|
|
|
|
buffer, used);
|
2010-01-15 22:58:44 +01:00
|
|
|
else
|
2010-11-13 02:06:41 +01:00
|
|
|
fprintf(stderr, "%s", buffer);
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2007-02-02 23:23:36 +00:00
|
|
|
#define debugdump(a,x,y,z)
|
2007-02-02 16:21:20 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* decrypt() decrypts 'len' bytes from 'source' to 'dest'.
|
|
|
|
*
|
2009-08-24 23:57:15 +02:00
|
|
|
* returns 0 on success and negative on failure
|
2007-02-02 16:21:20 +00:00
|
|
|
*/
|
|
|
|
|
2009-08-24 23:57:15 +02:00
|
|
|
static int
|
2007-08-06 20:48:04 +00:00
|
|
|
decrypt(LIBSSH2_SESSION * session, unsigned char *source,
|
|
|
|
unsigned char *dest, int len)
|
2007-02-02 16:21:20 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
struct transportpacket *p = &session->packet;
|
|
|
|
int blocksize = session->remote.crypt->blocksize;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* if we get called with a len that isn't an even number of blocksizes
|
2007-08-06 20:48:04 +00:00
|
|
|
we risk losing those extra bytes */
|
2007-05-28 17:56:08 +00:00
|
|
|
assert((len % blocksize) == 0);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
while (len >= blocksize) {
|
2012-09-11 09:33:34 +02:00
|
|
|
if (session->remote.crypt->crypt(session, source, blocksize,
|
2007-05-28 17:56:08 +00:00
|
|
|
&session->remote.crypt_abstract)) {
|
|
|
|
LIBSSH2_FREE(session, p->payload);
|
2010-10-07 11:18:14 +02:00
|
|
|
return LIBSSH2_ERROR_DECRYPT;
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* if the crypt() function would write to a given address it
|
2007-06-06 12:34:06 +00:00
|
|
|
wouldn't have to memcpy() and we could avoid this memcpy()
|
|
|
|
too */
|
2007-05-28 17:56:08 +00:00
|
|
|
memcpy(dest, source, blocksize);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
len -= blocksize; /* less bytes left */
|
|
|
|
dest += blocksize; /* advance write pointer */
|
|
|
|
source += blocksize; /* advance read pointer */
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2010-04-17 13:18:15 +02:00
|
|
|
return LIBSSH2_ERROR_NONE; /* all is fine */
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fullpacket() gets called when a full packet has been received and properly
|
|
|
|
* collected.
|
|
|
|
*/
|
2009-08-24 23:57:15 +02:00
|
|
|
static int
|
2007-08-06 20:48:04 +00:00
|
|
|
fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
|
2007-02-02 16:21:20 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
unsigned char macbuf[MAX_MACSIZE];
|
|
|
|
struct transportpacket *p = &session->packet;
|
2007-06-06 12:34:06 +00:00
|
|
|
int rc;
|
2012-08-19 13:47:50 +02:00
|
|
|
int compressed;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->fullpacket_state == libssh2_NB_state_idle) {
|
|
|
|
session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
|
2007-08-06 20:48:04 +00:00
|
|
|
session->fullpacket_payload_len = p->packet_length - 1;
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (encrypted) {
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
/* Calculate MAC hash */
|
2007-08-06 20:48:04 +00:00
|
|
|
session->remote.mac->hash(session, macbuf, /* store hash here */
|
2007-06-06 12:34:06 +00:00
|
|
|
session->remote.seqno,
|
|
|
|
p->init, 5,
|
2007-08-06 20:48:04 +00:00
|
|
|
p->payload,
|
|
|
|
session->fullpacket_payload_len,
|
2007-06-06 12:34:06 +00:00
|
|
|
&session->remote.mac_abstract);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
/* Compare the calculated hash with the MAC we just read from
|
|
|
|
* the network. The read one is at the very end of the payload
|
|
|
|
* buffer. Note that 'payload_len' here is the packet_length
|
|
|
|
* field which includes the padding but not the MAC.
|
|
|
|
*/
|
|
|
|
if (memcmp(macbuf, p->payload + session->fullpacket_payload_len,
|
|
|
|
session->remote.mac->mac_len)) {
|
|
|
|
session->fullpacket_macstate = LIBSSH2_MAC_INVALID;
|
|
|
|
}
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->remote.seqno++;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
/* ignore the padding */
|
|
|
|
session->fullpacket_payload_len -= p->padding_length;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
/* Check for and deal with decompression */
|
2012-08-19 13:47:50 +02:00
|
|
|
compressed =
|
|
|
|
session->local.comp != NULL &&
|
|
|
|
session->local.comp->compress &&
|
|
|
|
((session->state & LIBSSH2_STATE_AUTHENTICATED) ||
|
|
|
|
session->local.comp->use_in_auth);
|
|
|
|
|
|
|
|
if (compressed && session->remote.comp_abstract) {
|
2011-02-16 13:56:05 +01:00
|
|
|
/*
|
|
|
|
* The buffer for the decompression (remote.comp_abstract) is
|
|
|
|
* initialised in time when it is needed so as long it is NULL we
|
|
|
|
* cannot decompress.
|
|
|
|
*/
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
unsigned char *data;
|
2010-04-17 13:34:44 +02:00
|
|
|
size_t data_len;
|
2010-11-03 14:52:42 +01:00
|
|
|
rc = session->remote.comp->decomp(session,
|
2010-10-21 21:00:28 +02:00
|
|
|
&data, &data_len,
|
|
|
|
LIBSSH2_PACKET_MAXDECOMP,
|
|
|
|
p->payload,
|
|
|
|
session->fullpacket_payload_len,
|
|
|
|
&session->remote.comp_abstract);
|
2010-11-03 15:03:57 +01:00
|
|
|
LIBSSH2_FREE(session, p->payload);
|
|
|
|
if(rc)
|
2010-10-08 16:26:50 +02:00
|
|
|
return rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-11-03 15:03:57 +01:00
|
|
|
p->payload = data;
|
|
|
|
session->fullpacket_payload_len = data_len;
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->fullpacket_packet_type = p->payload[0];
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
debugdump(session, "libssh2_transport_read() plain",
|
2007-06-06 12:34:06 +00:00
|
|
|
p->payload, session->fullpacket_payload_len);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->fullpacket_state = libssh2_NB_state_created;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->fullpacket_state == libssh2_NB_state_created) {
|
2009-03-17 13:48:35 +00:00
|
|
|
rc = _libssh2_packet_add(session, p->payload,
|
|
|
|
session->fullpacket_payload_len,
|
|
|
|
session->fullpacket_macstate);
|
2014-02-18 23:38:23 +01:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
|
|
|
return rc;
|
|
|
|
if (rc) {
|
|
|
|
session->fullpacket_state = libssh2_NB_state_idle;
|
2009-08-24 23:13:14 +02:00
|
|
|
return rc;
|
2014-02-18 23:38:23 +01:00
|
|
|
}
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->fullpacket_state = libssh2_NB_state_idle;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
return session->fullpacket_packet_type;
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-13 22:15:27 +00:00
|
|
|
/*
|
2009-03-26 15:41:14 +00:00
|
|
|
* _libssh2_transport_read
|
2009-03-13 22:15:27 +00:00
|
|
|
*
|
2009-08-24 23:57:15 +02:00
|
|
|
* Collect a packet into the input queue.
|
2007-06-06 12:34:06 +00:00
|
|
|
*
|
2009-08-24 23:57:15 +02:00
|
|
|
* Returns packet type added to input queue (0 if nothing added), or a
|
|
|
|
* negative error number.
|
2007-02-02 16:21:20 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function reads the binary stream as specified in chapter 6 of RFC4253
|
|
|
|
* "The Secure Shell (SSH) Transport Layer Protocol"
|
2009-08-20 00:56:05 +02:00
|
|
|
*
|
2010-04-16 00:18:51 +02:00
|
|
|
* DOES NOT call _libssh2_error() for ANY error case.
|
2007-02-02 16:21:20 +00:00
|
|
|
*/
|
2009-08-24 23:50:53 +02:00
|
|
|
int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
2007-02-02 16:21:20 +00:00
|
|
|
{
|
2010-10-07 11:18:14 +02:00
|
|
|
int rc;
|
2007-05-28 17:56:08 +00:00
|
|
|
struct transportpacket *p = &session->packet;
|
|
|
|
int remainbuf;
|
|
|
|
int remainpack;
|
|
|
|
int numbytes;
|
|
|
|
int numdecrypt;
|
|
|
|
unsigned char block[MAX_BLOCKSIZE];
|
|
|
|
int blocksize;
|
|
|
|
int encrypted = 1;
|
2011-02-26 05:18:17 +01:00
|
|
|
size_t total_num;
|
2008-07-03 16:26:55 +00:00
|
|
|
|
2009-05-20 14:30:05 +02:00
|
|
|
/* default clear the bit */
|
|
|
|
session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
|
|
|
|
|
2008-07-03 16:26:55 +00:00
|
|
|
/*
|
|
|
|
* All channels, systems, subsystems, etc eventually make it down here
|
|
|
|
* when looking for more incoming data. If a key exchange is going on
|
2009-03-13 22:15:27 +00:00
|
|
|
* (LIBSSH2_STATE_EXCHANGING_KEYS bit is set) then the remote end will
|
|
|
|
* ONLY send key exchange related traffic. In non-blocking mode, there is
|
|
|
|
* a chance to break out of the kex_exchange function with an EAGAIN
|
|
|
|
* status, and never come back to it. If LIBSSH2_STATE_EXCHANGING_KEYS is
|
|
|
|
* active, then we must redirect to the key exchange. However, if
|
|
|
|
* kex_exchange is active (as in it is the one that calls this execution
|
2008-07-03 16:26:55 +00:00
|
|
|
* of packet_read, then don't redirect, as that would be an infinite loop!
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (session->state & LIBSSH2_STATE_EXCHANGING_KEYS &&
|
|
|
|
!(session->state & LIBSSH2_STATE_KEX_ACTIVE)) {
|
|
|
|
|
|
|
|
/* Whoever wants a packet won't get anything until the key re-exchange
|
|
|
|
* is done!
|
|
|
|
*/
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the"
|
2012-03-16 16:29:00 +01:00
|
|
|
" key re-exchange from _libssh2_transport_read");
|
2010-04-24 21:14:16 +02:00
|
|
|
rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
|
2009-08-24 23:50:53 +02:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
2008-07-03 16:26:55 +00:00
|
|
|
}
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
/*
|
|
|
|
* =============================== NOTE ===============================
|
|
|
|
* I know this is very ugly and not a really good use of "goto", but
|
|
|
|
* this case statement would be even uglier to do it any other way
|
|
|
|
*/
|
|
|
|
if (session->readPack_state == libssh2_NB_state_jump1) {
|
|
|
|
session->readPack_state = libssh2_NB_state_idle;
|
|
|
|
encrypted = session->readPack_encrypted;
|
2009-03-26 15:41:14 +00:00
|
|
|
goto libssh2_transport_read_point1;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
do {
|
2007-05-28 17:56:08 +00:00
|
|
|
if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
|
2010-04-17 13:18:15 +02:00
|
|
|
return LIBSSH2_ERROR_NONE;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (session->state & LIBSSH2_STATE_NEWKEYS) {
|
|
|
|
blocksize = session->remote.crypt->blocksize;
|
2007-06-06 12:34:06 +00:00
|
|
|
} else {
|
2007-08-06 20:48:04 +00:00
|
|
|
encrypted = 0; /* not encrypted */
|
|
|
|
blocksize = 5; /* not strictly true, but we can use 5 here to
|
|
|
|
make the checks below work fine still */
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* read/use a whole big chunk into a temporary area stored in
|
|
|
|
the LIBSSH2_SESSION struct. We will decrypt data from that
|
|
|
|
buffer into the packet buffer so this temp one doesn't have
|
|
|
|
to be able to keep a whole SSH packet, just be large enough
|
|
|
|
so that we can read big chunks from the network layer. */
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* how much data there is remaining in the buffer to deal with
|
|
|
|
before we should read more from the network */
|
|
|
|
remainbuf = p->writeidx - p->readidx;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* if remainbuf turns negative we have a bad internal error */
|
|
|
|
assert(remainbuf >= 0);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-08-10 22:22:45 +00:00
|
|
|
if (remainbuf < blocksize) {
|
|
|
|
/* If we have less than a blocksize left, it is too
|
2007-06-06 12:34:06 +00:00
|
|
|
little data to deal with, read more */
|
2007-05-28 17:56:08 +00:00
|
|
|
ssize_t nread;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* move any remainder to the start of the buffer so
|
|
|
|
that we can do a full refill */
|
2007-06-06 12:34:06 +00:00
|
|
|
if (remainbuf) {
|
|
|
|
memmove(p->buf, &p->buf[p->readidx], remainbuf);
|
2007-05-28 17:56:08 +00:00
|
|
|
p->readidx = 0;
|
|
|
|
p->writeidx = remainbuf;
|
2007-06-06 12:34:06 +00:00
|
|
|
} else {
|
2007-05-28 17:56:08 +00:00
|
|
|
/* nothing to move, just zero the indexes */
|
|
|
|
p->readidx = p->writeidx = 0;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
/* now read a big chunk from the network into the temp buffer */
|
2007-08-06 20:48:04 +00:00
|
|
|
nread =
|
2011-08-11 22:26:56 +02:00
|
|
|
LIBSSH2_RECV(session, &p->buf[remainbuf],
|
2009-08-07 11:33:59 +02:00
|
|
|
PACKETBUFSIZE - remainbuf,
|
|
|
|
LIBSSH2_SOCKET_RECV_FLAGS(session));
|
2010-11-12 21:53:35 +01:00
|
|
|
if (nread <= 0) {
|
2007-09-24 20:49:43 +00:00
|
|
|
/* check if this is due to EAGAIN and return the special
|
|
|
|
return code if so, error out normally otherwise */
|
2010-11-12 21:53:35 +01:00
|
|
|
if ((nread < 0) && (nread == -EAGAIN)) {
|
2009-08-25 00:54:47 +02:00
|
|
|
session->socket_block_directions |=
|
2008-11-24 13:31:00 +00:00
|
|
|
LIBSSH2_SESSION_BLOCK_INBOUND;
|
2010-04-17 13:18:15 +02:00
|
|
|
return LIBSSH2_ERROR_EAGAIN;
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2010-10-07 11:18:14 +02:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
|
2010-11-12 21:53:35 +01:00
|
|
|
"Error recving %d bytes (got %d)",
|
|
|
|
PACKETBUFSIZE - remainbuf, -nread);
|
2010-10-07 11:18:14 +02:00
|
|
|
return LIBSSH2_ERROR_SOCKET_RECV;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2010-11-13 12:25:06 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
|
|
|
|
"Recved %d/%d bytes to %p+%d", nread,
|
|
|
|
PACKETBUFSIZE - remainbuf, p->buf, remainbuf);
|
2009-05-20 14:30:05 +02:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
debugdump(session, "libssh2_transport_read() raw",
|
2007-05-28 17:56:08 +00:00
|
|
|
&p->buf[remainbuf], nread);
|
|
|
|
/* advance write pointer */
|
|
|
|
p->writeidx += nread;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* update remainbuf counter */
|
|
|
|
remainbuf = p->writeidx - p->readidx;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* how much data to deal with from the buffer */
|
|
|
|
numbytes = remainbuf;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (!p->total_num) {
|
2007-05-28 17:56:08 +00:00
|
|
|
/* No payload package area allocated yet. To know the
|
|
|
|
size of this payload, we need to decrypt the first
|
|
|
|
blocksize data. */
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-11-08 13:51:23 +00:00
|
|
|
if (numbytes < blocksize) {
|
|
|
|
/* we can't act on anything less than blocksize, but this
|
|
|
|
check is only done for the initial block since once we have
|
|
|
|
got the start of a block we can in fact deal with fractions
|
|
|
|
*/
|
2009-08-25 00:54:47 +02:00
|
|
|
session->socket_block_directions |=
|
|
|
|
LIBSSH2_SESSION_BLOCK_INBOUND;
|
2010-04-17 13:18:15 +02:00
|
|
|
return LIBSSH2_ERROR_EAGAIN;
|
2007-11-08 13:51:23 +00:00
|
|
|
}
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (encrypted) {
|
|
|
|
rc = decrypt(session, &p->buf[p->readidx], block, blocksize);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc != LIBSSH2_ERROR_NONE) {
|
2007-05-28 17:56:08 +00:00
|
|
|
return rc;
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
/* save the first 5 bytes of the decrypted package, to be
|
2007-06-06 12:34:06 +00:00
|
|
|
used in the hash calculation later down. */
|
2007-05-28 17:56:08 +00:00
|
|
|
memcpy(p->init, &p->buf[p->readidx], 5);
|
2007-06-06 12:34:06 +00:00
|
|
|
} else {
|
2007-05-28 17:56:08 +00:00
|
|
|
/* the data is plain, just copy it verbatim to
|
|
|
|
the working block buffer */
|
|
|
|
memcpy(block, &p->buf[p->readidx], blocksize);
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* advance the read pointer */
|
|
|
|
p->readidx += blocksize;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* we now have the initial blocksize bytes decrypted,
|
2007-06-06 12:34:06 +00:00
|
|
|
* and we can extract packet and padding length from it
|
|
|
|
*/
|
2009-03-17 13:48:35 +00:00
|
|
|
p->packet_length = _libssh2_ntohu32(block);
|
2009-07-27 22:54:18 +02:00
|
|
|
if (p->packet_length < 1)
|
2010-10-07 11:18:14 +02:00
|
|
|
return LIBSSH2_ERROR_DECRYPT;
|
2009-07-07 13:26:42 +02:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
p->padding_length = block[4];
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
/* total_num is the number of bytes following the initial
|
2007-06-06 12:34:06 +00:00
|
|
|
(5 bytes) packet length and padding length fields */
|
2011-02-26 05:18:17 +01:00
|
|
|
total_num =
|
2007-08-06 20:48:04 +00:00
|
|
|
p->packet_length - 1 +
|
|
|
|
(encrypted ? session->remote.mac->mac_len : 0);
|
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* RFC4253 section 6.1 Maximum Packet Length says:
|
|
|
|
*
|
|
|
|
* "All implementations MUST be able to process
|
|
|
|
* packets with uncompressed payload length of 32768
|
|
|
|
* bytes or less and total packet size of 35000 bytes
|
|
|
|
* or less (including length, padding length, payload,
|
|
|
|
* padding, and MAC.)."
|
|
|
|
*/
|
2011-02-26 05:18:17 +01:00
|
|
|
if (total_num > LIBSSH2_PACKET_MAXPAYLOAD) {
|
2010-04-17 13:18:15 +02:00
|
|
|
return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* Get a packet handle put data into. We get one to
|
|
|
|
hold all data, including padding and MAC. */
|
2011-02-26 05:18:17 +01:00
|
|
|
p->payload = LIBSSH2_ALLOC(session, total_num);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (!p->payload) {
|
2010-04-17 13:18:15 +02:00
|
|
|
return LIBSSH2_ERROR_ALLOC;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2011-02-26 05:18:17 +01:00
|
|
|
p->total_num = total_num;
|
2007-05-28 17:56:08 +00:00
|
|
|
/* init write pointer to start of payload buffer */
|
|
|
|
p->wptr = p->payload;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (blocksize > 5) {
|
2007-05-28 17:56:08 +00:00
|
|
|
/* copy the data from index 5 to the end of
|
|
|
|
the blocksize from the temporary buffer to
|
|
|
|
the start of the decrypted buffer */
|
2007-08-06 20:48:04 +00:00
|
|
|
memcpy(p->wptr, &block[5], blocksize - 5);
|
|
|
|
p->wptr += blocksize - 5; /* advance write pointer */
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* init the data_num field to the number of bytes of
|
|
|
|
the package read so far */
|
|
|
|
p->data_num = p->wptr - p->payload;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* we already dealt with a blocksize worth of data */
|
|
|
|
numbytes -= blocksize;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* how much there is left to add to the current payload
|
2007-06-06 12:34:06 +00:00
|
|
|
package */
|
2007-05-28 17:56:08 +00:00
|
|
|
remainpack = p->total_num - p->data_num;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (numbytes > remainpack) {
|
2007-08-06 20:48:04 +00:00
|
|
|
/* if we have more data in the buffer than what is going into this
|
2007-06-06 12:34:06 +00:00
|
|
|
particular packet, we limit this round to this packet only */
|
2007-05-28 17:56:08 +00:00
|
|
|
numbytes = remainpack;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (encrypted) {
|
2007-05-28 17:56:08 +00:00
|
|
|
/* At the end of the incoming stream, there is a MAC,
|
|
|
|
and we don't want to decrypt that since we need it
|
|
|
|
"raw". We MUST however decrypt the padding data
|
|
|
|
since it is used for the hash later on. */
|
|
|
|
int skip = session-> |