*/
#include "includes.h"
-RCSID("$OpenBSD: packet.c,v 1.49 2001/02/08 19:30:52 itojun Exp $");
+RCSID("$OpenBSD: packet.c,v 1.61 2001/04/05 10:42:51 markus Exp $");
#include "xmalloc.h"
#include "buffer.h"
#include "ssh1.h"
#include "ssh2.h"
-#include <openssl/bn.h>
-#include <openssl/dh.h>
-#include <openssl/hmac.h>
#include "cipher.h"
#include "kex.h"
-#include "hmac.h"
+#include "mac.h"
#include "log.h"
#include "canohost.h"
/* Scratch buffer for packet compression/decompression. */
static Buffer compression_buffer;
+static int compression_buffer_ready = 0;
/* Flag indicating whether packet compression/decompression is enabled. */
static int packet_compression = 0;
int use_ssh2_packet_format = 0;
/* Session key information for Encryption and MAC */
-Kex *kex = NULL;
+Newkeys *newkeys[MODE_MAX];
-void
-packet_set_kex(Kex *k)
-{
- if( k->mac[MODE_IN ].key == NULL ||
- k->enc[MODE_IN ].key == NULL ||
- k->enc[MODE_IN ].iv == NULL ||
- k->mac[MODE_OUT].key == NULL ||
- k->enc[MODE_OUT].key == NULL ||
- k->enc[MODE_OUT].iv == NULL)
- fatal("bad KEX");
- kex = k;
-}
-void
-clear_enc_keys(Enc *enc, int len)
-{
- memset(enc->iv, 0, len);
- memset(enc->key, 0, len);
- xfree(enc->iv);
- xfree(enc->key);
- enc->iv = NULL;
- enc->key = NULL;
-}
void
packet_set_ssh2_format(void)
{
DBG(debug("use_ssh2_packet_format"));
use_ssh2_packet_format = 1;
+ newkeys[MODE_IN] = newkeys[MODE_OUT] = NULL;
}
/*
buffer_free(&output);
buffer_free(&outgoing_packet);
buffer_free(&incoming_packet);
- if (packet_compression) {
+ if (compression_buffer_ready) {
buffer_free(&compression_buffer);
buffer_compress_uninit();
}
* Level is compression level 1 (fastest) - 9 (slow, best) as in gzip.
*/
-/*** XXXXX todo: kex means re-init */
+void
+packet_init_compression()
+{
+ if (compression_buffer_ready == 1)
+ return;
+ compression_buffer_ready = 1;
+ buffer_init(&compression_buffer);
+}
+
void
packet_start_compression(int level)
{
- if (packet_compression)
+ if (packet_compression && !use_ssh2_packet_format)
fatal("Compression already enabled.");
packet_compression = 1;
- buffer_init(&compression_buffer);
- buffer_compress_init(level);
+ packet_init_compression();
+ buffer_compress_init_send(level);
+ buffer_compress_init_recv();
}
/*
void
packet_start(int type)
{
- DBG(debug("packet_start[%d]",type));
+ DBG(debug("packet_start[%d]", type));
if (use_ssh2_packet_format)
packet_start2(type);
else
*/
}
+void
+set_newkeys(int mode)
+{
+ Enc *enc;
+ Mac *mac;
+ Comp *comp;
+ CipherContext *cc;
+
+ debug("newkeys: mode %d", mode);
+
+ cc = (mode == MODE_OUT) ? &send_context : &receive_context;
+ if (newkeys[mode] != NULL) {
+ debug("newkeys: rekeying");
+ /* todo: free old keys, reset compression/cipher-ctxt; */
+ memset(cc, 0, sizeof(*cc));
+ enc = &newkeys[mode]->enc;
+ mac = &newkeys[mode]->mac;
+ comp = &newkeys[mode]->comp;
+ memset(mac->key, 0, mac->key_len);
+ xfree(enc->name);
+ xfree(enc->iv);
+ xfree(enc->key);
+ xfree(mac->name);
+ xfree(mac->key);
+ xfree(comp->name);
+ xfree(newkeys[mode]);
+ }
+ newkeys[mode] = kex_get_newkeys(mode);
+ if (newkeys[mode] == NULL)
+ fatal("newkeys: no keys for mode %d", mode);
+ enc = &newkeys[mode]->enc;
+ mac = &newkeys[mode]->mac;
+ comp = &newkeys[mode]->comp;
+ if (mac->md != NULL)
+ mac->enabled = 1;
+ DBG(debug("cipher_init_context: %d", mode));
+ cipher_init(cc, enc->cipher, enc->key, enc->cipher->key_len,
+ enc->iv, enc->cipher->block_size);
+ memset(enc->iv, 0, enc->cipher->block_size);
+ memset(enc->key, 0, enc->cipher->key_len);
+ if (comp->type != 0 && comp->enabled == 0) {
+ packet_init_compression();
+ if (mode == MODE_OUT)
+ buffer_compress_init_send(6);
+ else
+ buffer_compress_init_recv();
+ comp->enabled = 1;
+ }
+}
+
/*
* Finalize packet in SSH2 format (compress, mac, encrypt, enqueue)
*/
void
packet_send2(void)
{
+ static u_int32_t seqnr = 0;
u_char *macbuf = NULL;
char *cp;
u_int packet_length = 0;
u_int i, padlen, len;
u_int32_t rand = 0;
- static u_int seqnr = 0;
int type;
Enc *enc = NULL;
Mac *mac = NULL;
Comp *comp = NULL;
int block_size;
- if (kex != NULL) {
- enc = &kex->enc[MODE_OUT];
- mac = &kex->mac[MODE_OUT];
- comp = &kex->comp[MODE_OUT];
+ if (newkeys[MODE_OUT] != NULL) {
+ enc = &newkeys[MODE_OUT]->enc;
+ mac = &newkeys[MODE_OUT]->mac;
+ comp = &newkeys[MODE_OUT]->comp;
}
block_size = enc ? enc->cipher->block_size : 8;
if (i % 4 == 0)
rand = arc4random();
cp[i] = rand & 0xff;
- rand <<= 8;
+ rand >>= 8;
}
} else {
/* clear padding */
/* compute MAC over seqnr and packet(length fields, payload, padding) */
if (mac && mac->enabled) {
- macbuf = hmac( mac->md, seqnr,
+ macbuf = mac_compute(mac, seqnr,
(u_char *) buffer_ptr(&outgoing_packet),
- buffer_len(&outgoing_packet),
- mac->key, mac->key_len
- );
+ buffer_len(&outgoing_packet));
DBG(debug("done calc MAC out #%d", seqnr));
}
/* encrypt packet and append to output buffer. */
log("outgoing seqnr wraps around");
buffer_clear(&outgoing_packet);
- if (type == SSH2_MSG_NEWKEYS) {
- if (kex==NULL || mac==NULL || enc==NULL || comp==NULL)
- fatal("packet_send2: no KEX");
- if (mac->md != NULL)
- mac->enabled = 1;
- DBG(debug("cipher_init send_context"));
- cipher_init(&send_context, enc->cipher,
- enc->key, enc->cipher->key_len,
- enc->iv, enc->cipher->block_size);
- clear_enc_keys(enc, kex->we_need);
- if (comp->type != 0 && comp->enabled == 0) {
- comp->enabled = 1;
- if (! packet_compression)
- packet_start_compression(6);
- }
- }
+ if (type == SSH2_MSG_NEWKEYS)
+ set_newkeys(MODE_OUT);
}
void
packet_read(int *payload_len_ptr)
{
int type, len;
- fd_set set;
+ fd_set *setp;
char buf[8192];
DBG(debug("packet_read()"));
+ setp = (fd_set *)xmalloc(howmany(connection_in+1, NFDBITS) *
+ sizeof(fd_mask));
+
/* Since we are blocking, ensure that all written packets have been sent. */
packet_write_wait();
|| type == SSH_CMSG_EXIT_CONFIRMATION))
packet_integrity_check(*payload_len_ptr, 0, type);
/* If we got a packet, return it. */
- if (type != SSH_MSG_NONE)
+ if (type != SSH_MSG_NONE) {
+ xfree(setp);
return type;
+ }
/*
* Otherwise, wait for some data to arrive, add it to the
* buffer, and try again.
*/
- FD_ZERO(&set);
- FD_SET(connection_in, &set);
+ memset(setp, 0, howmany(connection_in + 1, NFDBITS) *
+ sizeof(fd_mask));
+ FD_SET(connection_in, setp);
/* Wait for some data to arrive. */
- select(connection_in + 1, &set, NULL, NULL, NULL);
+ while (select(connection_in + 1, setp, NULL, NULL, NULL) == -1 &&
+ (errno == EAGAIN || errno == EINTR))
+ ;
/* Read data from the socket. */
len = read(connection_in, buf, sizeof(buf));
int
packet_read_poll2(int *payload_len_ptr)
{
+ static u_int32_t seqnr = 0;
+ static u_int packet_length = 0;
u_int padlen, need;
u_char buf[8], *macbuf;
u_char *ucp;
char *cp;
- static u_int packet_length = 0;
- static u_int seqnr = 0;
int type;
int maclen, block_size;
Enc *enc = NULL;
Mac *mac = NULL;
Comp *comp = NULL;
- if (kex != NULL) {
- enc = &kex->enc[MODE_IN];
- mac = &kex->mac[MODE_IN];
- comp = &kex->comp[MODE_IN];
+ if (newkeys[MODE_IN] != NULL) {
+ enc = &newkeys[MODE_IN]->enc;
+ mac = &newkeys[MODE_IN]->mac;
+ comp = &newkeys[MODE_IN]->comp;
}
maclen = mac && mac->enabled ? mac->mac_len : 0;
block_size = enc ? enc->cipher->block_size : 8;
* increment sequence number for incoming packet
*/
if (mac && mac->enabled) {
- macbuf = hmac( mac->md, seqnr,
+ macbuf = mac_compute(mac, seqnr,
(u_char *) buffer_ptr(&incoming_packet),
- buffer_len(&incoming_packet),
- mac->key, mac->key_len
- );
+ buffer_len(&incoming_packet));
if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0)
packet_disconnect("Corrupted MAC on input.");
DBG(debug("MAC #%d ok", seqnr));
/* extract packet type */
type = (u_char)buf[0];
- if (type == SSH2_MSG_NEWKEYS) {
- if (kex==NULL || mac==NULL || enc==NULL || comp==NULL)
- fatal("packet_read_poll2: no KEX");
- if (mac->md != NULL)
- mac->enabled = 1;
- DBG(debug("cipher_init receive_context"));
- cipher_init(&receive_context, enc->cipher,
- enc->key, enc->cipher->key_len,
- enc->iv, enc->cipher->block_size);
- clear_enc_keys(enc, kex->we_need);
- if (comp->type != 0 && comp->enabled == 0) {
- comp->enabled = 1;
- if (! packet_compression)
- packet_start_compression(6);
- }
- }
+ if (type == SSH2_MSG_NEWKEYS)
+ set_newkeys(MODE_IN);
#ifdef PACKET_DEBUG
- fprintf(stderr, "read/plain[%d]:\r\n",type);
+ fprintf(stderr, "read/plain[%d]:\r\n", type);
buffer_dump(&incoming_packet);
#endif
return (u_char)type;
void
packet_write_wait()
{
+ fd_set *setp;
+
+ setp = (fd_set *)xmalloc(howmany(connection_out + 1, NFDBITS) *
+ sizeof(fd_mask));
packet_write_poll();
while (packet_have_data_to_write()) {
- fd_set set;
- FD_ZERO(&set);
- FD_SET(connection_out, &set);
- select(connection_out + 1, NULL, &set, NULL, NULL);
+ memset(setp, 0, howmany(connection_out + 1, NFDBITS) *
+ sizeof(fd_mask));
+ FD_SET(connection_out, setp);
+ while (select(connection_out + 1, NULL, setp, NULL, NULL) == -1 &&
+ (errno == EAGAIN || errno == EINTR))
+ ;
packet_write_poll();
}
+ xfree(setp);
}
/* Returns true if there is buffered data to write to the connection. */
packet_set_interactive(int interactive)
{
static int called = 0;
+#if defined(IP_TOS) && !defined(IP_TOS_IS_BROKEN)
int lowdelay = IPTOS_LOWDELAY;
int throughput = IPTOS_THROUGHPUT;
+#endif
int on = 1;
if (called)
max_packet_size = s;
return s;
}
+
+/*
+ * 9.2. Ignored Data Message
+ *
+ * byte SSH_MSG_IGNORE
+ * string data
+ *
+ * All implementations MUST understand (and ignore) this message at any
+ * time (after receiving the protocol version). No implementation is
+ * required to send them. This message can be used as an additional
+ * protection measure against advanced traffic analysis techniques.
+ */
+/* size of current + ignore message should be n*sumlen bytes (w/o mac) */
+void
+packet_inject_ignore(int sumlen)
+{
+ int blocksize, padlen, have, need, nb, mini, nbytes;
+ Enc *enc = NULL;
+
+ if (use_ssh2_packet_format == 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)
+{
+ u_int32_t rand = 0;
+ int i;
+
+ packet_start(compat20 ? SSH2_MSG_IGNORE : SSH_MSG_IGNORE);
+ packet_put_int(nbytes);
+ for(i = 0; i < nbytes; i++) {
+ if (i % 4 == 0)
+ rand = arc4random();
+ packet_put_char(rand & 0xff);
+ rand >>= 8;
+ }
+}