diff options
Diffstat (limited to 'packet.c')
-rw-r--r-- | packet.c | 142 |
1 files changed, 92 insertions, 50 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: packet.c,v 1.229 2016/02/17 22:20:14 djm Exp $ */ | 1 | /* $OpenBSD: packet.c,v 1.234 2016/07/18 11:35:33 markus Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -52,6 +52,7 @@ | |||
52 | #include <arpa/inet.h> | 52 | #include <arpa/inet.h> |
53 | 53 | ||
54 | #include <errno.h> | 54 | #include <errno.h> |
55 | #include <netdb.h> | ||
55 | #include <stdarg.h> | 56 | #include <stdarg.h> |
56 | #include <stdio.h> | 57 | #include <stdio.h> |
57 | #include <stdlib.h> | 58 | #include <stdlib.h> |
@@ -195,6 +196,7 @@ struct session_state { | |||
195 | 196 | ||
196 | /* XXX discard incoming data after MAC error */ | 197 | /* XXX discard incoming data after MAC error */ |
197 | u_int packet_discard; | 198 | u_int packet_discard; |
199 | size_t packet_discard_mac_already; | ||
198 | struct sshmac *packet_discard_mac; | 200 | struct sshmac *packet_discard_mac; |
199 | 201 | ||
200 | /* Used in packet_read_poll2() */ | 202 | /* Used in packet_read_poll2() */ |
@@ -296,7 +298,7 @@ ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out) | |||
296 | (r = cipher_init(&state->receive_context, none, | 298 | (r = cipher_init(&state->receive_context, none, |
297 | (const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) { | 299 | (const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) { |
298 | error("%s: cipher_init failed: %s", __func__, ssh_err(r)); | 300 | error("%s: cipher_init failed: %s", __func__, ssh_err(r)); |
299 | free(ssh); | 301 | free(ssh); /* XXX need ssh_free_session_state? */ |
300 | return NULL; | 302 | return NULL; |
301 | } | 303 | } |
302 | state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL; | 304 | state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL; |
@@ -332,16 +334,18 @@ ssh_packet_stop_discard(struct ssh *ssh) | |||
332 | 334 | ||
333 | if (state->packet_discard_mac) { | 335 | if (state->packet_discard_mac) { |
334 | char buf[1024]; | 336 | char buf[1024]; |
337 | size_t dlen = PACKET_MAX_SIZE; | ||
335 | 338 | ||
339 | if (dlen > state->packet_discard_mac_already) | ||
340 | dlen -= state->packet_discard_mac_already; | ||
336 | memset(buf, 'a', sizeof(buf)); | 341 | memset(buf, 'a', sizeof(buf)); |
337 | while (sshbuf_len(state->incoming_packet) < | 342 | while (sshbuf_len(state->incoming_packet) < dlen) |
338 | PACKET_MAX_SIZE) | ||
339 | if ((r = sshbuf_put(state->incoming_packet, buf, | 343 | if ((r = sshbuf_put(state->incoming_packet, buf, |
340 | sizeof(buf))) != 0) | 344 | sizeof(buf))) != 0) |
341 | return r; | 345 | return r; |
342 | (void) mac_compute(state->packet_discard_mac, | 346 | (void) mac_compute(state->packet_discard_mac, |
343 | state->p_read.seqnr, | 347 | state->p_read.seqnr, |
344 | sshbuf_ptr(state->incoming_packet), PACKET_MAX_SIZE, | 348 | sshbuf_ptr(state->incoming_packet), dlen, |
345 | NULL, 0); | 349 | NULL, 0); |
346 | } | 350 | } |
347 | logit("Finished discarding for %.200s port %d", | 351 | logit("Finished discarding for %.200s port %d", |
@@ -351,7 +355,7 @@ ssh_packet_stop_discard(struct ssh *ssh) | |||
351 | 355 | ||
352 | static int | 356 | static int |
353 | ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc, | 357 | ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc, |
354 | struct sshmac *mac, u_int packet_length, u_int discard) | 358 | struct sshmac *mac, size_t mac_already, u_int discard) |
355 | { | 359 | { |
356 | struct session_state *state = ssh->state; | 360 | struct session_state *state = ssh->state; |
357 | int r; | 361 | int r; |
@@ -361,11 +365,16 @@ ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc, | |||
361 | return r; | 365 | return r; |
362 | return SSH_ERR_MAC_INVALID; | 366 | return SSH_ERR_MAC_INVALID; |
363 | } | 367 | } |
364 | if (packet_length != PACKET_MAX_SIZE && mac && mac->enabled) | 368 | /* |
369 | * Record number of bytes over which the mac has already | ||
370 | * been computed in order to minimize timing attacks. | ||
371 | */ | ||
372 | if (mac && mac->enabled) { | ||
365 | state->packet_discard_mac = mac; | 373 | state->packet_discard_mac = mac; |
366 | if (sshbuf_len(state->input) >= discard && | 374 | state->packet_discard_mac_already = mac_already; |
367 | (r = ssh_packet_stop_discard(ssh)) != 0) | 375 | } |
368 | return r; | 376 | if (sshbuf_len(state->input) >= discard) |
377 | return ssh_packet_stop_discard(ssh); | ||
369 | state->packet_discard = discard - sshbuf_len(state->input); | 378 | state->packet_discard = discard - sshbuf_len(state->input); |
370 | return 0; | 379 | return 0; |
371 | } | 380 | } |
@@ -379,6 +388,9 @@ ssh_packet_connection_is_on_socket(struct ssh *ssh) | |||
379 | struct sockaddr_storage from, to; | 388 | struct sockaddr_storage from, to; |
380 | socklen_t fromlen, tolen; | 389 | socklen_t fromlen, tolen; |
381 | 390 | ||
391 | if (state->connection_in == -1 || state->connection_out == -1) | ||
392 | return 0; | ||
393 | |||
382 | /* filedescriptors in and out are the same, so it's a socket */ | 394 | /* filedescriptors in and out are the same, so it's a socket */ |
383 | if (state->connection_in == state->connection_out) | 395 | if (state->connection_in == state->connection_out) |
384 | return 1; | 396 | return 1; |
@@ -468,10 +480,14 @@ ssh_remote_ipaddr(struct ssh *ssh) | |||
468 | if (ssh->remote_ipaddr == NULL) { | 480 | if (ssh->remote_ipaddr == NULL) { |
469 | if (ssh_packet_connection_is_on_socket(ssh)) { | 481 | if (ssh_packet_connection_is_on_socket(ssh)) { |
470 | ssh->remote_ipaddr = get_peer_ipaddr(sock); | 482 | ssh->remote_ipaddr = get_peer_ipaddr(sock); |
471 | ssh->remote_port = get_sock_port(sock, 0); | 483 | ssh->remote_port = get_peer_port(sock); |
484 | ssh->local_ipaddr = get_local_ipaddr(sock); | ||
485 | ssh->local_port = get_local_port(sock); | ||
472 | } else { | 486 | } else { |
473 | ssh->remote_ipaddr = strdup("UNKNOWN"); | 487 | ssh->remote_ipaddr = strdup("UNKNOWN"); |
474 | ssh->remote_port = 0; | 488 | ssh->remote_port = 65535; |
489 | ssh->local_ipaddr = strdup("UNKNOWN"); | ||
490 | ssh->local_port = 65535; | ||
475 | } | 491 | } |
476 | } | 492 | } |
477 | return ssh->remote_ipaddr; | 493 | return ssh->remote_ipaddr; |
@@ -486,6 +502,27 @@ ssh_remote_port(struct ssh *ssh) | |||
486 | return ssh->remote_port; | 502 | return ssh->remote_port; |
487 | } | 503 | } |
488 | 504 | ||
505 | /* | ||
506 | * Returns the IP-address of the local host as a string. The returned | ||
507 | * string must not be freed. | ||
508 | */ | ||
509 | |||
510 | const char * | ||
511 | ssh_local_ipaddr(struct ssh *ssh) | ||
512 | { | ||
513 | (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ | ||
514 | return ssh->local_ipaddr; | ||
515 | } | ||
516 | |||
517 | /* Returns the port number of the local host. */ | ||
518 | |||
519 | int | ||
520 | ssh_local_port(struct ssh *ssh) | ||
521 | { | ||
522 | (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ | ||
523 | return ssh->local_port; | ||
524 | } | ||
525 | |||
489 | /* Closes the connection and clears and frees internal data structures. */ | 526 | /* Closes the connection and clears and frees internal data structures. */ |
490 | 527 | ||
491 | void | 528 | void |
@@ -1142,7 +1179,7 @@ ssh_packet_send2_wrapped(struct ssh *ssh) | |||
1142 | { | 1179 | { |
1143 | struct session_state *state = ssh->state; | 1180 | struct session_state *state = ssh->state; |
1144 | u_char type, *cp, macbuf[SSH_DIGEST_MAX_LENGTH]; | 1181 | u_char type, *cp, macbuf[SSH_DIGEST_MAX_LENGTH]; |
1145 | u_char padlen, pad = 0; | 1182 | u_char tmp, padlen, pad = 0; |
1146 | u_int authlen = 0, aadlen = 0; | 1183 | u_int authlen = 0, aadlen = 0; |
1147 | u_int len; | 1184 | u_int len; |
1148 | struct sshenc *enc = NULL; | 1185 | struct sshenc *enc = NULL; |
@@ -1200,14 +1237,24 @@ ssh_packet_send2_wrapped(struct ssh *ssh) | |||
1200 | if (padlen < 4) | 1237 | if (padlen < 4) |
1201 | padlen += block_size; | 1238 | padlen += block_size; |
1202 | if (state->extra_pad) { | 1239 | if (state->extra_pad) { |
1203 | /* will wrap if extra_pad+padlen > 255 */ | 1240 | tmp = state->extra_pad; |
1204 | state->extra_pad = | 1241 | state->extra_pad = |
1205 | roundup(state->extra_pad, block_size); | 1242 | roundup(state->extra_pad, block_size); |
1206 | pad = state->extra_pad - | 1243 | /* check if roundup overflowed */ |
1207 | ((len + padlen) % state->extra_pad); | 1244 | if (state->extra_pad < tmp) |
1245 | return SSH_ERR_INVALID_ARGUMENT; | ||
1246 | tmp = (len + padlen) % state->extra_pad; | ||
1247 | /* Check whether pad calculation below will underflow */ | ||
1248 | if (tmp > state->extra_pad) | ||
1249 | return SSH_ERR_INVALID_ARGUMENT; | ||
1250 | pad = state->extra_pad - tmp; | ||
1208 | DBG(debug3("%s: adding %d (len %d padlen %d extra_pad %d)", | 1251 | DBG(debug3("%s: adding %d (len %d padlen %d extra_pad %d)", |
1209 | __func__, pad, len, padlen, state->extra_pad)); | 1252 | __func__, pad, len, padlen, state->extra_pad)); |
1253 | tmp = padlen; | ||
1210 | padlen += pad; | 1254 | padlen += pad; |
1255 | /* Check whether padlen calculation overflowed */ | ||
1256 | if (padlen < tmp) | ||
1257 | return SSH_ERR_INVALID_ARGUMENT; /* overflow */ | ||
1211 | state->extra_pad = 0; | 1258 | state->extra_pad = 0; |
1212 | } | 1259 | } |
1213 | if ((r = sshbuf_reserve(state->outgoing_packet, padlen, &cp)) != 0) | 1260 | 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) | |||
1661 | { | 1708 | { |
1662 | struct session_state *state = ssh->state; | 1709 | struct session_state *state = ssh->state; |
1663 | u_int padlen, need; | 1710 | u_int padlen, need; |
1664 | u_char *cp, macbuf[SSH_DIGEST_MAX_LENGTH]; | 1711 | u_char *cp; |
1665 | u_int maclen, aadlen = 0, authlen = 0, block_size; | 1712 | u_int maclen, aadlen = 0, authlen = 0, block_size; |
1666 | struct sshenc *enc = NULL; | 1713 | struct sshenc *enc = NULL; |
1667 | struct sshmac *mac = NULL; | 1714 | struct sshmac *mac = NULL; |
@@ -1726,8 +1773,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) | |||
1726 | sshbuf_dump(state->incoming_packet, stderr); | 1773 | sshbuf_dump(state->incoming_packet, stderr); |
1727 | #endif | 1774 | #endif |
1728 | logit("Bad packet length %u.", state->packlen); | 1775 | logit("Bad packet length %u.", state->packlen); |
1729 | return ssh_packet_start_discard(ssh, enc, mac, | 1776 | return ssh_packet_start_discard(ssh, enc, mac, 0, |
1730 | state->packlen, PACKET_MAX_SIZE); | 1777 | PACKET_MAX_SIZE); |
1731 | } | 1778 | } |
1732 | if ((r = sshbuf_consume(state->input, block_size)) != 0) | 1779 | if ((r = sshbuf_consume(state->input, block_size)) != 0) |
1733 | goto out; | 1780 | goto out; |
@@ -1749,8 +1796,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) | |||
1749 | if (need % block_size != 0) { | 1796 | if (need % block_size != 0) { |
1750 | logit("padding error: need %d block %d mod %d", | 1797 | logit("padding error: need %d block %d mod %d", |
1751 | need, block_size, need % block_size); | 1798 | need, block_size, need % block_size); |
1752 | return ssh_packet_start_discard(ssh, enc, mac, | 1799 | return ssh_packet_start_discard(ssh, enc, mac, 0, |
1753 | state->packlen, PACKET_MAX_SIZE - block_size); | 1800 | PACKET_MAX_SIZE - block_size); |
1754 | } | 1801 | } |
1755 | /* | 1802 | /* |
1756 | * check if the entire packet has been received and | 1803 | * 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) | |||
1761 | * 'maclen' bytes of message authentication code. | 1808 | * 'maclen' bytes of message authentication code. |
1762 | */ | 1809 | */ |
1763 | if (sshbuf_len(state->input) < aadlen + need + authlen + maclen) | 1810 | if (sshbuf_len(state->input) < aadlen + need + authlen + maclen) |
1764 | return 0; | 1811 | return 0; /* packet is incomplete */ |
1765 | #ifdef PACKET_DEBUG | 1812 | #ifdef PACKET_DEBUG |
1766 | fprintf(stderr, "read_poll enc/full: "); | 1813 | fprintf(stderr, "read_poll enc/full: "); |
1767 | sshbuf_dump(state->input, stderr); | 1814 | sshbuf_dump(state->input, stderr); |
1768 | #endif | 1815 | #endif |
1769 | /* EtM: compute mac over encrypted input */ | 1816 | /* EtM: check mac over encrypted input */ |
1770 | if (mac && mac->enabled && mac->etm) { | 1817 | if (mac && mac->enabled && mac->etm) { |
1771 | if ((r = mac_compute(mac, state->p_read.seqnr, | 1818 | if ((r = mac_check(mac, state->p_read.seqnr, |
1772 | sshbuf_ptr(state->input), aadlen + need, | 1819 | sshbuf_ptr(state->input), aadlen + need, |
1773 | macbuf, sizeof(macbuf))) != 0) | 1820 | sshbuf_ptr(state->input) + aadlen + need + authlen, |
1821 | maclen)) != 0) { | ||
1822 | if (r == SSH_ERR_MAC_INVALID) | ||
1823 | logit("Corrupted MAC on input."); | ||
1774 | goto out; | 1824 | goto out; |
1825 | } | ||
1775 | } | 1826 | } |
1776 | if ((r = sshbuf_reserve(state->incoming_packet, aadlen + need, | 1827 | if ((r = sshbuf_reserve(state->incoming_packet, aadlen + need, |
1777 | &cp)) != 0) | 1828 | &cp)) != 0) |
@@ -1781,26 +1832,22 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) | |||
1781 | goto out; | 1832 | goto out; |
1782 | if ((r = sshbuf_consume(state->input, aadlen + need + authlen)) != 0) | 1833 | if ((r = sshbuf_consume(state->input, aadlen + need + authlen)) != 0) |
1783 | goto out; | 1834 | goto out; |
1784 | /* | ||
1785 | * compute MAC over seqnr and packet, | ||
1786 | * increment sequence number for incoming packet | ||
1787 | */ | ||
1788 | if (mac && mac->enabled) { | 1835 | if (mac && mac->enabled) { |
1789 | if (!mac->etm) | 1836 | /* Not EtM: check MAC over cleartext */ |
1790 | if ((r = mac_compute(mac, state->p_read.seqnr, | 1837 | if (!mac->etm && (r = mac_check(mac, state->p_read.seqnr, |
1791 | sshbuf_ptr(state->incoming_packet), | 1838 | sshbuf_ptr(state->incoming_packet), |
1792 | sshbuf_len(state->incoming_packet), | 1839 | sshbuf_len(state->incoming_packet), |
1793 | macbuf, sizeof(macbuf))) != 0) | 1840 | sshbuf_ptr(state->input), maclen)) != 0) { |
1841 | if (r != SSH_ERR_MAC_INVALID) | ||
1794 | goto out; | 1842 | goto out; |
1795 | if (timingsafe_bcmp(macbuf, sshbuf_ptr(state->input), | ||
1796 | mac->mac_len) != 0) { | ||
1797 | logit("Corrupted MAC on input."); | 1843 | logit("Corrupted MAC on input."); |
1798 | if (need > PACKET_MAX_SIZE) | 1844 | if (need > PACKET_MAX_SIZE) |
1799 | return SSH_ERR_INTERNAL_ERROR; | 1845 | return SSH_ERR_INTERNAL_ERROR; |
1800 | return ssh_packet_start_discard(ssh, enc, mac, | 1846 | return ssh_packet_start_discard(ssh, enc, mac, |
1801 | state->packlen, PACKET_MAX_SIZE - need); | 1847 | sshbuf_len(state->incoming_packet), |
1848 | PACKET_MAX_SIZE - need); | ||
1802 | } | 1849 | } |
1803 | 1850 | /* Remove MAC from input buffer */ | |
1804 | DBG(debug("MAC #%d ok", state->p_read.seqnr)); | 1851 | DBG(debug("MAC #%d ok", state->p_read.seqnr)); |
1805 | if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) | 1852 | if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) |
1806 | goto out; | 1853 | goto out; |
@@ -2045,24 +2092,19 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r) | |||
2045 | { | 2092 | { |
2046 | switch (r) { | 2093 | switch (r) { |
2047 | case SSH_ERR_CONN_CLOSED: | 2094 | case SSH_ERR_CONN_CLOSED: |
2048 | logit("Connection closed by %.200s port %d", | 2095 | logdie("Connection closed by %.200s port %d", |
2049 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); | 2096 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); |
2050 | cleanup_exit(255); | ||
2051 | case SSH_ERR_CONN_TIMEOUT: | 2097 | case SSH_ERR_CONN_TIMEOUT: |
2052 | logit("Connection %s %.200s port %d timed out", | 2098 | logdie("Connection %s %.200s port %d timed out", |
2053 | ssh->state->server_side ? "from" : "to", | 2099 | ssh->state->server_side ? "from" : "to", |
2054 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); | 2100 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); |
2055 | cleanup_exit(255); | ||
2056 | case SSH_ERR_DISCONNECTED: | 2101 | case SSH_ERR_DISCONNECTED: |
2057 | logit("Disconnected from %.200s port %d", | 2102 | logdie("Disconnected from %.200s port %d", |
2058 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); | 2103 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); |
2059 | cleanup_exit(255); | ||
2060 | case SSH_ERR_SYSTEM_ERROR: | 2104 | case SSH_ERR_SYSTEM_ERROR: |
2061 | if (errno == ECONNRESET) { | 2105 | if (errno == ECONNRESET) |
2062 | logit("Connection reset by %.200s port %d", | 2106 | logdie("Connection reset by %.200s port %d", |
2063 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); | 2107 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); |
2064 | cleanup_exit(255); | ||
2065 | } | ||
2066 | /* FALLTHROUGH */ | 2108 | /* FALLTHROUGH */ |
2067 | case SSH_ERR_NO_CIPHER_ALG_MATCH: | 2109 | case SSH_ERR_NO_CIPHER_ALG_MATCH: |
2068 | case SSH_ERR_NO_MAC_ALG_MATCH: | 2110 | case SSH_ERR_NO_MAC_ALG_MATCH: |
@@ -2070,14 +2112,14 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r) | |||
2070 | case SSH_ERR_NO_KEX_ALG_MATCH: | 2112 | case SSH_ERR_NO_KEX_ALG_MATCH: |
2071 | case SSH_ERR_NO_HOSTKEY_ALG_MATCH: | 2113 | case SSH_ERR_NO_HOSTKEY_ALG_MATCH: |
2072 | if (ssh && ssh->kex && ssh->kex->failed_choice) { | 2114 | if (ssh && ssh->kex && ssh->kex->failed_choice) { |
2073 | fatal("Unable to negotiate with %.200s port %d: %s. " | 2115 | logdie("Unable to negotiate with %.200s port %d: %s. " |
2074 | "Their offer: %s", ssh_remote_ipaddr(ssh), | 2116 | "Their offer: %s", ssh_remote_ipaddr(ssh), |
2075 | ssh_remote_port(ssh), ssh_err(r), | 2117 | ssh_remote_port(ssh), ssh_err(r), |
2076 | ssh->kex->failed_choice); | 2118 | ssh->kex->failed_choice); |
2077 | } | 2119 | } |
2078 | /* FALLTHROUGH */ | 2120 | /* FALLTHROUGH */ |
2079 | default: | 2121 | default: |
2080 | fatal("%s%sConnection %s %.200s port %d: %s", | 2122 | logdie("%s%sConnection %s %.200s port %d: %s", |
2081 | tag != NULL ? tag : "", tag != NULL ? ": " : "", | 2123 | tag != NULL ? tag : "", tag != NULL ? ": " : "", |
2082 | ssh->state->server_side ? "from" : "to", | 2124 | ssh->state->server_side ? "from" : "to", |
2083 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ssh_err(r)); | 2125 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ssh_err(r)); |