version 1.104, 2003/04/01 10:22:21 |
version 1.105, 2003/04/02 09:48:07 |
|
|
#include "includes.h" |
#include "includes.h" |
RCSID("$OpenBSD$"); |
RCSID("$OpenBSD$"); |
|
|
|
#include <sys/queue.h> |
|
|
#include "xmalloc.h" |
#include "xmalloc.h" |
#include "buffer.h" |
#include "buffer.h" |
#include "packet.h" |
#include "packet.h" |
|
|
|
|
/* Session key information for Encryption and MAC */ |
/* Session key information for Encryption and MAC */ |
Newkeys *newkeys[MODE_MAX]; |
Newkeys *newkeys[MODE_MAX]; |
static u_int32_t read_seqnr = 0; |
static struct packet_state { |
static u_int32_t send_seqnr = 0; |
u_int32_t seqnr; |
|
u_int32_t packets; |
|
u_int64_t blocks; |
|
} p_read, p_send; |
|
|
|
static u_int64_t max_blocks_in, max_blocks_out; |
|
static u_int32_t rekey_limit; |
|
|
/* Session key for protocol v1 */ |
/* Session key for protocol v1 */ |
static u_char ssh1_key[SSH_SESSION_KEY_LENGTH]; |
static u_char ssh1_key[SSH_SESSION_KEY_LENGTH]; |
static u_int ssh1_keylen; |
static u_int ssh1_keylen; |
|
|
/* roundup current message to extra_pad bytes */ |
/* roundup current message to extra_pad bytes */ |
static u_char extra_pad = 0; |
static u_char extra_pad = 0; |
|
|
|
struct packet { |
|
TAILQ_ENTRY(packet) next; |
|
u_char type; |
|
Buffer payload; |
|
}; |
|
TAILQ_HEAD(, packet) outgoing; |
|
|
/* |
/* |
* 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. |
|
|
buffer_init(&output); |
buffer_init(&output); |
buffer_init(&outgoing_packet); |
buffer_init(&outgoing_packet); |
buffer_init(&incoming_packet); |
buffer_init(&incoming_packet); |
|
TAILQ_INIT(&outgoing); |
} |
} |
/* Kludge: arrange the close function to be called from fatal(). */ |
/* Kludge: arrange the close function to be called from fatal(). */ |
fatal_add_cleanup((void (*) (void *)) packet_close, NULL); |
fatal_add_cleanup((void (*) (void *)) packet_close, NULL); |
|
|
return (cipher_get_number(receive_context.cipher)); |
return (cipher_get_number(receive_context.cipher)); |
} |
} |
|
|
|
void |
u_int32_t |
packet_get_state(int mode, u_int32_t *seqnr, u_int64_t *blocks, u_int32_t *packets) |
packet_get_seqnr(int mode) |
|
{ |
{ |
return (mode == MODE_IN ? read_seqnr : send_seqnr); |
struct packet_state *state; |
|
|
|
state = (mode == MODE_IN) ? &p_read : &p_send; |
|
*seqnr = state->seqnr; |
|
*blocks = state->blocks; |
|
*packets = state->packets; |
} |
} |
|
|
void |
void |
packet_set_seqnr(int mode, u_int32_t seqnr) |
packet_set_state(int mode, u_int32_t seqnr, u_int64_t blocks, u_int32_t packets) |
{ |
{ |
if (mode == MODE_IN) |
struct packet_state *state; |
read_seqnr = seqnr; |
|
else if (mode == MODE_OUT) |
state = (mode == MODE_IN) ? &p_read : &p_send; |
send_seqnr = seqnr; |
state->seqnr = seqnr; |
else |
state->blocks = blocks; |
fatal("packet_set_seqnr: bad mode %d", mode); |
state->packets = packets; |
} |
} |
|
|
/* returns 1 if connection is via ipv4 */ |
/* returns 1 if connection is via ipv4 */ |
|
|
Mac *mac; |
Mac *mac; |
Comp *comp; |
Comp *comp; |
CipherContext *cc; |
CipherContext *cc; |
|
u_int64_t *max_blocks; |
int encrypt; |
int encrypt; |
|
|
debug2("set_newkeys: mode %d", mode); |
debug2("set_newkeys: mode %d", mode); |
|
|
if (mode == MODE_OUT) { |
if (mode == MODE_OUT) { |
cc = &send_context; |
cc = &send_context; |
encrypt = CIPHER_ENCRYPT; |
encrypt = CIPHER_ENCRYPT; |
|
p_send.packets = p_send.blocks = 0; |
|
max_blocks = &max_blocks_out; |
} else { |
} else { |
cc = &receive_context; |
cc = &receive_context; |
encrypt = CIPHER_DECRYPT; |
encrypt = CIPHER_DECRYPT; |
|
p_read.packets = p_read.blocks = 0; |
|
max_blocks = &max_blocks_in; |
} |
} |
if (newkeys[mode] != NULL) { |
if (newkeys[mode] != NULL) { |
debug("set_newkeys: rekeying"); |
debug("set_newkeys: rekeying"); |
|
|
buffer_compress_init_recv(); |
buffer_compress_init_recv(); |
comp->enabled = 1; |
comp->enabled = 1; |
} |
} |
|
*max_blocks = ((u_int64_t)1 << (enc->block_size*2)); |
|
if (rekey_limit) |
|
*max_blocks = MIN(*max_blocks, rekey_limit / enc->block_size); |
} |
} |
|
|
/* |
/* |
* Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) |
* Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) |
*/ |
*/ |
static void |
static void |
packet_send2(void) |
packet_send2_wrapped(void) |
{ |
{ |
u_char type, *cp, *macbuf = NULL; |
u_char type, *cp, *macbuf = NULL; |
u_char padlen, pad; |
u_char padlen, pad; |
|
|
|
|
/* 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, send_seqnr, |
macbuf = mac_compute(mac, p_send.seqnr, |
buffer_ptr(&outgoing_packet), |
buffer_ptr(&outgoing_packet), |
buffer_len(&outgoing_packet)); |
buffer_len(&outgoing_packet)); |
DBG(debug("done calc MAC out #%d", send_seqnr)); |
DBG(debug("done calc MAC out #%d", p_send.seqnr)); |
} |
} |
/* encrypt packet and append to output buffer. */ |
/* encrypt packet and append to output buffer. */ |
cp = buffer_append_space(&output, buffer_len(&outgoing_packet)); |
cp = buffer_append_space(&output, buffer_len(&outgoing_packet)); |
|
|
buffer_dump(&output); |
buffer_dump(&output); |
#endif |
#endif |
/* increment sequence number for outgoing packets */ |
/* increment sequence number for outgoing packets */ |
if (++send_seqnr == 0) |
if (++p_send.seqnr == 0) |
log("outgoing seqnr wraps around"); |
log("outgoing seqnr wraps around"); |
|
if (++p_send.packets == 0) |
|
if (!(datafellows & SSH_BUG_NOREKEY)) |
|
fatal("XXX too many packets with same key"); |
|
p_send.blocks += (packet_length + 4) / block_size; |
buffer_clear(&outgoing_packet); |
buffer_clear(&outgoing_packet); |
|
|
if (type == SSH2_MSG_NEWKEYS) |
if (type == SSH2_MSG_NEWKEYS) |
set_newkeys(MODE_OUT); |
set_newkeys(MODE_OUT); |
} |
} |
|
|
|
static void |
|
packet_send2(void) |
|
{ |
|
static int rekeying = 0; |
|
struct packet *p; |
|
u_char type, *cp; |
|
|
|
cp = buffer_ptr(&outgoing_packet); |
|
type = cp[5]; |
|
|
|
/* during rekeying we can only send key exchange messages */ |
|
if (rekeying) { |
|
if (!((type >= SSH2_MSG_TRANSPORT_MIN) && |
|
(type <= SSH2_MSG_TRANSPORT_MAX))) { |
|
debug("enqueue packet: %u", type); |
|
p = xmalloc(sizeof(*p)); |
|
p->type = type; |
|
memcpy(&p->payload, &outgoing_packet, sizeof(Buffer)); |
|
buffer_init(&outgoing_packet); |
|
TAILQ_INSERT_TAIL(&outgoing, p, next); |
|
return; |
|
} |
|
} |
|
|
|
/* rekeying starts with sending KEXINIT */ |
|
if (type == SSH2_MSG_KEXINIT) |
|
rekeying = 1; |
|
|
|
packet_send2_wrapped(); |
|
|
|
/* after a NEWKEYS message we can send the complete queue */ |
|
if (type == SSH2_MSG_NEWKEYS) { |
|
rekeying = 0; |
|
while ((p = TAILQ_FIRST(&outgoing))) { |
|
type = p->type; |
|
debug("dequeue packet: %u", type); |
|
buffer_free(&outgoing_packet); |
|
memcpy(&outgoing_packet, &p->payload, |
|
sizeof(Buffer)); |
|
TAILQ_REMOVE(&outgoing, p, next); |
|
xfree(p); |
|
packet_send2_wrapped(); |
|
} |
|
} |
|
} |
|
|
void |
void |
packet_send(void) |
packet_send(void) |
{ |
{ |
|
|
* increment sequence number for incoming packet |
* increment sequence number for incoming packet |
*/ |
*/ |
if (mac && mac->enabled) { |
if (mac && mac->enabled) { |
macbuf = mac_compute(mac, read_seqnr, |
macbuf = mac_compute(mac, p_read.seqnr, |
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", read_seqnr)); |
DBG(debug("MAC #%d ok", p_read.seqnr)); |
buffer_consume(&input, mac->mac_len); |
buffer_consume(&input, mac->mac_len); |
} |
} |
if (seqnr_p != NULL) |
if (seqnr_p != NULL) |
*seqnr_p = read_seqnr; |
*seqnr_p = p_read.seqnr; |
if (++read_seqnr == 0) |
if (++p_read.seqnr == 0) |
log("incoming seqnr wraps around"); |
log("incoming seqnr wraps around"); |
|
if (++p_read.packets == 0) |
|
if (!(datafellows & SSH_BUG_NOREKEY)) |
|
fatal("XXX too many packets with same key"); |
|
p_read.blocks += (packet_length + 4) / block_size; |
|
|
/* get padlen */ |
/* get padlen */ |
cp = buffer_ptr(&incoming_packet); |
cp = buffer_ptr(&incoming_packet); |
|
|
packet_put_char(rand & 0xff); |
packet_put_char(rand & 0xff); |
rand >>= 8; |
rand >>= 8; |
} |
} |
|
} |
|
|
|
#define MAX_PACKETS (1<<31) |
|
int |
|
packet_need_rekeying(void) |
|
{ |
|
if (datafellows & SSH_BUG_NOREKEY) |
|
return 0; |
|
return |
|
(p_send.packets > MAX_PACKETS) || |
|
(p_read.packets > MAX_PACKETS) || |
|
(max_blocks_out && (p_send.blocks > max_blocks_out)) || |
|
(max_blocks_in && (p_read.blocks > max_blocks_in)); |
|
} |
|
|
|
void |
|
packet_set_rekey_limit(u_int32_t bytes) |
|
{ |
|
rekey_limit = bytes; |
} |
} |