summaryrefslogtreecommitdiff
path: root/packet.c
diff options
context:
space:
mode:
authorAlistair Delva <adelva@google.com>2020-08-21 00:00:13 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-08-21 00:00:13 +0000
commited358b3546c776c1c677fd88eb8f716cf6187510 (patch)
tree3c6134bcb2cda4b9dccc57b4a8b997a945aab62d /packet.c
parent22246b08952d746a7cc5a292570636cf4277598f (diff)
parent44a1065de8a58c51a021243a28bfa01e87822e4f (diff)
Merge changes I934c73d4,I28cdc9a0,I9e734da9,I3c079d86
* changes: UPSTREAM: depend UPSTREAM: upstream: avoid possible NULL deref; from Pedro Martelletto Revert "upstream: fix compilation with DEBUG_KEXDH; bz#3160 ok dtucker@" Merge upstream-master into master
Diffstat (limited to 'packet.c')
-rw-r--r--packet.c1726
1 files changed, 785 insertions, 941 deletions
diff --git a/packet.c b/packet.c
index b1219c85..e7abb341 100644
--- a/packet.c
+++ b/packet.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.208 2015/02/13 18:57:00 markus Exp $ */
+/* $OpenBSD: packet.c,v 1.291 2020/03/06 18:20:44 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -38,8 +38,7 @@
*/
#include "includes.h"
-
-#include <sys/param.h> /* MIN roundup */
+
#include <sys/types.h>
#include "openbsd-compat/sys-queue.h"
#include <sys/socket.h>
@@ -52,25 +51,37 @@
#include <arpa/inet.h>
#include <errno.h>
+#include <netdb.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
#include <signal.h>
#include <time.h>
-#include <zlib.h>
+/*
+ * Explicitly include OpenSSL before zlib as some versions of OpenSSL have
+ * "free_func" in their headers, which zlib typedefs.
+ */
+#ifdef WITH_OPENSSL
+# include <openssl/bn.h>
+# include <openssl/evp.h>
+# ifdef OPENSSL_HAS_ECC
+# include <openssl/ec.h>
+# endif
+#endif
-#include "buffer.h" /* typedefs XXX */
-#include "key.h" /* typedefs XXX */
+#ifdef WITH_ZLIB
+#include <zlib.h>
+#endif
#include "xmalloc.h"
-#include "crc32.h"
-#include "deattack.h"
#include "compat.h"
-#include "ssh1.h"
#include "ssh2.h"
#include "cipher.h"
#include "sshkey.h"
@@ -83,7 +94,6 @@
#include "channels.h"
#include "ssh.h"
#include "packet.h"
-#include "roaming.h"
#include "ssherr.h"
#include "sshbuf.h"
@@ -122,10 +132,10 @@ struct session_state {
u_int remote_protocol_flags;
/* Encryption context for receiving data. Only used for decryption. */
- struct sshcipher_ctx receive_context;
+ struct sshcipher_ctx *receive_context;
/* Encryption context for sending data. Only used for encryption. */
- struct sshcipher_ctx send_context;
+ struct sshcipher_ctx *send_context;
/* Buffer for raw input data from the socket. */
struct sshbuf *input;
@@ -142,20 +152,16 @@ struct session_state {
/* Scratch buffer for packet compression/decompression. */
struct sshbuf *compression_buffer;
+#ifdef WITH_ZLIB
/* Incoming/outgoing compression dictionaries */
z_stream compression_in_stream;
z_stream compression_out_stream;
+#endif
int compression_in_started;
int compression_out_started;
int compression_in_failures;
int compression_out_failures;
- /*
- * Flag indicating whether packet compression/decompression is
- * enabled.
- */
- int packet_compression;
-
/* default maximum packet size */
u_int max_packet_size;
@@ -181,22 +187,18 @@ struct session_state {
struct packet_state p_read, p_send;
/* Volume-based rekeying */
- u_int64_t max_blocks_in, max_blocks_out;
- u_int32_t rekey_limit;
+ u_int64_t max_blocks_in, max_blocks_out, rekey_limit;
/* Time-based rekeying */
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;
/* 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() */
@@ -205,6 +207,9 @@ struct session_state {
/* Used in packet_send2 */
int rekeying;
+ /* Used in ssh_packet_send_mux() */
+ int mux;
+
/* Used in packet_set_interactive */
int set_interactive_called;
@@ -214,8 +219,9 @@ 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;
TAILQ_HEAD(, packet) outgoing;
};
@@ -228,6 +234,7 @@ ssh_alloc_session_state(void)
if ((ssh = calloc(1, sizeof(*ssh))) == NULL ||
(state = calloc(1, sizeof(*state))) == NULL ||
+ (ssh->kex = kex_new()) == NULL ||
(state->input = sshbuf_new()) == NULL ||
(state->output = sshbuf_new()) == NULL ||
(state->outgoing_packet = sshbuf_new()) == NULL ||
@@ -250,6 +257,10 @@ ssh_alloc_session_state(void)
ssh->state = state;
return ssh;
fail:
+ if (ssh) {
+ kex_free(ssh->kex);
+ free(ssh);
+ }
if (state) {
sshbuf_free(state->input);
sshbuf_free(state->output);
@@ -257,13 +268,25 @@ ssh_alloc_session_state(void)
sshbuf_free(state->outgoing_packet);
free(state);
}
- free(ssh);
return NULL;
}
+void
+ssh_packet_set_input_hook(struct ssh *ssh, ssh_packet_hook_fn *hook, void *ctx)
+{
+ ssh->state->hook_in = hook;
+ ssh->state->hook_in_ctx = ctx;
+}
+
+/* Returns nonzero if rekeying is in progress */
+int
+ssh_packet_is_rekeying(struct ssh *ssh)
+{
+ return ssh->state->rekeying || 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)
@@ -279,7 +302,7 @@ ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out)
if (ssh == NULL)
ssh = ssh_alloc_session_state();
if (ssh == NULL) {
- error("%s: cound not allocate state", __func__);
+ error("%s: could not allocate state", __func__);
return NULL;
}
state = ssh->state;
@@ -290,10 +313,10 @@ 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); /* XXX need ssh_free_session_state? */
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.
@@ -317,6 +340,38 @@ ssh_packet_set_timeout(struct ssh *ssh, int timeout, int count)
state->packet_timeout_ms = timeout * count * 1000;
}
+void
+ssh_packet_set_mux(struct ssh *ssh)
+{
+ ssh->state->mux = 1;
+ ssh->state->rekeying = 0;
+}
+
+int
+ssh_packet_get_mux(struct ssh *ssh)
+{
+ return ssh->state->mux;
+}
+
+int
+ssh_packet_set_log_preamble(struct ssh *ssh, const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ free(ssh->log_preamble);
+ if (fmt == NULL)
+ ssh->log_preamble = NULL;
+ else {
+ va_start(args, fmt);
+ r = vasprintf(&ssh->log_preamble, fmt, args);
+ va_end(args);
+ if (r < 0 || ssh->log_preamble == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ }
+ return 0;
+}
+
int
ssh_packet_stop_discard(struct ssh *ssh)
{
@@ -325,25 +380,28 @@ 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", ssh_remote_ipaddr(ssh));
+ logit("Finished discarding for %.200s port %d",
+ ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
return SSH_ERR_MAC_INVALID;
}
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;
@@ -353,11 +411,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;
}
@@ -367,22 +430,28 @@ ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc,
int
ssh_packet_connection_is_on_socket(struct ssh *ssh)
{
- struct session_state *state = ssh->state;
+ struct session_state *state;
struct sockaddr_storage from, to;
socklen_t fromlen, tolen;
+ if (ssh == NULL || ssh->state == NULL)
+ return 0;
+
+ state = ssh->state;
+ 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;
fromlen = sizeof(from);
memset(&from, 0, sizeof(from));
if (getpeername(state->connection_in, (struct sockaddr *)&from,
- &fromlen) < 0)
+ &fromlen) == -1)
return 0;
tolen = sizeof(to);
memset(&to, 0, sizeof(to));
if (getpeername(state->connection_out, (struct sockaddr *)&to,
- &tolen) < 0)
+ &tolen) == -1)
return 0;
if (fromlen != tolen || memcmp(&from, &to, fromlen) != 0)
return 0;
@@ -408,7 +477,7 @@ ssh_packet_connection_af(struct ssh *ssh)
memset(&to, 0, sizeof(to));
if (getsockname(ssh->state->connection_out, (struct sockaddr *)&to,
- &tolen) < 0)
+ &tolen) == -1)
return 0;
#ifdef IPV4_IN_IPV6
if (to.ss_family == AF_INET6 &&
@@ -454,42 +523,99 @@ ssh_packet_get_connection_out(struct ssh *ssh)
const char *
ssh_remote_ipaddr(struct ssh *ssh)
{
+ int sock;
+
/* Check whether we have cached the ipaddr. */
- if (ssh->remote_ipaddr == NULL)
- ssh->remote_ipaddr = ssh_packet_connection_is_on_socket(ssh) ?
- get_peer_ipaddr(ssh->state->connection_in) :
- strdup("UNKNOWN");
- if (ssh->remote_ipaddr == NULL)
- return "UNKNOWN";
+ if (ssh->remote_ipaddr == NULL) {
+ if (ssh_packet_connection_is_on_socket(ssh)) {
+ sock = ssh->state->connection_in;
+ ssh->remote_ipaddr = get_peer_ipaddr(sock);
+ 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 = xstrdup("UNKNOWN");
+ ssh->remote_port = 65535;
+ ssh->local_ipaddr = xstrdup("UNKNOWN");
+ ssh->local_port = 65535;
+ }
+ }
return ssh->remote_ipaddr;
}
+/* Returns the port number of the remote host. */
+
+int
+ssh_remote_port(struct ssh *ssh)
+{
+ (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */
+ 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;
+}
+
+/* Returns the routing domain of the input socket, or NULL if unavailable */
+const char *
+ssh_packet_rdomain_in(struct ssh *ssh)
+{
+ if (ssh->rdomain_in != NULL)
+ return ssh->rdomain_in;
+ if (!ssh_packet_connection_is_on_socket(ssh))
+ return NULL;
+ ssh->rdomain_in = get_rdomain(ssh->state->connection_in);
+ return ssh->rdomain_in;
+}
+
/* 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;
- int r;
u_int mode;
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 */
+ }
+#ifdef WITH_ZLIB
+ /* compression 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;
@@ -503,7 +629,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,
@@ -514,16 +640,30 @@ ssh_packet_close(struct ssh *ssh)
inflateEnd(stream);
}
}
- if ((r = cipher_cleanup(&state->send_context)) != 0)
- error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r));
- if ((r = cipher_cleanup(&state->receive_context)) != 0)
- error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r));
- if (ssh->remote_ipaddr) {
+#endif /* WITH_ZLIB */
+ cipher_free(state->send_context);
+ cipher_free(state->receive_context);
+ state->send_context = state->receive_context = NULL;
+ if (do_close) {
+ free(ssh->local_ipaddr);
+ ssh->local_ipaddr = NULL;
free(ssh->remote_ipaddr);
ssh->remote_ipaddr = NULL;
+ free(ssh->state);
+ ssh->state = 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. */
@@ -556,6 +696,7 @@ ssh_packet_init_compression(struct ssh *ssh)
return 0;
}
+#ifdef WITH_ZLIB
static int
start_compression_out(struct ssh *ssh, int level)
{
@@ -593,21 +734,6 @@ start_compression_in(struct ssh *ssh)
return 0;
}
-int
-ssh_packet_start_compression(struct ssh *ssh, int level)
-{
- int r;
-
- if (ssh->state->packet_compression && !compat20)
- return SSH_ERR_INTERNAL_ERROR;
- ssh->state->packet_compression = 1;
- if ((r = ssh_packet_init_compression(ssh)) != 0 ||
- (r = start_compression_in(ssh)) != 0 ||
- (r = start_compression_out(ssh, level)) != 0)
- return r;
- return 0;
-}
-
/* XXX remove need for separate compression buffer */
static int
compress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out)
@@ -702,214 +828,40 @@ uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out)
/* NOTREACHED */
}
-/* Serialise compression state into a blob for privsep */
+#else /* WITH_ZLIB */
+
static int
-ssh_packet_get_compress_state(struct sshbuf *m, struct ssh *ssh)
+start_compression_out(struct ssh *ssh, int level)
{
- struct session_state *state = ssh->state;
- struct sshbuf *b;
- int r;
-
- if ((b = sshbuf_new()) == NULL)
- return SSH_ERR_ALLOC_FAIL;
- if (state->compression_in_started) {
- if ((r = sshbuf_put_string(b, &state->compression_in_stream,
- sizeof(state->compression_in_stream))) != 0)
- goto out;
- } else if ((r = sshbuf_put_string(b, NULL, 0)) != 0)
- goto out;
- if (state->compression_out_started) {
- if ((r = sshbuf_put_string(b, &state->compression_out_stream,
- sizeof(state->compression_out_stream))) != 0)
- goto out;
- } else if ((r = sshbuf_put_string(b, NULL, 0)) != 0)
- goto out;
- r = sshbuf_put_stringb(m, b);
- out:
- sshbuf_free(b);
- return r;
+ return SSH_ERR_INTERNAL_ERROR;
}
-/* Deserialise compression state from a blob for privsep */
static int
-ssh_packet_set_compress_state(struct ssh *ssh, struct sshbuf *m)
+start_compression_in(struct ssh *ssh)
{
- struct session_state *state = ssh->state;
- struct sshbuf *b = NULL;
- int r;
- const u_char *inblob, *outblob;
- size_t inl, outl;
-
- if ((r = sshbuf_froms(m, &b)) != 0)
- goto out;
- if ((r = sshbuf_get_string_direct(b, &inblob, &inl)) != 0 ||
- (r = sshbuf_get_string_direct(b, &outblob, &outl)) != 0)
- goto out;
- if (inl == 0)
- state->compression_in_started = 0;
- else if (inl != sizeof(state->compression_in_stream)) {
- r = SSH_ERR_INTERNAL_ERROR;
- goto out;
- } else {
- state->compression_in_started = 1;
- memcpy(&state->compression_in_stream, inblob, inl);
- }
- if (outl == 0)
- state->compression_out_started = 0;
- else if (outl != sizeof(state->compression_out_stream)) {
- r = SSH_ERR_INTERNAL_ERROR;
- goto out;
- } else {
- state->compression_out_started = 1;
- memcpy(&state->compression_out_stream, outblob, outl);
- }
- r = 0;
- out:
- sshbuf_free(b);
- return r;
+ return SSH_ERR_INTERNAL_ERROR;
}
-void
-ssh_packet_set_compress_hooks(struct ssh *ssh, void *ctx,
- void *(*allocfunc)(void *, u_int, u_int),
- void (*freefunc)(void *, void *))
+static int
+compress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out)
{
- ssh->state->compression_out_stream.zalloc = (alloc_func)allocfunc;
- ssh->state->compression_out_stream.zfree = (free_func)freefunc;
- ssh->state->compression_out_stream.opaque = ctx;
- ssh->state->compression_in_stream.zalloc = (alloc_func)allocfunc;
- ssh->state->compression_in_stream.zfree = (free_func)freefunc;
- ssh->state->compression_in_stream.opaque = ctx;
+ return SSH_ERR_INTERNAL_ERROR;
}
-/*
- * 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)
+static int
+uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out)
{
-#ifdef 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;
- }
-#endif /* WITH_SSH1 */
+ return SSH_ERR_INTERNAL_ERROR;
}
+#endif /* WITH_ZLIB */
-/*
- * Finalizes and sends the packet. If the encryption key has been set,
- * encrypts the packet before sending.
- */
-
-int
-ssh_packet_send1(struct ssh *ssh)
+void
+ssh_clear_newkeys(struct ssh *ssh, int mode)
{
- 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;
+ if (ssh->kex && ssh->kex->newkeys[mode]) {
+ kex_free_newkeys(ssh->kex->newkeys[mode]);
+ ssh->kex->newkeys[mode] = NULL;
}
- /* 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 (!state->send_context.plaintext) {
- 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
@@ -919,43 +871,38 @@ ssh_set_newkeys(struct ssh *ssh, int mode)
struct sshenc *enc;
struct sshmac *mac;
struct sshcomp *comp;
- struct sshcipher_ctx *cc;
+ struct sshcipher_ctx **ccp;
+ struct packet_state *ps;
u_int64_t *max_blocks;
const char *wmsg;
int r, crypt_type;
+ const char *dir = mode == MODE_OUT ? "out" : "in";
debug2("set_newkeys: mode %d", mode);
if (mode == MODE_OUT) {
- cc = &state->send_context;
+ ccp = &state->send_context;
crypt_type = CIPHER_ENCRYPT;
- state->p_send.packets = state->p_send.blocks = 0;
+ ps = &state->p_send;
max_blocks = &state->max_blocks_out;
} else {
- cc = &state->receive_context;
+ ccp = &state->receive_context;
crypt_type = CIPHER_DECRYPT;
- state->p_read.packets = state->p_read.blocks = 0;
+ ps = &state->p_read;
max_blocks = &state->max_blocks_in;
}
if (state->newkeys[mode] != NULL) {
- debug("set_newkeys: rekeying");
- if ((r = cipher_cleanup(cc)) != 0)
- return r;
- 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]);
+ debug("%s: rekeying %s, input %llu bytes %llu blocks, "
+ "output %llu bytes %llu blocks", __func__, dir,
+ (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);
+ 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;
/* move newkeys from kex to state */
if ((state->newkeys[mode] = ssh->kex->newkeys[mode]) == NULL)
return SSH_ERR_INTERNAL_ERROR;
@@ -968,12 +915,14 @@ ssh_set_newkeys(struct ssh *ssh, int mode)
return r;
}
mac->enabled = 1;
- DBG(debug("cipher_init_context: %d", mode));
- if ((r = cipher_init(cc, enc->cipher, enc->key, enc->key_len,
+ DBG(debug("%s: cipher_init_context: %s", __func__, dir));
+ cipher_free(*ccp);
+ *ccp = NULL;
+ if ((r = cipher_init(ccp, enc->cipher, enc->key, enc->key_len,
enc->iv, enc->iv_len, crypt_type)) != 0)
return r;
if (!state->cipher_warning_done &&
- (wmsg = cipher_warning_message(cc)) != NULL) {
+ (wmsg = cipher_warning_message(*ccp)) != NULL) {
error("Warning: %s", wmsg);
state->cipher_warning_done = 1;
}
@@ -997,18 +946,69 @@ 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);
else
*max_blocks = ((u_int64_t)1 << 30) / enc->block_size;
if (state->rekey_limit)
- *max_blocks = MIN(*max_blocks,
+ *max_blocks = MINIMUM(*max_blocks,
state->rekey_limit / enc->block_size);
+ debug("rekey %s after %llu blocks", dir,
+ (unsigned long long)*max_blocks);
return 0;
}
+#define MAX_PACKETS (1U<<31)
+static int
+ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len)
+{
+ struct session_state *state = ssh->state;
+ u_int32_t out_blocks;
+
+ /* XXX client can't cope with rekeying pre-auth */
+ if (!state->after_authentication)
+ return 0;
+
+ /* Haven't keyed yet or KEX in progress. */
+ if (ssh_packet_is_rekeying(ssh))
+ return 0;
+
+ /* Peer can't rekey */
+ if (ssh->compat & SSH_BUG_NOREKEY)
+ return 0;
+
+ /*
+ * Permit one packet in or out per rekey - this allows us to
+ * make progress when rekey limits are very small.
+ */
+ if (state->p_send.packets == 0 && state->p_read.packets == 0)
+ return 0;
+
+ /* Time-based rekeying */
+ if (state->rekey_interval != 0 &&
+ (int64_t)state->rekey_time + state->rekey_interval <= monotime())
+ return 1;
+
+ /*
+ * 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;
+
+ /* Rekey after (cipher-specific) maximum blocks */
+ out_blocks = ROUNDUP(outbound_packet_len,
+ state->newkeys[MODE_OUT]->enc.block_size);
+ return (state->max_blocks_out &&
+ (state->p_send.blocks + out_blocks > state->max_blocks_out)) ||
+ (state->max_blocks_in &&
+ (state->p_read.blocks > state->max_blocks_in));
+}
+
/*
* Delayed compression for SSH2 is enabled after authentication:
* This happens on the server side after a SSH2_MSG_USERAUTH_SUCCESS is sent,
@@ -1047,6 +1047,20 @@ ssh_packet_enable_delayed_compress(struct ssh *ssh)
return 0;
}
+/* Used to mute debug logging for noisy packet types */
+int
+ssh_packet_log_type(u_char type)
+{
+ switch (type) {
+ case SSH2_MSG_CHANNEL_DATA:
+ case SSH2_MSG_CHANNEL_EXTENDED_DATA:
+ case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
/*
* Finalize packet in SSH2 format (compress, mac, encrypt, enqueue)
*/
@@ -1055,7 +1069,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;
@@ -1075,7 +1089,8 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0;
type = (sshbuf_ptr(state->outgoing_packet))[5];
-
+ if (ssh_packet_log_type(type))
+ debug3("send packet: type %u", type);
#ifdef PACKET_DEBUG
fprintf(stderr, "plain: ");
sshbuf_dump(state->outgoing_packet, stderr);
@@ -1112,19 +1127,29 @@ 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);
+ ROUNDUP(state->extra_pad, block_size);
+ /* 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)
goto out;
- if (enc && !state->send_context.plaintext) {
+ if (enc && !cipher_ctx_is_plaintext(state->send_context)) {
/* random padding */
arc4random_buf(cp, padlen);
} else {
@@ -1156,7 +1181,7 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
if ((r = sshbuf_reserve(state->output,
sshbuf_len(state->outgoing_packet) + authlen, &cp)) != 0)
goto out;
- if ((r = cipher_crypt(&state->send_context, state->p_send.seqnr, cp,
+ if ((r = cipher_crypt(state->send_context, state->p_send.seqnr, cp,
sshbuf_ptr(state->outgoing_packet),
len - aadlen, aadlen, authlen)) != 0)
goto out;
@@ -1197,34 +1222,58 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
return r;
}
+/* returns non-zero if the specified packet type is usec by KEX */
+static int
+ssh_packet_type_is_kex(u_char type)
+{
+ return
+ type >= SSH2_MSG_TRANSPORT_MIN &&
+ type <= SSH2_MSG_TRANSPORT_MAX &&
+ type != SSH2_MSG_SERVICE_REQUEST &&
+ type != SSH2_MSG_SERVICE_ACCEPT &&
+ type != SSH2_MSG_EXT_INFO;
+}
+
int
ssh_packet_send2(struct ssh *ssh)
{
struct session_state *state = ssh->state;
struct packet *p;
u_char type;
- int r;
+ int r, need_rekey;
+ if (sshbuf_len(state->outgoing_packet) < 6)
+ return SSH_ERR_INTERNAL_ERROR;
type = sshbuf_ptr(state->outgoing_packet)[5];
+ need_rekey = !ssh_packet_type_is_kex(type) &&
+ ssh_packet_need_rekeying(ssh, sshbuf_len(state->outgoing_packet));
- /* during rekeying we can only send key exchange messages */
- if (state->rekeying) {
- if ((type < SSH2_MSG_TRANSPORT_MIN) ||
- (type > SSH2_MSG_TRANSPORT_MAX) ||
- (type == SSH2_MSG_SERVICE_REQUEST) ||
- (type == SSH2_MSG_SERVICE_ACCEPT)) {
- debug("enqueue packet: %u", type);
- p = calloc(1, sizeof(*p));
- if (p == NULL)
- return SSH_ERR_ALLOC_FAIL;
- p->type = type;
- p->payload = state->outgoing_packet;
- TAILQ_INSERT_TAIL(&state->outgoing, p, next);
- state->outgoing_packet = sshbuf_new();
- if (state->outgoing_packet == NULL)
- return SSH_ERR_ALLOC_FAIL;
- return 0;
+ /*
+ * During rekeying we can only send key exchange messages.
+ * Queue everything else.
+ */
+ if ((need_rekey || state->rekeying) && !ssh_packet_type_is_kex(type)) {
+ if (need_rekey)
+ debug3("%s: rekex triggered", __func__);
+ debug("enqueue packet: %u", type);
+ p = calloc(1, sizeof(*p));
+ if (p == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ p->type = type;
+ p->payload = state->outgoing_packet;
+ TAILQ_INSERT_TAIL(&state->outgoing, p, next);
+ state->outgoing_packet = sshbuf_new();
+ if (state->outgoing_packet == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if (need_rekey) {
+ /*
+ * This packet triggered a rekey, so send the
+ * KEXINIT now.
+ * NB. reenters this function via kex_start_rekex().
+ */
+ return kex_start_rekex(ssh);
}
+ return 0;
}
/* rekeying starts with sending KEXINIT */
@@ -1240,10 +1289,22 @@ ssh_packet_send2(struct ssh *ssh)
state->rekey_time = monotime();
while ((p = TAILQ_FIRST(&state->outgoing))) {
type = p->type;
+ /*
+ * If this packet triggers a rekex, then skip the
+ * remaining packets in the queue for now.
+ * NB. re-enters this function via kex_start_rekex.
+ */
+ if (ssh_packet_need_rekeying(ssh,
+ sshbuf_len(p->payload))) {
+ debug3("%s: queued packet triggered rekex",
+ __func__);
+ return kex_start_rekex(ssh);
+ }
debug("dequeue packet: %u", type);
sshbuf_free(state->outgoing_packet);
state->outgoing_packet = p->payload;
TAILQ_REMOVE(&state->outgoing, p, next);
+ memset(p, 0, sizeof(*p));
free(p);
if ((r = ssh_packet_send2_wrapped(ssh)) != 0)
return r;
@@ -1262,14 +1323,14 @@ int
ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
{
struct session_state *state = ssh->state;
- int len, r, ms_remain, cont;
+ int len, r, ms_remain;
fd_set *setp;
char buf[8192];
struct timeval timeout, start, *timeoutp = NULL;
DBG(debug("packet_read()"));
- setp = (fd_set *)calloc(howmany(state->connection_in + 1,
+ setp = calloc(howmany(state->connection_in + 1,
NFDBITS), sizeof(fd_mask));
if (setp == NULL)
return SSH_ERR_ALLOC_FAIL;
@@ -1279,7 +1340,7 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
* been sent.
*/
if ((r = ssh_packet_write_wait(ssh)) != 0)
- return r;
+ goto out;
/* Stay in the loop until we have received a complete packet. */
for (;;) {
@@ -1287,13 +1348,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;
@@ -1311,17 +1365,19 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
}
/* Wait for some data to arrive. */
for (;;) {
- if (state->packet_timeout_ms != -1) {
+ if (state->packet_timeout_ms > 0) {
ms_to_timeval(&timeout, ms_remain);
- gettimeofday(&start, NULL);
+ monotime_tv(&start);
}
if ((r = select(state->connection_in + 1, setp,
NULL, NULL, timeoutp)) >= 0)
break;
if (errno != EAGAIN && errno != EINTR &&
- errno != EWOULDBLOCK)
- break;
- if (state->packet_timeout_ms == -1)
+ errno != EWOULDBLOCK) {
+ r = SSH_ERR_SYSTEM_ERROR;
+ goto out;
+ }
+ if (state->packet_timeout_ms <= 0)
continue;
ms_subtract_diff(&start, &ms_remain);
if (ms_remain <= 0) {
@@ -1329,23 +1385,26 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
break;
}
}
- if (r == 0)
- return SSH_ERR_CONN_TIMEOUT;
+ if (r == 0) {
+ r = SSH_ERR_CONN_TIMEOUT;
+ goto out;
+ }
/* Read data from the socket. */
- do {
- cont = 0;
- len = roaming_read(state->connection_in, buf,
- sizeof(buf), &cont);
- } while (len == 0 && cont);
- if (len == 0)
- return SSH_ERR_CONN_CLOSED;
- if (len < 0)
- return SSH_ERR_SYSTEM_ERROR;
+ len = read(state->connection_in, buf, sizeof(buf));
+ if (len == 0) {
+ r = SSH_ERR_CONN_CLOSED;
+ goto out;
+ }
+ if (len == -1) {
+ r = SSH_ERR_SYSTEM_ERROR;
+ goto out;
+ }
/* Append it to the buffer. */
if ((r = ssh_packet_process_incoming(ssh, buf, len)) != 0)
- return r;
+ goto out;
}
+ out:
free(setp);
return r;
}
@@ -1384,150 +1443,41 @@ 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)
+static int
+ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
{
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;
+ size_t need;
int r;
+ if (ssh->kex)
+ return SSH_ERR_INTERNAL_ERROR;
*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 (!state->receive_context.plaintext) {
- 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. */
+ cp = sshbuf_ptr(state->input);
+ if (state->packlen == 0) {
+ if (sshbuf_len(state->input) < 4 + 1)
+ return 0; /* packet is incomplete */
+ state->packlen = PEEK_U32(cp);
+ if (state->packlen < 4 + 1 ||
+ state->packlen > PACKET_MAX_SIZE)
+ return SSH_ERR_MESSAGE_INCOMPLETE;
+ }
+ need = state->packlen + 4;
+ if (sshbuf_len(state->input) < need)
+ return 0; /* packet is incomplete */
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:
+ if ((r = sshbuf_put(state->incoming_packet, cp + 4,
+ state->packlen)) != 0 ||
+ (r = sshbuf_consume(state->input, need)) != 0 ||
+ (r = sshbuf_get_u8(state->incoming_packet, NULL)) != 0 ||
+ (r = sshbuf_get_u8(state->incoming_packet, typep)) != 0)
+ return r;
+ if (ssh_packet_log_type(*typep))
+ debug3("%s: type %u", __func__, *typep);
+ /* sshbuf_dump(state->incoming_packet, stderr); */
+ /* reset for next packet */
+ state->packlen = 0;
return r;
}
@@ -1536,13 +1486,16 @@ 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;
struct sshcomp *comp = NULL;
int r;
+ if (state->mux)
+ return ssh_packet_read_poll2_mux(ssh, typep, seqnr_p);
+
*typep = SSH_MSG_NONE;
if (state->packet_discard)
@@ -1561,7 +1514,7 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0;
if (aadlen && state->packlen == 0) {
- if (cipher_get_length(&state->receive_context,
+ if (cipher_get_length(state->receive_context,
&state->packlen, state->p_read.seqnr,
sshbuf_ptr(state->input), sshbuf_len(state->input)) != 0)
return 0;
@@ -1573,6 +1526,7 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
logit("Bad packet length %u.", state->packlen);
if ((r = sshpkt_disconnect(ssh, "Packet corrupt")) != 0)
return r;
+ return SSH_ERR_CONN_CORRUPT;
}
sshbuf_reset(state->incoming_packet);
} else if (state->packlen == 0) {
@@ -1586,7 +1540,7 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
if ((r = sshbuf_reserve(state->incoming_packet, block_size,
&cp)) != 0)
goto out;
- if ((r = cipher_crypt(&state->receive_context,
+ if ((r = cipher_crypt(state->receive_context,
state->p_send.seqnr, cp, sshbuf_ptr(state->input),
block_size, 0, 0)) != 0)
goto out;
@@ -1600,8 +1554,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;
@@ -1623,8 +1577,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
@@ -1635,46 +1589,46 @@ 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)
goto out;
- if ((r = cipher_crypt(&state->receive_context, state->p_read.seqnr, cp,
+ if ((r = cipher_crypt(state->receive_context, state->p_read.seqnr, cp,
sshbuf_ptr(state->input), need, aadlen, authlen)) != 0)
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)
+ if (need + block_size > 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 - block_size);
}
-
+ /* 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;
@@ -1725,6 +1679,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
*/
if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0)
goto out;
+ if (ssh_packet_log_type(*typep))
+ debug3("receive packet: type %u", *typep);
if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) {
if ((r = sshpkt_disconnect(ssh,
"Invalid ssh2 packet type: %d", *typep)) != 0 ||
@@ -1732,9 +1688,11 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
return r;
return SSH_ERR_PROTOCOL_ERROR;
}
- if (*typep == SSH2_MSG_NEWKEYS)
- r = ssh_set_newkeys(ssh, MODE_IN);
- else if (*typep == SSH2_MSG_USERAUTH_SUCCESS && !state->server_side)
+ if (state->hook_in != NULL &&
+ (r = state->hook_in(ssh, state->incoming_packet, typep,
+ state->hook_in_ctx)) != 0)
+ return r;
+ if (*typep == SSH2_MSG_USERAUTH_SUCCESS && !state->server_side)
r = ssh_packet_enable_delayed_compress(ssh);
else
r = 0;
@@ -1744,6 +1702,13 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
#endif
/* reset for next packet */
state->packlen = 0;
+
+ /* do we need to rekey? */
+ if (ssh_packet_need_rekeying(ssh, 0)) {
+ debug3("%s: rekex triggered", __func__);
+ if ((r = kex_start_rekex(ssh)) != 0)
+ return r;
+ }
out:
return r;
}
@@ -1758,74 +1723,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) {
- if (msg)
- 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: %u: %.400s",
- ssh_remote_ipaddr(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: %.400s",
- ssh_remote_ipaddr(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;
}
}
}
@@ -1877,51 +1816,101 @@ 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)
+ debug3("sending debug message: %s", buf);
+
+ 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));
}
+void
+sshpkt_fmt_connection_id(struct ssh *ssh, char *s, size_t l)
+{
+ snprintf(s, l, "%.200s%s%s port %d",
+ ssh->log_preamble ? ssh->log_preamble : "",
+ ssh->log_preamble ? " " : "",
+ ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
+}
+
/*
* Pretty-print connection-terminating errors and exit.
*/
-void
-sshpkt_fatal(struct ssh *ssh, const char *tag, int r)
+static void
+sshpkt_vfatal(struct ssh *ssh, int r, const char *fmt, va_list ap)
{
+ char *tag = NULL, remote_id[512];
+ int oerrno = errno;
+
+ sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
+
switch (r) {
case SSH_ERR_CONN_CLOSED:
- logit("Connection closed by %.200s", ssh_remote_ipaddr(ssh));
- cleanup_exit(255);
+ ssh_packet_clear_keys(ssh);
+ logdie("Connection closed by %s", remote_id);
case SSH_ERR_CONN_TIMEOUT:
- logit("Connection to %.200s timed out while "
- "waiting to write", ssh_remote_ipaddr(ssh));
- cleanup_exit(255);
+ 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) {
+ 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:
+ case SSH_ERR_NO_COMPRESS_ALG_MATCH:
+ 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);
+ errno = oerrno;
+ logdie("Unable to negotiate with %s: %s. "
+ "Their offer: %s", remote_id, ssh_err(r),
+ ssh->kex->failed_choice);
+ }
+ /* FALLTHROUGH */
default:
- fatal("%s%sConnection to %.200s: %s",
+ if (vasprintf(&tag, fmt, ap) == -1) {
+ ssh_packet_clear_keys(ssh);
+ logdie("%s: could not allocate failure message",
+ __func__);
+ }
+ ssh_packet_clear_keys(ssh);
+ errno = oerrno;
+ logdie("%s%sConnection %s %s: %s",
tag != NULL ? tag : "", tag != NULL ? ": " : "",
- ssh_remote_ipaddr(ssh), ssh_err(r));
+ ssh->state->server_side ? "from" : "to",
+ remote_id, ssh_err(r));
}
}
+void
+sshpkt_fatal(struct ssh *ssh, int r, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ sshpkt_vfatal(ssh, r, fmt, ap);
+ /* NOTREACHED */
+ va_end(ap);
+ logdie("%s: should have exited", __func__);
+}
+
/*
* Logs the error plus constructs and sends a disconnect packet, closes the
* connection, and exits. This function never returns. The error message
@@ -1931,7 +1920,7 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r)
void
ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...)
{
- char buf[1024];
+ char buf[1024], remote_id[512];
va_list args;
static int disconnecting = 0;
int r;
@@ -1944,22 +1933,23 @@ ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...)
* Format the message. Note that the caller must make sure the
* message is of limited size.
*/
+ sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
/* Display the error locally */
- logit("Disconnecting: %.100s", buf);
+ logit("Disconnecting %s: %.100s", remote_id, buf);
/*
* Send the disconnect message to the other side, and wait
* for it to get sent.
*/
if ((r = sshpkt_disconnect(ssh, "%s", buf)) != 0)
- sshpkt_fatal(ssh, __func__, r);
+ sshpkt_fatal(ssh, r, "%s", __func__);
if ((r = ssh_packet_write_wait(ssh)) != 0)
- sshpkt_fatal(ssh, __func__, r);
+ sshpkt_fatal(ssh, r, "%s", __func__);
/* Close the connection. */
ssh_packet_close(ssh);
@@ -1975,19 +1965,18 @@ ssh_packet_write_poll(struct ssh *ssh)
{
struct session_state *state = ssh->state;
int len = sshbuf_len(state->output);
- int cont, r;
+ int r;
if (len > 0) {
- cont = 0;
- len = roaming_write(state->connection_out,
- sshbuf_ptr(state->output), len, &cont);
+ len = write(state->connection_out,
+ sshbuf_ptr(state->output), len);
if (len == -1) {
if (errno == EINTR || errno == EAGAIN ||
errno == EWOULDBLOCK)
return 0;
return SSH_ERR_SYSTEM_ERROR;
}
- if (len == 0 && !cont)
+ if (len == 0)
return SSH_ERR_CONN_CLOSED;
if ((r = sshbuf_consume(state->output, len)) != 0)
return r;
@@ -2007,11 +1996,14 @@ ssh_packet_write_wait(struct ssh *ssh)
struct timeval start, timeout, *timeoutp = NULL;
struct session_state *state = ssh->state;
- setp = (fd_set *)calloc(howmany(state->connection_out + 1,
+ setp = calloc(howmany(state->connection_out + 1,
NFDBITS), sizeof(fd_mask));
if (setp == NULL)
return SSH_ERR_ALLOC_FAIL;
- ssh_packet_write_poll(ssh);
+ if ((r = ssh_packet_write_poll(ssh)) != 0) {
+ free(setp);
+ return r;
+ }
while (ssh_packet_have_data_to_write(ssh)) {
memset(setp, 0, howmany(state->connection_out + 1,
NFDBITS) * sizeof(fd_mask));
@@ -2022,9 +2014,9 @@ ssh_packet_write_wait(struct ssh *ssh)
timeoutp = &timeout;
}
for (;;) {
- if (state->packet_timeout_ms != -1) {
+ if (state->packet_timeout_ms > 0) {
ms_to_timeval(&timeout, ms_remain);
- gettimeofday(&start, NULL);
+ monotime_tv(&start);
}
if ((ret = select(state->connection_out + 1,
NULL, setp, NULL, timeoutp)) >= 0)
@@ -2032,7 +2024,7 @@ ssh_packet_write_wait(struct ssh *ssh)
if (errno != EAGAIN && errno != EINTR &&
errno != EWOULDBLOCK)
break;
- if (state->packet_timeout_ms == -1)
+ if (state->packet_timeout_ms <= 0)
continue;
ms_subtract_diff(&start, &ms_remain);
if (ms_remain <= 0) {
@@ -2076,14 +2068,14 @@ 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
case AF_INET:
debug3("%s: set IP_TOS 0x%02x", __func__, tos);
if (setsockopt(ssh->state->connection_in,
- IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0)
+ IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1)
error("setsockopt IP_TOS %d: %.100s:",
tos, strerror(errno));
break;
@@ -2092,7 +2084,7 @@ ssh_packet_set_tos(struct ssh *ssh, int tos)
case AF_INET6:
debug3("%s: set IPV6_TCLASS 0x%02x", __func__, tos);
if (setsockopt(ssh->state->connection_in,
- IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) < 0)
+ IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) == -1)
error("setsockopt IPV6_TCLASS %d: %.100s:",
tos, strerror(errno));
break;
@@ -2169,60 +2161,11 @@ 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)
+ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, u_int32_t seconds)
{
- 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;
- }
-}
-
-#define MAX_PACKETS (1U<<31)
-int
-ssh_packet_need_rekeying(struct ssh *ssh)
-{
- struct session_state *state = ssh->state;
-
- if (ssh->compat & SSH_BUG_NOREKEY)
- return 0;
- return
- (state->p_send.packets > MAX_PACKETS) ||
- (state->p_read.packets > MAX_PACKETS) ||
- (state->max_blocks_out &&
- (state->p_send.blocks > state->max_blocks_out)) ||
- (state->max_blocks_in &&
- (state->p_read.blocks > state->max_blocks_in)) ||
- (state->rekey_interval != 0 && state->rekey_time +
- state->rekey_interval <= monotime());
-}
-
-void
-ssh_packet_set_rekey_limits(struct ssh *ssh, u_int32_t bytes, time_t seconds)
-{
- debug3("rekey after %lld bytes, %d seconds", (long long)bytes,
- (int)seconds);
+ debug3("rekey after %llu bytes, %u seconds", (unsigned long long)bytes,
+ (unsigned int)seconds);
ssh->state->rekey_limit = bytes;
ssh->state->rekey_interval = seconds;
}
@@ -2241,6 +2184,7 @@ void
ssh_packet_set_server(struct ssh *ssh)
{
ssh->state->server_side = 1;
+ ssh->kex->server = 1; /* XXX unify? */
}
void
@@ -2261,77 +2205,18 @@ ssh_packet_get_output(struct ssh *ssh)
return (void *)ssh->state->output;
}
-/* XXX TODO update roaming to new API (does not work anyway) */
-/*
- * Save the state for the real connection, and use a separate state when
- * resuming a suspended connection.
- */
-void
-ssh_packet_backup_state(struct ssh *ssh,
- struct ssh *backup_state)
-{
- struct ssh *tmp;
-
- close(ssh->state->connection_in);
- ssh->state->connection_in = -1;
- close(ssh->state->connection_out);
- ssh->state->connection_out = -1;
- if (backup_state)
- tmp = backup_state;
- else
- tmp = ssh_alloc_session_state();
- backup_state = ssh;
- ssh = tmp;
-}
-
-/* XXX FIXME FIXME FIXME */
-/*
- * Swap in the old state when resuming a connecion.
- */
-void
-ssh_packet_restore_state(struct ssh *ssh,
- struct ssh *backup_state)
-{
- struct ssh *tmp;
- u_int len;
- int r;
-
- tmp = backup_state;
- backup_state = ssh;
- ssh = tmp;
- ssh->state->connection_in = backup_state->state->connection_in;
- backup_state->state->connection_in = -1;
- ssh->state->connection_out = backup_state->state->connection_out;
- backup_state->state->connection_out = -1;
- len = sshbuf_len(backup_state->state->input);
- if (len > 0) {
- if ((r = sshbuf_putb(ssh->state->input,
- backup_state->state->input)) != 0)
- fatal("%s: %s", __func__, ssh_err(r));
- sshbuf_reset(backup_state->state->input);
- add_recv_bytes(len);
- }
-}
-
/* Reset after_authentication and reset compression in post-auth privsep */
static int
ssh_packet_set_postauth(struct ssh *ssh)
{
- struct sshcomp *comp;
- int r, mode;
+ int r;
debug("%s: called", __func__);
/* This was set in net child, but is not visible in user child */
ssh->state->after_authentication = 1;
ssh->state->rekeying = 0;
- for (mode = 0; mode < MODE_MAX; mode++) {
- if (ssh->state->newkeys[mode] == NULL)
- continue;
- comp = &ssh->state->newkeys[mode]->comp;
- if (comp && comp->enabled &&
- (r = ssh_packet_init_compression(ssh)) != 0)
- return r;
- }
+ if ((r = ssh_packet_enable_delayed_compress(ssh)) != 0)
+ return r;
return 0;
}
@@ -2346,13 +2231,15 @@ kex_to_blob(struct sshbuf *m, struct kex *kex)
if ((r = sshbuf_put_string(m, kex->session_id,
kex->session_id_len)) != 0 ||
(r = sshbuf_put_u32(m, kex->we_need)) != 0 ||
+ (r = sshbuf_put_cstring(m, kex->hostkey_alg)) != 0 ||
(r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 ||
+ (r = sshbuf_put_u32(m, kex->hostkey_nid)) != 0 ||
(r = sshbuf_put_u32(m, kex->kex_type)) != 0 ||
(r = sshbuf_put_stringb(m, kex->my)) != 0 ||
(r = sshbuf_put_stringb(m, kex->peer)) != 0 ||
- (r = sshbuf_put_u32(m, kex->flags)) != 0 ||
- (r = sshbuf_put_cstring(m, kex->client_version_string)) != 0 ||
- (r = sshbuf_put_cstring(m, kex->server_version_string)) != 0)
+ (r = sshbuf_put_stringb(m, kex->client_version)) != 0 ||
+ (r = sshbuf_put_stringb(m, kex->server_version)) != 0 ||
+ (r = sshbuf_put_u32(m, kex->flags)) != 0)
return r;
return 0;
}
@@ -2374,15 +2261,13 @@ newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode)
enc = &newkey->enc;
mac = &newkey->mac;
comp = &newkey->comp;
- cc = (mode == MODE_OUT) ? &ssh->state->send_context :
- &ssh->state->receive_context;
+ cc = (mode == MODE_OUT) ? ssh->state->send_context :
+ ssh->state->receive_context;
if ((r = cipher_get_keyiv(cc, enc->iv, enc->iv_len)) != 0)
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 ||
@@ -2395,13 +2280,11 @@ newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode)
goto out;
}
if ((r = sshbuf_put_u32(b, comp->type)) != 0 ||
- (r = sshbuf_put_u32(b, comp->enabled)) != 0 ||
(r = sshbuf_put_cstring(b, comp->name)) != 0)
goto out;
r = sshbuf_put_stringb(m, b);
out:
- if (b != NULL)
- sshbuf_free(b);
+ sshbuf_free(b);
return r;
}
@@ -2410,64 +2293,25 @@ 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_get_number(state->receive_context.cipher);
- 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_u32(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;
- }
-
- 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;
+ int r;
- if ((r = ssh_packet_get_compress_state(m, ssh)) != 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;
- if (compat20) {
- if ((r = sshbuf_put_u64(m, get_sent_bytes())) != 0 ||
- (r = sshbuf_put_u64(m, get_recv_bytes())) != 0)
- return r;
- }
return 0;
}
@@ -2497,12 +2341,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;
@@ -2518,14 +2365,8 @@ newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode)
mac->key_len = maclen;
}
if ((r = sshbuf_get_u32(b, &comp->type)) != 0 ||
- (r = sshbuf_get_u32(b, (u_int *)&comp->enabled)) != 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;
@@ -2536,10 +2377,8 @@ newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode)
newkey = NULL;
r = 0;
out:
- if (newkey != NULL)
- free(newkey);
- if (b != NULL)
- sshbuf_free(b);
+ free(newkey);
+ sshbuf_free(b);
return r;
}
@@ -2550,37 +2389,30 @@ kex_from_blob(struct sshbuf *m, struct kex **kexp)
struct kex *kex;
int r;
- if ((kex = calloc(1, sizeof(struct kex))) == NULL ||
- (kex->my = sshbuf_new()) == NULL ||
- (kex->peer = sshbuf_new()) == NULL) {
- r = SSH_ERR_ALLOC_FAIL;
- goto out;
- }
+ if ((kex = kex_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
if ((r = sshbuf_get_string(m, &kex->session_id, &kex->session_id_len)) != 0 ||
(r = sshbuf_get_u32(m, &kex->we_need)) != 0 ||
+ (r = sshbuf_get_cstring(m, &kex->hostkey_alg, NULL)) != 0 ||
(r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 ||
+ (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_nid)) != 0 ||
(r = sshbuf_get_u32(m, &kex->kex_type)) != 0 ||
(r = sshbuf_get_stringb(m, kex->my)) != 0 ||
(r = sshbuf_get_stringb(m, kex->peer)) != 0 ||
- (r = sshbuf_get_u32(m, &kex->flags)) != 0 ||
- (r = sshbuf_get_cstring(m, &kex->client_version_string, NULL)) != 0 ||
- (r = sshbuf_get_cstring(m, &kex->server_version_string, NULL)) != 0)
+ (r = sshbuf_get_stringb(m, kex->client_version)) != 0 ||
+ (r = sshbuf_get_stringb(m, kex->server_version)) != 0 ||
+ (r = sshbuf_get_u32(m, &kex->flags)) != 0)
goto out;
kex->server = 1;
kex->done = 1;
r = 0;
out:
if (r != 0 || kexp == NULL) {
- if (kex != NULL) {
- if (kex->my != NULL)
- sshbuf_free(kex->my);
- if (kex->peer != NULL)
- sshbuf_free(kex->peer);
- free(kex);
- }
+ kex_free(kex);
if (kexp != NULL)
*kexp = NULL;
} else {
+ kex_free(*kexp);
*kexp = kex;
}
return r;
@@ -2594,65 +2426,35 @@ 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;
- u_int64_t sent_bytes = 0, recv_bytes = 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_u32(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_compress_state(ssh, m)) != 0 ||
- (r = ssh_packet_set_postauth(ssh)) != 0)
+ if ((r = ssh_packet_set_postauth(ssh)) != 0)
return r;
sshbuf_reset(state->input);
@@ -2663,12 +2465,6 @@ ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m)
(r = sshbuf_put(state->output, output, olen)) != 0)
return r;
- if (compat20) {
- if ((r = sshbuf_get_u64(m, &sent_bytes)) != 0 ||
- (r = sshbuf_get_u64(m, &recv_bytes)) != 0)
- return r;
- roam_set_bytes(sent_bytes, recv_bytes);
- }
if (sshbuf_len(m))
return SSH_ERR_INVALID_FORMAT;
debug3("%s: done", __func__);
@@ -2727,23 +2523,22 @@ sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v)
return sshbuf_put_stringb(ssh->state->outgoing_packet, v);
}
-#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
int
-sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g)
+sshpkt_getb_froms(struct ssh *ssh, struct sshbuf **valp)
{
- return sshbuf_put_ec(ssh->state->outgoing_packet, v, g);
+ return sshbuf_froms(ssh->state->incoming_packet, valp);
}
-#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
-#ifdef WITH_SSH1
+#ifdef WITH_OPENSSL
+#ifdef OPENSSL_HAS_ECC
int
-sshpkt_put_bignum1(struct ssh *ssh, const BIGNUM *v)
+sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g)
{
- return sshbuf_put_bignum1(ssh->state->outgoing_packet, v);
+ return sshbuf_put_ec(ssh->state->outgoing_packet, v, g);
}
-#endif /* WITH_SSH1 */
+#endif /* OPENSSL_HAS_ECC */
+
-#ifdef WITH_OPENSSL
int
sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v)
{
@@ -2790,32 +2585,30 @@ sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp)
}
int
-sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp)
+sshpkt_peek_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp)
{
- return sshbuf_get_cstring(ssh->state->incoming_packet, valp, lenp);
+ return sshbuf_peek_string_direct(ssh->state->incoming_packet, valp, lenp);
}
-#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
int
-sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g)
+sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp)
{
- return sshbuf_get_ec(ssh->state->incoming_packet, v, g);
+ return sshbuf_get_cstring(ssh->state->incoming_packet, valp, lenp);
}
-#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
-#ifdef WITH_SSH1
+#ifdef WITH_OPENSSL
+#ifdef OPENSSL_HAS_ECC
int
-sshpkt_get_bignum1(struct ssh *ssh, BIGNUM *v)
+sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g)
{
- return sshbuf_get_bignum1(ssh->state->incoming_packet, v);
+ return sshbuf_get_ec(ssh->state->incoming_packet, v, g);
}
-#endif /* WITH_SSH1 */
+#endif /* OPENSSL_HAS_ECC */
-#ifdef WITH_OPENSSL
int
-sshpkt_get_bignum2(struct ssh *ssh, BIGNUM *v)
+sshpkt_get_bignum2(struct ssh *ssh, BIGNUM **valp)
{
- return sshbuf_get_bignum2(ssh->state->incoming_packet, v);
+ return sshbuf_get_bignum2(ssh->state->incoming_packet, valp);
}
#endif /* WITH_OPENSSL */
@@ -2840,15 +2633,74 @@ 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
+ssh_packet_send_mux(struct ssh *ssh)
+{
+ struct session_state *state = ssh->state;
+ u_char type, *cp;
+ size_t len;
+ int r;
+
+ if (ssh->kex)
+ return SSH_ERR_INTERNAL_ERROR;
+ len = sshbuf_len(state->outgoing_packet);
+ if (len < 6)
+ return SSH_ERR_INTERNAL_ERROR;
+ cp = sshbuf_mutable_ptr(state->outgoing_packet);
+ type = cp[5];
+ if (ssh_packet_log_type(type))
+ debug3("%s: type %u", __func__, type);
+ /* drop everything, but the connection protocol */
+ if (type >= SSH2_MSG_CONNECTION_MIN &&
+ type <= SSH2_MSG_CONNECTION_MAX) {
+ POKE_U32(cp, len - 4);
+ if ((r = sshbuf_putb(state->output,
+ state->outgoing_packet)) != 0)
+ return r;
+ /* sshbuf_dump(state->output, stderr); */
+ }
+ sshbuf_reset(state->outgoing_packet);
+ 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 */
@@ -2856,10 +2708,9 @@ sshpkt_start(struct ssh *ssh, u_char type)
int
sshpkt_send(struct ssh *ssh)
{
- if (compat20)
- return ssh_packet_send2(ssh);
- else
- return ssh_packet_send1(ssh);
+ if (ssh->state && ssh->state->mux)
+ return ssh_packet_send_mux(ssh);
+ return ssh_packet_send2(ssh);
}
int
@@ -2873,19 +2724,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;
}