diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/kex.c | 8 | ||||
-rw-r--r-- | src/packet.c | 103 | ||||
-rw-r--r-- | src/packet_crypt.c | 35 | ||||
-rw-r--r-- | src/wrapper.c | 18 |
4 files changed, 115 insertions, 49 deletions
@@ -146,8 +146,8 @@ static const char *default_methods[] = { PUBLIC_KEY_ALGORITHMS, AES BLOWFISH DES, AES BLOWFISH DES, - "hmac-sha2-256,hmac-sha2-512,hmac-sha1", - "hmac-sha2-256,hmac-sha2-512,hmac-sha1", + "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1", + "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1", "none", "none", "", @@ -161,8 +161,8 @@ static const char *supported_methods[] = { PUBLIC_KEY_ALGORITHMS, CHACHA20 AES BLOWFISH DES_SUPPORTED, CHACHA20 AES BLOWFISH DES_SUPPORTED, - "hmac-sha2-256,hmac-sha2-512,hmac-sha1", - "hmac-sha2-256,hmac-sha2-512,hmac-sha1", + "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1", + "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1", ZLIB, ZLIB, "", diff --git a/src/packet.c b/src/packet.c index d0c5d60b..6acd2835 100644 --- a/src/packet.c +++ b/src/packet.c @@ -1045,6 +1045,8 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) size_t processed = 0; /* number of byte processed from the callback */ enum ssh_packet_filter_result_e filter_result; struct ssh_crypto_struct *crypto = NULL; + bool etm = false; + int etm_packet_offset = 0; bool ok; crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); @@ -1052,9 +1054,17 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) current_macsize = hmac_digest_len(crypto->in_hmac); blocksize = crypto->in_cipher->blocksize; lenfield_blocksize = crypto->in_cipher->lenfield_blocksize; + etm = crypto->in_hmac_etm; } - if (lenfield_blocksize == 0) { + if (etm) { + /* In EtM mode packet size is unencrypted. This means + * we need to use this offset and set the block size + * that is part of the encrypted part to 0. + */ + etm_packet_offset = sizeof(uint32_t); + lenfield_blocksize = 0; + } else if (lenfield_blocksize == 0) { lenfield_blocksize = blocksize; } if (data == NULL) { @@ -1077,10 +1087,10 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) #endif switch(session->packet_state) { case PACKET_STATE_INIT: - if (receivedlen < lenfield_blocksize) { + if (receivedlen < lenfield_blocksize + etm_packet_offset) { /* - * We didn't receive enough data to read at least one - * block size, give up + * We didn't receive enough data to read either at least one + * block size or the unencrypted length in EtM mode. */ #ifdef DEBUG_PACKET SSH_LOG(SSH_LOG_PACKET, @@ -1107,13 +1117,20 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) } } - ptr = ssh_buffer_allocate(session->in_buffer, lenfield_blocksize); - if (ptr == NULL) { - goto error; + if (!etm) { + ptr = ssh_buffer_allocate(session->in_buffer, lenfield_blocksize); + if (ptr == NULL) { + goto error; + } + packet_len = ssh_packet_decrypt_len(session, ptr, (uint8_t *)data); + to_be_read = packet_len - lenfield_blocksize + sizeof(uint32_t); + } else { + /* Length is unencrypted in case of Encrypt-then-MAC */ + packet_len = PULL_BE_U32(data, 0); + to_be_read = packet_len - etm_packet_offset; } - processed += lenfield_blocksize; - packet_len = ssh_packet_decrypt_len(session, ptr, (uint8_t *)data); + processed += lenfield_blocksize + etm_packet_offset; if (packet_len > MAX_PACKET_LEN) { ssh_set_error(session, SSH_FATAL, @@ -1121,7 +1138,6 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) packet_len, packet_len); goto error; } - to_be_read = packet_len - lenfield_blocksize + sizeof(uint32_t); if (to_be_read < 0) { /* remote sshd sends invalid sizes? */ ssh_set_error(session, @@ -1136,7 +1152,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) FALL_THROUGH; case PACKET_STATE_SIZEREAD: packet_len = session->in_packet.len; - processed = lenfield_blocksize; + processed = lenfield_blocksize + etm_packet_offset; to_be_read = packet_len + sizeof(uint32_t) + current_macsize; /* if to_be_read is zero, the whole packet was blocksize bytes. */ if (to_be_read != 0) { @@ -1151,13 +1167,13 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) return 0; } - packet_second_block = (uint8_t*)data + lenfield_blocksize; + packet_second_block = (uint8_t*)data + lenfield_blocksize + etm_packet_offset; processed = to_be_read - current_macsize; } /* remaining encrypted bytes from the packet, MAC not included */ packet_remaining = - packet_len - (lenfield_blocksize - sizeof(uint32_t)); + packet_len - (lenfield_blocksize - sizeof(uint32_t) + etm_packet_offset); cleartext_packet = ssh_buffer_allocate(session->in_buffer, packet_remaining); if (cleartext_packet == NULL) { @@ -1166,16 +1182,30 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) if (packet_second_block != NULL) { if (crypto != NULL) { + mac = packet_second_block + packet_remaining; + + if (etm) { + rc = ssh_packet_hmac_verify(session, + data, + processed, + mac, + crypto->in_hmac); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "HMAC error"); + goto error; + } + } /* - * Decrypt the rest of the packet (lenfield_blocksize bytes - * already have been decrypted) + * Decrypt the packet. In case of EtM mode, the length is already + * known as it's unencrypted. In the other case, lenfield_blocksize bytes + * already have been decrypted. */ if (packet_remaining > 0) { rc = ssh_packet_decrypt(session, cleartext_packet, (uint8_t *)data, - lenfield_blocksize, - processed - lenfield_blocksize); + lenfield_blocksize + etm_packet_offset, + processed - (lenfield_blocksize + etm_packet_offset)); if (rc < 0) { ssh_set_error(session, SSH_FATAL, @@ -1183,16 +1213,17 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) goto error; } } - mac = packet_second_block + packet_remaining; - rc = ssh_packet_hmac_verify(session, - ssh_buffer_get(session->in_buffer), - ssh_buffer_get_len(session->in_buffer), - mac, - crypto->in_hmac); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "HMAC error"); - goto error; + if (!etm) { + rc = ssh_packet_hmac_verify(session, + ssh_buffer_get(session->in_buffer), + ssh_buffer_get_len(session->in_buffer), + mac, + crypto->in_hmac); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "HMAC error"); + goto error; + } } processed += current_macsize; } else { @@ -1212,8 +1243,10 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) } #endif - /* skip the size field which has been processed before */ - ssh_buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); + if (!etm) { + /* skip the size field which has been processed before */ + ssh_buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); + } rc = ssh_buffer_get_u8(session->in_buffer, &padding); if (rc == 0) { @@ -1525,12 +1558,15 @@ static int packet_send2(ssh_session session) uint8_t header[5] = {0}; uint8_t type, *payload; int rc = SSH_ERROR; + bool etm = false; + int etm_packet_offset = 0; crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT); if (crypto) { blocksize = crypto->out_cipher->blocksize; lenfield_blocksize = crypto->out_cipher->lenfield_blocksize; hmac_type = crypto->out_hmac; + etm = crypto->out_hmac_etm; } else { hmac_type = session->next_crypto->out_hmac; } @@ -1539,6 +1575,11 @@ static int packet_send2(ssh_session session) type = payload[0]; /* type is the first byte of the packet now */ payloadsize = currentlen; + if (etm) { + etm_packet_offset = sizeof(uint32_t); + lenfield_blocksize = 0; + } + #ifdef WITH_ZLIB if (crypto != NULL && crypto->do_compress_out && ssh_buffer_get_len(session->out_buffer) > 0) { @@ -1551,8 +1592,8 @@ static int packet_send2(ssh_session session) #endif /* WITH_ZLIB */ compsize = currentlen; /* compressed payload + packet len (4) + padding_size len (1) */ - /* totallen - lenfield_blocksize must be equal to 0 (mod blocksize) */ - padding_size = (blocksize - ((blocksize - lenfield_blocksize + currentlen + 5) % blocksize)); + /* totallen - lenfield_blocksize - etm_packet_offset must be equal to 0 (mod blocksize) */ + padding_size = (blocksize - ((blocksize - lenfield_blocksize - etm_packet_offset + currentlen + 5) % blocksize)); if (padding_size < 4) { padding_size += blocksize; } @@ -1567,7 +1608,7 @@ static int packet_send2(ssh_session session) } } - finallen = currentlen + padding_size + 1; + finallen = currentlen - etm_packet_offset + padding_size + 1; PUSH_BE_U32(header, 0, finallen); PUSH_BE_U8(header, 4, padding_size); diff --git a/src/packet_crypt.c b/src/packet_crypt.c index 2fd8e0fe..2ed097ad 100644 --- a/src/packet_crypt.c +++ b/src/packet_crypt.c @@ -42,6 +42,7 @@ #include "libssh/wrapper.h" #include "libssh/crypto.h" #include "libssh/buffer.h" +#include "libssh/bytearray.h" /** @internal * @brief decrypt the packet length from a raw encrypted packet, and store the first decrypted @@ -132,9 +133,11 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) struct ssh_cipher_struct *cipher = NULL; HMACCTX ctx = NULL; char *out = NULL; + int etm_packet_offset = 0; unsigned int finallen, blocksize; uint32_t seq, lenfield_blocksize; enum ssh_hmac_e type; + bool etm; assert(len); @@ -145,7 +148,15 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) blocksize = crypto->out_cipher->blocksize; lenfield_blocksize = crypto->out_cipher->lenfield_blocksize; - if ((len - lenfield_blocksize) % blocksize != 0) { + + type = crypto->out_hmac; + etm = crypto->out_hmac_etm; + + if (etm) { + etm_packet_offset = sizeof(uint32_t); + } + + if ((len - lenfield_blocksize - etm_packet_offset) % blocksize != 0) { ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set" " on at least one blocksize (received %d)", len); return NULL; @@ -155,23 +166,35 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) return NULL; } - type = crypto->out_hmac; seq = ntohl(session->send_seq); cipher = crypto->out_cipher; if (cipher->aead_encrypt != NULL) { cipher->aead_encrypt(cipher, data, out, len, crypto->hmacbuf, session->send_seq); + memcpy(data, out, len); } else { ctx = hmac_init(crypto->encryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { SAFE_FREE(out); return NULL; } - hmac_update(ctx,(unsigned char *)&seq,sizeof(uint32_t)); - hmac_update(ctx,data,len); - hmac_final(ctx, crypto->hmacbuf, &finallen); + if (!etm) { + hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t)); + hmac_update(ctx, data, len); + hmac_final(ctx, crypto->hmacbuf, &finallen); + } + + cipher->encrypt(cipher, (uint8_t*)data + etm_packet_offset, out, len - etm_packet_offset); + memcpy((uint8_t*)data + etm_packet_offset, out, len - etm_packet_offset); + + if (etm) { + PUSH_BE_U32(data, 0, len - etm_packet_offset); + hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t)); + hmac_update(ctx, data, len); + hmac_final(ctx, crypto->hmacbuf, &finallen); + } #ifdef DEBUG_CRYPTO ssh_print_hexa("mac: ",data,hmac_digest_len(type)); if (finallen != hmac_digest_len(type)) { @@ -179,9 +202,7 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) } ssh_print_hexa("Packet hmac", crypto->hmacbuf, hmac_digest_len(type)); #endif - cipher->encrypt(cipher, data, out, len); } - memcpy(data, out, len); explicit_bzero(out, len); SAFE_FREE(out); diff --git a/src/wrapper.c b/src/wrapper.c index 33f55840..e13b4c27 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -56,13 +56,17 @@ #include "libssh/curve25519.h" static struct ssh_hmac_struct ssh_hmac_tab[] = { - { "hmac-sha1", SSH_HMAC_SHA1, false }, - { "hmac-sha2-256", SSH_HMAC_SHA256, false }, - { "hmac-sha2-512", SSH_HMAC_SHA512, false }, - { "hmac-md5", SSH_HMAC_MD5, false }, - { "aead-poly1305", SSH_HMAC_AEAD_POLY1305, false }, - { "aead-gcm", SSH_HMAC_AEAD_GCM, false }, - { NULL, 0, false } + { "hmac-sha1", SSH_HMAC_SHA1, false }, + { "hmac-sha2-256", SSH_HMAC_SHA256, false }, + { "hmac-sha2-512", SSH_HMAC_SHA512, false }, + { "hmac-md5", SSH_HMAC_MD5, false }, + { "aead-poly1305", SSH_HMAC_AEAD_POLY1305, false }, + { "aead-gcm", SSH_HMAC_AEAD_GCM, false }, + { "hmac-sha1-etm@openssh.com", SSH_HMAC_SHA1, true }, + { "hmac-sha2-256-etm@openssh.com", SSH_HMAC_SHA256, true }, + { "hmac-sha2-512-etm@openssh.com", SSH_HMAC_SHA512, true }, + { "hmac-md5-etm@openssh.com", SSH_HMAC_MD5, true }, + { NULL, 0, false } }; struct ssh_hmac_struct *ssh_get_hmactab(void) { |