upstream commit

Reduce timing attack against obsolete CBC modes by always
computing the MAC over a fixed size of data. Reported by Jean Paul
Degabriele, Kenny Paterson, Torben Hansen and Martin Albrecht. ok djm@

Upstream-ID: f20a13279b00ba0afbacbcc1f04e62e9d41c2912
This commit is contained in:
markus@openbsd.org 2016-07-18 11:35:33 +00:00 committed by Damien Miller
parent dbf788b4d9
commit b98a2a8348
1 changed files with 23 additions and 14 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.c,v 1.233 2016/07/18 06:08:01 djm Exp $ */ /* $OpenBSD: packet.c,v 1.234 2016/07/18 11:35:33 markus Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -196,6 +196,7 @@ struct session_state {
/* XXX discard incoming data after MAC error */ /* XXX discard incoming data after MAC error */
u_int packet_discard; u_int packet_discard;
size_t packet_discard_mac_already;
struct sshmac *packet_discard_mac; struct sshmac *packet_discard_mac;
/* Used in packet_read_poll2() */ /* Used in packet_read_poll2() */
@ -333,16 +334,18 @@ ssh_packet_stop_discard(struct ssh *ssh)
if (state->packet_discard_mac) { if (state->packet_discard_mac) {
char buf[1024]; char buf[1024];
size_t dlen = PACKET_MAX_SIZE;
if (dlen > state->packet_discard_mac_already)
dlen -= state->packet_discard_mac_already;
memset(buf, 'a', sizeof(buf)); memset(buf, 'a', sizeof(buf));
while (sshbuf_len(state->incoming_packet) < while (sshbuf_len(state->incoming_packet) < dlen)
PACKET_MAX_SIZE)
if ((r = sshbuf_put(state->incoming_packet, buf, if ((r = sshbuf_put(state->incoming_packet, buf,
sizeof(buf))) != 0) sizeof(buf))) != 0)
return r; return r;
(void) mac_compute(state->packet_discard_mac, (void) mac_compute(state->packet_discard_mac,
state->p_read.seqnr, state->p_read.seqnr,
sshbuf_ptr(state->incoming_packet), PACKET_MAX_SIZE, sshbuf_ptr(state->incoming_packet), dlen,
NULL, 0); NULL, 0);
} }
logit("Finished discarding for %.200s port %d", logit("Finished discarding for %.200s port %d",
@ -352,7 +355,7 @@ ssh_packet_stop_discard(struct ssh *ssh)
static int static int
ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc, ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc,
struct sshmac *mac, u_int packet_length, u_int discard) struct sshmac *mac, size_t mac_already, u_int discard)
{ {
struct session_state *state = ssh->state; struct session_state *state = ssh->state;
int r; int r;
@ -362,11 +365,16 @@ ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc,
return r; return r;
return SSH_ERR_MAC_INVALID; return SSH_ERR_MAC_INVALID;
} }
if (packet_length != PACKET_MAX_SIZE && mac && mac->enabled) /*
* Record number of bytes over which the mac has already
* been computed in order to minimize timing attacks.
*/
if (mac && mac->enabled) {
state->packet_discard_mac = mac; state->packet_discard_mac = mac;
if (sshbuf_len(state->input) >= discard && state->packet_discard_mac_already = mac_already;
(r = ssh_packet_stop_discard(ssh)) != 0) }
return r; if (sshbuf_len(state->input) >= discard)
return ssh_packet_stop_discard(ssh);
state->packet_discard = discard - sshbuf_len(state->input); state->packet_discard = discard - sshbuf_len(state->input);
return 0; return 0;
} }
@ -1765,8 +1773,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
sshbuf_dump(state->incoming_packet, stderr); sshbuf_dump(state->incoming_packet, stderr);
#endif #endif
logit("Bad packet length %u.", state->packlen); logit("Bad packet length %u.", state->packlen);
return ssh_packet_start_discard(ssh, enc, mac, return ssh_packet_start_discard(ssh, enc, mac, 0,
state->packlen, PACKET_MAX_SIZE); PACKET_MAX_SIZE);
} }
if ((r = sshbuf_consume(state->input, block_size)) != 0) if ((r = sshbuf_consume(state->input, block_size)) != 0)
goto out; goto out;
@ -1788,8 +1796,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
if (need % block_size != 0) { if (need % block_size != 0) {
logit("padding error: need %d block %d mod %d", logit("padding error: need %d block %d mod %d",
need, block_size, need % block_size); need, block_size, need % block_size);
return ssh_packet_start_discard(ssh, enc, mac, return ssh_packet_start_discard(ssh, enc, mac, 0,
state->packlen, PACKET_MAX_SIZE - block_size); PACKET_MAX_SIZE - block_size);
} }
/* /*
* check if the entire packet has been received and * check if the entire packet has been received and
@ -1836,7 +1844,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
if (need > PACKET_MAX_SIZE) if (need > PACKET_MAX_SIZE)
return SSH_ERR_INTERNAL_ERROR; return SSH_ERR_INTERNAL_ERROR;
return ssh_packet_start_discard(ssh, enc, mac, return ssh_packet_start_discard(ssh, enc, mac,
state->packlen, PACKET_MAX_SIZE - need); sshbuf_len(state->incoming_packet),
PACKET_MAX_SIZE - need);
} }
/* Remove MAC from input buffer */ /* Remove MAC from input buffer */
DBG(debug("MAC #%d ok", state->p_read.seqnr)); DBG(debug("MAC #%d ok", state->p_read.seqnr));