aboutsummaryrefslogtreecommitdiff
path: root/packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'packet.c')
-rw-r--r--packet.c800
1 files changed, 206 insertions, 594 deletions
diff --git a/packet.c b/packet.c
index 2f3a2ec7075c..f114ea52c648 100644
--- a/packet.c
+++ b/packet.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.247 2017/03/11 13:07:35 markus Exp $ */
+/* $OpenBSD: packet.c,v 1.264 2017/09/12 06:32:07 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -68,9 +68,7 @@
#include "xmalloc.h"
#include "crc32.h"
-#include "deattack.h"
#include "compat.h"
-#include "ssh1.h"
#include "ssh2.h"
#include "cipher.h"
#include "sshkey.h"
@@ -186,10 +184,6 @@ struct session_state {
u_int32_t rekey_interval; /* how often in seconds */
time_t rekey_time; /* time of last rekeying */
- /* Session key for protocol v1 */
- u_char ssh1_key[SSH_SESSION_KEY_LENGTH];
- u_int ssh1_keylen;
-
/* roundup current message to extra_pad bytes */
u_char extra_pad;
@@ -216,9 +210,6 @@ struct session_state {
/* One-off warning about weak ciphers */
int cipher_warning_done;
- /* SSH1 CRC compensation attack detector */
- struct deattack_ctx deattack;
-
/* Hook for fuzzing inbound packets */
ssh_packet_hook_fn *hook_in;
void *hook_in_ctx;
@@ -278,13 +269,12 @@ ssh_packet_set_input_hook(struct ssh *ssh, ssh_packet_hook_fn *hook, void *ctx)
int
ssh_packet_is_rekeying(struct ssh *ssh)
{
- return compat20 &&
- (ssh->state->rekeying || (ssh->kex != NULL && ssh->kex->done == 0));
+ return ssh->state->rekeying ||
+ (ssh->kex != NULL && ssh->kex->done == 0);
}
/*
- * Sets the descriptors used for communication. Disables encryption until
- * packet_set_encryption_key is called.
+ * Sets the descriptors used for communication.
*/
struct ssh *
ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out)
@@ -315,7 +305,6 @@ ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out)
return NULL;
}
state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL;
- deattack_init(&state->deattack);
/*
* Cache the IP address of the remote connection for use in error
* messages that might be generated after the connection has closed.
@@ -570,8 +559,8 @@ ssh_local_port(struct ssh *ssh)
/* Closes the connection and clears and frees internal data structures. */
-void
-ssh_packet_close(struct ssh *ssh)
+static void
+ssh_packet_close_internal(struct ssh *ssh, int do_close)
{
struct session_state *state = ssh->state;
u_int mode;
@@ -579,20 +568,25 @@ ssh_packet_close(struct ssh *ssh)
if (!state->initialized)
return;
state->initialized = 0;
- if (state->connection_in == state->connection_out) {
- shutdown(state->connection_out, SHUT_RDWR);
- close(state->connection_out);
- } else {
- close(state->connection_in);
- close(state->connection_out);
+ if (do_close) {
+ if (state->connection_in == state->connection_out) {
+ close(state->connection_out);
+ } else {
+ close(state->connection_in);
+ close(state->connection_out);
+ }
}
sshbuf_free(state->input);
sshbuf_free(state->output);
sshbuf_free(state->outgoing_packet);
sshbuf_free(state->incoming_packet);
- for (mode = 0; mode < MODE_MAX; mode++)
- kex_free_newkeys(state->newkeys[mode]);
- if (state->compression_buffer) {
+ for (mode = 0; mode < MODE_MAX; mode++) {
+ kex_free_newkeys(state->newkeys[mode]); /* current keys */
+ state->newkeys[mode] = NULL;
+ ssh_clear_newkeys(ssh, mode); /* next keys */
+ }
+ /* comression state is in shared mem, so we can only release it once */
+ if (do_close && state->compression_buffer) {
sshbuf_free(state->compression_buffer);
if (state->compression_out_started) {
z_streamp stream = &state->compression_out_stream;
@@ -606,7 +600,7 @@ ssh_packet_close(struct ssh *ssh)
deflateEnd(stream);
}
if (state->compression_in_started) {
- z_streamp stream = &state->compression_out_stream;
+ z_streamp stream = &state->compression_in_stream;
debug("compress incoming: "
"raw data %llu, compressed %llu, factor %.2f",
(unsigned long long)stream->total_out,
@@ -620,10 +614,24 @@ ssh_packet_close(struct ssh *ssh)
cipher_free(state->send_context);
cipher_free(state->receive_context);
state->send_context = state->receive_context = NULL;
- free(ssh->remote_ipaddr);
- ssh->remote_ipaddr = NULL;
- free(ssh->state);
- ssh->state = NULL;
+ if (do_close) {
+ free(ssh->remote_ipaddr);
+ ssh->remote_ipaddr = NULL;
+ free(ssh->state);
+ ssh->state = NULL;
+ }
+}
+
+void
+ssh_packet_close(struct ssh *ssh)
+{
+ ssh_packet_close_internal(ssh, 1);
+}
+
+void
+ssh_packet_clear_keys(struct ssh *ssh)
+{
+ ssh_packet_close_internal(ssh, 0);
}
/* Sets remote side protocol flags. */
@@ -698,7 +706,7 @@ ssh_packet_start_compression(struct ssh *ssh, int level)
{
int r;
- if (ssh->state->packet_compression && !compat20)
+ if (ssh->state->packet_compression)
return SSH_ERR_INTERNAL_ERROR;
ssh->state->packet_compression = 1;
if ((r = ssh_packet_init_compression(ssh)) != 0 ||
@@ -802,136 +810,13 @@ uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out)
/* NOTREACHED */
}
-/*
- * Causes any further packets to be encrypted using the given key. The same
- * key is used for both sending and reception. However, both directions are
- * encrypted independently of each other.
- */
-
void
-ssh_packet_set_encryption_key(struct ssh *ssh, const u_char *key, u_int keylen, int number)
+ssh_clear_newkeys(struct ssh *ssh, int mode)
{
-#ifndef WITH_SSH1
- fatal("no SSH protocol 1 support");
-#else /* WITH_SSH1 */
- struct session_state *state = ssh->state;
- const struct sshcipher *cipher = cipher_by_number(number);
- int r;
- const char *wmsg;
-
- if (cipher == NULL)
- fatal("%s: unknown cipher number %d", __func__, number);
- if (keylen < 20)
- fatal("%s: keylen too small: %d", __func__, keylen);
- if (keylen > SSH_SESSION_KEY_LENGTH)
- fatal("%s: keylen too big: %d", __func__, keylen);
- memcpy(state->ssh1_key, key, keylen);
- state->ssh1_keylen = keylen;
- if ((r = cipher_init(&state->send_context, cipher, key, keylen,
- NULL, 0, CIPHER_ENCRYPT)) != 0 ||
- (r = cipher_init(&state->receive_context, cipher, key, keylen,
- NULL, 0, CIPHER_DECRYPT) != 0))
- fatal("%s: cipher_init failed: %s", __func__, ssh_err(r));
- if (!state->cipher_warning_done &&
- ((wmsg = cipher_warning_message(state->send_context)) != NULL ||
- (wmsg = cipher_warning_message(state->send_context)) != NULL)) {
- error("Warning: %s", wmsg);
- state->cipher_warning_done = 1;
+ if (ssh->kex && ssh->kex->newkeys[mode]) {
+ kex_free_newkeys(ssh->kex->newkeys[mode]);
+ ssh->kex->newkeys[mode] = NULL;
}
-#endif /* WITH_SSH1 */
-}
-
-/*
- * Finalizes and sends the packet. If the encryption key has been set,
- * encrypts the packet before sending.
- */
-
-int
-ssh_packet_send1(struct ssh *ssh)
-{
- struct session_state *state = ssh->state;
- u_char buf[8], *cp;
- int r, padding, len;
- u_int checksum;
-
- /*
- * If using packet compression, compress the payload of the outgoing
- * packet.
- */
- if (state->packet_compression) {
- sshbuf_reset(state->compression_buffer);
- /* Skip padding. */
- if ((r = sshbuf_consume(state->outgoing_packet, 8)) != 0)
- goto out;
- /* padding */
- if ((r = sshbuf_put(state->compression_buffer,
- "\0\0\0\0\0\0\0\0", 8)) != 0)
- goto out;
- if ((r = compress_buffer(ssh, state->outgoing_packet,
- state->compression_buffer)) != 0)
- goto out;
- sshbuf_reset(state->outgoing_packet);
- if ((r = sshbuf_putb(state->outgoing_packet,
- state->compression_buffer)) != 0)
- goto out;
- }
- /* Compute packet length without padding (add checksum, remove padding). */
- len = sshbuf_len(state->outgoing_packet) + 4 - 8;
-
- /* Insert padding. Initialized to zero in packet_start1() */
- padding = 8 - len % 8;
- if (!cipher_ctx_is_plaintext(state->send_context)) {
- cp = sshbuf_mutable_ptr(state->outgoing_packet);
- if (cp == NULL) {
- r = SSH_ERR_INTERNAL_ERROR;
- goto out;
- }
- arc4random_buf(cp + 8 - padding, padding);
- }
- if ((r = sshbuf_consume(state->outgoing_packet, 8 - padding)) != 0)
- goto out;
-
- /* Add check bytes. */
- checksum = ssh_crc32(sshbuf_ptr(state->outgoing_packet),
- sshbuf_len(state->outgoing_packet));
- POKE_U32(buf, checksum);
- if ((r = sshbuf_put(state->outgoing_packet, buf, 4)) != 0)
- goto out;
-
-#ifdef PACKET_DEBUG
- fprintf(stderr, "packet_send plain: ");
- sshbuf_dump(state->outgoing_packet, stderr);
-#endif
-
- /* Append to output. */
- POKE_U32(buf, len);
- if ((r = sshbuf_put(state->output, buf, 4)) != 0)
- goto out;
- if ((r = sshbuf_reserve(state->output,
- sshbuf_len(state->outgoing_packet), &cp)) != 0)
- goto out;
- if ((r = cipher_crypt(state->send_context, 0, cp,
- sshbuf_ptr(state->outgoing_packet),
- sshbuf_len(state->outgoing_packet), 0, 0)) != 0)
- goto out;
-
-#ifdef PACKET_DEBUG
- fprintf(stderr, "encrypted: ");
- sshbuf_dump(state->output, stderr);
-#endif
- state->p_send.packets++;
- state->p_send.bytes += len +
- sshbuf_len(state->outgoing_packet);
- sshbuf_reset(state->outgoing_packet);
-
- /*
- * Note that the packet is now only buffered in output. It won't be
- * actually sent until ssh_packet_write_wait or ssh_packet_write_poll
- * is called.
- */
- r = 0;
- out:
- return r;
}
int
@@ -944,45 +829,33 @@ ssh_set_newkeys(struct ssh *ssh, int mode)
struct sshcipher_ctx **ccp;
struct packet_state *ps;
u_int64_t *max_blocks;
- const char *wmsg, *dir;
+ const char *wmsg;
int r, crypt_type;
debug2("set_newkeys: mode %d", mode);
if (mode == MODE_OUT) {
- dir = "output";
ccp = &state->send_context;
crypt_type = CIPHER_ENCRYPT;
ps = &state->p_send;
max_blocks = &state->max_blocks_out;
} else {
- dir = "input";
ccp = &state->receive_context;
crypt_type = CIPHER_DECRYPT;
ps = &state->p_read;
max_blocks = &state->max_blocks_in;
}
if (state->newkeys[mode] != NULL) {
- debug("%s: rekeying after %llu %s blocks"
- " (%llu bytes total)", __func__,
- (unsigned long long)ps->blocks, dir,
- (unsigned long long)ps->bytes);
+ debug("set_newkeys: rekeying, input %llu bytes %llu blocks, "
+ "output %llu bytes %llu blocks",
+ (unsigned long long)state->p_read.bytes,
+ (unsigned long long)state->p_read.blocks,
+ (unsigned long long)state->p_send.bytes,
+ (unsigned long long)state->p_send.blocks);
cipher_free(*ccp);
*ccp = NULL;
- enc = &state->newkeys[mode]->enc;
- mac = &state->newkeys[mode]->mac;
- comp = &state->newkeys[mode]->comp;
- mac_clear(mac);
- explicit_bzero(enc->iv, enc->iv_len);
- explicit_bzero(enc->key, enc->key_len);
- explicit_bzero(mac->key, mac->key_len);
- free(enc->name);
- free(enc->iv);
- free(enc->key);
- free(mac->name);
- free(mac->key);
- free(comp->name);
- free(state->newkeys[mode]);
+ kex_free_newkeys(state->newkeys[mode]);
+ state->newkeys[mode] = NULL;
}
/* note that both bytes and the seqnr are not reset */
ps->packets = ps->blocks = 0;
@@ -1027,7 +900,8 @@ ssh_set_newkeys(struct ssh *ssh, int mode)
}
/*
* The 2^(blocksize*2) limit is too expensive for 3DES,
- * blowfish, etc, so enforce a 1GB limit for small blocksizes.
+ * so enforce a 1GB limit for small blocksizes.
+ * See RFC4344 section 3.2.
*/
if (enc->block_size >= 16)
*max_blocks = (u_int64_t)1 << (enc->block_size*2);
@@ -1071,7 +945,10 @@ ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len)
(int64_t)state->rekey_time + state->rekey_interval <= monotime())
return 1;
- /* Always rekey when MAX_PACKETS sent in either direction */
+ /*
+ * Always rekey when MAX_PACKETS sent in either direction
+ * As per RFC4344 section 3.1 we do this after 2^31 packets.
+ */
if (state->p_send.packets > MAX_PACKETS ||
state->p_read.packets > MAX_PACKETS)
return 1;
@@ -1424,13 +1301,6 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p);
if (r != 0)
break;
- if (!compat20 && (
- *typep == SSH_SMSG_SUCCESS
- || *typep == SSH_SMSG_FAILURE
- || *typep == SSH_CMSG_EOF
- || *typep == SSH_CMSG_EXIT_CONFIRMATION))
- if ((r = sshpkt_get_end(ssh)) != 0)
- break;
/* If we got a packet, return it. */
if (*typep != SSH_MSG_NONE)
break;
@@ -1524,153 +1394,6 @@ ssh_packet_read_expect(struct ssh *ssh, u_int expected_type)
return 0;
}
-/* Checks if a full packet is available in the data received so far via
- * packet_process_incoming. If so, reads the packet; otherwise returns
- * SSH_MSG_NONE. This does not wait for data from the connection.
- *
- * SSH_MSG_DISCONNECT is handled specially here. Also,
- * SSH_MSG_IGNORE messages are skipped by this function and are never returned
- * to higher levels.
- */
-
-int
-ssh_packet_read_poll1(struct ssh *ssh, u_char *typep)
-{
- struct session_state *state = ssh->state;
- u_int len, padded_len;
- const char *emsg;
- const u_char *cp;
- u_char *p;
- u_int checksum, stored_checksum;
- int r;
-
- *typep = SSH_MSG_NONE;
-
- /* Check if input size is less than minimum packet size. */
- if (sshbuf_len(state->input) < 4 + 8)
- return 0;
- /* Get length of incoming packet. */
- len = PEEK_U32(sshbuf_ptr(state->input));
- if (len < 1 + 2 + 2 || len > 256 * 1024) {
- if ((r = sshpkt_disconnect(ssh, "Bad packet length %u",
- len)) != 0)
- return r;
- return SSH_ERR_CONN_CORRUPT;
- }
- padded_len = (len + 8) & ~7;
-
- /* Check if the packet has been entirely received. */
- if (sshbuf_len(state->input) < 4 + padded_len)
- return 0;
-
- /* The entire packet is in buffer. */
-
- /* Consume packet length. */
- if ((r = sshbuf_consume(state->input, 4)) != 0)
- goto out;
-
- /*
- * Cryptographic attack detector for ssh
- * (C)1998 CORE-SDI, Buenos Aires Argentina
- * Ariel Futoransky(futo@core-sdi.com)
- */
- if (!cipher_ctx_is_plaintext(state->receive_context)) {
- emsg = NULL;
- switch (detect_attack(&state->deattack,
- sshbuf_ptr(state->input), padded_len)) {
- case DEATTACK_OK:
- break;
- case DEATTACK_DETECTED:
- emsg = "crc32 compensation attack detected";
- break;
- case DEATTACK_DOS_DETECTED:
- emsg = "deattack denial of service detected";
- break;
- default:
- emsg = "deattack error";
- break;
- }
- if (emsg != NULL) {
- error("%s", emsg);
- if ((r = sshpkt_disconnect(ssh, "%s", emsg)) != 0 ||
- (r = ssh_packet_write_wait(ssh)) != 0)
- return r;
- return SSH_ERR_CONN_CORRUPT;
- }
- }
-
- /* Decrypt data to incoming_packet. */
- sshbuf_reset(state->incoming_packet);
- if ((r = sshbuf_reserve(state->incoming_packet, padded_len, &p)) != 0)
- goto out;
- if ((r = cipher_crypt(state->receive_context, 0, p,
- sshbuf_ptr(state->input), padded_len, 0, 0)) != 0)
- goto out;
-
- if ((r = sshbuf_consume(state->input, padded_len)) != 0)
- goto out;
-
-#ifdef PACKET_DEBUG
- fprintf(stderr, "read_poll plain: ");
- sshbuf_dump(state->incoming_packet, stderr);
-#endif
-
- /* Compute packet checksum. */
- checksum = ssh_crc32(sshbuf_ptr(state->incoming_packet),
- sshbuf_len(state->incoming_packet) - 4);
-
- /* Skip padding. */
- if ((r = sshbuf_consume(state->incoming_packet, 8 - len % 8)) != 0)
- goto out;
-
- /* Test check bytes. */
- if (len != sshbuf_len(state->incoming_packet)) {
- error("%s: len %d != sshbuf_len %zd", __func__,
- len, sshbuf_len(state->incoming_packet));
- if ((r = sshpkt_disconnect(ssh, "invalid packet length")) != 0 ||
- (r = ssh_packet_write_wait(ssh)) != 0)
- return r;
- return SSH_ERR_CONN_CORRUPT;
- }
-
- cp = sshbuf_ptr(state->incoming_packet) + len - 4;
- stored_checksum = PEEK_U32(cp);
- if (checksum != stored_checksum) {
- error("Corrupted check bytes on input");
- if ((r = sshpkt_disconnect(ssh, "connection corrupted")) != 0 ||
- (r = ssh_packet_write_wait(ssh)) != 0)
- return r;
- return SSH_ERR_CONN_CORRUPT;
- }
- if ((r = sshbuf_consume_end(state->incoming_packet, 4)) < 0)
- goto out;
-
- if (state->packet_compression) {
- sshbuf_reset(state->compression_buffer);
- if ((r = uncompress_buffer(ssh, state->incoming_packet,
- state->compression_buffer)) != 0)
- goto out;
- sshbuf_reset(state->incoming_packet);
- if ((r = sshbuf_putb(state->incoming_packet,
- state->compression_buffer)) != 0)
- goto out;
- }
- state->p_read.packets++;
- state->p_read.bytes += padded_len + 4;
- if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0)
- goto out;
- if (*typep < SSH_MSG_MIN || *typep > SSH_MSG_MAX) {
- error("Invalid ssh1 packet type: %d", *typep);
- if ((r = sshpkt_disconnect(ssh, "invalid packet type")) != 0 ||
- (r = ssh_packet_write_wait(ssh)) != 0)
- return r;
- return SSH_ERR_PROTOCOL_ERROR;
- }
- r = 0;
- out:
- return r;
-}
-
static int
ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
{
@@ -1951,75 +1674,48 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
for (;;) {
msg = NULL;
- if (compat20) {
- r = ssh_packet_read_poll2(ssh, typep, seqnr_p);
- if (r != 0)
- return r;
- if (*typep) {
- state->keep_alive_timeouts = 0;
- DBG(debug("received packet type %d", *typep));
- }
- switch (*typep) {
- case SSH2_MSG_IGNORE:
- debug3("Received SSH2_MSG_IGNORE");
- break;
- case SSH2_MSG_DEBUG:
- if ((r = sshpkt_get_u8(ssh, NULL)) != 0 ||
- (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
- (r = sshpkt_get_string(ssh, NULL, NULL)) != 0) {
- free(msg);
- return r;
- }
- debug("Remote: %.900s", msg);
- free(msg);
- break;
- case SSH2_MSG_DISCONNECT:
- if ((r = sshpkt_get_u32(ssh, &reason)) != 0 ||
- (r = sshpkt_get_string(ssh, &msg, NULL)) != 0)
- return r;
- /* Ignore normal client exit notifications */
- do_log2(ssh->state->server_side &&
- reason == SSH2_DISCONNECT_BY_APPLICATION ?
- SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR,
- "Received disconnect from %s port %d:"
- "%u: %.400s", ssh_remote_ipaddr(ssh),
- ssh_remote_port(ssh), reason, msg);
- free(msg);
- return SSH_ERR_DISCONNECTED;
- case SSH2_MSG_UNIMPLEMENTED:
- if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0)
- return r;
- debug("Received SSH2_MSG_UNIMPLEMENTED for %u",
- seqnr);
- break;
- default:
- return 0;
- }
- } else {
- r = ssh_packet_read_poll1(ssh, typep);
- switch (*typep) {
- case SSH_MSG_NONE:
- return SSH_MSG_NONE;
- case SSH_MSG_IGNORE:
- break;
- case SSH_MSG_DEBUG:
- if ((r = sshpkt_get_string(ssh, &msg, NULL)) != 0)
- return r;
- debug("Remote: %.900s", msg);
- free(msg);
- break;
- case SSH_MSG_DISCONNECT:
- if ((r = sshpkt_get_string(ssh, &msg, NULL)) != 0)
- return r;
- error("Received disconnect from %s port %d: "
- "%.400s", ssh_remote_ipaddr(ssh),
- ssh_remote_port(ssh), msg);
+ r = ssh_packet_read_poll2(ssh, typep, seqnr_p);
+ if (r != 0)
+ return r;
+ if (*typep) {
+ state->keep_alive_timeouts = 0;
+ DBG(debug("received packet type %d", *typep));
+ }
+ switch (*typep) {
+ case SSH2_MSG_IGNORE:
+ debug3("Received SSH2_MSG_IGNORE");
+ break;
+ case SSH2_MSG_DEBUG:
+ if ((r = sshpkt_get_u8(ssh, NULL)) != 0 ||
+ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
+ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0) {
free(msg);
- return SSH_ERR_DISCONNECTED;
- default:
- DBG(debug("received packet type %d", *typep));
- return 0;
+ return r;
}
+ debug("Remote: %.900s", msg);
+ free(msg);
+ break;
+ case SSH2_MSG_DISCONNECT:
+ if ((r = sshpkt_get_u32(ssh, &reason)) != 0 ||
+ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0)
+ return r;
+ /* Ignore normal client exit notifications */
+ do_log2(ssh->state->server_side &&
+ reason == SSH2_DISCONNECT_BY_APPLICATION ?
+ SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR,
+ "Received disconnect from %s port %d:"
+ "%u: %.400s", ssh_remote_ipaddr(ssh),
+ ssh_remote_port(ssh), reason, msg);
+ free(msg);
+ return SSH_ERR_DISCONNECTED;
+ case SSH2_MSG_UNIMPLEMENTED:
+ if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0)
+ return r;
+ debug("Received SSH2_MSG_UNIMPLEMENTED for %u",
+ seqnr);
+ break;
+ default:
+ return 0;
}
}
}
@@ -2071,27 +1767,19 @@ ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...)
va_list args;
int r;
- if (compat20 && (ssh->compat & SSH_BUG_DEBUG))
+ if ((ssh->compat & SSH_BUG_DEBUG))
return;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
- if (compat20) {
- if ((r = sshpkt_start(ssh, SSH2_MSG_DEBUG)) != 0 ||
- (r = sshpkt_put_u8(ssh, 0)) != 0 || /* always display */
- (r = sshpkt_put_cstring(ssh, buf)) != 0 ||
- (r = sshpkt_put_cstring(ssh, "")) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- } else {
- if ((r = sshpkt_start(ssh, SSH_MSG_DEBUG)) != 0 ||
- (r = sshpkt_put_cstring(ssh, buf)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- }
- if ((r = ssh_packet_write_wait(ssh)) != 0)
+ if ((r = sshpkt_start(ssh, SSH2_MSG_DEBUG)) != 0 ||
+ (r = sshpkt_put_u8(ssh, 0)) != 0 || /* always display */
+ (r = sshpkt_put_cstring(ssh, buf)) != 0 ||
+ (r = sshpkt_put_cstring(ssh, "")) != 0 ||
+ (r = sshpkt_send(ssh)) != 0 ||
+ (r = ssh_packet_write_wait(ssh)) != 0)
fatal("%s: %s", __func__, ssh_err(r));
}
@@ -2116,15 +1804,20 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r)
switch (r) {
case SSH_ERR_CONN_CLOSED:
+ ssh_packet_clear_keys(ssh);
logdie("Connection closed by %s", remote_id);
case SSH_ERR_CONN_TIMEOUT:
+ ssh_packet_clear_keys(ssh);
logdie("Connection %s %s timed out",
ssh->state->server_side ? "from" : "to", remote_id);
case SSH_ERR_DISCONNECTED:
+ ssh_packet_clear_keys(ssh);
logdie("Disconnected from %s", remote_id);
case SSH_ERR_SYSTEM_ERROR:
- if (errno == ECONNRESET)
+ if (errno == ECONNRESET) {
+ ssh_packet_clear_keys(ssh);
logdie("Connection reset by %s", remote_id);
+ }
/* FALLTHROUGH */
case SSH_ERR_NO_CIPHER_ALG_MATCH:
case SSH_ERR_NO_MAC_ALG_MATCH:
@@ -2132,12 +1825,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) {
+ ssh_packet_clear_keys(ssh);
logdie("Unable to negotiate with %s: %s. "
"Their offer: %s", remote_id, ssh_err(r),
ssh->kex->failed_choice);
}
/* FALLTHROUGH */
default:
+ ssh_packet_clear_keys(ssh);
logdie("%s%sConnection %s %s: %s",
tag != NULL ? tag : "", tag != NULL ? ": " : "",
ssh->state->server_side ? "from" : "to",
@@ -2302,7 +1997,7 @@ void
ssh_packet_set_tos(struct ssh *ssh, int tos)
{
#ifndef IP_TOS_IS_BROKEN
- if (!ssh_packet_connection_is_on_socket(ssh))
+ if (!ssh_packet_connection_is_on_socket(ssh) || tos == INT_MAX)
return;
switch (ssh_packet_connection_af(ssh)) {
# ifdef IP_TOS
@@ -2395,36 +2090,6 @@ ssh_packet_get_maxsize(struct ssh *ssh)
return ssh->state->max_packet_size;
}
-/*
- * 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.
- */
-void
-ssh_packet_send_ignore(struct ssh *ssh, int nbytes)
-{
- u_int32_t rnd = 0;
- int r, i;
-
- if ((r = sshpkt_start(ssh, compat20 ?
- SSH2_MSG_IGNORE : SSH_MSG_IGNORE)) != 0 ||
- (r = sshpkt_put_u32(ssh, nbytes)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- for (i = 0; i < nbytes; i++) {
- if (i % 4 == 0)
- rnd = arc4random();
- if ((r = sshpkt_put_u8(ssh, (u_char)rnd & 0xff)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- rnd >>= 8;
- }
-}
-
void
ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, u_int32_t seconds)
{
@@ -2528,9 +2193,7 @@ newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode)
return r;
if ((b = sshbuf_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
- /* The cipher struct is constant and shared, you export pointer */
if ((r = sshbuf_put_cstring(b, enc->name)) != 0 ||
- (r = sshbuf_put(b, &enc->cipher, sizeof(enc->cipher))) != 0 ||
(r = sshbuf_put_u32(b, enc->enabled)) != 0 ||
(r = sshbuf_put_u32(b, enc->block_size)) != 0 ||
(r = sshbuf_put_string(b, enc->key, enc->key_len)) != 0 ||
@@ -2556,54 +2219,22 @@ int
ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m)
{
struct session_state *state = ssh->state;
- u_char *p;
- size_t slen, rlen;
- int r, ssh1cipher;
-
- if (!compat20) {
- ssh1cipher = cipher_ctx_get_number(state->receive_context);
- slen = cipher_get_keyiv_len(state->send_context);
- rlen = cipher_get_keyiv_len(state->receive_context);
- if ((r = sshbuf_put_u32(m, state->remote_protocol_flags)) != 0 ||
- (r = sshbuf_put_u32(m, ssh1cipher)) != 0 ||
- (r = sshbuf_put_string(m, state->ssh1_key, state->ssh1_keylen)) != 0 ||
- (r = sshbuf_put_u32(m, slen)) != 0 ||
- (r = sshbuf_reserve(m, slen, &p)) != 0 ||
- (r = cipher_get_keyiv(state->send_context, p, slen)) != 0 ||
- (r = sshbuf_put_u32(m, rlen)) != 0 ||
- (r = sshbuf_reserve(m, rlen, &p)) != 0 ||
- (r = cipher_get_keyiv(state->receive_context, p, rlen)) != 0)
- return r;
- } else {
- if ((r = kex_to_blob(m, ssh->kex)) != 0 ||
- (r = newkeys_to_blob(m, ssh, MODE_OUT)) != 0 ||
- (r = newkeys_to_blob(m, ssh, MODE_IN)) != 0 ||
- (r = sshbuf_put_u64(m, state->rekey_limit)) != 0 ||
- (r = sshbuf_put_u32(m, state->rekey_interval)) != 0 ||
- (r = sshbuf_put_u32(m, state->p_send.seqnr)) != 0 ||
- (r = sshbuf_put_u64(m, state->p_send.blocks)) != 0 ||
- (r = sshbuf_put_u32(m, state->p_send.packets)) != 0 ||
- (r = sshbuf_put_u64(m, state->p_send.bytes)) != 0 ||
- (r = sshbuf_put_u32(m, state->p_read.seqnr)) != 0 ||
- (r = sshbuf_put_u64(m, state->p_read.blocks)) != 0 ||
- (r = sshbuf_put_u32(m, state->p_read.packets)) != 0 ||
- (r = sshbuf_put_u64(m, state->p_read.bytes)) != 0)
- return r;
- }
+ int r;
- slen = cipher_get_keycontext(state->send_context, NULL);
- rlen = cipher_get_keycontext(state->receive_context, NULL);
- if ((r = sshbuf_put_u32(m, slen)) != 0 ||
- (r = sshbuf_reserve(m, slen, &p)) != 0)
- return r;
- if (cipher_get_keycontext(state->send_context, p) != (int)slen)
- return SSH_ERR_INTERNAL_ERROR;
- if ((r = sshbuf_put_u32(m, rlen)) != 0 ||
- (r = sshbuf_reserve(m, rlen, &p)) != 0)
- return r;
- if (cipher_get_keycontext(state->receive_context, p) != (int)rlen)
- return SSH_ERR_INTERNAL_ERROR;
- if ((r = sshbuf_put_stringb(m, state->input)) != 0 ||
+ if ((r = kex_to_blob(m, ssh->kex)) != 0 ||
+ (r = newkeys_to_blob(m, ssh, MODE_OUT)) != 0 ||
+ (r = newkeys_to_blob(m, ssh, MODE_IN)) != 0 ||
+ (r = sshbuf_put_u64(m, state->rekey_limit)) != 0 ||
+ (r = sshbuf_put_u32(m, state->rekey_interval)) != 0 ||
+ (r = sshbuf_put_u32(m, state->p_send.seqnr)) != 0 ||
+ (r = sshbuf_put_u64(m, state->p_send.blocks)) != 0 ||
+ (r = sshbuf_put_u32(m, state->p_send.packets)) != 0 ||
+ (r = sshbuf_put_u64(m, state->p_send.bytes)) != 0 ||
+ (r = sshbuf_put_u32(m, state->p_read.seqnr)) != 0 ||
+ (r = sshbuf_put_u64(m, state->p_read.blocks)) != 0 ||
+ (r = sshbuf_put_u32(m, state->p_read.packets)) != 0 ||
+ (r = sshbuf_put_u64(m, state->p_read.bytes)) != 0 ||
+ (r = sshbuf_put_stringb(m, state->input)) != 0 ||
(r = sshbuf_put_stringb(m, state->output)) != 0)
return r;
@@ -2636,12 +2267,15 @@ newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode)
comp = &newkey->comp;
if ((r = sshbuf_get_cstring(b, &enc->name, NULL)) != 0 ||
- (r = sshbuf_get(b, &enc->cipher, sizeof(enc->cipher))) != 0 ||
(r = sshbuf_get_u32(b, (u_int *)&enc->enabled)) != 0 ||
(r = sshbuf_get_u32(b, &enc->block_size)) != 0 ||
(r = sshbuf_get_string(b, &enc->key, &keylen)) != 0 ||
(r = sshbuf_get_string(b, &enc->iv, &ivlen)) != 0)
goto out;
+ if ((enc->cipher = cipher_by_name(enc->name)) == NULL) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
if (cipher_authlen(enc->cipher) == 0) {
if ((r = sshbuf_get_cstring(b, &mac->name, NULL)) != 0)
goto out;
@@ -2659,11 +2293,6 @@ newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode)
if ((r = sshbuf_get_u32(b, &comp->type)) != 0 ||
(r = sshbuf_get_cstring(b, &comp->name, NULL)) != 0)
goto out;
- if (enc->name == NULL ||
- cipher_by_name(enc->name) != enc->cipher) {
- r = SSH_ERR_INVALID_FORMAT;
- goto out;
- }
if (sshbuf_len(b) != 0) {
r = SSH_ERR_INVALID_FORMAT;
goto out;
@@ -2728,61 +2357,33 @@ int
ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m)
{
struct session_state *state = ssh->state;
- const u_char *ssh1key, *ivin, *ivout, *keyin, *keyout, *input, *output;
- size_t ssh1keylen, rlen, slen, ilen, olen;
+ const u_char *input, *output;
+ size_t ilen, olen;
int r;
- u_int ssh1cipher = 0;
- if (!compat20) {
- if ((r = sshbuf_get_u32(m, &state->remote_protocol_flags)) != 0 ||
- (r = sshbuf_get_u32(m, &ssh1cipher)) != 0 ||
- (r = sshbuf_get_string_direct(m, &ssh1key, &ssh1keylen)) != 0 ||
- (r = sshbuf_get_string_direct(m, &ivout, &slen)) != 0 ||
- (r = sshbuf_get_string_direct(m, &ivin, &rlen)) != 0)
- return r;
- if (ssh1cipher > INT_MAX)
- return SSH_ERR_KEY_UNKNOWN_CIPHER;
- ssh_packet_set_encryption_key(ssh, ssh1key, ssh1keylen,
- (int)ssh1cipher);
- if (cipher_get_keyiv_len(state->send_context) != (int)slen ||
- cipher_get_keyiv_len(state->receive_context) != (int)rlen)
- return SSH_ERR_INVALID_FORMAT;
- if ((r = cipher_set_keyiv(state->send_context, ivout)) != 0 ||
- (r = cipher_set_keyiv(state->receive_context, ivin)) != 0)
- return r;
- } else {
- if ((r = kex_from_blob(m, &ssh->kex)) != 0 ||
- (r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 ||
- (r = newkeys_from_blob(m, ssh, MODE_IN)) != 0 ||
- (r = sshbuf_get_u64(m, &state->rekey_limit)) != 0 ||
- (r = sshbuf_get_u32(m, &state->rekey_interval)) != 0 ||
- (r = sshbuf_get_u32(m, &state->p_send.seqnr)) != 0 ||
- (r = sshbuf_get_u64(m, &state->p_send.blocks)) != 0 ||
- (r = sshbuf_get_u32(m, &state->p_send.packets)) != 0 ||
- (r = sshbuf_get_u64(m, &state->p_send.bytes)) != 0 ||
- (r = sshbuf_get_u32(m, &state->p_read.seqnr)) != 0 ||
- (r = sshbuf_get_u64(m, &state->p_read.blocks)) != 0 ||
- (r = sshbuf_get_u32(m, &state->p_read.packets)) != 0 ||
- (r = sshbuf_get_u64(m, &state->p_read.bytes)) != 0)
- return r;
- /*
- * We set the time here so that in post-auth privsep slave we
- * count from the completion of the authentication.
- */
- state->rekey_time = monotime();
- /* XXX ssh_set_newkeys overrides p_read.packets? XXX */
- if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0 ||
- (r = ssh_set_newkeys(ssh, MODE_OUT)) != 0)
- return r;
- }
- if ((r = sshbuf_get_string_direct(m, &keyout, &slen)) != 0 ||
- (r = sshbuf_get_string_direct(m, &keyin, &rlen)) != 0)
+ if ((r = kex_from_blob(m, &ssh->kex)) != 0 ||
+ (r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 ||
+ (r = newkeys_from_blob(m, ssh, MODE_IN)) != 0 ||
+ (r = sshbuf_get_u64(m, &state->rekey_limit)) != 0 ||
+ (r = sshbuf_get_u32(m, &state->rekey_interval)) != 0 ||
+ (r = sshbuf_get_u32(m, &state->p_send.seqnr)) != 0 ||
+ (r = sshbuf_get_u64(m, &state->p_send.blocks)) != 0 ||
+ (r = sshbuf_get_u32(m, &state->p_send.packets)) != 0 ||
+ (r = sshbuf_get_u64(m, &state->p_send.bytes)) != 0 ||
+ (r = sshbuf_get_u32(m, &state->p_read.seqnr)) != 0 ||
+ (r = sshbuf_get_u64(m, &state->p_read.blocks)) != 0 ||
+ (r = sshbuf_get_u32(m, &state->p_read.packets)) != 0 ||
+ (r = sshbuf_get_u64(m, &state->p_read.bytes)) != 0)
+ return r;
+ /*
+ * We set the time here so that in post-auth privsep slave we
+ * count from the completion of the authentication.
+ */
+ state->rekey_time = monotime();
+ /* XXX ssh_set_newkeys overrides p_read.packets? XXX */
+ if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0 ||
+ (r = ssh_set_newkeys(ssh, MODE_OUT)) != 0)
return r;
- if (cipher_get_keycontext(state->send_context, NULL) != (int)slen ||
- cipher_get_keycontext(state->receive_context, NULL) != (int)rlen)
- return SSH_ERR_INVALID_FORMAT;
- cipher_set_keycontext(state->send_context, keyout);
- cipher_set_keycontext(state->receive_context, keyin);
if ((r = ssh_packet_set_postauth(ssh)) != 0)
return r;
@@ -2862,13 +2463,6 @@ sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g)
}
#endif /* OPENSSL_HAS_ECC */
-#ifdef WITH_SSH1
-int
-sshpkt_put_bignum1(struct ssh *ssh, const BIGNUM *v)
-{
- return sshbuf_put_bignum1(ssh->state->outgoing_packet, v);
-}
-#endif /* WITH_SSH1 */
int
sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v)
@@ -2916,6 +2510,12 @@ sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp)
}
int
+sshpkt_peek_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp)
+{
+ return sshbuf_peek_string_direct(ssh->state->incoming_packet, valp, lenp);
+}
+
+int
sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp)
{
return sshbuf_get_cstring(ssh->state->incoming_packet, valp, lenp);
@@ -2930,13 +2530,6 @@ sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g)
}
#endif /* OPENSSL_HAS_ECC */
-#ifdef WITH_SSH1
-int
-sshpkt_get_bignum1(struct ssh *ssh, BIGNUM *v)
-{
- return sshbuf_get_bignum1(ssh->state->incoming_packet, v);
-}
-#endif /* WITH_SSH1 */
int
sshpkt_get_bignum2(struct ssh *ssh, BIGNUM *v)
@@ -2966,15 +2559,13 @@ sshpkt_ptr(struct ssh *ssh, size_t *lenp)
int
sshpkt_start(struct ssh *ssh, u_char type)
{
- u_char buf[9];
- int len;
+ u_char buf[6]; /* u32 packet length, u8 pad len, u8 type */
DBG(debug("packet_start[%d]", type));
- len = compat20 ? 6 : 9;
- memset(buf, 0, len - 1);
- buf[len - 1] = type;
+ memset(buf, 0, sizeof(buf));
+ buf[sizeof(buf) - 1] = type;
sshbuf_reset(ssh->state->outgoing_packet);
- return sshbuf_put(ssh->state->outgoing_packet, buf, len);
+ return sshbuf_put(ssh->state->outgoing_packet, buf, sizeof(buf));
}
static int
@@ -3007,6 +2598,37 @@ ssh_packet_send_mux(struct ssh *ssh)
return 0;
}
+/*
+ * 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.
+ */
+int
+sshpkt_msg_ignore(struct ssh *ssh, u_int nbytes)
+{
+ u_int32_t rnd = 0;
+ int r;
+ u_int i;
+
+ if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 ||
+ (r = sshpkt_put_u32(ssh, nbytes)) != 0)
+ return r;
+ for (i = 0; i < nbytes; i++) {
+ if (i % 4 == 0)
+ rnd = arc4random();
+ if ((r = sshpkt_put_u8(ssh, (u_char)rnd & 0xff)) != 0)
+ return r;
+ rnd >>= 8;
+ }
+ return 0;
+}
+
/* send it */
int
@@ -3014,10 +2636,7 @@ sshpkt_send(struct ssh *ssh)
{
if (ssh->state && ssh->state->mux)
return ssh_packet_send_mux(ssh);
- if (compat20)
- return ssh_packet_send2(ssh);
- else
- return ssh_packet_send1(ssh);
+ return ssh_packet_send2(ssh);
}
int
@@ -3031,19 +2650,12 @@ sshpkt_disconnect(struct ssh *ssh, const char *fmt,...)
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
- if (compat20) {
- if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 ||
- (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 ||
- (r = sshpkt_put_cstring(ssh, buf)) != 0 ||
- (r = sshpkt_put_cstring(ssh, "")) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- return r;
- } else {
- if ((r = sshpkt_start(ssh, SSH_MSG_DISCONNECT)) != 0 ||
- (r = sshpkt_put_cstring(ssh, buf)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- return r;
- }
+ if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 ||
+ (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 ||
+ (r = sshpkt_put_cstring(ssh, buf)) != 0 ||
+ (r = sshpkt_put_cstring(ssh, "")) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ return r;
return 0;
}