-/* $OpenBSD: packet.c,v 1.157 2008/07/10 18:08:11 markus Exp $ */
+/* $OpenBSD: packet.c,v 1.160 2009/02/13 11:50:21 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
#define DBG(x)
#endif
+#define PACKET_MAX_SIZE (256 * 1024)
+
/*
* This variable contains the file descriptors used for communicating with
* the other side. connection_in is used for reading; connection_out for
/* roundup current message to extra_pad bytes */
static u_char extra_pad = 0;
+/* XXX discard incoming data after MAC error */
+static u_int packet_discard = 0;
+static Mac *packet_discard_mac = NULL;
+
struct packet {
TAILQ_ENTRY(packet) next;
u_char type;
packet_timeout_ms = timeout * count * 1000;
}
+static void
+packet_stop_discard(void)
+{
+ if (packet_discard_mac) {
+ char buf[1024];
+
+ memset(buf, 'a', sizeof(buf));
+ while (buffer_len(&incoming_packet) < PACKET_MAX_SIZE)
+ buffer_append(&incoming_packet, buf, sizeof(buf));
+ (void) mac_compute(packet_discard_mac,
+ p_read.seqnr,
+ buffer_ptr(&incoming_packet),
+ PACKET_MAX_SIZE);
+ }
+ logit("Finished discarding for %.200s", get_remote_ipaddr());
+ cleanup_exit(255);
+}
+
+static void
+packet_start_discard(Enc *enc, Mac *mac, u_int packet_length, u_int discard)
+{
+ if (enc == NULL || !cipher_is_cbc(enc->cipher))
+ packet_disconnect("Packet corrupt");
+ if (packet_length != PACKET_MAX_SIZE && mac && mac->enabled)
+ packet_discard_mac = mac;
+ if (buffer_len(&input) >= discard)
+ packet_stop_discard();
+ packet_discard = discard - buffer_len(&input);
+}
+
/* Returns 1 if remote host is connected via socket, 0 if not. */
int
/*
* Finalize packet in SSH2 format (compress, mac, encrypt, enqueue)
*/
-static void
+static int
packet_send2_wrapped(void)
{
u_char type, *cp, *macbuf = NULL;
set_newkeys(MODE_OUT);
else if (type == SSH2_MSG_USERAUTH_SUCCESS && server_side)
packet_enable_delayed_compress();
+ return(packet_length);
}
-static void
+static int
packet_send2(void)
{
+ static int packet_length = 0;
static int rekeying = 0;
struct packet *p;
u_char type, *cp;
memcpy(&p->payload, &outgoing_packet, sizeof(Buffer));
buffer_init(&outgoing_packet);
TAILQ_INSERT_TAIL(&outgoing, p, next);
- return;
+ return(sizeof(Buffer));
}
}
if (type == SSH2_MSG_KEXINIT)
rekeying = 1;
- packet_send2_wrapped();
+ packet_length = packet_send2_wrapped();
/* after a NEWKEYS message we can send the complete queue */
if (type == SSH2_MSG_NEWKEYS) {
sizeof(Buffer));
TAILQ_REMOVE(&outgoing, p, next);
xfree(p);
- packet_send2_wrapped();
+ packet_length += packet_send2_wrapped();
}
}
+ return(packet_length);
}
-void
+int
packet_send(void)
{
+ int packet_len = 0;
if (compat20)
- packet_send2();
+ packet_len = packet_send2();
else
packet_send1();
DBG(debug("packet_send done"));
+ return(packet_len);
}
/*
Mac *mac = NULL;
Comp *comp = NULL;
+ if (packet_discard)
+ return SSH_MSG_NONE;
+
if (newkeys[MODE_IN] != NULL) {
enc = &newkeys[MODE_IN]->enc;
mac = &newkeys[MODE_IN]->mac;
block_size);
cp = buffer_ptr(&incoming_packet);
packet_length = get_u32(cp);
- if (packet_length < 1 + 4 || packet_length > 256 * 1024) {
+ if (packet_length < 1 + 4 || packet_length > PACKET_MAX_SIZE) {
#ifdef PACKET_DEBUG
buffer_dump(&incoming_packet);
#endif
- packet_disconnect("Bad packet length %u.", packet_length);
+ logit("Bad packet length %u.", packet_length);
+ packet_start_discard(enc, mac, packet_length,
+ PACKET_MAX_SIZE);
+ return SSH_MSG_NONE;
}
DBG(debug("input: packet len %u", packet_length+4));
buffer_consume(&input, block_size);
need = 4 + packet_length - block_size;
DBG(debug("partial packet %d, need %d, maclen %d", block_size,
need, maclen));
- if (need % block_size != 0)
- fatal("padding error: need %d block %d mod %d",
+ if (need % block_size != 0) {
+ logit("padding error: need %d block %d mod %d",
need, block_size, need % block_size);
+ packet_start_discard(enc, mac, packet_length,
+ PACKET_MAX_SIZE - block_size);
+ return SSH_MSG_NONE;
+ }
/*
* check if the entire packet has been received and
* decrypt into incoming_packet
macbuf = mac_compute(mac, p_read.seqnr,
buffer_ptr(&incoming_packet),
buffer_len(&incoming_packet));
- if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0)
- packet_disconnect("Corrupted MAC on input.");
+ if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) {
+ logit("Corrupted MAC on input.");
+ if (need > PACKET_MAX_SIZE)
+ fatal("internal error need %d", need);
+ packet_start_discard(enc, mac, packet_length,
+ PACKET_MAX_SIZE - need);
+ return SSH_MSG_NONE;
+ }
+
DBG(debug("MAC #%d ok", p_read.seqnr));
buffer_consume(&input, mac->mac_len);
}
+ /* XXX now it's safe to use fatal/packet_disconnect */
if (seqnr_p != NULL)
*seqnr_p = p_read.seqnr;
if (++p_read.seqnr == 0)
void
packet_process_incoming(const char *buf, u_int len)
{
+ if (packet_discard) {
+ keep_alive_timeouts = 0; /* ?? */
+ if (len >= packet_discard)
+ packet_stop_discard();
+ packet_discard -= len;
+ return;
+ }
buffer_append(&input, buf, len);
}
/* Checks if there is any buffered output, and tries to write some of the output. */
-void
+int
packet_write_poll(void)
{
- int len = buffer_len(&output);
+ int len = 0;
+ len = buffer_len(&output);
if (len > 0) {
len = write(connection_out, buffer_ptr(&output), len);
if (len == -1) {
if (errno == EINTR || errno == EAGAIN ||
errno == EWOULDBLOCK)
- return;
+ return (0);
fatal("Write failed: %.100s", strerror(errno));
}
if (len == 0)
fatal("Write connection closed");
buffer_consume(&output, len);
}
+ return(len);
}
* written.
*/
-void
+int
packet_write_wait(void)
{
fd_set *setp;
int ret, ms_remain;
struct timeval start, timeout, *timeoutp = NULL;
+ u_int bytes_sent = 0;
setp = (fd_set *)xcalloc(howmany(connection_out + 1, NFDBITS),
sizeof(fd_mask));
- packet_write_poll();
+ bytes_sent += packet_write_poll();
while (packet_have_data_to_write()) {
memset(setp, 0, howmany(connection_out + 1, NFDBITS) *
sizeof(fd_mask));
"waiting to write", get_remote_ipaddr());
cleanup_exit(255);
}
- packet_write_poll();
+ bytes_sent += packet_write_poll();
}
xfree(setp);
+ return (bytes_sent);
}
/* Returns true if there is buffered data to write to the connection. */
}
}
+int rekey_requested = 0;
+void
+packet_request_rekeying(void)
+{
+ rekey_requested = 1;
+}
+
#define MAX_PACKETS (1U<<31)
int
packet_need_rekeying(void)
{
if (datafellows & SSH_BUG_NOREKEY)
return 0;
+ if (rekey_requested == 1)
+ {
+ rekey_requested = 0;
+ return 1;
+ }
return
(p_send.packets > MAX_PACKETS) ||
(p_read.packets > MAX_PACKETS) ||
{
after_authentication = 1;
}
+
+int
+packet_authentication_state(void)
+{
+ return(after_authentication);
+}