aboutsummaryrefslogtreecommitdiff
path: root/packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'packet.c')
-rw-r--r--packet.c142
1 files changed, 92 insertions, 50 deletions
diff --git a/packet.c b/packet.c
index f406c0755a54..d6dad2da6141 100644
--- a/packet.c
+++ b/packet.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.229 2016/02/17 22:20:14 djm Exp $ */
+/* $OpenBSD: packet.c,v 1.234 2016/07/18 11:35:33 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -52,6 +52,7 @@
#include <arpa/inet.h>
#include <errno.h>
+#include <netdb.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -195,6 +196,7 @@ struct session_state {
/* XXX discard incoming data after MAC error */
u_int packet_discard;
+ size_t packet_discard_mac_already;
struct sshmac *packet_discard_mac;
/* Used in packet_read_poll2() */
@@ -296,7 +298,7 @@ ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out)
(r = cipher_init(&state->receive_context, none,
(const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) {
error("%s: cipher_init failed: %s", __func__, ssh_err(r));
- free(ssh);
+ free(ssh); /* XXX need ssh_free_session_state? */
return NULL;
}
state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL;
@@ -332,16 +334,18 @@ ssh_packet_stop_discard(struct ssh *ssh)
if (state->packet_discard_mac) {
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));
- while (sshbuf_len(state->incoming_packet) <
- PACKET_MAX_SIZE)
+ while (sshbuf_len(state->incoming_packet) < dlen)
if ((r = sshbuf_put(state->incoming_packet, buf,
sizeof(buf))) != 0)
return r;
(void) mac_compute(state->packet_discard_mac,
state->p_read.seqnr,
- sshbuf_ptr(state->incoming_packet), PACKET_MAX_SIZE,
+ sshbuf_ptr(state->incoming_packet), dlen,
NULL, 0);
}
logit("Finished discarding for %.200s port %d",
@@ -351,7 +355,7 @@ ssh_packet_stop_discard(struct ssh *ssh)
static int
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;
int r;
@@ -361,11 +365,16 @@ ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc,
return r;
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;
- if (sshbuf_len(state->input) >= discard &&
- (r = ssh_packet_stop_discard(ssh)) != 0)
- return r;
+ state->packet_discard_mac_already = mac_already;
+ }
+ if (sshbuf_len(state->input) >= discard)
+ return ssh_packet_stop_discard(ssh);
state->packet_discard = discard - sshbuf_len(state->input);
return 0;
}
@@ -379,6 +388,9 @@ ssh_packet_connection_is_on_socket(struct ssh *ssh)
struct sockaddr_storage from, to;
socklen_t fromlen, tolen;
+ if (state->connection_in == -1 || state->connection_out == -1)
+ return 0;
+
/* filedescriptors in and out are the same, so it's a socket */
if (state->connection_in == state->connection_out)
return 1;
@@ -468,10 +480,14 @@ ssh_remote_ipaddr(struct ssh *ssh)
if (ssh->remote_ipaddr == NULL) {
if (ssh_packet_connection_is_on_socket(ssh)) {
ssh->remote_ipaddr = get_peer_ipaddr(sock);
- ssh->remote_port = get_sock_port(sock, 0);
+ ssh->remote_port = get_peer_port(sock);
+ ssh->local_ipaddr = get_local_ipaddr(sock);
+ ssh->local_port = get_local_port(sock);
} else {
ssh->remote_ipaddr = strdup("UNKNOWN");
- ssh->remote_port = 0;
+ ssh->remote_port = 65535;
+ ssh->local_ipaddr = strdup("UNKNOWN");
+ ssh->local_port = 65535;
}
}
return ssh->remote_ipaddr;
@@ -486,6 +502,27 @@ ssh_remote_port(struct ssh *ssh)
return ssh->remote_port;
}
+/*
+ * Returns the IP-address of the local host as a string. The returned
+ * string must not be freed.
+ */
+
+const char *
+ssh_local_ipaddr(struct ssh *ssh)
+{
+ (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */
+ return ssh->local_ipaddr;
+}
+
+/* Returns the port number of the local host. */
+
+int
+ssh_local_port(struct ssh *ssh)
+{
+ (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */
+ return ssh->local_port;
+}
+
/* Closes the connection and clears and frees internal data structures. */
void
@@ -1142,7 +1179,7 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
{
struct session_state *state = ssh->state;
u_char type, *cp, macbuf[SSH_DIGEST_MAX_LENGTH];
- u_char padlen, pad = 0;
+ u_char tmp, padlen, pad = 0;
u_int authlen = 0, aadlen = 0;
u_int len;
struct sshenc *enc = NULL;
@@ -1200,14 +1237,24 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
if (padlen < 4)
padlen += block_size;
if (state->extra_pad) {
- /* will wrap if extra_pad+padlen > 255 */
+ tmp = state->extra_pad;
state->extra_pad =
roundup(state->extra_pad, block_size);
- pad = state->extra_pad -
- ((len + padlen) % state->extra_pad);
+ /* check if roundup overflowed */
+ if (state->extra_pad < tmp)
+ return SSH_ERR_INVALID_ARGUMENT;
+ tmp = (len + padlen) % state->extra_pad;
+ /* Check whether pad calculation below will underflow */
+ if (tmp > state->extra_pad)
+ return SSH_ERR_INVALID_ARGUMENT;
+ pad = state->extra_pad - tmp;
DBG(debug3("%s: adding %d (len %d padlen %d extra_pad %d)",
__func__, pad, len, padlen, state->extra_pad));
+ tmp = padlen;
padlen += pad;
+ /* Check whether padlen calculation overflowed */
+ if (padlen < tmp)
+ return SSH_ERR_INVALID_ARGUMENT; /* overflow */
state->extra_pad = 0;
}
if ((r = sshbuf_reserve(state->outgoing_packet, padlen, &cp)) != 0)
@@ -1661,7 +1708,7 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
{
struct session_state *state = ssh->state;
u_int padlen, need;
- u_char *cp, macbuf[SSH_DIGEST_MAX_LENGTH];
+ u_char *cp;
u_int maclen, aadlen = 0, authlen = 0, block_size;
struct sshenc *enc = NULL;
struct sshmac *mac = NULL;
@@ -1726,8 +1773,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
sshbuf_dump(state->incoming_packet, stderr);
#endif
logit("Bad packet length %u.", state->packlen);
- return ssh_packet_start_discard(ssh, enc, mac,
- state->packlen, PACKET_MAX_SIZE);
+ return ssh_packet_start_discard(ssh, enc, mac, 0,
+ PACKET_MAX_SIZE);
}
if ((r = sshbuf_consume(state->input, block_size)) != 0)
goto out;
@@ -1749,8 +1796,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
if (need % block_size != 0) {
logit("padding error: need %d block %d mod %d",
need, block_size, need % block_size);
- return ssh_packet_start_discard(ssh, enc, mac,
- state->packlen, PACKET_MAX_SIZE - block_size);
+ return ssh_packet_start_discard(ssh, enc, mac, 0,
+ PACKET_MAX_SIZE - block_size);
}
/*
* check if the entire packet has been received and
@@ -1761,17 +1808,21 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
* 'maclen' bytes of message authentication code.
*/
if (sshbuf_len(state->input) < aadlen + need + authlen + maclen)
- return 0;
+ return 0; /* packet is incomplete */
#ifdef PACKET_DEBUG
fprintf(stderr, "read_poll enc/full: ");
sshbuf_dump(state->input, stderr);
#endif
- /* EtM: compute mac over encrypted input */
+ /* EtM: check mac over encrypted input */
if (mac && mac->enabled && mac->etm) {
- if ((r = mac_compute(mac, state->p_read.seqnr,
+ if ((r = mac_check(mac, state->p_read.seqnr,
sshbuf_ptr(state->input), aadlen + need,
- macbuf, sizeof(macbuf))) != 0)
+ sshbuf_ptr(state->input) + aadlen + need + authlen,
+ maclen)) != 0) {
+ if (r == SSH_ERR_MAC_INVALID)
+ logit("Corrupted MAC on input.");
goto out;
+ }
}
if ((r = sshbuf_reserve(state->incoming_packet, aadlen + need,
&cp)) != 0)
@@ -1781,26 +1832,22 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
goto out;
if ((r = sshbuf_consume(state->input, aadlen + need + authlen)) != 0)
goto out;
- /*
- * compute MAC over seqnr and packet,
- * increment sequence number for incoming packet
- */
if (mac && mac->enabled) {
- if (!mac->etm)
- if ((r = mac_compute(mac, state->p_read.seqnr,
- sshbuf_ptr(state->incoming_packet),
- sshbuf_len(state->incoming_packet),
- macbuf, sizeof(macbuf))) != 0)
+ /* Not EtM: check MAC over cleartext */
+ if (!mac->etm && (r = mac_check(mac, state->p_read.seqnr,
+ sshbuf_ptr(state->incoming_packet),
+ sshbuf_len(state->incoming_packet),
+ sshbuf_ptr(state->input), maclen)) != 0) {
+ if (r != SSH_ERR_MAC_INVALID)
goto out;
- if (timingsafe_bcmp(macbuf, sshbuf_ptr(state->input),
- mac->mac_len) != 0) {
logit("Corrupted MAC on input.");
if (need > PACKET_MAX_SIZE)
return SSH_ERR_INTERNAL_ERROR;
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 */
DBG(debug("MAC #%d ok", state->p_read.seqnr));
if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0)
goto out;
@@ -2045,24 +2092,19 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r)
{
switch (r) {
case SSH_ERR_CONN_CLOSED:
- logit("Connection closed by %.200s port %d",
+ logdie("Connection closed by %.200s port %d",
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
- cleanup_exit(255);
case SSH_ERR_CONN_TIMEOUT:
- logit("Connection %s %.200s port %d timed out",
+ logdie("Connection %s %.200s port %d timed out",
ssh->state->server_side ? "from" : "to",
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
- cleanup_exit(255);
case SSH_ERR_DISCONNECTED:
- logit("Disconnected from %.200s port %d",
+ logdie("Disconnected from %.200s port %d",
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
- cleanup_exit(255);
case SSH_ERR_SYSTEM_ERROR:
- if (errno == ECONNRESET) {
- logit("Connection reset by %.200s port %d",
+ if (errno == ECONNRESET)
+ logdie("Connection reset by %.200s port %d",
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
- cleanup_exit(255);
- }
/* FALLTHROUGH */
case SSH_ERR_NO_CIPHER_ALG_MATCH:
case SSH_ERR_NO_MAC_ALG_MATCH:
@@ -2070,14 +2112,14 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r)
case SSH_ERR_NO_KEX_ALG_MATCH:
case SSH_ERR_NO_HOSTKEY_ALG_MATCH:
if (ssh && ssh->kex && ssh->kex->failed_choice) {
- fatal("Unable to negotiate with %.200s port %d: %s. "
+ logdie("Unable to negotiate with %.200s port %d: %s. "
"Their offer: %s", ssh_remote_ipaddr(ssh),
ssh_remote_port(ssh), ssh_err(r),
ssh->kex->failed_choice);
}
/* FALLTHROUGH */
default:
- fatal("%s%sConnection %s %.200s port %d: %s",
+ logdie("%s%sConnection %s %.200s port %d: %s",
tag != NULL ? tag : "", tag != NULL ? ": " : "",
ssh->state->server_side ? "from" : "to",
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ssh_err(r));