summaryrefslogtreecommitdiff
path: root/libs/libssh2/src/transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libssh2/src/transport.c')
-rw-r--r--libs/libssh2/src/transport.c419
1 files changed, 292 insertions, 127 deletions
diff --git a/libs/libssh2/src/transport.c b/libs/libssh2/src/transport.c
index 531f5aa15a..e1120656c3 100644
--- a/libs/libssh2/src/transport.c
+++ b/libs/libssh2/src/transport.c
@@ -156,7 +156,7 @@ decrypt(LIBSSH2_SESSION * session, unsigned char *source,
lowerfirstlast = LAST_BLOCK;
}
- if(session->remote.crypt->crypt(session, source, decryptlen,
+ if(session->remote.crypt->crypt(session, 0, source, decryptlen,
&session->remote.crypt_abstract,
lowerfirstlast)) {
LIBSSH2_FREE(session, p->payload);
@@ -186,33 +186,39 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
struct transportpacket *p = &session->packet;
int rc;
int compressed;
+ const LIBSSH2_MAC_METHOD *remote_mac = NULL;
uint32_t seq = session->remote.seqno;
+ if(!encrypted || (!CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET) &&
+ !CRYPT_FLAG_R(session, INTEGRATED_MAC))) {
+ remote_mac = session->remote.mac;
+ }
+
if(session->fullpacket_state == libssh2_NB_state_idle) {
session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
session->fullpacket_payload_len = p->packet_length - 1;
- if(encrypted && !CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
+ if(encrypted && remote_mac) {
/* Calculate MAC hash */
- int etm = session->remote.mac->etm;
- size_t mac_len = session->remote.mac->mac_len;
+ int etm = remote_mac->etm;
+ size_t mac_len = remote_mac->mac_len;
if(etm) {
/* store hash here */
- session->remote.mac->hash(session, macbuf,
- session->remote.seqno,
- p->payload, p->total_num - mac_len,
- NULL, 0,
- &session->remote.mac_abstract);
+ remote_mac->hash(session, macbuf,
+ session->remote.seqno,
+ p->payload, p->total_num - mac_len,
+ NULL, 0,
+ &session->remote.mac_abstract);
}
else {
/* store hash here */
- session->remote.mac->hash(session, macbuf,
- session->remote.seqno,
- p->init, 5,
- p->payload,
- session->fullpacket_payload_len,
- &session->remote.mac_abstract);
+ remote_mac->hash(session, macbuf,
+ session->remote.seqno,
+ p->init, 5,
+ p->payload,
+ session->fullpacket_payload_len,
+ &session->remote.mac_abstract);
}
/* Compare the calculated hash with the MAC we just read from
@@ -272,6 +278,10 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
p->payload = decrypt_buffer;
}
}
+ else if(encrypted && CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ /* etm trim off padding byte from payload */
+ memmove(p->payload, &p->payload[1], p->packet_length - 1);
+ }
session->remote.seqno++;
@@ -370,6 +380,8 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
use them */
int encrypted = 1; /* whether the packet is encrypted or not */
int firstlast = FIRST_BLOCK; /* if the first or last block to decrypt */
+ unsigned int auth_len = 0; /* length of the authentication tag */
+ const LIBSSH2_MAC_METHOD *remote_mac = NULL; /* The remote MAC, if used */
/* default clear the bit */
session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
@@ -425,7 +437,16 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
make the checks below work fine still */
}
- etm = encrypted && session->local.mac ? session->local.mac->etm : 0;
+ if(encrypted) {
+ if(CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ auth_len = session->remote.crypt->auth_len;
+ }
+ else {
+ remote_mac = session->remote.mac;
+ }
+ }
+
+ etm = encrypted && remote_mac ? remote_mac->etm : 0;
/* read/use a whole big chunk into a temporary area stored in
the LIBSSH2_SESSION struct. We will decrypt data from that
@@ -440,7 +461,9 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
/* if remainbuf turns negative we have a bad internal error */
assert(remainbuf >= 0);
- if(remainbuf < blocksize) {
+ if(remainbuf < blocksize ||
+ (CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)
+ && ((ssize_t)p->total_num) > remainbuf)) {
/* If we have less than a blocksize left, it is too
little data to deal with, read more */
ssize_t nread;
@@ -516,7 +539,35 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
}
if(etm) {
- p->packet_length = _libssh2_ntohu32(&p->buf[p->readidx]);
+ /* etm size field is not encrypted */
+ memcpy(block, &p->buf[p->readidx], 4);
+ memcpy(p->init, &p->buf[p->readidx], 4);
+ }
+ else if(encrypted && session->remote.crypt->get_len) {
+ unsigned int len = 0;
+ unsigned char *ptr = NULL;
+
+ rc = session->remote.crypt->get_len(session,
+ session->remote.seqno,
+ &p->buf[p->readidx],
+ numbytes,
+ &len,
+ &session->remote.crypt_abstract);
+
+ if(rc != LIBSSH2_ERROR_NONE) {
+ p->total_num = 0; /* no packet buffer available */
+ if(p->payload)
+ LIBSSH2_FREE(session, p->payload);
+ p->payload = NULL;
+ return rc;
+ }
+
+ /* store size in buffers for use below */
+ ptr = &block[0];
+ _libssh2_store_u32(&ptr, len);
+
+ ptr = &p->init[0];
+ _libssh2_store_u32(&ptr, len);
}
else {
if(encrypted) {
@@ -546,33 +597,61 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
p->packet_length = _libssh2_ntohu32(block);
}
- if(p->packet_length < 1) {
- return LIBSSH2_ERROR_DECRYPT;
- }
- else if(p->packet_length > LIBSSH2_PACKET_MAXPAYLOAD) {
- return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
- }
+ if(!encrypted || !CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ if(p->packet_length < 1) {
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+ else if(p->packet_length > LIBSSH2_PACKET_MAXPAYLOAD) {
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+ }
- if(etm) {
- /* we collect entire undecrypted packet including the
- packet length field that we run MAC over */
- total_num = 4 + p->packet_length +
- session->remote.mac->mac_len;
+ if(etm) {
+ /* we collect entire undecrypted packet including the
+ packet length field that we run MAC over */
+ p->packet_length = _libssh2_ntohu32(block);
+ total_num = 4 + p->packet_length +
+ remote_mac->mac_len;
+ }
+ else {
+ /* padding_length has not been authenticated yet, but it
+ won't actually be used (except for the sanity check
+ immediately following) until after the entire packet is
+ authenticated, so this is safe. */
+ p->padding_length = block[4];
+ if(p->padding_length > p->packet_length - 1) {
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+
+ /* total_num is the number of bytes following the initial
+ (5 bytes) packet length and padding length fields */
+ total_num = p->packet_length - 1 +
+ (encrypted ? remote_mac->mac_len : 0);
+ }
}
else {
- /* padding_length has not been authenticated yet, but it won't
- actually be used (except for the sanity check immediately
- following) until after the entire packet is authenticated,
- so this is safe. */
- p->padding_length = block[4];
- if(p->padding_length > p->packet_length - 1) {
+ /* advance the read pointer past size field if the packet
+ length is not required for decryption */
+
+ /* add size field to be included in total packet size
+ * calculation so it doesn't get dropped off on subsequent
+ * partial reads
+ */
+ total_num = 4;
+
+ p->packet_length = _libssh2_ntohu32(block);
+ if(p->packet_length < 1)
return LIBSSH2_ERROR_DECRYPT;
- }
- /* total_num is the number of bytes following the initial
- (5 bytes) packet length and padding length fields */
- total_num = p->packet_length - 1 +
- (encrypted ? session->remote.mac->mac_len : 0);
+ /* total_num may include size field, however due to existing
+ * logic it needs to be removed after the entire packet is read
+ */
+
+ total_num += p->packet_length +
+ (remote_mac ? remote_mac->mac_len : 0) + auth_len;
+
+ /* don't know what padding is until we decrypt the full
+ packet */
+ p->padding_length = 0;
}
/* RFC4253 section 6.1 Maximum Packet Length says:
@@ -597,32 +676,42 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
/* init write pointer to start of payload buffer */
p->wptr = p->payload;
- if(!etm && blocksize > 5) {
- /* copy the data from index 5 to the end of
- the blocksize from the temporary buffer to
- the start of the decrypted buffer */
- if(blocksize - 5 <= (int) total_num) {
- memcpy(p->wptr, &block[5], blocksize - 5);
- p->wptr += blocksize - 5; /* advance write pointer */
- if(etm) {
- /* advance past unencrypted packet length */
- p->wptr += 4;
+ if(!encrypted || !CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ if(!etm && blocksize > 5) {
+ /* copy the data from index 5 to the end of
+ the blocksize from the temporary buffer to
+ the start of the decrypted buffer */
+ if(blocksize - 5 <= (int) total_num) {
+ memcpy(p->wptr, &block[5], blocksize - 5);
+ p->wptr += blocksize - 5; /* advance write pointer */
+ if(etm) {
+ /* advance past unencrypted packet length */
+ p->wptr += 4;
+ }
+ }
+ else {
+ if(p->payload)
+ LIBSSH2_FREE(session, p->payload);
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
}
}
- else {
- if(p->payload)
- LIBSSH2_FREE(session, p->payload);
- return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
- }
- }
- /* init the data_num field to the number of bytes of
- the package read so far */
- p->data_num = p->wptr - p->payload;
+ /* init the data_num field to the number of bytes of
+ the package read so far */
+ p->data_num = p->wptr - p->payload;
+
+ /* we already dealt with a blocksize worth of data */
+ if(!etm)
+ numbytes -= blocksize;
+ }
+ else {
+ /* haven't started reading payload yet */
+ p->data_num = 0;
- /* we already dealt with a blocksize worth of data */
- if(!etm)
- numbytes -= blocksize;
+ /* we already dealt with packet size worth of data */
+ if(!encrypted)
+ numbytes -= 4;
+ }
}
/* how much there is left to add to the current payload
@@ -635,12 +724,26 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
numbytes = remainpack;
}
+ if(encrypted && CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ if(numbytes < remainpack) {
+ /* need a full packet before checking MAC */
+ session->socket_block_directions |=
+ LIBSSH2_SESSION_BLOCK_INBOUND;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ /* we have a full packet, now remove the size field from numbytes
+ and total_num to process only the packet data */
+ numbytes -= 4;
+ p->total_num -= 4;
+ }
+
if(encrypted && !etm) {
/* 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->remote.mac->mac_len;
+ int skip = (remote_mac ? remote_mac->mac_len : 0) + auth_len;
if(CRYPT_FLAG_R(session, INTEGRATED_MAC))
/* This crypto method DOES need the MAC to go through
@@ -687,11 +790,44 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
/* if there are bytes to decrypt, do that */
if(numdecrypt > 0) {
/* now decrypt the lot */
- rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt,
- firstlast);
- if(rc != LIBSSH2_ERROR_NONE) {
- p->total_num = 0; /* no packet buffer available */
- return rc;
+ if(CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ rc = session->remote.crypt->crypt(session,
+ session->remote.seqno,
+ &p->buf[p->readidx],
+ numdecrypt,
+ &session->remote.crypt_abstract,
+ 0);
+
+ if(rc != LIBSSH2_ERROR_NONE) {
+ p->total_num = 0; /* no packet buffer available */
+ return rc;
+ }
+
+ memcpy(p->wptr, &p->buf[p->readidx], numbytes);
+
+ /* advance read index past size field now that we've decrypted
+ full packet */
+ p->readidx += 4;
+
+ /* include auth tag in bytes decrypted */
+ numdecrypt += auth_len;
+
+ /* set padding now that the packet has been verified and
+ decrypted */
+ p->padding_length = p->wptr[0];
+
+ if(p->padding_length > p->packet_length - 1) {
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+ }
+ else {
+ rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt,
+ firstlast);
+
+ if(rc != LIBSSH2_ERROR_NONE) {
+ p->total_num = 0; /* no packet buffer available */
+ return rc;
+ }
}
/* advance the read pointer */
@@ -783,7 +919,8 @@ send_existing(LIBSSH2_SESSION *session, const unsigned char *data,
make the caller really notice his/hers flaw, we return error for
this case */
_libssh2_debug((session, LIBSSH2_TRACE_SOCKET,
- "Address is different, but will resume nonetheless"));
+ "Address is different, returning EAGAIN"));
+ return LIBSSH2_ERROR_EAGAIN;
}
*ret = 1; /* set to make our parent return */
@@ -869,6 +1006,8 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
ssize_t ret;
int rc;
const unsigned char *orgdata = data;
+ const LIBSSH2_MAC_METHOD *local_mac = NULL;
+ unsigned int auth_len = 0;
size_t orgdata_len = data_len;
size_t crypt_offset, etm_crypt_offset;
@@ -908,7 +1047,15 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0;
- etm = encrypted && session->local.mac ? session->local.mac->etm : 0;
+ if(encrypted && session->local.crypt &&
+ CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ auth_len = session->local.crypt->auth_len;
+ }
+ else {
+ local_mac = session->local.mac;
+ }
+
+ etm = encrypted && local_mac ? local_mac->etm : 0;
compressed = session->local.comp &&
session->local.comp->compress &&
@@ -973,7 +1120,8 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
4 for the packet_length field */
/* subtract 4 bytes of the packet_length field when padding AES-GCM
or with ETM */
- crypt_offset = (etm || (encrypted && CRYPT_FLAG_R(session, PKTLEN_AAD)))
+ crypt_offset = (etm || auth_len ||
+ (encrypted && CRYPT_FLAG_R(session, PKTLEN_AAD)))
? 4 : 0;
etm_crypt_offset = etm ? 4 : 0;
@@ -1004,7 +1152,9 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
/* append the MAC length to the total_length size */
total_length =
- packet_length + (encrypted ? session->local.mac->mac_len : 0);
+ packet_length + (encrypted && local_mac ? local_mac->mac_len : 0);
+
+ total_length += auth_len;
/* store packet_length, which is the size of the whole packet except
the MAC and the packet_length field itself */
@@ -1027,63 +1177,78 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
fields except the MAC field itself. This is skipped in the
INTEGRATED_MAC case, where the crypto algorithm also does its
own hash. */
- if(!etm && !CRYPT_FLAG_R(session, INTEGRATED_MAC)) {
- if(session->local.mac->hash(session, p->outbuf + packet_length,
- session->local.seqno, p->outbuf,
- packet_length, NULL, 0,
- &session->local.mac_abstract))
+ if(!etm && local_mac && !CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
+ if(local_mac->hash(session, p->outbuf + packet_length,
+ session->local.seqno, p->outbuf,
+ packet_length, NULL, 0,
+ &session->local.mac_abstract))
return _libssh2_error(session, LIBSSH2_ERROR_MAC_FAILURE,
"Failed to calculate MAC");
}
- /* Encrypt the whole packet data, one block size at a time.
- The MAC field is not encrypted unless INTEGRATED_MAC. */
- /* Some crypto back-ends could handle a single crypt() call for
- encryption, but (presumably) others cannot, so break it up
- into blocksize-sized chunks to satisfy them all. */
- for(i = etm_crypt_offset; i < packet_length;
- i += session->local.crypt->blocksize) {
- unsigned char *ptr = &p->outbuf[i];
- size_t bsize = LIBSSH2_MIN(session->local.crypt->blocksize,
- (int)(packet_length-i));
- /* The INTEGRATED_MAC case always has an extra call below, so it
- will never be LAST_BLOCK up here. */
- int firstlast = i == 0 ? FIRST_BLOCK :
- (!CRYPT_FLAG_L(session, INTEGRATED_MAC)
- && (i == packet_length - session->local.crypt->blocksize)
- ? LAST_BLOCK: MIDDLE_BLOCK);
- /* In the AAD case, the last block would be only 4 bytes because
- everything is offset by 4 since the initial packet_length isn't
- encrypted. In this case, combine that last short packet with the
- previous one since AES-GCM crypt() assumes that the entire MAC
- is available in that packet so it can set that to the
- authentication tag. */
- if(!CRYPT_FLAG_L(session, INTEGRATED_MAC))
- if(i > packet_length - 2*bsize) {
- /* increase the final block size */
- bsize = packet_length - i;
- /* advance the loop counter by the extra amount */
- i += bsize - session->local.crypt->blocksize;
- }
- _libssh2_debug((session, LIBSSH2_TRACE_SOCKET,
- "crypting bytes %lu-%lu", (unsigned long)i,
- (unsigned long)(i + bsize - 1)));
- if(session->local.crypt->crypt(session, ptr,
- bsize,
+ if(CRYPT_FLAG_L(session, REQUIRES_FULL_PACKET)) {
+ if(session->local.crypt->crypt(session,
+ session->local.seqno,
+ p->outbuf,
+ packet_length,
&session->local.crypt_abstract,
- firstlast))
- return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ 0)) {
+ return LIBSSH2_ERROR_ENCRYPT;
+ }
}
- /* Call crypt() one last time so it can be filled in with the MAC */
- if(CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
- int authlen = session->local.mac->mac_len;
- assert((size_t)total_length <=
- packet_length + session->local.crypt->blocksize);
- if(session->local.crypt->crypt(session, &p->outbuf[packet_length],
- authlen,
- &session->local.crypt_abstract,
- LAST_BLOCK))
- return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ else {
+ /* Encrypt the whole packet data, one block size at a time.
+ The MAC field is not encrypted unless INTEGRATED_MAC. */
+ /* Some crypto back-ends could handle a single crypt() call for
+ encryption, but (presumably) others cannot, so break it up
+ into blocksize-sized chunks to satisfy them all. */
+ for(i = etm_crypt_offset; i < packet_length;
+ i += session->local.crypt->blocksize) {
+ unsigned char *ptr = &p->outbuf[i];
+ size_t bsize = LIBSSH2_MIN(session->local.crypt->blocksize,
+ (int)(packet_length-i));
+ /* The INTEGRATED_MAC case always has an extra call below, so
+ it will never be LAST_BLOCK up here. */
+ int firstlast = i == 0 ? FIRST_BLOCK :
+ (!CRYPT_FLAG_L(session, INTEGRATED_MAC)
+ && (i == packet_length - session->local.crypt->blocksize)
+ ? LAST_BLOCK : MIDDLE_BLOCK);
+ /* In the AAD case, the last block would be only 4 bytes
+ because everything is offset by 4 since the initial
+ packet_length isn't encrypted. In this case, combine that last
+ short packet with the previous one since AES-GCM crypt()
+ assumes that the entire MAC is available in that packet so it
+ can set that to the authentication tag. */
+ if(!CRYPT_FLAG_L(session, INTEGRATED_MAC))
+ if(i > packet_length - 2*bsize) {
+ /* increase the final block size */
+ bsize = packet_length - i;
+ /* advance the loop counter by the extra amount */
+ i += bsize - session->local.crypt->blocksize;
+ }
+ _libssh2_debug((session, LIBSSH2_TRACE_SOCKET,
+ "crypting bytes %lu-%lu", (unsigned long)i,
+ (unsigned long)(i + bsize - 1)));
+ if(session->local.crypt->crypt(session, 0, ptr,
+ bsize,
+ &session->local.crypt_abstract,
+ firstlast))
+ return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ }
+
+ /* Call crypt one last time so it can be filled in with the MAC */
+ if(CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
+ int authlen = local_mac->mac_len;
+ assert((size_t)total_length <=
+ packet_length + session->local.crypt->blocksize);
+ if(session->local.crypt->crypt(session,
+ 0,
+ &p->outbuf[packet_length],
+ authlen,
+ &session->local.crypt_abstract,
+ LAST_BLOCK))
+ return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ }
}
if(etm) {
@@ -1092,10 +1257,10 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
calculated on the entire packet (length plain the rest
encrypted), including all fields except the MAC field
itself. */
- if(session->local.mac->hash(session, p->outbuf + packet_length,
- session->local.seqno, p->outbuf,
- packet_length, NULL, 0,
- &session->local.mac_abstract))
+ if(local_mac->hash(session, p->outbuf + packet_length,
+ session->local.seqno, p->outbuf,
+ packet_length, NULL, 0,
+ &session->local.mac_abstract))
return _libssh2_error(session, LIBSSH2_ERROR_MAC_FAILURE,
"Failed to calculate MAC");
}