version 1.70, 2001/09/27 11:59:37 |
version 1.70.2.2, 2002/03/07 17:37:46 |
|
|
#include "mac.h" |
#include "mac.h" |
#include "log.h" |
#include "log.h" |
#include "canohost.h" |
#include "canohost.h" |
|
#include "misc.h" |
|
|
#ifdef PACKET_DEBUG |
#ifdef PACKET_DEBUG |
#define DBG(x) x |
#define DBG(x) x |
|
|
/* Session key information for Encryption and MAC */ |
/* Session key information for Encryption and MAC */ |
Newkeys *newkeys[MODE_MAX]; |
Newkeys *newkeys[MODE_MAX]; |
|
|
|
/* roundup current message to extra_pad bytes */ |
|
static u_char extra_pad = 0; |
|
|
/* |
/* |
* Sets the descriptors used for communication. Disables encryption until |
* Sets the descriptors used for communication. Disables encryption until |
* packet_set_encryption_key is called. |
* packet_set_encryption_key is called. |
|
|
fatal("packet_set_connection: cannot load cipher 'none'"); |
fatal("packet_set_connection: cannot load cipher 'none'"); |
connection_in = fd_in; |
connection_in = fd_in; |
connection_out = fd_out; |
connection_out = fd_out; |
cipher_init(&send_context, none, (u_char *) "", 0, NULL, 0); |
cipher_init(&send_context, none, "", 0, NULL, 0, CIPHER_ENCRYPT); |
cipher_init(&receive_context, none, (u_char *) "", 0, NULL, 0); |
cipher_init(&receive_context, none, "", 0, NULL, 0, CIPHER_DECRYPT); |
newkeys[MODE_IN] = newkeys[MODE_OUT] = NULL; |
newkeys[MODE_IN] = newkeys[MODE_OUT] = NULL; |
if (!initialized) { |
if (!initialized) { |
initialized = 1; |
initialized = 1; |
|
|
/* Returns 1 if remote host is connected via socket, 0 if not. */ |
/* Returns 1 if remote host is connected via socket, 0 if not. */ |
|
|
int |
int |
packet_connection_is_on_socket() |
packet_connection_is_on_socket(void) |
{ |
{ |
struct sockaddr_storage from, to; |
struct sockaddr_storage from, to; |
socklen_t fromlen, tolen; |
socklen_t fromlen, tolen; |
|
|
/* returns 1 if connection is via ipv4 */ |
/* returns 1 if connection is via ipv4 */ |
|
|
int |
int |
packet_connection_is_ipv4() |
packet_connection_is_ipv4(void) |
{ |
{ |
struct sockaddr_storage to; |
struct sockaddr_storage to; |
socklen_t tolen = sizeof(to); |
socklen_t tolen = sizeof(to); |
|
|
/* Sets the connection into non-blocking mode. */ |
/* Sets the connection into non-blocking mode. */ |
|
|
void |
void |
packet_set_nonblocking() |
packet_set_nonblocking(void) |
{ |
{ |
/* Set the socket into non-blocking mode. */ |
/* Set the socket into non-blocking mode. */ |
if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0) |
if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0) |
|
|
/* Returns the socket used for reading. */ |
/* Returns the socket used for reading. */ |
|
|
int |
int |
packet_get_connection_in() |
packet_get_connection_in(void) |
{ |
{ |
return connection_in; |
return connection_in; |
} |
} |
|
|
/* Returns the descriptor used for writing. */ |
/* Returns the descriptor used for writing. */ |
|
|
int |
int |
packet_get_connection_out() |
packet_get_connection_out(void) |
{ |
{ |
return connection_out; |
return connection_out; |
} |
} |
|
|
/* Closes the connection and clears and frees internal data structures. */ |
/* Closes the connection and clears and frees internal data structures. */ |
|
|
void |
void |
packet_close() |
packet_close(void) |
{ |
{ |
if (!initialized) |
if (!initialized) |
return; |
return; |
|
|
buffer_free(&compression_buffer); |
buffer_free(&compression_buffer); |
buffer_compress_uninit(); |
buffer_compress_uninit(); |
} |
} |
|
cipher_cleanup(&send_context); |
|
cipher_cleanup(&receive_context); |
} |
} |
|
|
/* Sets remote side protocol flags. */ |
/* Sets remote side protocol flags. */ |
|
|
/* Returns the remote protocol flags set earlier by the above function. */ |
/* Returns the remote protocol flags set earlier by the above function. */ |
|
|
u_int |
u_int |
packet_get_protocol_flags() |
packet_get_protocol_flags(void) |
{ |
{ |
return remote_protocol_flags; |
return remote_protocol_flags; |
} |
} |
|
|
fatal("packet_set_encryption_key: unknown cipher number %d", number); |
fatal("packet_set_encryption_key: unknown cipher number %d", number); |
if (keylen < 20) |
if (keylen < 20) |
fatal("packet_set_encryption_key: keylen too small: %d", keylen); |
fatal("packet_set_encryption_key: keylen too small: %d", keylen); |
cipher_init(&receive_context, cipher, key, keylen, NULL, 0); |
cipher_init(&send_context, cipher, key, keylen, NULL, 0, CIPHER_ENCRYPT); |
cipher_init(&send_context, cipher, key, keylen, NULL, 0); |
cipher_init(&receive_context, cipher, key, keylen, NULL, 0, CIPHER_DECRYPT); |
} |
} |
|
|
/* Start constructing a packet to send. */ |
/* Start constructing a packet to send. */ |
|
|
buffer_put_int(&outgoing_packet, value); |
buffer_put_int(&outgoing_packet, value); |
} |
} |
void |
void |
packet_put_string(const char *buf, u_int len) |
packet_put_string(const void *buf, u_int len) |
{ |
{ |
buffer_put_string(&outgoing_packet, buf, len); |
buffer_put_string(&outgoing_packet, buf, len); |
} |
} |
|
|
buffer_put_cstring(&outgoing_packet, str); |
buffer_put_cstring(&outgoing_packet, str); |
} |
} |
void |
void |
packet_put_raw(const char *buf, u_int len) |
packet_put_raw(const void *buf, u_int len) |
{ |
{ |
buffer_append(&outgoing_packet, buf, len); |
buffer_append(&outgoing_packet, buf, len); |
} |
} |
|
|
static void |
static void |
packet_send1(void) |
packet_send1(void) |
{ |
{ |
char buf[8], *cp; |
u_char buf[8], *cp; |
int i, padding, len; |
int i, padding, len; |
u_int checksum; |
u_int checksum; |
u_int32_t rand = 0; |
u_int32_t rand = 0; |
|
|
buffer_compress(&outgoing_packet, &compression_buffer); |
buffer_compress(&outgoing_packet, &compression_buffer); |
buffer_clear(&outgoing_packet); |
buffer_clear(&outgoing_packet); |
buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), |
buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), |
buffer_len(&compression_buffer)); |
buffer_len(&compression_buffer)); |
} |
} |
/* Compute packet length without padding (add checksum, remove padding). */ |
/* Compute packet length without padding (add checksum, remove padding). */ |
len = buffer_len(&outgoing_packet) + 4 - 8; |
len = buffer_len(&outgoing_packet) + 4 - 8; |
|
|
/* Insert padding. Initialized to zero in packet_start1() */ |
/* Insert padding. Initialized to zero in packet_start1() */ |
padding = 8 - len % 8; |
padding = 8 - len % 8; |
if (send_context.cipher->number != SSH_CIPHER_NONE) { |
if (!send_context.plaintext) { |
cp = buffer_ptr(&outgoing_packet); |
cp = buffer_ptr(&outgoing_packet); |
for (i = 0; i < padding; i++) { |
for (i = 0; i < padding; i++) { |
if (i % 4 == 0) |
if (i % 4 == 0) |
|
|
buffer_consume(&outgoing_packet, 8 - padding); |
buffer_consume(&outgoing_packet, 8 - padding); |
|
|
/* Add check bytes. */ |
/* Add check bytes. */ |
checksum = ssh_crc32((u_char *) buffer_ptr(&outgoing_packet), |
checksum = ssh_crc32(buffer_ptr(&outgoing_packet), |
buffer_len(&outgoing_packet)); |
buffer_len(&outgoing_packet)); |
PUT_32BIT(buf, checksum); |
PUT_32BIT(buf, checksum); |
buffer_append(&outgoing_packet, buf, 4); |
buffer_append(&outgoing_packet, buf, 4); |
|
|
/* Append to output. */ |
/* Append to output. */ |
PUT_32BIT(buf, len); |
PUT_32BIT(buf, len); |
buffer_append(&output, buf, 4); |
buffer_append(&output, buf, 4); |
buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); |
cp = buffer_append_space(&output, buffer_len(&outgoing_packet)); |
cipher_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), |
cipher_crypt(&send_context, cp, buffer_ptr(&outgoing_packet), |
buffer_len(&outgoing_packet)); |
buffer_len(&outgoing_packet)); |
|
|
#ifdef PACKET_DEBUG |
#ifdef PACKET_DEBUG |
fprintf(stderr, "encrypted: "); |
fprintf(stderr, "encrypted: "); |
|
|
Mac *mac; |
Mac *mac; |
Comp *comp; |
Comp *comp; |
CipherContext *cc; |
CipherContext *cc; |
|
int encrypt; |
|
|
debug("newkeys: mode %d", mode); |
debug("newkeys: mode %d", mode); |
|
|
cc = (mode == MODE_OUT) ? &send_context : &receive_context; |
if (mode == MODE_OUT) { |
|
cc = &send_context; |
|
encrypt = CIPHER_ENCRYPT; |
|
} else { |
|
cc = &receive_context; |
|
encrypt = CIPHER_DECRYPT; |
|
} |
if (newkeys[mode] != NULL) { |
if (newkeys[mode] != NULL) { |
debug("newkeys: rekeying"); |
debug("newkeys: rekeying"); |
/* todo: free old keys, reset compression/cipher-ctxt; */ |
cipher_cleanup(cc); |
memset(cc, 0, sizeof(*cc)); |
|
enc = &newkeys[mode]->enc; |
enc = &newkeys[mode]->enc; |
mac = &newkeys[mode]->mac; |
mac = &newkeys[mode]->mac; |
comp = &newkeys[mode]->comp; |
comp = &newkeys[mode]->comp; |
|
|
if (mac->md != NULL) |
if (mac->md != NULL) |
mac->enabled = 1; |
mac->enabled = 1; |
DBG(debug("cipher_init_context: %d", mode)); |
DBG(debug("cipher_init_context: %d", mode)); |
cipher_init(cc, enc->cipher, enc->key, enc->cipher->key_len, |
cipher_init(cc, enc->cipher, enc->key, enc->key_len, |
enc->iv, enc->cipher->block_size); |
enc->iv, enc->block_size, encrypt); |
memset(enc->iv, 0, enc->cipher->block_size); |
memset(enc->iv, 0, enc->block_size); |
memset(enc->key, 0, enc->cipher->key_len); |
memset(enc->key, 0, enc->key_len); |
if (comp->type != 0 && comp->enabled == 0) { |
if (comp->type != 0 && comp->enabled == 0) { |
packet_init_compression(); |
packet_init_compression(); |
if (mode == MODE_OUT) |
if (mode == MODE_OUT) |
|
|
packet_send2(void) |
packet_send2(void) |
{ |
{ |
static u_int32_t seqnr = 0; |
static u_int32_t seqnr = 0; |
u_char type, *ucp, *macbuf = NULL; |
u_char type, *cp, *macbuf = NULL; |
char *cp; |
u_char padlen, pad; |
u_int packet_length = 0; |
u_int packet_length = 0; |
u_int i, padlen, len; |
u_int i, len; |
u_int32_t rand = 0; |
u_int32_t rand = 0; |
Enc *enc = NULL; |
Enc *enc = NULL; |
Mac *mac = NULL; |
Mac *mac = NULL; |
|
|
mac = &newkeys[MODE_OUT]->mac; |
mac = &newkeys[MODE_OUT]->mac; |
comp = &newkeys[MODE_OUT]->comp; |
comp = &newkeys[MODE_OUT]->comp; |
} |
} |
block_size = enc ? enc->cipher->block_size : 8; |
block_size = enc ? enc->block_size : 8; |
|
|
ucp = (u_char *) buffer_ptr(&outgoing_packet); |
cp = buffer_ptr(&outgoing_packet); |
type = ucp[5]; |
type = cp[5]; |
|
|
#ifdef PACKET_DEBUG |
#ifdef PACKET_DEBUG |
fprintf(stderr, "plain: "); |
fprintf(stderr, "plain: "); |
|
|
padlen = block_size - (len % block_size); |
padlen = block_size - (len % block_size); |
if (padlen < 4) |
if (padlen < 4) |
padlen += block_size; |
padlen += block_size; |
buffer_append_space(&outgoing_packet, &cp, padlen); |
if (extra_pad) { |
if (enc && enc->cipher->number != SSH_CIPHER_NONE) { |
/* will wrap if extra_pad+padlen > 255 */ |
|
extra_pad = roundup(extra_pad, block_size); |
|
pad = extra_pad - ((len + padlen) % extra_pad); |
|
debug("packet_send2: adding %d (len %d padlen %d extra_pad %d)", |
|
pad, len, padlen, extra_pad); |
|
padlen += pad; |
|
extra_pad = 0; |
|
} |
|
cp = buffer_append_space(&outgoing_packet, padlen); |
|
if (enc && !send_context.plaintext) { |
/* random padding */ |
/* random padding */ |
for (i = 0; i < padlen; i++) { |
for (i = 0; i < padlen; i++) { |
if (i % 4 == 0) |
if (i % 4 == 0) |
|
|
} |
} |
/* packet_length includes payload, padding and padding length field */ |
/* packet_length includes payload, padding and padding length field */ |
packet_length = buffer_len(&outgoing_packet) - 4; |
packet_length = buffer_len(&outgoing_packet) - 4; |
ucp = (u_char *)buffer_ptr(&outgoing_packet); |
cp = buffer_ptr(&outgoing_packet); |
PUT_32BIT(ucp, packet_length); |
PUT_32BIT(cp, packet_length); |
ucp[4] = padlen; |
cp[4] = padlen; |
DBG(debug("send: len %d (includes padlen %d)", packet_length+4, padlen)); |
DBG(debug("send: len %d (includes padlen %d)", packet_length+4, padlen)); |
|
|
/* compute MAC over seqnr and packet(length fields, payload, padding) */ |
/* compute MAC over seqnr and packet(length fields, payload, padding) */ |
if (mac && mac->enabled) { |
if (mac && mac->enabled) { |
macbuf = mac_compute(mac, seqnr, |
macbuf = mac_compute(mac, seqnr, |
(u_char *) buffer_ptr(&outgoing_packet), |
buffer_ptr(&outgoing_packet), |
buffer_len(&outgoing_packet)); |
buffer_len(&outgoing_packet)); |
DBG(debug("done calc MAC out #%d", seqnr)); |
DBG(debug("done calc MAC out #%d", seqnr)); |
} |
} |
/* encrypt packet and append to output buffer. */ |
/* encrypt packet and append to output buffer. */ |
buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); |
cp = buffer_append_space(&output, buffer_len(&outgoing_packet)); |
cipher_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), |
cipher_crypt(&send_context, cp, buffer_ptr(&outgoing_packet), |
buffer_len(&outgoing_packet)); |
buffer_len(&outgoing_packet)); |
/* append unencrypted MAC */ |
/* append unencrypted MAC */ |
if (mac && mac->enabled) |
if (mac && mac->enabled) |
|
|
} |
} |
|
|
void |
void |
packet_send() |
packet_send(void) |
{ |
{ |
if (compat20) |
if (compat20) |
packet_send2(); |
packet_send2(); |
|
|
*/ |
*/ |
|
|
int |
int |
packet_read(int *payload_len_ptr) |
packet_read_seqnr(u_int32_t *seqnr_p) |
{ |
{ |
int type, len; |
int type, len; |
fd_set *setp; |
fd_set *setp; |
|
|
/* Stay in the loop until we have received a complete packet. */ |
/* Stay in the loop until we have received a complete packet. */ |
for (;;) { |
for (;;) { |
/* Try to read a packet from the buffer. */ |
/* Try to read a packet from the buffer. */ |
type = packet_read_poll(payload_len_ptr); |
type = packet_read_poll_seqnr(seqnr_p); |
if (!compat20 && ( |
if (!compat20 && ( |
type == SSH_SMSG_SUCCESS |
type == SSH_SMSG_SUCCESS |
|| type == SSH_SMSG_FAILURE |
|| type == SSH_SMSG_FAILURE |
|| type == SSH_CMSG_EOF |
|| type == SSH_CMSG_EOF |
|| type == SSH_CMSG_EXIT_CONFIRMATION)) |
|| type == SSH_CMSG_EXIT_CONFIRMATION)) |
packet_integrity_check(*payload_len_ptr, 0, type); |
packet_check_eom(); |
/* If we got a packet, return it. */ |
/* If we got a packet, return it. */ |
if (type != SSH_MSG_NONE) { |
if (type != SSH_MSG_NONE) { |
xfree(setp); |
xfree(setp); |
|
|
/* NOTREACHED */ |
/* NOTREACHED */ |
} |
} |
|
|
|
int |
|
packet_read(void) |
|
{ |
|
return packet_read_seqnr(NULL); |
|
} |
|
|
/* |
/* |
* Waits until a packet has been received, verifies that its type matches |
* Waits until a packet has been received, verifies that its type matches |
* that given, and gives a fatal error and exits if there is a mismatch. |
* that given, and gives a fatal error and exits if there is a mismatch. |
*/ |
*/ |
|
|
void |
void |
packet_read_expect(int *payload_len_ptr, int expected_type) |
packet_read_expect(int expected_type) |
{ |
{ |
int type; |
int type; |
|
|
type = packet_read(payload_len_ptr); |
type = packet_read(); |
if (type != expected_type) |
if (type != expected_type) |
packet_disconnect("Protocol error: expected packet type %d, got %d", |
packet_disconnect("Protocol error: expected packet type %d, got %d", |
expected_type, type); |
expected_type, type); |
|
|
* SSH_MSG_DISCONNECT is handled specially here. Also, |
* SSH_MSG_DISCONNECT is handled specially here. Also, |
* SSH_MSG_IGNORE messages are skipped by this function and are never returned |
* SSH_MSG_IGNORE messages are skipped by this function and are never returned |
* to higher levels. |
* to higher levels. |
* |
|
* The returned payload_len does include space consumed by: |
|
* Packet length |
|
* Padding |
|
* Packet type |
|
* Check bytes |
|
*/ |
*/ |
|
|
static int |
static int |
packet_read_poll1(int *payload_len_ptr) |
packet_read_poll1(void) |
{ |
{ |
u_int len, padded_len; |
u_int len, padded_len; |
u_char *ucp, type; |
u_char *cp, type; |
char *cp; |
|
u_int checksum, stored_checksum; |
u_int checksum, stored_checksum; |
|
|
/* Check if input size is less than minimum packet size. */ |
/* Check if input size is less than minimum packet size. */ |
if (buffer_len(&input) < 4 + 8) |
if (buffer_len(&input) < 4 + 8) |
return SSH_MSG_NONE; |
return SSH_MSG_NONE; |
/* Get length of incoming packet. */ |
/* Get length of incoming packet. */ |
ucp = (u_char *) buffer_ptr(&input); |
cp = buffer_ptr(&input); |
len = GET_32BIT(ucp); |
len = GET_32BIT(cp); |
if (len < 1 + 2 + 2 || len > 256 * 1024) |
if (len < 1 + 2 + 2 || len > 256 * 1024) |
packet_disconnect("Bad packet length %d.", len); |
packet_disconnect("Bad packet length %d.", len); |
padded_len = (len + 8) & ~7; |
padded_len = (len + 8) & ~7; |
|
|
* (C)1998 CORE-SDI, Buenos Aires Argentina |
* (C)1998 CORE-SDI, Buenos Aires Argentina |
* Ariel Futoransky(futo@core-sdi.com) |
* Ariel Futoransky(futo@core-sdi.com) |
*/ |
*/ |
if (receive_context.cipher->number != SSH_CIPHER_NONE && |
if (!receive_context.plaintext && |
detect_attack(buffer_ptr(&input), padded_len, NULL) == DEATTACK_DETECTED) |
detect_attack(buffer_ptr(&input), padded_len, NULL) == DEATTACK_DETECTED) |
packet_disconnect("crc32 compensation attack: network attack detected"); |
packet_disconnect("crc32 compensation attack: network attack detected"); |
|
|
/* Decrypt data to incoming_packet. */ |
/* Decrypt data to incoming_packet. */ |
buffer_clear(&incoming_packet); |
buffer_clear(&incoming_packet); |
buffer_append_space(&incoming_packet, &cp, padded_len); |
cp = buffer_append_space(&incoming_packet, padded_len); |
cipher_decrypt(&receive_context, cp, buffer_ptr(&input), padded_len); |
cipher_crypt(&receive_context, cp, buffer_ptr(&input), padded_len); |
|
|
buffer_consume(&input, padded_len); |
buffer_consume(&input, padded_len); |
|
|
|
|
#endif |
#endif |
|
|
/* Compute packet checksum. */ |
/* Compute packet checksum. */ |
checksum = ssh_crc32((u_char *) buffer_ptr(&incoming_packet), |
checksum = ssh_crc32(buffer_ptr(&incoming_packet), |
buffer_len(&incoming_packet) - 4); |
buffer_len(&incoming_packet) - 4); |
|
|
/* Skip padding. */ |
/* Skip padding. */ |
|
|
|
|
/* Test check bytes. */ |
/* Test check bytes. */ |
if (len != buffer_len(&incoming_packet)) |
if (len != buffer_len(&incoming_packet)) |
packet_disconnect("packet_read_poll: len %d != buffer_len %d.", |
packet_disconnect("packet_read_poll1: len %d != buffer_len %d.", |
len, buffer_len(&incoming_packet)); |
len, buffer_len(&incoming_packet)); |
|
|
ucp = (u_char *) buffer_ptr(&incoming_packet) + len - 4; |
cp = (u_char *)buffer_ptr(&incoming_packet) + len - 4; |
stored_checksum = GET_32BIT(ucp); |
stored_checksum = GET_32BIT(cp); |
if (checksum != stored_checksum) |
if (checksum != stored_checksum) |
packet_disconnect("Corrupted check bytes on input."); |
packet_disconnect("Corrupted check bytes on input."); |
buffer_consume_end(&incoming_packet, 4); |
buffer_consume_end(&incoming_packet, 4); |
|
|
buffer_len(&compression_buffer)); |
buffer_len(&compression_buffer)); |
} |
} |
type = buffer_get_char(&incoming_packet); |
type = buffer_get_char(&incoming_packet); |
*payload_len_ptr = buffer_len(&incoming_packet); |
|
return type; |
return type; |
} |
} |
|
|
static int |
static int |
packet_read_poll2(int *payload_len_ptr) |
packet_read_poll2(u_int32_t *seqnr_p) |
{ |
{ |
static u_int32_t seqnr = 0; |
static u_int32_t seqnr = 0; |
static u_int packet_length = 0; |
static u_int packet_length = 0; |
u_int padlen, need; |
u_int padlen, need; |
u_char *macbuf, *ucp, type; |
u_char *macbuf, *cp, type; |
char *cp; |
|
int maclen, block_size; |
int maclen, block_size; |
Enc *enc = NULL; |
Enc *enc = NULL; |
Mac *mac = NULL; |
Mac *mac = NULL; |
|
|
comp = &newkeys[MODE_IN]->comp; |
comp = &newkeys[MODE_IN]->comp; |
} |
} |
maclen = mac && mac->enabled ? mac->mac_len : 0; |
maclen = mac && mac->enabled ? mac->mac_len : 0; |
block_size = enc ? enc->cipher->block_size : 8; |
block_size = enc ? enc->block_size : 8; |
|
|
if (packet_length == 0) { |
if (packet_length == 0) { |
/* |
/* |
|
|
if (buffer_len(&input) < block_size) |
if (buffer_len(&input) < block_size) |
return SSH_MSG_NONE; |
return SSH_MSG_NONE; |
buffer_clear(&incoming_packet); |
buffer_clear(&incoming_packet); |
buffer_append_space(&incoming_packet, &cp, block_size); |
cp = buffer_append_space(&incoming_packet, block_size); |
cipher_decrypt(&receive_context, cp, buffer_ptr(&input), |
cipher_crypt(&receive_context, cp, buffer_ptr(&input), |
block_size); |
block_size); |
ucp = (u_char *) buffer_ptr(&incoming_packet); |
cp = buffer_ptr(&incoming_packet); |
packet_length = GET_32BIT(ucp); |
packet_length = GET_32BIT(cp); |
if (packet_length < 1 + 4 || packet_length > 256 * 1024) { |
if (packet_length < 1 + 4 || packet_length > 256 * 1024) { |
buffer_dump(&incoming_packet); |
buffer_dump(&incoming_packet); |
packet_disconnect("Bad packet length %d.", packet_length); |
packet_disconnect("Bad packet length %d.", packet_length); |
|
|
fprintf(stderr, "read_poll enc/full: "); |
fprintf(stderr, "read_poll enc/full: "); |
buffer_dump(&input); |
buffer_dump(&input); |
#endif |
#endif |
buffer_append_space(&incoming_packet, &cp, need); |
cp = buffer_append_space(&incoming_packet, need); |
cipher_decrypt(&receive_context, cp, buffer_ptr(&input), need); |
cipher_crypt(&receive_context, cp, buffer_ptr(&input), need); |
buffer_consume(&input, need); |
buffer_consume(&input, need); |
/* |
/* |
* compute MAC over seqnr and packet, |
* compute MAC over seqnr and packet, |
|
|
*/ |
*/ |
if (mac && mac->enabled) { |
if (mac && mac->enabled) { |
macbuf = mac_compute(mac, seqnr, |
macbuf = mac_compute(mac, seqnr, |
(u_char *) buffer_ptr(&incoming_packet), |
buffer_ptr(&incoming_packet), |
buffer_len(&incoming_packet)); |
buffer_len(&incoming_packet)); |
if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) |
if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) |
packet_disconnect("Corrupted MAC on input."); |
packet_disconnect("Corrupted MAC on input."); |
DBG(debug("MAC #%d ok", seqnr)); |
DBG(debug("MAC #%d ok", seqnr)); |
buffer_consume(&input, mac->mac_len); |
buffer_consume(&input, mac->mac_len); |
} |
} |
|
if (seqnr_p != NULL) |
|
*seqnr_p = seqnr; |
if (++seqnr == 0) |
if (++seqnr == 0) |
log("incoming seqnr wraps around"); |
log("incoming seqnr wraps around"); |
|
|
/* get padlen */ |
/* get padlen */ |
cp = buffer_ptr(&incoming_packet) + 4; |
cp = buffer_ptr(&incoming_packet); |
padlen = (u_char) *cp; |
padlen = cp[4]; |
DBG(debug("input: padlen %d", padlen)); |
DBG(debug("input: padlen %d", padlen)); |
if (padlen < 4) |
if (padlen < 4) |
packet_disconnect("Corrupted padlen %d on input.", padlen); |
packet_disconnect("Corrupted padlen %d on input.", padlen); |
|
|
type = buffer_get_char(&incoming_packet); |
type = buffer_get_char(&incoming_packet); |
if (type == SSH2_MSG_NEWKEYS) |
if (type == SSH2_MSG_NEWKEYS) |
set_newkeys(MODE_IN); |
set_newkeys(MODE_IN); |
*payload_len_ptr = buffer_len(&incoming_packet); |
|
#ifdef PACKET_DEBUG |
#ifdef PACKET_DEBUG |
fprintf(stderr, "read/plain[%d]:\r\n", type); |
fprintf(stderr, "read/plain[%d]:\r\n", type); |
buffer_dump(&incoming_packet); |
buffer_dump(&incoming_packet); |
|
|
} |
} |
|
|
int |
int |
packet_read_poll(int *payload_len_ptr) |
packet_read_poll_seqnr(u_int32_t *seqnr_p) |
{ |
{ |
int reason; |
int reason, seqnr; |
u_char type; |
u_char type; |
char *msg; |
char *msg; |
|
|
for (;;) { |
for (;;) { |
if (compat20) { |
if (compat20) { |
type = packet_read_poll2(payload_len_ptr); |
type = packet_read_poll2(seqnr_p); |
if (type) |
if (type) |
DBG(debug("received packet type %d", type)); |
DBG(debug("received packet type %d", type)); |
switch(type) { |
switch (type) { |
case SSH2_MSG_IGNORE: |
case SSH2_MSG_IGNORE: |
break; |
break; |
case SSH2_MSG_DEBUG: |
case SSH2_MSG_DEBUG: |
|
|
xfree(msg); |
xfree(msg); |
fatal_cleanup(); |
fatal_cleanup(); |
break; |
break; |
|
case SSH2_MSG_UNIMPLEMENTED: |
|
seqnr = packet_get_int(); |
|
debug("Received SSH2_MSG_UNIMPLEMENTED for %d", seqnr); |
|
break; |
default: |
default: |
return type; |
return type; |
break; |
break; |
} |
} |
} else { |
} else { |
type = packet_read_poll1(payload_len_ptr); |
type = packet_read_poll1(); |
switch(type) { |
switch (type) { |
case SSH_MSG_IGNORE: |
case SSH_MSG_IGNORE: |
break; |
break; |
case SSH_MSG_DEBUG: |
case SSH_MSG_DEBUG: |
|
|
} |
} |
} |
} |
|
|
|
int |
|
packet_read_poll(void) |
|
{ |
|
return packet_read_poll_seqnr(NULL); |
|
} |
|
|
/* |
/* |
* Buffers the given amount of input characters. This is intended to be used |
* Buffers the given amount of input characters. This is intended to be used |
* together with packet_read_poll. |
* together with packet_read_poll. |
|
|
/* Returns a character from the packet. */ |
/* Returns a character from the packet. */ |
|
|
u_int |
u_int |
packet_get_char() |
packet_get_char(void) |
{ |
{ |
char ch; |
char ch; |
buffer_get(&incoming_packet, &ch, 1); |
buffer_get(&incoming_packet, &ch, 1); |
|
|
/* Returns an integer from the packet data. */ |
/* Returns an integer from the packet data. */ |
|
|
u_int |
u_int |
packet_get_int() |
packet_get_int(void) |
{ |
{ |
return buffer_get_int(&incoming_packet); |
return buffer_get_int(&incoming_packet); |
} |
} |
|
|
*/ |
*/ |
|
|
void |
void |
packet_get_bignum(BIGNUM * value, int *length_ptr) |
packet_get_bignum(BIGNUM * value) |
{ |
{ |
*length_ptr = buffer_get_bignum(&incoming_packet, value); |
buffer_get_bignum(&incoming_packet, value); |
} |
} |
|
|
void |
void |
packet_get_bignum2(BIGNUM * value, int *length_ptr) |
packet_get_bignum2(BIGNUM * value) |
{ |
{ |
*length_ptr = buffer_get_bignum2(&incoming_packet, value); |
buffer_get_bignum2(&incoming_packet, value); |
} |
} |
|
|
char * |
void * |
packet_get_raw(int *length_ptr) |
packet_get_raw(int *length_ptr) |
{ |
{ |
int bytes = buffer_len(&incoming_packet); |
int bytes = buffer_len(&incoming_packet); |
|
|
* integer into which the length of the string is stored. |
* integer into which the length of the string is stored. |
*/ |
*/ |
|
|
char * |
void * |
packet_get_string(u_int *length_ptr) |
packet_get_string(u_int *length_ptr) |
{ |
{ |
return buffer_get_string(&incoming_packet, length_ptr); |
return buffer_get_string(&incoming_packet, length_ptr); |
|
|
/* Checks if there is any buffered output, and tries to write some of the output. */ |
/* Checks if there is any buffered output, and tries to write some of the output. */ |
|
|
void |
void |
packet_write_poll() |
packet_write_poll(void) |
{ |
{ |
int len = buffer_len(&output); |
int len = buffer_len(&output); |
if (len > 0) { |
if (len > 0) { |
|
|
*/ |
*/ |
|
|
void |
void |
packet_write_wait() |
packet_write_wait(void) |
{ |
{ |
fd_set *setp; |
fd_set *setp; |
|
|
|
|
/* Returns true if there is buffered data to write to the connection. */ |
/* Returns true if there is buffered data to write to the connection. */ |
|
|
int |
int |
packet_have_data_to_write() |
packet_have_data_to_write(void) |
{ |
{ |
return buffer_len(&output) != 0; |
return buffer_len(&output) != 0; |
} |
} |
|
|
/* Returns true if there is not too much data to write to the connection. */ |
/* Returns true if there is not too much data to write to the connection. */ |
|
|
int |
int |
packet_not_very_much_data_to_write() |
packet_not_very_much_data_to_write(void) |
{ |
{ |
if (interactive_mode) |
if (interactive_mode) |
return buffer_len(&output) < 16384; |
return buffer_len(&output) < 16384; |
|
|
static int called = 0; |
static int called = 0; |
int lowdelay = IPTOS_LOWDELAY; |
int lowdelay = IPTOS_LOWDELAY; |
int throughput = IPTOS_THROUGHPUT; |
int throughput = IPTOS_THROUGHPUT; |
int on = 1; |
|
|
|
if (called) |
if (called) |
return; |
return; |
|
|
*/ |
*/ |
if (packet_connection_is_ipv4()) { |
if (packet_connection_is_ipv4()) { |
if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, |
if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, |
(void *) &lowdelay, sizeof(lowdelay)) < 0) |
&lowdelay, sizeof(lowdelay)) < 0) |
error("setsockopt IPTOS_LOWDELAY: %.100s", |
error("setsockopt IPTOS_LOWDELAY: %.100s", |
strerror(errno)); |
strerror(errno)); |
} |
} |
if (setsockopt(connection_in, IPPROTO_TCP, TCP_NODELAY, (void *) &on, |
set_nodelay(connection_in); |
sizeof(on)) < 0) |
|
error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); |
|
} else if (packet_connection_is_ipv4()) { |
} else if (packet_connection_is_ipv4()) { |
/* |
/* |
* Set IP options for a non-interactive connection. Use |
* Set IP options for a non-interactive connection. Use |
* IPTOS_THROUGHPUT. |
* IPTOS_THROUGHPUT. |
*/ |
*/ |
if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *) &throughput, |
if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, &throughput, |
sizeof(throughput)) < 0) |
sizeof(throughput)) < 0) |
error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno)); |
error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno)); |
} |
} |
|
|
/* Returns true if the current connection is interactive. */ |
/* Returns true if the current connection is interactive. */ |
|
|
int |
int |
packet_is_interactive() |
packet_is_interactive(void) |
{ |
{ |
return interactive_mode; |
return interactive_mode; |
} |
} |
|
|
return s; |
return s; |
} |
} |
|
|
|
/* roundup current message to pad bytes */ |
|
void |
|
packet_add_padding(u_char pad) |
|
{ |
|
extra_pad = pad; |
|
} |
|
|
/* |
/* |
* 9.2. Ignored Data Message |
* 9.2. Ignored Data Message |
* |
* |
|
|
* required to send them. This message can be used as an additional |
* required to send them. This message can be used as an additional |
* protection measure against advanced traffic analysis techniques. |
* protection measure against advanced traffic analysis techniques. |
*/ |
*/ |
/* size of current + ignore message should be n*sumlen bytes (w/o mac) */ |
|
void |
void |
packet_inject_ignore(int sumlen) |
|
{ |
|
int blocksize, padlen, have, need, nb, mini, nbytes; |
|
Enc *enc = NULL; |
|
|
|
if (compat20 == 0) |
|
return; |
|
|
|
have = buffer_len(&outgoing_packet); |
|
debug2("packet_inject_ignore: current %d", have); |
|
if (newkeys[MODE_OUT] != NULL) |
|
enc = &newkeys[MODE_OUT]->enc; |
|
blocksize = enc ? enc->cipher->block_size : 8; |
|
padlen = blocksize - (have % blocksize); |
|
if (padlen < 4) |
|
padlen += blocksize; |
|
have += padlen; |
|
have /= blocksize; /* # of blocks for current message */ |
|
|
|
nb = roundup(sumlen, blocksize) / blocksize; /* blocks for both */ |
|
mini = roundup(5+1+4+4, blocksize) / blocksize; /* minsize ignore msg */ |
|
need = nb - (have % nb); /* blocks for ignore */ |
|
if (need <= mini) |
|
need += nb; |
|
nbytes = (need - mini) * blocksize; /* size of ignore payload */ |
|
debug2("packet_inject_ignore: block %d have %d nb %d mini %d need %d", |
|
blocksize, have, nb, mini, need); |
|
|
|
/* enqueue current message and append a ignore message */ |
|
packet_send(); |
|
packet_send_ignore(nbytes); |
|
} |
|
|
|
void |
|
packet_send_ignore(int nbytes) |
packet_send_ignore(int nbytes) |
{ |
{ |
u_int32_t rand = 0; |
u_int32_t rand = 0; |
|
|
|
|
packet_start(compat20 ? SSH2_MSG_IGNORE : SSH_MSG_IGNORE); |
packet_start(compat20 ? SSH2_MSG_IGNORE : SSH_MSG_IGNORE); |
packet_put_int(nbytes); |
packet_put_int(nbytes); |
for(i = 0; i < nbytes; i++) { |
for (i = 0; i < nbytes; i++) { |
if (i % 4 == 0) |
if (i % 4 == 0) |
rand = arc4random(); |
rand = arc4random(); |
packet_put_char(rand & 0xff); |
packet_put_char(rand & 0xff); |