Skip to content

Instantly share code, notes, and snippets.

@FiloSottile
Created September 17, 2014 15:11
Show Gist options
  • Save FiloSottile/5de9589a968bdf928139 to your computer and use it in GitHub Desktop.
Save FiloSottile/5de9589a968bdf928139 to your computer and use it in GitHub Desktop.
diff --git a/HPN-README b/HPN-README
new file mode 100644
index 0000000..7cb3b97
--- /dev/null
+++ b/HPN-README
@@ -0,0 +1,129 @@
+Notes:
+
+MULTI-THREADED CIPHER:
+The AES cipher in CTR mode has been multithreaded (MTR-AES-CTR). This will allow ssh installations
+on hosts with multiple cores to use more than one processing core during encryption.
+Tests have show significant throughput performance increases when using MTR-AES-CTR up
+to and including a full gigabit per second on quad core systems. It should be possible to
+achieve full line rate on dual core systems but OS and data management overhead makes this
+more difficult to achieve. The cipher stream from MTR-AES-CTR is entirely compatible with single
+thread AES-CTR (ST-AES-CTR) implementations and should be 100% backward compatible. Optimal
+performance requires the MTR-AES-CTR mode be enabled on both ends of the connection.
+The MTR-AES-CTR replaces ST-AES-CTR and is used in exactly the same way with the same
+nomenclature.
+Use examples: ssh -caes128-ctr you@host.com
+ scp -oCipher=aes256-ctr file you@host.com:~/file
+
+NONE CIPHER:
+To use the NONE option you must have the NoneEnabled switch set on the server and
+you *must* have *both* NoneEnabled and NoneSwitch set to yes on the client. The NONE
+feature works with ALL ssh subsystems (as far as we can tell) *AS LONG AS* a tty is not
+spawned. If a user uses the -T switch to prevent a tty being created the NONE cipher will
+be disabled.
+
+The performance increase will only be as good as the network and TCP stack tuning
+on the reciever side of the connection allows. As a rule of thumb a user will need
+at least 10Mb/s connection with a 100ms RTT to see a doubling of performance. The
+HPN-SSH home page describes this in greater detail.
+
+http://www.psc.edu/networking/projects/hpn-ssh
+
+BUFFER SIZES:
+
+If HPN is disabled the receive buffer size will be set to the
+OpenSSH default of 64K.
+
+If an HPN system connects to a nonHPN system the receive buffer will
+be set to the HPNBufferSize value. The default is 2MB but user adjustable.
+
+If an HPN to HPN connection is established a number of different things might
+happen based on the user options and conditions.
+
+Conditions: HPNBufferSize NOT Set, TCPRcvBufPoll enabled, TCPRcvBuf NOT Set
+HPN Buffer Size = up to 64MB
+This is the default state. The HPN buffer size will grow to a maximum of 64MB
+as the TCP receive buffer grows. The maximum HPN Buffer size of 64MB is
+geared towards 10GigE transcontinental connections.
+
+Conditions: HPNBufferSize NOT Set, TCPRcvBufPoll disabled, TCPRcvBuf NOT Set
+HPN Buffer Size = TCP receive buffer value.
+Users on non-autotuning systesm should disable TCPRcvBufPoll in the
+ssh_cofig and sshd_config
+
+Conditions: HPNBufferSize SET, TCPRcvBufPoll disabled, TCPRcvBuf NOT Set
+HPN Buffer Size = minmum of TCP receive buffer and HPNBufferSize.
+This would be the system defined TCP receive buffer (RWIN).
+
+Conditions: HPNBufferSize SET, TCPRcvBufPoll disabled, TCPRcvBuf SET
+HPN Buffer Size = minmum of TCPRcvBuf and HPNBufferSize.
+Generally there is no need to set both.
+
+Conditions: HPNBufferSize SET, TCPRcvBufPoll enabled, TCPRcvBuf NOT Set
+HPN Buffer Size = grows to HPNBufferSize
+The buffer will grow up to the maximum size specified here.
+
+Conditions: HPNBufferSize SET, TCPRcvBufPoll enabled, TCPRcvBuf SET
+HPN Buffer Size = minmum of TCPRcvBuf and HPNBufferSize.
+Generally there is no need to set both of these, especially on autotuning
+systems. However, if the users wishes to override the autotuning this would be
+one way to do it.
+
+Conditions: HPNBufferSize NOT Set, TCPRcvBufPoll enabled, TCPRcvBuf SET
+HPN Buffer Size = TCPRcvBuf.
+This will override autotuning and set the TCP recieve buffer to the user defined
+value.
+
+
+HPN Specific Configuration options
+
+TcpRcvBuf=[int]KB client
+ set the TCP socket receive buffer to n Kilobytes. It can be set up to the
+maximum socket size allowed by the system. This is useful in situations where
+the tcp receive window is set low but the maximum buffer size is set
+higher (as is typical). This works on a per TCP connection basis. You can also
+use this to artifically limit the transfer rate of the connection. In these
+cases the throughput will be no more than n/RTT. The minimum buffer size is 1KB.
+Default is the current system wide tcp receive buffer size.
+
+TcpRcvBufPoll=[yes/no] client/server
+ enable of disable the polling of the tcp receive buffer through the life
+of the connection. You would want to make sure that this option is enabled
+for systems making use of autotuning kernels (linux 2.4.24+, 2.6, MS Vista)
+default is yes.
+
+NoneEnabled=[yes/no] client/server
+ enable or disable the use of the None cipher. Care must always be used
+when enabling this as it will allow users to send data in the clear. However,
+it is important to note that authentication information remains encrypted
+even if this option is enabled. Set to no by default.
+
+NoneSwitch=[yes/no] client
+ Switch the encryption cipher being used to the None cipher after
+authentication takes place. NoneEnabled must be enabled on both the client
+and server side of the connection. When the connection switches to the NONE
+cipher a warning is sent to STDERR. The connection attempt will fail with an
+error if a client requests a NoneSwitch from the server that does not explicitly
+have NoneEnabled set to yes. Note: The NONE cipher cannot be used in
+interactive (shell) sessions and it will fail silently. Set to no by default.
+
+HPNDisabled=[yes/no] client/server
+ In some situations, such as transfers on a local area network, the impact
+of the HPN code produces a net decrease in performance. In these cases it is
+helpful to disable the HPN functionality. By default HPNDisabled is set to no.
+
+HPNBufferSize=[int]KB client/server
+ This is the default buffer size the HPN functionality uses when interacting
+with nonHPN SSH installations. Conceptually this is similar to the TcpRcvBuf
+option as applied to the internal SSH flow control. This value can range from
+1KB to 64MB (1-65536). Use of oversized or undersized buffers can cause performance
+problems depending on the length of the network path. The default size of this buffer
+is 2MB.
+
+
+Credits: This patch was conceived, designed, and led by Chris Rapier (rapier@psc.edu)
+ The majority of the actual coding for versions up to HPN12v1 was performed
+ by Michael Stevens (mstevens@andrew.cmu.edu). The MT-AES-CTR cipher was
+ implemented by Ben Bennet (ben@psc.edu) and improved by Mike Tasota
+ (tasota@gmail.com) an NSF REU grant recipient for 2013.
+ This work was financed, in part, by Cisco System, Inc., the National
+ Library of Medicine, and the National Science Foundation.
diff --git a/Makefile.in b/Makefile.in
index 28a8ec4..c6b1a56 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -43,7 +43,7 @@ CC=@CC@
LD=@LD@
CFLAGS=@CFLAGS@
CPPFLAGS=-I. -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@
-LIBS=@LIBS@
+LIBS=@LIBS@ -lpthread
K5LIBS=@K5LIBS@
GSSLIBS=@GSSLIBS@
SSHLIBS=@SSHLIBS@
@@ -65,7 +65,7 @@ TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keys
LIBSSH_OBJS=authfd.o authfile.o bufaux.o bufbn.o buffer.o \
canohost.o channels.o cipher.o cipher-aes.o \
- cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \
+ cipher-bf1.o cipher-ctr.o cipher-ctr-mt.o cipher-3des1.o cleanup.o \
compat.o compress.o crc32.o deattack.o fatal.o hostfile.o \
log.o match.o md-sha256.o moduli.o nchan.o packet.o \
readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \
diff --git a/auth2.c b/auth2.c
index a5490c0..555661f 100644
--- a/auth2.c
+++ b/auth2.c
@@ -49,6 +49,7 @@
#include "dispatch.h"
#include "pathnames.h"
#include "buffer.h"
+#include "canohost.h"
#ifdef GSSAPI
#include "ssh-gss.h"
@@ -72,6 +73,9 @@ extern Authmethod method_hostbased;
extern Authmethod method_gssapi;
#endif
+static int log_flag = 0;
+
+
Authmethod *authmethods[] = {
&method_none,
&method_pubkey,
@@ -224,6 +228,11 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
service = packet_get_cstring(NULL);
method = packet_get_cstring(NULL);
debug("userauth-request for user %s service %s method %s", user, service, method);
+ if (!log_flag) {
+ logit("SSH: Server;Ltype: Authname;Remote: %s-%d;Name: %s",
+ get_remote_ipaddr(), get_remote_port(), user);
+ log_flag = 1;
+ }
debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
if ((style = strchr(user, ':')) != NULL)
diff --git a/buffer.c b/buffer.c
index d240f67..88e16d0 100644
--- a/buffer.c
+++ b/buffer.c
@@ -128,7 +128,7 @@ restart:
/* Increase the size of the buffer and retry. */
newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ);
- if (newlen > BUFFER_MAX_LEN)
+ if (newlen > BUFFER_MAX_LEN_HPN)
fatal("buffer_append_space: alloc %u not supported",
newlen);
buffer->buf = xrealloc(buffer->buf, 1, newlen);
diff --git a/buffer.h b/buffer.h
index 7df8a38..244de01 100644
--- a/buffer.h
+++ b/buffer.h
@@ -16,6 +16,9 @@
#ifndef BUFFER_H
#define BUFFER_H
+/* move the following to a more appropriate place and name */
+#define BUFFER_MAX_LEN_HPN 0x4000000 /* 64MB */
+
typedef struct {
u_char *buf; /* Buffer for data. */
u_int alloc; /* Number of bytes allocated for data. */
diff --git a/channels.c b/channels.c
index 9efe89c..6d6d89c 100644
--- a/channels.c
+++ b/channels.c
@@ -173,8 +173,14 @@ static void port_open_helper(Channel *c, char *rtype);
static int connect_next(struct channel_connect *);
static void channel_connect_ctx_free(struct channel_connect *);
+
+static int hpn_disabled = 0;
+static int hpn_buffer_size = 2 * 1024 * 1024;
+
/* -- channel core */
+
+
Channel *
channel_by_id(int id)
{
@@ -323,6 +329,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
c->local_window_max = window;
c->local_consumed = 0;
c->local_maxpacket = maxpack;
+ c->dynamic_window = 0;
c->remote_id = -1;
c->remote_name = xstrdup(remote_name);
c->remote_window = 0;
@@ -819,11 +826,35 @@ channel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset)
FD_SET(c->sock, writeset);
}
+int channel_tcpwinsz () {
+ u_int32_t tcpwinsz = 0;
+ socklen_t optsz = sizeof(tcpwinsz);
+ int ret = -1;
+
+ /* if we aren't on a socket return 128KB*/
+ if(!packet_connection_is_on_socket())
+ return(128*1024);
+ ret = getsockopt(packet_get_connection_in(),
+ SOL_SOCKET, SO_RCVBUF, &tcpwinsz, &optsz);
+ /* return no more than 64MB */
+ if ((ret == 0) && tcpwinsz > BUFFER_MAX_LEN_HPN)
+ tcpwinsz = BUFFER_MAX_LEN_HPN;
+ debug2("tcpwinsz: %d for connection: %d", tcpwinsz,
+ packet_get_connection_in());
+ return(tcpwinsz);
+}
+
static void
channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset)
{
u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
+ /* check buffer limits */
+ if ((!c->tcpwinsz) || (c->dynamic_window > 0))
+ c->tcpwinsz = channel_tcpwinsz();
+
+ limit = MIN(limit, 2 * c->tcpwinsz);
+
if (c->istate == CHAN_INPUT_OPEN &&
limit > 0 &&
buffer_len(&c->input) < limit &&
@@ -1815,14 +1846,21 @@ channel_check_window(Channel *c)
c->local_maxpacket*3) ||
c->local_window < c->local_window_max/2) &&
c->local_consumed > 0) {
+ u_int addition = 0;
+ /* adjust max window size if we are in a dynamic environment */
+ if (c->dynamic_window && (c->tcpwinsz > c->local_window_max)) {
+ /* grow the window somewhat aggressively to maintain pressure */
+ addition = 1.5*(c->tcpwinsz - c->local_window_max);
+ c->local_window_max += addition;
+ }
packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
packet_put_int(c->remote_id);
- packet_put_int(c->local_consumed);
+ packet_put_int(c->local_consumed + addition);
packet_send();
debug2("channel %d: window %d sent adjust %d",
c->self, c->local_window,
c->local_consumed);
- c->local_window += c->local_consumed;
+ c->local_window += c->local_consumed + addition;
c->local_consumed = 0;
}
return 1;
@@ -2182,11 +2220,12 @@ channel_after_select(fd_set *readset, fd_set *writeset)
/* If there is data to send to the connection, enqueue some of it now. */
-void
+int
channel_output_poll(void)
{
Channel *c;
u_int i, len;
+ int packet_length = 0;
for (i = 0; i < channels_alloc; i++) {
c = channels[i];
@@ -2234,7 +2273,7 @@ channel_output_poll(void)
packet_start(SSH2_MSG_CHANNEL_DATA);
packet_put_int(c->remote_id);
packet_put_string(data, dlen);
- packet_send();
+ packet_length = packet_send();
c->remote_window -= dlen + 4;
free(data);
}
@@ -2264,7 +2303,7 @@ channel_output_poll(void)
SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA);
packet_put_int(c->remote_id);
packet_put_string(buffer_ptr(&c->input), len);
- packet_send();
+ packet_length = packet_send();
buffer_consume(&c->input, len);
c->remote_window -= len;
}
@@ -2299,12 +2338,13 @@ channel_output_poll(void)
packet_put_int(c->remote_id);
packet_put_int(SSH2_EXTENDED_DATA_STDERR);
packet_put_string(buffer_ptr(&c->extended), len);
- packet_send();
+ packet_length = packet_send();
buffer_consume(&c->extended, len);
c->remote_window -= len;
debug2("channel %d: sent ext data %d", c->self, len);
}
}
+ return (packet_length);
}
@@ -2738,6 +2778,15 @@ channel_fwd_bind_addr(const char *listen_addr, int *wildcardp,
return addr;
}
+
+void
+channel_set_hpn(int external_hpn_disabled, int external_hpn_buffer_size)
+{
+ hpn_disabled = external_hpn_disabled;
+ hpn_buffer_size = external_hpn_buffer_size;
+ debug("HPN Disabled: %d, HPN Buffer Size: %d", hpn_disabled, hpn_buffer_size);
+}
+
static int
channel_setup_fwd_listener(int type, const char *listen_addr,
u_short listen_port, int *allocated_listen_port,
@@ -2864,9 +2913,15 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
}
/* Allocate a channel number for the socket. */
+ /* explicitly test for hpn disabled option. if true use smaller window size */
+ if (hpn_disabled)
c = channel_new("port listener", type, sock, sock, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
0, "port listener", 1);
+ else
+ c = channel_new("port listener", type, sock, sock, -1,
+ hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT,
+ 0, "port listener", 1);
c->path = xstrdup(host);
c->host_port = port_to_connect;
c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
@@ -3514,10 +3569,17 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
for (n = 0; n < num_socks; n++) {
sock = socks[n];
+ /* Is this really necassary? */
+ if (hpn_disabled)
nc = channel_new("x11 listener",
SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
0, "X11 inet listener", 1);
+ else
+ nc = channel_new("x11 listener",
+ SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
+ hpn_buffer_size, CHAN_X11_PACKET_DEFAULT,
+ 0, "X11 inet listener", 1);
nc->single_connection = single_connection;
(*chanids)[n] = nc->self;
}
diff --git a/channels.h b/channels.h
index 4fab9d7..8579356 100644
--- a/channels.h
+++ b/channels.h
@@ -132,8 +132,10 @@ struct Channel {
u_int local_window_max;
u_int local_consumed;
u_int local_maxpacket;
+ int dynamic_window;
int extended_usage;
int single_connection;
+ u_int tcpwinsz;
char *ctype; /* type */
@@ -169,8 +171,10 @@ struct Channel {
/* default window/packet sizes for tcp/x11-fwd-channel */
#define CHAN_SES_PACKET_DEFAULT (32*1024)
#define CHAN_SES_WINDOW_DEFAULT (64*CHAN_SES_PACKET_DEFAULT)
+
#define CHAN_TCP_PACKET_DEFAULT (32*1024)
#define CHAN_TCP_WINDOW_DEFAULT (64*CHAN_TCP_PACKET_DEFAULT)
+
#define CHAN_X11_PACKET_DEFAULT (16*1024)
#define CHAN_X11_WINDOW_DEFAULT (4*CHAN_X11_PACKET_DEFAULT)
@@ -245,7 +249,7 @@ void channel_input_status_confirm(int, u_int32_t, void *);
void channel_prepare_select(fd_set **, fd_set **, int *, u_int*,
time_t*, int);
void channel_after_select(fd_set *, fd_set *);
-void channel_output_poll(void);
+int channel_output_poll(void);
int channel_not_very_much_buffered_data(void);
void channel_close_all(void);
@@ -306,4 +310,7 @@ void chan_rcvd_ieof(Channel *);
void chan_write_failed(Channel *);
void chan_obuf_empty(Channel *);
+/* hpn handler */
+void channel_set_hpn(int, int);
+
#endif
diff --git a/cipher-ctr-mt.c b/cipher-ctr-mt.c
new file mode 100644
index 0000000..c13ddf0
--- /dev/null
+++ b/cipher-ctr-mt.c
@@ -0,0 +1,507 @@
+/*
+ * OpenSSH Multi-threaded AES-CTR Cipher
+ *
+ * Author: Benjamin Bennett <ben@psc.edu>
+ * Author: Mike Tasota <tasota@gmail.com>
+ * Author: Chris Rapier <rapier@psc.edu>
+ * Copyright (c) 2008-2013 Pittsburgh Supercomputing Center. All rights reserved.
+ *
+ * Based on original OpenSSH AES-CTR cipher. Small portions remain unchanged,
+ * Copyright (c) 2003 Markus Friedl <markus@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "includes.h"
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+
+#include "xmalloc.h"
+#include "log.h"
+
+/* compatibility with old or broken OpenSSL versions */
+#include "openbsd-compat/openssl-compat.h"
+
+#ifndef USE_BUILTIN_RIJNDAEL
+#include <openssl/aes.h>
+#endif
+
+#include <pthread.h>
+
+/*-------------------- TUNABLES --------------------*/
+/* Number of pregen threads to use */
+#define CIPHER_THREADS 2
+
+/* Number of keystream queues */
+#define NUMKQ (CIPHER_THREADS + 2)
+
+/* Length of a keystream queue */
+#define KQLEN 4096
+
+/* Processor cacheline length */
+#define CACHELINE_LEN 64
+
+/* Collect thread stats and print at cancellation when in debug mode */
+/* #define CIPHER_THREAD_STATS */
+
+/* Use single-byte XOR instead of 8-byte XOR */
+/* #define CIPHER_BYTE_XOR */
+/*-------------------- END TUNABLES --------------------*/
+
+
+const EVP_CIPHER *evp_aes_ctr_mt(void);
+
+#ifdef CIPHER_THREAD_STATS
+/*
+ * Struct to collect thread stats
+ */
+struct thread_stats {
+ u_int fills;
+ u_int skips;
+ u_int waits;
+ u_int drains;
+};
+
+/*
+ * Debug print the thread stats
+ * Use with pthread_cleanup_push for displaying at thread cancellation
+ */
+static void
+thread_loop_stats(void *x)
+{
+ struct thread_stats *s = x;
+
+ debug("tid %lu - %u fills, %u skips, %u waits", pthread_self(),
+ s->fills, s->skips, s->waits);
+}
+
+ #define STATS_STRUCT(s) struct thread_stats s
+ #define STATS_INIT(s) { memset(&s, 0, sizeof(s)); }
+ #define STATS_FILL(s) { s.fills++; }
+ #define STATS_SKIP(s) { s.skips++; }
+ #define STATS_WAIT(s) { s.waits++; }
+ #define STATS_DRAIN(s) { s.drains++; }
+#else
+ #define STATS_STRUCT(s)
+ #define STATS_INIT(s)
+ #define STATS_FILL(s)
+ #define STATS_SKIP(s)
+ #define STATS_WAIT(s)
+ #define STATS_DRAIN(s)
+#endif
+
+/* Keystream Queue state */
+enum {
+ KQINIT,
+ KQEMPTY,
+ KQFILLING,
+ KQFULL,
+ KQDRAINING
+};
+
+/* Keystream Queue struct */
+struct kq {
+ u_char keys[KQLEN][AES_BLOCK_SIZE];
+ u_char ctr[AES_BLOCK_SIZE];
+ u_char pad0[CACHELINE_LEN];
+ volatile int qstate;
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+ u_char pad1[CACHELINE_LEN];
+};
+
+/* Context struct */
+struct ssh_aes_ctr_ctx
+{
+ struct kq q[NUMKQ];
+ AES_KEY aes_ctx;
+ STATS_STRUCT(stats);
+ u_char aes_counter[AES_BLOCK_SIZE];
+ pthread_t tid[CIPHER_THREADS];
+ int state;
+ int qidx;
+ int ridx;
+};
+
+/* <friedl>
+ * increment counter 'ctr',
+ * the counter is of size 'len' bytes and stored in network-byte-order.
+ * (LSB at ctr[len-1], MSB at ctr[0])
+ */
+static void
+ssh_ctr_inc(u_char *ctr, u_int len)
+{
+ int i;
+
+ for (i = len - 1; i >= 0; i--)
+ if (++ctr[i]) /* continue on overflow */
+ return;
+}
+
+/*
+ * Add num to counter 'ctr'
+ */
+static void
+ssh_ctr_add(u_char *ctr, uint32_t num, u_int len)
+{
+ int i;
+ uint16_t n;
+
+ for (n = 0, i = len - 1; i >= 0 && (num || n); i--) {
+ n = ctr[i] + (num & 0xff) + n;
+ num >>= 8;
+ ctr[i] = n & 0xff;
+ n >>= 8;
+ }
+}
+
+/*
+ * Threads may be cancelled in a pthread_cond_wait, we must free the mutex
+ */
+static void
+thread_loop_cleanup(void *x)
+{
+ pthread_mutex_unlock((pthread_mutex_t *)x);
+}
+
+/*
+ * The life of a pregen thread:
+ * Find empty keystream queues and fill them using their counter.
+ * When done, update counter for the next fill.
+ */
+static void *
+thread_loop(void *x)
+{
+ AES_KEY key;
+ STATS_STRUCT(stats);
+ struct ssh_aes_ctr_ctx *c = x;
+ struct kq *q;
+ int i;
+ int qidx;
+
+ /* Threads stats on cancellation */
+ STATS_INIT(stats);
+#ifdef CIPHER_THREAD_STATS
+ pthread_cleanup_push(thread_loop_stats, &stats);
+#endif
+
+ /* Thread local copy of AES key */
+ memcpy(&key, &c->aes_ctx, sizeof(key));
+
+ /*
+ * Handle the special case of startup, one thread must fill
+ * the first KQ then mark it as draining. Lock held throughout.
+ */
+ if (pthread_equal(pthread_self(), c->tid[0])) {
+ q = &c->q[0];
+ pthread_mutex_lock(&q->lock);
+ if (q->qstate == KQINIT) {
+ for (i = 0; i < KQLEN; i++) {
+ AES_encrypt(q->ctr, q->keys[i], &key);
+ ssh_ctr_inc(q->ctr, AES_BLOCK_SIZE);
+ }
+ ssh_ctr_add(q->ctr, KQLEN * (NUMKQ - 1), AES_BLOCK_SIZE);
+ q->qstate = KQDRAINING;
+ STATS_FILL(stats);
+ pthread_cond_broadcast(&q->cond);
+ }
+ pthread_mutex_unlock(&q->lock);
+ }
+ else
+ STATS_SKIP(stats);
+
+ /*
+ * Normal case is to find empty queues and fill them, skipping over
+ * queues already filled by other threads and stopping to wait for
+ * a draining queue to become empty.
+ *
+ * Multiple threads may be waiting on a draining queue and awoken
+ * when empty. The first thread to wake will mark it as filling,
+ * others will move on to fill, skip, or wait on the next queue.
+ */
+ for (qidx = 1;; qidx = (qidx + 1) % NUMKQ) {
+ /* Check if I was cancelled, also checked in cond_wait */
+ pthread_testcancel();
+
+ /* Lock queue and block if its draining */
+ q = &c->q[qidx];
+ pthread_mutex_lock(&q->lock);
+ pthread_cleanup_push(thread_loop_cleanup, &q->lock);
+ while (q->qstate == KQDRAINING || q->qstate == KQINIT) {
+ STATS_WAIT(stats);
+ pthread_cond_wait(&q->cond, &q->lock);
+ }
+ pthread_cleanup_pop(0);
+
+ /* If filling or full, somebody else got it, skip */
+ if (q->qstate != KQEMPTY) {
+ pthread_mutex_unlock(&q->lock);
+ STATS_SKIP(stats);
+ continue;
+ }
+
+ /*
+ * Empty, let's fill it.
+ * Queue lock is relinquished while we do this so others
+ * can see that it's being filled.
+ */
+ q->qstate = KQFILLING;
+ pthread_mutex_unlock(&q->lock);
+ for (i = 0; i < KQLEN; i++) {
+ AES_encrypt(q->ctr, q->keys[i], &key);
+ ssh_ctr_inc(q->ctr, AES_BLOCK_SIZE);
+ }
+
+ /* Re-lock, mark full and signal consumer */
+ pthread_mutex_lock(&q->lock);
+ ssh_ctr_add(q->ctr, KQLEN * (NUMKQ - 1), AES_BLOCK_SIZE);
+ q->qstate = KQFULL;
+ STATS_FILL(stats);
+ pthread_cond_signal(&q->cond);
+ pthread_mutex_unlock(&q->lock);
+ }
+
+#ifdef CIPHER_THREAD_STATS
+ /* Stats */
+ pthread_cleanup_pop(1);
+#endif
+
+ return NULL;
+}
+
+static int
+ssh_aes_ctr(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src,
+ u_int len)
+{
+ struct ssh_aes_ctr_ctx *c;
+ struct kq *q, *oldq;
+ int ridx;
+ u_char *buf;
+
+ if (len == 0)
+ return (1);
+ if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL)
+ return (0);
+
+ q = &c->q[c->qidx];
+ ridx = c->ridx;
+
+ /* src already padded to block multiple */
+ while (len > 0) {
+ buf = q->keys[ridx];
+
+#ifdef CIPHER_BYTE_XOR
+ dest[0] = src[0] ^ buf[0];
+ dest[1] = src[1] ^ buf[1];
+ dest[2] = src[2] ^ buf[2];
+ dest[3] = src[3] ^ buf[3];
+ dest[4] = src[4] ^ buf[4];
+ dest[5] = src[5] ^ buf[5];
+ dest[6] = src[6] ^ buf[6];
+ dest[7] = src[7] ^ buf[7];
+ dest[8] = src[8] ^ buf[8];
+ dest[9] = src[9] ^ buf[9];
+ dest[10] = src[10] ^ buf[10];
+ dest[11] = src[11] ^ buf[11];
+ dest[12] = src[12] ^ buf[12];
+ dest[13] = src[13] ^ buf[13];
+ dest[14] = src[14] ^ buf[14];
+ dest[15] = src[15] ^ buf[15];
+#else
+ *(uint64_t *)dest = *(uint64_t *)src ^ *(uint64_t *)buf;
+ *(uint64_t *)(dest + 8) = *(uint64_t *)(src + 8) ^
+ *(uint64_t *)(buf + 8);
+#endif
+
+ dest += 16;
+ src += 16;
+ len -= 16;
+ ssh_ctr_inc(ctx->iv, AES_BLOCK_SIZE);
+
+ /* Increment read index, switch queues on rollover */
+ if ((ridx = (ridx + 1) % KQLEN) == 0) {
+ oldq = q;
+
+ /* Mark next queue draining, may need to wait */
+ c->qidx = (c->qidx + 1) % NUMKQ;
+ q = &c->q[c->qidx];
+ pthread_mutex_lock(&q->lock);
+ while (q->qstate != KQFULL) {
+ STATS_WAIT(c->stats);
+ pthread_cond_wait(&q->cond, &q->lock);
+ }
+ q->qstate = KQDRAINING;
+ pthread_mutex_unlock(&q->lock);
+
+ /* Mark consumed queue empty and signal producers */
+ pthread_mutex_lock(&oldq->lock);
+ oldq->qstate = KQEMPTY;
+ STATS_DRAIN(c->stats);
+ pthread_cond_broadcast(&oldq->cond);
+ pthread_mutex_unlock(&oldq->lock);
+ }
+ }
+ c->ridx = ridx;
+ return (1);
+}
+
+#define HAVE_NONE 0
+#define HAVE_KEY 1
+#define HAVE_IV 2
+static int
+ssh_aes_ctr_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv,
+ int enc)
+{
+ struct ssh_aes_ctr_ctx *c;
+ int i;
+
+ if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) {
+ c = xmalloc(sizeof(*c));
+
+ c->state = HAVE_NONE;
+ for (i = 0; i < NUMKQ; i++) {
+ pthread_mutex_init(&c->q[i].lock, NULL);
+ pthread_cond_init(&c->q[i].cond, NULL);
+ }
+
+ STATS_INIT(c->stats);
+
+ EVP_CIPHER_CTX_set_app_data(ctx, c);
+ }
+
+ if (c->state == (HAVE_KEY | HAVE_IV)) {
+ /* Cancel pregen threads */
+ for (i = 0; i < CIPHER_THREADS; i++)
+ pthread_cancel(c->tid[i]);
+ for (i = 0; i < CIPHER_THREADS; i++)
+ pthread_join(c->tid[i], NULL);
+ /* Start over getting key & iv */
+ c->state = HAVE_NONE;
+ }
+
+ if (key != NULL) {
+ AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+ &c->aes_ctx);
+ c->state |= HAVE_KEY;
+ }
+
+ if (iv != NULL) {
+ memcpy(ctx->iv, iv, AES_BLOCK_SIZE);
+ c->state |= HAVE_IV;
+ }
+
+ if (c->state == (HAVE_KEY | HAVE_IV)) {
+ /* Clear queues */
+ memcpy(c->q[0].ctr, ctx->iv, AES_BLOCK_SIZE);
+ c->q[0].qstate = KQINIT;
+ for (i = 1; i < NUMKQ; i++) {
+ memcpy(c->q[i].ctr, ctx->iv, AES_BLOCK_SIZE);
+ ssh_ctr_add(c->q[i].ctr, i * KQLEN, AES_BLOCK_SIZE);
+ c->q[i].qstate = KQEMPTY;
+ }
+ c->qidx = 0;
+ c->ridx = 0;
+
+ /* Start threads */
+ for (i = 0; i < CIPHER_THREADS; i++) {
+ debug("spawned a thread");
+ pthread_create(&c->tid[i], NULL, thread_loop, c);
+ }
+ pthread_mutex_lock(&c->q[0].lock);
+ while (c->q[0].qstate != KQDRAINING)
+ pthread_cond_wait(&c->q[0].cond, &c->q[0].lock);
+ pthread_mutex_unlock(&c->q[0].lock);
+
+ }
+ return (1);
+}
+
+/* this function is no longer used but might prove handy in the future
+ * this comment also applies to ssh_aes_ctr_thread_reconstruction
+ */
+void
+ssh_aes_ctr_thread_destroy(EVP_CIPHER_CTX *ctx)
+{
+ struct ssh_aes_ctr_ctx *c;
+ int i;
+ c = EVP_CIPHER_CTX_get_app_data(ctx);
+ /* destroy threads */
+ for (i = 0; i < CIPHER_THREADS; i++) {
+ pthread_cancel(c->tid[i]);
+ }
+ for (i = 0; i < CIPHER_THREADS; i++) {
+ pthread_join(c->tid[i], NULL);
+ }
+}
+
+void
+ssh_aes_ctr_thread_reconstruction(EVP_CIPHER_CTX *ctx)
+{
+ struct ssh_aes_ctr_ctx *c;
+ int i;
+ c = EVP_CIPHER_CTX_get_app_data(ctx);
+ /* reconstruct threads */
+ for (i = 0; i < CIPHER_THREADS; i++) {
+ debug("spawned a thread");
+ pthread_create(&c->tid[i], NULL, thread_loop, c);
+ }
+}
+
+static int
+ssh_aes_ctr_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ struct ssh_aes_ctr_ctx *c;
+ int i;
+
+ if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) != NULL) {
+#ifdef CIPHER_THREAD_STATS
+ debug("main thread: %u drains, %u waits", c->stats.drains,
+ c->stats.waits);
+#endif
+ /* Cancel pregen threads */
+ for (i = 0; i < CIPHER_THREADS; i++)
+ pthread_cancel(c->tid[i]);
+ for (i = 0; i < CIPHER_THREADS; i++)
+ pthread_join(c->tid[i], NULL);
+
+ memset(c, 0, sizeof(*c));
+ free(c);
+ EVP_CIPHER_CTX_set_app_data(ctx, NULL);
+ }
+ return (1);
+}
+
+/* <friedl> */
+const EVP_CIPHER *
+evp_aes_ctr_mt(void)
+{
+ static EVP_CIPHER aes_ctr;
+
+ memset(&aes_ctr, 0, sizeof(EVP_CIPHER));
+ aes_ctr.nid = NID_undef;
+ aes_ctr.block_size = AES_BLOCK_SIZE;
+ aes_ctr.iv_len = AES_BLOCK_SIZE;
+ aes_ctr.key_len = 16;
+ aes_ctr.init = ssh_aes_ctr_init;
+ aes_ctr.cleanup = ssh_aes_ctr_cleanup;
+ aes_ctr.do_cipher = ssh_aes_ctr;
+#ifndef SSH_OLD_EVP
+ aes_ctr.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH |
+ EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV;
+#endif
+ return (&aes_ctr);
+}
diff --git a/cipher.c b/cipher.c
index 53d9b4f..24c285c 100644
--- a/cipher.c
+++ b/cipher.c
@@ -57,6 +57,13 @@ extern const EVP_CIPHER *evp_ssh1_bf(void);
extern const EVP_CIPHER *evp_ssh1_3des(void);
extern void ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int);
+/* for multi-threaded aes-ctr cipher */
+extern const EVP_CIPHER *evp_aes_ctr_mt(void);
+
+/* no longer needed. replaced by evp pointer swap */
+/* extern void ssh_aes_ctr_thread_destroy(EVP_CIPHER_CTX *ctx); */
+/* extern void ssh_aes_ctr_thread_reconstruction(EVP_CIPHER_CTX *ctx); */
+
struct Cipher {
char *name;
int number; /* for ssh1 only */
@@ -71,7 +78,7 @@ struct Cipher {
const EVP_CIPHER *(*evptype)(void);
};
-static const struct Cipher ciphers[] = {
+static struct Cipher ciphers[] = {
{ "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null },
{ "des", SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc },
{ "3des", SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des },
@@ -106,6 +113,28 @@ static const struct Cipher ciphers[] = {
/*--*/
+/* used to get the cipher name so when force rekeying to handle the
+ * single to multithreaded ctr cipher swap we only rekey when appropriate
+*/
+char *
+cipher_return_name(const Cipher *c)
+{
+ return (c->name);
+}
+
+/* in order to get around sandbox and forking issues with a threaded cipher
+ * we set the initial pre-auth aes-ctr cipher to the default OpenSSH cipher
+ * post auth we set them to the new evp as defined by cipher-ctr-mt
+*/
+
+void
+cipher_reset_multithreaded()
+{
+ (cipher_by_name("aes128-ctr"))->evptype = evp_aes_ctr_mt;
+ (cipher_by_name("aes192-ctr"))->evptype = evp_aes_ctr_mt;
+ (cipher_by_name("aes256-ctr"))->evptype = evp_aes_ctr_mt;
+}
+
/* Returns a list of supported ciphers separated by the specified char. */
char *
cipher_alg_list(char sep, int auth_only)
@@ -190,7 +219,7 @@ cipher_mask_ssh1(int client)
return mask;
}
-const Cipher *
+Cipher *
cipher_by_name(const char *name)
{
const Cipher *c;
@@ -203,7 +232,7 @@ cipher_by_name(const char *name)
const Cipher *
cipher_by_number(int id)
{
- const Cipher *c;
+ Cipher *c;
for (c = ciphers; c->name != NULL; c++)
if (c->number == id)
return c;
@@ -224,7 +253,8 @@ ciphers_valid(const char *names)
for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0';
(p = strsep(&cp, CIPHER_SEP))) {
c = cipher_by_name(p);
- if (c == NULL || c->number != SSH_CIPHER_SSH2) {
+ if (c == NULL || (c->number != SSH_CIPHER_SSH2 &&
+ c->number != SSH_CIPHER_NONE)) {
debug("bad cipher %s [%s]", p, names);
free(cipher_list);
return 0;
@@ -479,6 +509,7 @@ cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len)
}
switch (c->number) {
+ case SSH_CIPHER_NONE:
case SSH_CIPHER_SSH2:
case SSH_CIPHER_DES:
case SSH_CIPHER_BLOWFISH:
@@ -518,6 +549,7 @@ cipher_set_keyiv(CipherContext *cc, u_char *iv)
return;
switch (c->number) {
+ case SSH_CIPHER_NONE:
case SSH_CIPHER_SSH2:
case SSH_CIPHER_DES:
case SSH_CIPHER_BLOWFISH:
diff --git a/cipher.h b/cipher.h
index 133d2e7..3cd2da5 100644
--- a/cipher.h
+++ b/cipher.h
@@ -72,8 +72,11 @@ struct CipherContext {
const Cipher *cipher;
};
+void ssh_aes_ctr_thread_destroy(EVP_CIPHER_CTX *ctx); // defined in cipher-ctr-mt.c
+void ssh_aes_ctr_thread_reconstruction(EVP_CIPHER_CTX *ctx);
+
u_int cipher_mask_ssh1(int);
-const Cipher *cipher_by_name(const char *);
+Cipher *cipher_by_name(const char *);
const Cipher *cipher_by_number(int);
int cipher_number(const char *);
char *cipher_name(int);
@@ -93,6 +96,8 @@ u_int cipher_seclen(const Cipher *);
u_int cipher_authlen(const Cipher *);
u_int cipher_ivlen(const Cipher *);
u_int cipher_is_cbc(const Cipher *);
+void cipher_reset_multithreaded(void);
+char *cipher_return_name(const Cipher *);
u_int cipher_get_number(const Cipher *);
void cipher_get_keyiv(CipherContext *, u_char *, u_int);
diff --git a/clientloop.c b/clientloop.c
index 59ad3a2..e144fb6 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1891,9 +1891,15 @@ client_request_x11(const char *request_type, int rchan)
sock = x11_connect_display();
if (sock < 0)
return NULL;
+ /* again is this really necessary for X11? */
+ if (options.hpn_disabled)
c = channel_new("x11",
SSH_CHANNEL_X11_OPEN, sock, sock, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
+ else
+ c = channel_new("x11",
+ SSH_CHANNEL_X11_OPEN, sock, sock, -1,
+ options.hpn_buffer_size, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
c->force_drain = 1;
return c;
}
@@ -1913,9 +1919,15 @@ client_request_agent(const char *request_type, int rchan)
sock = ssh_get_authentication_socket();
if (sock < 0)
return NULL;
+ if (options.hpn_disabled)
+ c = channel_new("authentication agent connection",
+ SSH_CHANNEL_OPEN, sock, sock, -1,
+ CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
+ "authentication agent connection", 1);
+ else
c = channel_new("authentication agent connection",
SSH_CHANNEL_OPEN, sock, sock, -1,
- CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0,
+ options.hpn_buffer_size, options.hpn_buffer_size, 0,
"authentication agent connection", 1);
c->force_drain = 1;
return c;
@@ -1943,10 +1955,18 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun)
return -1;
}
+ if(options.hpn_disabled)
c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
- CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
+ CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
+ 0, "tun", 1);
+ else
+ c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
+ options.hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT,
+ 0, "tun", 1);
c->datagram = 1;
+
+
#if defined(SSH_TUN_FILTER)
if (options.tun_open == SSH_TUNMODE_POINTOPOINT)
channel_register_filter(c->self, sys_tun_infilter,
diff --git a/compat.c b/compat.c
index 9d9fabe..235fc59 100644
--- a/compat.c
+++ b/compat.c
@@ -172,6 +172,15 @@ compat_datafellows(const char *version)
if (match_pattern_list(version, check[i].pat,
strlen(check[i].pat), 0) == 1) {
datafellows = check[i].bugs;
+ /* Check to see if the remote side is OpenSSH and not HPN */
+ if(strstr(version,"OpenSSH") != NULL)
+ {
+ if (strstr(version,"hpn") == NULL)
+ {
+ datafellows |= SSH_BUG_LARGEWINDOW;
+ debug("Remote is NON-HPN aware");
+ }
+ }
debug("match: %s pat %s compat 0x%08x",
version, check[i].pat, datafellows);
return;
diff --git a/compat.h b/compat.h
index b174fa1..9937347 100644
--- a/compat.h
+++ b/compat.h
@@ -59,6 +59,7 @@
#define SSH_BUG_RFWD_ADDR 0x02000000
#define SSH_NEW_OPENSSH 0x04000000
#define SSH_BUG_DYNAMIC_RPORT 0x08000000
+#define SSH_BUG_LARGEWINDOW 0x10000000
void enable_compat13(void);
void enable_compat20(void);
diff --git a/kex.c b/kex.c
index 74e2b86..16bd693 100644
--- a/kex.c
+++ b/kex.c
@@ -49,6 +49,7 @@
#include "dispatch.h"
#include "monitor.h"
#include "roaming.h"
+#include "canohost.h"
#include "digest.h"
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
@@ -146,7 +147,7 @@ kex_names_valid(const char *names)
}
/* put algorithm proposal into buffer */
-static void
+void
kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
{
u_int i;
@@ -460,6 +461,11 @@ kex_choose_conf(Kex *kex)
int nenc, nmac, ncomp;
u_int mode, ctos, need, dh_need, authlen;
int first_kex_follows, type;
+ int log_flag = 0;
+ int auth_flag;
+
+ auth_flag = packet_authentication_state();
+ debug ("AUTH STATE IS %d", auth_flag);
my = kex_buf2prop(&kex->my, NULL);
peer = kex_buf2prop(&kex->peer, &first_kex_follows);
@@ -497,11 +503,34 @@ kex_choose_conf(Kex *kex)
if (authlen == 0)
choose_mac(&newkeys->mac, cprop[nmac], sprop[nmac]);
choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp]);
+ debug("REQUESTED ENC.NAME is '%s'", newkeys->enc.name);
+ if (strcmp(newkeys->enc.name, "none") == 0) {
+ debug("Requesting NONE. Authflag is %d", auth_flag);
+ if (auth_flag == 1) {
+ debug("None requested post authentication.");
+ } else {
+ fatal("Pre-authentication none cipher requests are not allowed.");
+ }
+ }
debug("kex: %s %s %s %s",
ctos ? "client->server" : "server->client",
newkeys->enc.name,
authlen == 0 ? newkeys->mac.name : "<implicit>",
newkeys->comp.name);
+ /* client starts withctos = 0 && log flag = 0 and no log*/
+ /* 2nd client pass ctos=1 and flag = 1 so no log*/
+ /* server starts with ctos =1 && log_flag = 0 so log */
+ /* 2nd sever pass ctos = 1 && log flag = 1 so no log*/
+ /* -cjr*/
+ if (ctos && !log_flag) {
+ logit("SSH: Server;Ltype: Kex;Remote: %s-%d;Enc: %s;MAC: %s;Comp: %s",
+ get_remote_ipaddr(),
+ get_remote_port(),
+ newkeys->enc.name,
+ newkeys->mac.name,
+ newkeys->comp.name);
+ }
+ log_flag = 1;
}
choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
diff --git a/kex.h b/kex.h
index c85680e..91a524a 100644
--- a/kex.h
+++ b/kex.h
@@ -146,6 +146,7 @@ struct Kex {
};
int kex_names_valid(const char *);
+void kex_prop2buf(Buffer *, char *proposal[PROPOSAL_MAX]);
char *kex_alg_list(char);
Kex *kex_setup(char *[PROPOSAL_MAX]);
diff --git a/myproposal.h b/myproposal.h
index 3a0f5ae..9e24f24 100644
--- a/myproposal.h
+++ b/myproposal.h
@@ -133,6 +133,8 @@
#define KEX_DEFAULT_COMP "none,zlib@openssh.com,zlib"
#define KEX_DEFAULT_LANG ""
+#define KEX_ENCRYPT_INCLUDE_NONE KEX_DEFAULT_ENCRYPT \
+ ",none"
static char *myproposal[PROPOSAL_MAX] = {
KEX_DEFAULT_KEX,
diff --git a/packet.c b/packet.c
index 54c0558..0bcac42 100644
--- a/packet.c
+++ b/packet.c
@@ -848,7 +848,7 @@ packet_enable_delayed_compress(void)
/*
* Finalize packet in SSH2 format (compress, mac, encrypt, enqueue)
*/
-static void
+static int
packet_send2_wrapped(void)
{
u_char type, *cp, *macbuf = NULL;
@@ -980,11 +980,14 @@ packet_send2_wrapped(void)
set_newkeys(MODE_OUT);
else if (type == SSH2_MSG_USERAUTH_SUCCESS && active_state->server_side)
packet_enable_delayed_compress();
+ return (len-4);
}
-static void
+static int
packet_send2(void)
{
+
+ static int packet_length = 0;
struct packet *p;
u_char type, *cp;
@@ -1004,7 +1007,7 @@ packet_send2(void)
sizeof(Buffer));
buffer_init(&active_state->outgoing_packet);
TAILQ_INSERT_TAIL(&active_state->outgoing, p, next);
- return;
+ return(sizeof(Buffer));
}
}
@@ -1012,7 +1015,7 @@ packet_send2(void)
if (type == SSH2_MSG_KEXINIT)
active_state->rekeying = 1;
- packet_send2_wrapped();
+ packet_length = packet_send2_wrapped();
/* after a NEWKEYS message we can send the complete queue */
if (type == SSH2_MSG_NEWKEYS) {
@@ -1026,19 +1029,22 @@ packet_send2(void)
sizeof(Buffer));
TAILQ_REMOVE(&active_state->outgoing, p, next);
free(p);
- packet_send2_wrapped();
+ packet_length += packet_send2_wrapped();
}
}
+ return(packet_length);
}
-void
+int
packet_send(void)
{
+ int packet_len = 0;
if (compat20)
- packet_send2();
+ packet_len = packet_send2();
else
packet_send1();
DBG(debug("packet_send done"));
+ return(packet_len);
}
/*
@@ -1718,12 +1724,15 @@ packet_disconnect(const char *fmt,...)
/* Checks if there is any buffered output, and tries to write some of the output. */
-void
+int
packet_write_poll(void)
{
- int len = buffer_len(&active_state->output);
+
+ int len = 0;
int cont;
+ len = buffer_len(&active_state->output);
+
if (len > 0) {
cont = 0;
len = roaming_write(active_state->connection_out,
@@ -1731,13 +1740,14 @@ packet_write_poll(void)
if (len == -1) {
if (errno == EINTR || errno == EAGAIN ||
errno == EWOULDBLOCK)
- return;
+ return(0);
fatal("Write failed: %.100s", strerror(errno));
}
if (len == 0 && !cont)
fatal("Write connection closed");
buffer_consume(&active_state->output, len);
}
+ return(len);
}
/*
@@ -1938,12 +1948,25 @@ packet_send_ignore(int nbytes)
}
}
+/* this supports the forced rekeying required for the NONE cipher */
+int rekey_requested = 0;
+void
+packet_request_rekeying(void)
+{
+ rekey_requested = 1;
+}
+
#define MAX_PACKETS (1U<<31)
int
packet_need_rekeying(void)
{
if (datafellows & SSH_BUG_NOREKEY)
return 0;
+ if (rekey_requested == 1)
+ {
+ rekey_requested = 0;
+ return 1;
+ }
return
(active_state->p_send.packets > MAX_PACKETS) ||
(active_state->p_read.packets > MAX_PACKETS) ||
@@ -1955,6 +1978,12 @@ packet_need_rekeying(void)
active_state->rekey_interval <= monotime());
}
+int
+packet_authentication_state(void)
+{
+ return(active_state->after_authentication);
+}
+
void
packet_set_rekey_limits(u_int32_t bytes, time_t seconds)
{
@@ -2009,6 +2038,18 @@ packet_get_newkeys(int mode)
return (void *)active_state->newkeys[mode];
}
+void *
+packet_get_receive_context(void)
+{
+ return (void*)&(active_state->receive_context);
+}
+
+void *
+packet_get_send_context(void)
+{
+ return (void*)&(active_state->send_context);
+}
+
/*
* Save the state for the real connection, and use a separate state when
* resuming a suspended connection.
diff --git a/packet.h b/packet.h
index f8edf85..8a781d5 100644
--- a/packet.h
+++ b/packet.h
@@ -23,6 +23,7 @@
#include <openssl/ec.h>
#endif
+void packet_request_rekeying(void);
void packet_set_connection(int, int);
void packet_set_timeout(int, int);
void packet_set_nonblocking(void);
@@ -38,6 +39,8 @@ void packet_set_interactive(int, int, int);
int packet_is_interactive(void);
void packet_set_server(void);
void packet_set_authenticated(void);
+void* packet_get_receive_context(void);
+void* packet_get_send_context(void);
void packet_start(u_char);
void packet_put_char(int ch);
@@ -51,7 +54,7 @@ void packet_put_ecpoint(const EC_GROUP *, const EC_POINT *);
void packet_put_string(const void *buf, u_int len);
void packet_put_cstring(const char *str);
void packet_put_raw(const void *buf, u_int len);
-void packet_send(void);
+int packet_send(void);
int packet_read(void);
void packet_read_expect(int type);
@@ -85,7 +88,7 @@ int packet_get_ssh1_cipher(void);
void packet_set_iv(int, u_char *);
void *packet_get_newkeys(int);
-void packet_write_poll(void);
+int packet_write_poll(void);
void packet_write_wait(void);
int packet_have_data_to_write(void);
int packet_not_very_much_data_to_write(void);
@@ -103,6 +106,10 @@ int packet_inc_alive_timeouts(void);
int packet_set_maxsize(u_int);
u_int packet_get_maxsize(void);
+/* for forced packet rekeying post auth */
+void packet_request_rekeying(void);
+int packet_authentication_state(void);
+
/* don't allow remaining bytes after the end of the message */
#define packet_check_eom() \
do { \
diff --git a/progressmeter.c b/progressmeter.c
index bbbc706..02e5d6d 100644
--- a/progressmeter.c
+++ b/progressmeter.c
@@ -69,6 +69,8 @@ static char *file; /* name of the file being transferred */
static off_t start_pos; /* initial position of transfer */
static off_t end_pos; /* ending position of transfer */
static off_t cur_pos; /* transfer position as of last refresh */
+static off_t last_pos;
+static off_t max_delta_pos = 0;
static volatile off_t *counter; /* progress counter */
static long stalled; /* how long we have been stalled */
static int bytes_per_second; /* current speed in bytes per second */
@@ -129,12 +131,17 @@ refresh_progress_meter(void)
int hours, minutes, seconds;
int i, len;
int file_len;
+ off_t delta_pos;
transferred = *counter - (cur_pos ? cur_pos : start_pos);
cur_pos = *counter;
now = monotime();
bytes_left = end_pos - cur_pos;
+ delta_pos = cur_pos - last_pos;
+ if (delta_pos > max_delta_pos)
+ max_delta_pos = delta_pos;
+
if (bytes_left > 0)
elapsed = now - last_update;
else {
@@ -159,7 +166,7 @@ refresh_progress_meter(void)
/* filename */
buf[0] = '\0';
- file_len = win_size - 35;
+ file_len = win_size - 45;
if (file_len > 0) {
len = snprintf(buf, file_len + 1, "\r%s", file);
if (len < 0)
@@ -189,6 +196,15 @@ refresh_progress_meter(void)
(off_t)bytes_per_second);
strlcat(buf, "/s ", win_size);
+ /* instantaneous rate */
+ if (bytes_left > 0)
+ format_rate(buf + strlen(buf), win_size - strlen(buf),
+ delta_pos);
+ else
+ format_rate(buf + strlen(buf), win_size - strlen(buf),
+ max_delta_pos);
+ strlcat(buf, "/s ", win_size);
+
/* ETA */
if (!transferred)
stalled += elapsed;
@@ -225,6 +241,7 @@ refresh_progress_meter(void)
atomicio(vwrite, STDOUT_FILENO, buf, win_size - 1);
last_update = now;
+ last_pos = cur_pos;
}
/*ARGSUSED*/
diff --git a/readconf.c b/readconf.c
index dc884c9..955bdcb 100644
--- a/readconf.c
+++ b/readconf.c
@@ -145,6 +145,8 @@ typedef enum {
oSendEnv, oControlPath, oControlMaster, oControlPersist,
oHashKnownHosts,
oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
+ oTcpRcvBufPoll, oTcpRcvBuf, oHPNDisabled, oHPNBufferSize,
+ oNoneEnabled, oNoneSwitch,
oVisualHostKey, oUseRoaming,
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
@@ -255,6 +257,12 @@ static struct {
{ "kexalgorithms", oKexAlgorithms },
{ "ipqos", oIPQoS },
{ "requesttty", oRequestTTY },
+ { "noneenabled", oNoneEnabled },
+ { "noneswitch", oNoneSwitch },
+ { "tcprcvbufpoll", oTcpRcvBufPoll },
+ { "tcprcvbuf", oTcpRcvBuf },
+ { "hpndisabled", oHPNDisabled },
+ { "hpnbuffersize", oHPNBufferSize },
{ "proxyusefdpass", oProxyUseFdpass },
{ "canonicaldomains", oCanonicalDomains },
{ "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
@@ -853,6 +861,36 @@ parse_time:
intptr = &options->check_host_ip;
goto parse_flag;
+ case oHPNDisabled:
+ intptr = &options->hpn_disabled;
+ goto parse_flag;
+
+ case oHPNBufferSize:
+ intptr = &options->hpn_buffer_size;
+ goto parse_int;
+
+ case oTcpRcvBufPoll:
+ intptr = &options->tcp_rcv_buf_poll;
+ goto parse_flag;
+
+ case oNoneEnabled:
+ intptr = &options->none_enabled;
+ goto parse_flag;
+
+ /* we check to see if the command comes from the */
+ /* command line or not. If it does then enable it */
+ /* otherwise fail. NONE should never be a default configuration */
+ case oNoneSwitch:
+ if(strcmp(filename,"command-line") == 0) {
+ intptr = &options->none_switch;
+ goto parse_flag;
+ } else {
+ error("NoneSwitch is found in %.200s.\nYou may only use this configuration option from the command line", filename);
+ error("Continuing...");
+ debug("NoneSwitch directive found in %.200s.", filename);
+ return 0;
+ }
+
case oVerifyHostKeyDNS:
intptr = &options->verify_host_key_dns;
multistate_ptr = multistate_yesnoask;
@@ -1015,6 +1053,10 @@ parse_int:
intptr = &options->connection_attempts;
goto parse_int;
+ case oTcpRcvBuf:
+ intptr = &options->tcp_rcv_buf;
+ goto parse_int;
+
case oCipher:
intptr = &options->cipher;
arg = strdelim(&s);
@@ -1561,6 +1603,12 @@ initialize_options(Options * options)
options->ip_qos_interactive = -1;
options->ip_qos_bulk = -1;
options->request_tty = -1;
+ options->none_switch = -1;
+ options->none_enabled = -1;
+ options->hpn_disabled = -1;
+ options->hpn_buffer_size = -1;
+ options->tcp_rcv_buf_poll = -1;
+ options->tcp_rcv_buf = -1;
options->proxy_use_fdpass = -1;
options->ignored_unknown = NULL;
options->num_canonical_domains = 0;
@@ -1707,6 +1755,32 @@ fill_default_options(Options * options)
options->server_alive_interval = 0;
if (options->server_alive_count_max == -1)
options->server_alive_count_max = 3;
+ if (options->none_switch == -1)
+ options->none_switch = 0;
+ if (options->none_enabled == -1)
+ options->none_enabled = 0;
+ if (options->hpn_disabled == -1)
+ options->hpn_disabled = 0;
+ if (options->hpn_buffer_size > -1)
+ {
+ /* if a user tries to set the size to 0 set it to 1KB */
+ if (options->hpn_buffer_size == 0)
+ options->hpn_buffer_size = 1;
+ /*limit the buffer to 64MB*/
+ if (options->hpn_buffer_size > 64*1024)
+ {
+ options->hpn_buffer_size = 64*1024*1024;
+ debug("User requested buffer larger than 64MB. Request reverted to 64MB");
+ }
+ else options->hpn_buffer_size *= 1024;
+ debug("hpn_buffer_size set to %d", options->hpn_buffer_size);
+ }
+ if (options->tcp_rcv_buf == 0)
+ options->tcp_rcv_buf = 1;
+ if (options->tcp_rcv_buf > -1)
+ options->tcp_rcv_buf *=1024;
+ if (options->tcp_rcv_buf_poll == -1)
+ options->tcp_rcv_buf_poll = 1;
if (options->control_master == -1)
options->control_master = 0;
if (options->control_persist == -1) {
diff --git a/readconf.h b/readconf.h
index 75e3f8f..7cd016f 100644
--- a/readconf.h
+++ b/readconf.h
@@ -66,6 +66,10 @@ typedef struct {
int compression_level; /* Compression level 1 (fast) to 9
* (best). */
int tcp_keep_alive; /* Set SO_KEEPALIVE. */
+ int tcp_rcv_buf; /* user switch to set tcp recv buffer */
+ int tcp_rcv_buf_poll; /* Option to poll recv buf every window transfer */
+ int hpn_disabled; /* Switch to disable HPN buffer management */
+ int hpn_buffer_size; /* User definable size for HPN buffer window */
int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */
int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */
LogLevel log_level; /* Level for logging. */
@@ -115,7 +119,10 @@ typedef struct {
int enable_ssh_keysign;
int64_t rekey_limit;
+ int none_switch; /* Use none cipher */
+ int none_enabled; /* Allow none to be used */
int rekey_interval;
+
int no_host_authentication_for_localhost;
int identities_only;
int server_alive_interval;
diff --git a/scp.c b/scp.c
index 18d3b1d..2ab8f15 100644
--- a/scp.c
+++ b/scp.c
@@ -749,7 +749,7 @@ source(int argc, char **argv)
off_t i, statbytes;
size_t amt;
int fd = -1, haderr, indx;
- char *last, *name, buf[2048], encname[MAXPATHLEN];
+ char *last, *name, buf[16384], encname[MAXPATHLEN];
int len;
for (indx = 0; indx < argc; ++indx) {
@@ -914,7 +914,7 @@ sink(int argc, char **argv)
off_t size, statbytes;
unsigned long long ull;
int setimes, targisdir, wrerrno = 0;
- char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
+ char ch, *cp, *np, *targ, *why, *vect[1], buf[16384];
struct timeval tv[2];
#define atime tv[0]
diff --git a/servconf.c b/servconf.c
index 7ba65d5..d492a04 100644
--- a/servconf.c
+++ b/servconf.c
@@ -150,6 +150,10 @@ initialize_server_options(ServerOptions *options)
options->revoked_keys_file = NULL;
options->trusted_user_ca_keys = NULL;
options->authorized_principals_file = NULL;
+ options->none_enabled = -1;
+ options->tcp_rcv_buf_poll = -1;
+ options->hpn_disabled = -1;
+ options->hpn_buffer_size = -1;
options->ip_qos_interactive = -1;
options->ip_qos_bulk = -1;
options->version_addendum = NULL;
@@ -158,6 +162,11 @@ initialize_server_options(ServerOptions *options)
void
fill_default_server_options(ServerOptions *options)
{
+ /* needed for hpn socket tests */
+ int sock;
+ int socksize;
+ int socksizelen = sizeof(int);
+
/* Portable-specific options */
if (options->use_pam == -1)
options->use_pam = 0;
@@ -294,6 +303,44 @@ fill_default_server_options(ServerOptions *options)
}
if (options->permit_tun == -1)
options->permit_tun = SSH_TUNMODE_NO;
+ if (options->none_enabled == -1)
+ options->none_enabled = 0;
+ if (options->hpn_disabled == -1)
+ options->hpn_disabled = 0;
+
+ if (options->hpn_buffer_size == -1) {
+ /* option not explicitly set. Now we have to figure out */
+ /* what value to use */
+ if (options->hpn_disabled == 1) {
+ options->hpn_buffer_size = CHAN_SES_WINDOW_DEFAULT;
+ } else {
+ /* get the current RCV size and set it to that */
+ /*create a socket but don't connect it */
+ /* we use that the get the rcv socket size */
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ &socksize, &socksizelen);
+ close(sock);
+ options->hpn_buffer_size = socksize;
+ debug ("HPN Buffer Size: %d", options->hpn_buffer_size);
+
+ }
+ } else {
+ /* we have to do this incase the user sets both values in a contradictory */
+ /* manner. hpn_disabled overrrides hpn_buffer_size*/
+ if (options->hpn_disabled <= 0) {
+ if (options->hpn_buffer_size == 0)
+ options->hpn_buffer_size = 1;
+ /* limit the maximum buffer to 64MB */
+ if (options->hpn_buffer_size > 64*1024) {
+ options->hpn_buffer_size = 64*1024*1024;
+ } else {
+ options->hpn_buffer_size *= 1024;
+ }
+ } else
+ options->hpn_buffer_size = CHAN_TCP_WINDOW_DEFAULT;
+ }
+
if (options->ip_qos_interactive == -1)
options->ip_qos_interactive = IPTOS_LOWDELAY;
if (options->ip_qos_bulk == -1)
@@ -345,9 +392,10 @@ typedef enum {
sUsePrivilegeSeparation, sAllowAgentForwarding,
sHostCertificate,
sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
+ sTcpRcvBufPoll, sHPNDisabled, sHPNBufferSize,
sKexAlgorithms, sIPQoS, sVersionAddendum,
sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
- sAuthenticationMethods, sHostKeyAgent,
+ sAuthenticationMethods, sNoneEnabled, sHostKeyAgent,
sDeprecated, sUnsupported
} ServerOpCodes;
@@ -468,6 +516,10 @@ static struct {
{ "revokedkeys", sRevokedKeys, SSHCFG_ALL },
{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
{ "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
+ { "noneenabled", sNoneEnabled, SSHCFG_ALL },
+ { "hpndisabled", sHPNDisabled, SSHCFG_ALL },
+ { "hpnbuffersize", sHPNBufferSize, SSHCFG_ALL },
+ { "tcprcvbufpoll", sTcpRcvBufPoll, SSHCFG_ALL },
{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
{ "ipqos", sIPQoS, SSHCFG_ALL },
{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
@@ -500,6 +552,7 @@ parse_token(const char *cp, const char *filename,
for (i = 0; keywords[i].name; i++)
if (strcasecmp(cp, keywords[i].name) == 0) {
+ debug ("Config token is %s", keywords[i].name);
*flags = keywords[i].flags;
return keywords[i].opcode;
}
@@ -1042,10 +1095,27 @@ process_server_config_line(ServerOptions *options, char *line,
*intptr = value;
break;
+
+ case sTcpRcvBufPoll:
+ intptr = &options->tcp_rcv_buf_poll;
+ goto parse_flag;
+
+ case sHPNDisabled:
+ intptr = &options->hpn_disabled;
+ goto parse_flag;
+
+ case sHPNBufferSize:
+ intptr = &options->hpn_buffer_size;
+ goto parse_int;
+
case sIgnoreUserKnownHosts:
intptr = &options->ignore_user_known_hosts;
goto parse_flag;
+ case sNoneEnabled:
+ intptr = &options->none_enabled;
+ goto parse_flag;
+
case sRhostsRSAAuthentication:
intptr = &options->rhosts_rsa_authentication;
goto parse_flag;
diff --git a/servconf.h b/servconf.h
index 752d1c5..2945153 100644
--- a/servconf.h
+++ b/servconf.h
@@ -164,6 +164,11 @@ typedef struct {
char *adm_forced_command;
int use_pam; /* Enable auth via PAM */
+ int tcp_rcv_buf_poll; /* poll tcp rcv window in autotuning kernels*/
+ int hpn_disabled; /* disable hpn functionality. false by default */
+ int hpn_buffer_size; /* set the hpn buffer size - default 3MB */
+
+ int none_enabled; /* enable NONE cipher switch */
int permit_tun;
diff --git a/serverloop.c b/serverloop.c
index 2f8e3a0..7d3bdb7 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -94,10 +94,10 @@ static int fdin; /* Descriptor for stdin (for writing) */
static int fdout; /* Descriptor for stdout (for reading);
May be same number as fdin. */
static int fderr; /* Descriptor for stderr. May be -1. */
-static long stdin_bytes = 0; /* Number of bytes written to stdin. */
-static long stdout_bytes = 0; /* Number of stdout bytes sent to client. */
-static long stderr_bytes = 0; /* Number of stderr bytes sent to client. */
-static long fdout_bytes = 0; /* Number of stdout bytes read from program. */
+static u_long stdin_bytes = 0; /* Number of bytes written to stdin. */
+static u_long stdout_bytes = 0; /* Number of stdout bytes sent to client. */
+static u_long stderr_bytes = 0; /* Number of stderr bytes sent to client. */
+static u_long fdout_bytes = 0; /* Number of stdout bytes read from program. */
static int stdin_eof = 0; /* EOF message received from client. */
static int fdout_eof = 0; /* EOF encountered reading from fdout. */
static int fderr_eof = 0; /* EOF encountered readung from fderr. */
@@ -122,6 +122,20 @@ static volatile sig_atomic_t received_sigterm = 0;
static void server_init_dispatch(void);
/*
+ * Returns current time in seconds from Jan 1, 1970 with the maximum
+ * available resolution.
+ */
+
+static double
+get_current_time(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
+}
+
+
+/*
* we write to this pipe if a SIGCHLD is caught in order to avoid
* the race between select() and child_terminated
*/
@@ -421,6 +435,7 @@ process_input(fd_set *readset)
} else {
/* Buffer any received data. */
packet_process_incoming(buf, len);
+ fdout_bytes += len;
}
}
if (compat20)
@@ -443,6 +458,7 @@ process_input(fd_set *readset)
} else {
buffer_append(&stdout_buffer, buf, len);
fdout_bytes += len;
+ debug ("FD out now: %ld", fdout_bytes);
}
}
/* Read and buffer any available stderr data from the program. */
@@ -510,7 +526,7 @@ process_output(fd_set *writeset)
}
/* Send any buffered packet data to the client. */
if (FD_ISSET(connection_out, writeset))
- packet_write_poll();
+ stdin_bytes += packet_write_poll();
}
/*
@@ -824,11 +840,13 @@ void
server_loop2(Authctxt *authctxt)
{
fd_set *readset = NULL, *writeset = NULL;
+ double start_time, total_time;
int rekeying = 0, max_fd;
u_int nalloc = 0;
u_int64_t rekey_timeout_ms = 0;
debug("Entering interactive session for SSH2.");
+ start_time = get_current_time();
mysignal(SIGCHLD, sigchld_handler);
child_terminated = 0;
@@ -893,6 +911,11 @@ server_loop2(Authctxt *authctxt)
/* free remaining sessions, e.g. remove wtmp entries */
session_destroy_all(NULL);
+ total_time = get_current_time() - start_time;
+ logit("SSH: Server;LType: Throughput;Remote: %s-%d;IN: %lu;OUT: %lu;Duration: %.1f;tPut_in: %.1f;tPut_out: %.1f",
+ get_remote_ipaddr(), get_remote_port(),
+ stdin_bytes, fdout_bytes, total_time, stdin_bytes / total_time,
+ fdout_bytes / total_time);
}
static void
@@ -1015,8 +1038,12 @@ server_request_tun(void)
sock = tun_open(tun, mode);
if (sock < 0)
goto done;
+ if (options.hpn_disabled)
c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
+ else
+ c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
+ options.hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
c->datagram = 1;
#if defined(SSH_TUN_FILTER)
if (mode == SSH_TUNMODE_POINTOPOINT)
@@ -1052,6 +1079,8 @@ server_request_session(void)
c = channel_new("session", SSH_CHANNEL_LARVAL,
-1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT,
0, "server-session", 1);
+ if ((options.tcp_rcv_buf_poll) && (!options.hpn_disabled))
+ c->dynamic_window = 1;
if (session_open(the_authctxt, c->self) != 1) {
debug("session open failed, free channel %d", c->self);
channel_free(c);
diff --git a/session.c b/session.c
index 2bcf818..817afc9 100644
--- a/session.c
+++ b/session.c
@@ -237,6 +237,7 @@ auth_input_request_forwarding(struct passwd * pw)
}
/* Allocate a channel for the authentication agent socket. */
+ /* this shouldn't matter if its hpn or not - cjr */
nc = channel_new("auth socket",
SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
@@ -2331,10 +2332,16 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr, int ignore_fderr,
*/
if (s->chanid == -1)
fatal("no channel for session %d", s->self);
+ if (options.hpn_disabled)
channel_set_fds(s->chanid,
fdout, fdin, fderr,
ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
1, is_tty, CHAN_SES_WINDOW_DEFAULT);
+ else
+ channel_set_fds(s->chanid,
+ fdout, fdin, fderr,
+ ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
+ 1, is_tty, options.hpn_buffer_size);
}
/*
diff --git a/sftp.1 b/sftp.1
index a700c2a..8e00b13 100644
--- a/sftp.1
+++ b/sftp.1
@@ -261,7 +261,8 @@ diagnostic messages from
Specify how many requests may be outstanding at any one time.
Increasing this may slightly improve file transfer speed
but will increase memory usage.
-The default is 64 outstanding requests.
+The default is 256 outstanding requests providing for 8MB
+of outstanding data with a 32KB buffer.
.It Fl r
Recursively copy entire directories when uploading and downloading.
Note that
diff --git a/sftp.c b/sftp.c
index ad1f8c8..1575d5e 100644
--- a/sftp.c
+++ b/sftp.c
@@ -68,7 +68,7 @@ typedef void EditLine;
#include "sftp-client.h"
#define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
-#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
+#define DEFAULT_NUM_REQUESTS 256 /* # concurrent outstanding requests */
/* File to read commands from */
FILE* infile;
diff --git a/ssh.c b/ssh.c
index 1e6cb90..d69e047 100644
--- a/ssh.c
+++ b/ssh.c
@@ -773,6 +773,10 @@ main(int ac, char **av)
break;
case 'T':
options.request_tty = REQUEST_TTY_NO;
+ /* ensure that the user doesn't try to backdoor a */
+ /* null cipher switch on an interactive session */
+ /* so explicitly disable it no matter what */
+ options.none_switch=0;
break;
case 'o':
line = xstrdup(optarg);
@@ -1240,6 +1244,8 @@ control_persist_detach(void)
setproctitle("%s [mux]", options.control_path);
}
+extern const EVP_CIPHER *evp_aes_ctr_mt(void);
+
/* Do fork() after authentication. Used by "ssh -f" */
static void
fork_postauth(void)
@@ -1611,6 +1617,9 @@ ssh_session2_open(void)
{
Channel *c;
int window, packetmax, in, out, err;
+ int sock;
+ int socksize;
+ int socksizelen = sizeof(int);
if (stdin_null_flag) {
in = open(_PATH_DEVNULL, O_RDONLY);
@@ -1631,9 +1640,74 @@ ssh_session2_open(void)
if (!isatty(err))
set_nonblock(err);
- window = CHAN_SES_WINDOW_DEFAULT;
+ /* we need to check to see if what they want to do about buffer */
+ /* sizes here. In a hpn to nonhpn connection we want to limit */
+ /* the window size to something reasonable in case the far side */
+ /* has the large window bug. In hpn to hpn connection we want to */
+ /* use the max window size but allow the user to override it */
+ /* lastly if they disabled hpn then use the ssh std window size */
+
+ /* so why don't we just do a getsockopt() here and set the */
+ /* ssh window to that? In the case of a autotuning receive */
+ /* window the window would get stuck at the initial buffer */
+ /* size generally less than 96k. Therefore we need to set the */
+ /* maximum ssh window size to the maximum hpn buffer size */
+ /* unless the user has specifically set the tcprcvbufpoll */
+ /* to no. In which case we *can* just set the window to the */
+ /* minimum of the hpn buffer size and tcp receive buffer size */
+
+ if (tty_flag)
+ options.hpn_buffer_size = CHAN_SES_WINDOW_DEFAULT;
+ else
+ options.hpn_buffer_size = 2*1024*1024;
+
+ if (datafellows & SSH_BUG_LARGEWINDOW)
+ {
+ debug("HPN to Non-HPN Connection");
+ }
+ else
+ {
+ if (options.tcp_rcv_buf_poll <= 0)
+ {
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ &socksize, &socksizelen);
+ close(sock);
+ debug("socksize %d", socksize);
+ options.hpn_buffer_size = socksize;
+ debug ("HPNBufferSize set to TCP RWIN: %d", options.hpn_buffer_size);
+ }
+ else
+ {
+ if (options.tcp_rcv_buf > 0)
+ {
+ /*create a socket but don't connect it */
+ /* we use that the get the rcv socket size */
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ /* if they are using the tcp_rcv_buf option */
+ /* attempt to set the buffer size to that */
+ if (options.tcp_rcv_buf)
+ setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&options.tcp_rcv_buf,
+ sizeof(options.tcp_rcv_buf));
+ getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ &socksize, &socksizelen);
+ close(sock);
+ debug("socksize %d", socksize);
+ options.hpn_buffer_size = socksize;
+ debug ("HPNBufferSize set to user TCPRcvBuf: %d", options.hpn_buffer_size);
+ }
+ }
+ }
+
+ debug("Final hpn_buffer_size = %d", options.hpn_buffer_size);
+
+ window = options.hpn_buffer_size;
+
+ channel_set_hpn(options.hpn_disabled, options.hpn_buffer_size);
+
packetmax = CHAN_SES_PACKET_DEFAULT;
if (tty_flag) {
+ window = 4*CHAN_SES_PACKET_DEFAULT;
window >>= 1;
packetmax >>= 1;
}
@@ -1642,6 +1716,10 @@ ssh_session2_open(void)
window, packetmax, CHAN_EXTENDED_WRITE,
"client-session", /*nonblock*/0);
+ if ((options.tcp_rcv_buf_poll > 0) && (!options.hpn_disabled)) {
+ c->dynamic_window = 1;
+ debug ("Enabled Dynamic Window Scaling");
+ }
debug3("ssh_session2_open: channel_new: %d", c->self);
channel_send_open(c->self);
diff --git a/sshconnect.c b/sshconnect.c
index 573d7a8..9cf6947 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -263,6 +263,31 @@ ssh_kill_proxy_command(void)
}
/*
+ * Set TCP receive buffer if requested.
+ * Note: tuning needs to happen after the socket is
+ * created but before the connection happens
+ * so winscale is negotiated properly -cjr
+ */
+static void
+ssh_set_socket_recvbuf(int sock)
+{
+ void *buf = (void *)&options.tcp_rcv_buf;
+ int sz = sizeof(options.tcp_rcv_buf);
+ int socksize;
+ int socksizelen = sizeof(int);
+
+ debug("setsockopt Attempting to set SO_RCVBUF to %d", options.tcp_rcv_buf);
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, buf, sz) >= 0) {
+ getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &socksize, &socksizelen);
+ debug("setsockopt SO_RCVBUF: %.100s %d", strerror(errno), socksize);
+ }
+ else
+ error("Couldn't set socket receive buffer to %d: %.100s",
+ options.tcp_rcv_buf, strerror(errno));
+}
+
+
+/*
* Creates a (possibly privileged) socket for use as the ssh connection.
*/
static int
@@ -278,6 +303,9 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
}
fcntl(sock, F_SETFD, FD_CLOEXEC);
+ if (options.tcp_rcv_buf > 0)
+ ssh_set_socket_recvbuf(sock);
+
/* Bind the socket to an alternative local IP address */
if (options.bind_address == NULL && !privileged)
return sock;
@@ -520,10 +548,10 @@ send_client_banner(int connection_out, int minor1)
/* Send our own protocol version identification. */
if (compat20) {
xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n",
- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION);
+ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_RELEASE);
} else {
xasprintf(&client_version_string, "SSH-%d.%d-%.100s\n",
- PROTOCOL_MAJOR_1, minor1, SSH_VERSION);
+ PROTOCOL_MAJOR_1, minor1, SSH_RELEASE);
}
if (roaming_atomicio(vwrite, connection_out, client_version_string,
strlen(client_version_string)) != strlen(client_version_string))
diff --git a/sshconnect2.c b/sshconnect2.c
index 7f4ff41..0aeaed6 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -79,6 +79,12 @@
extern char *client_version_string;
extern char *server_version_string;
extern Options options;
+extern Kex *xxx_kex;
+
+/* tty_flag is set in ssh.c. use this in ssh_userauth2 */
+/* if it is set then prevent the switch to the null cipher */
+
+extern int tty_flag;
/*
* SSH2 key exchange
@@ -409,6 +415,45 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host,
pubkey_cleanup(&authctxt);
dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL);
+ /* if the user wants to use the none cipher do it */
+ /* post authentication and only if the right conditions are met */
+ /* both of the NONE commands must be true and there must be no */
+ /* tty allocated */
+ if ((options.none_switch == 1) && (options.none_enabled == 1))
+ {
+ if (!tty_flag) /* no null on tty sessions */
+ {
+ debug("Requesting none rekeying...");
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = "none";
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] = "none";
+ kex_prop2buf(&xxx_kex->my,myproposal);
+ packet_request_rekeying();
+ fprintf(stderr, "WARNING: ENABLED NONE CIPHER\n");
+ }
+ else
+ {
+ /* requested NONE cipher when in a tty */
+ debug("Cannot switch to NONE cipher with tty allocated");
+ fprintf(stderr, "NONE cipher switch disabled when a TTY is allocated\n");
+ }
+ }
+
+ /* if we are using aes-ctr there can be issues in either a fork or sandbox
+ * so the initial aes-ctr is defined to point to the original single process
+ * evp. After authentication we'll be past the fork and the sandboxed privsep
+ * so we repoint the define to the multithreaded evp. To start the threads we
+ * then force a rekey
+ */
+ CipherContext *ccsend;
+ ccsend = (CipherContext*)packet_get_send_context();
+
+ /* only do this for the ctr cipher. otherwise gcm mode breaks. Don't know why though */
+ if (strstr(cipher_return_name((Cipher*)ccsend->cipher), "ctr")) {
+ debug ("Single to Multithread CTR cipher swap - client request");
+ cipher_reset_multithreaded();
+ packet_request_rekeying();
+ }
+
debug("Authentication succeeded (%s).", authctxt.method->name);
}
diff --git a/sshd.c b/sshd.c
index 7523de9..c80b5b1 100644
--- a/sshd.c
+++ b/sshd.c
@@ -436,7 +436,7 @@ sshd_exchange_identification(int sock_in, int sock_out)
}
xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s%s",
- major, minor, SSH_VERSION,
+ major, minor, SSH_RELEASE,
*options.version_addendum == '\0' ? "" : " ",
options.version_addendum, newline);
@@ -489,6 +489,9 @@ sshd_exchange_identification(int sock_in, int sock_out)
}
debug("Client protocol version %d.%d; client software version %.100s",
remote_major, remote_minor, remote_version);
+ logit("SSH: Server;Ltype: Version;Remote: %s-%d;Protocol: %d.%d;Client: %.100s",
+ get_remote_ipaddr(), get_remote_port(),
+ remote_major, remote_minor, remote_version);
compat_datafellows(remote_version);
@@ -1082,6 +1085,8 @@ server_listen(void)
int ret, listen_sock, on = 1;
struct addrinfo *ai;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+ int socksize;
+ int socksizelen = sizeof(int);
for (ai = options.listen_addrs; ai; ai = ai->ai_next) {
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
@@ -1122,6 +1127,11 @@ server_listen(void)
debug("Bind to port %s on %s.", strport, ntop);
+ getsockopt(listen_sock, SOL_SOCKET, SO_RCVBUF,
+ &socksize, &socksizelen);
+ debug("Server TCP RWIN socket size: %d", socksize);
+ debug("HPN Buffer Size: %d", options.hpn_buffer_size);
+
/* Bind the socket to the desired port. */
if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) {
error("Bind to port %s on %s failed: %.200s.",
@@ -2058,6 +2068,9 @@ main(int ac, char **av)
remote_ip, remote_port,
get_local_ipaddr(sock_in), get_local_port());
+ /* set the HPN options for the child */
+ channel_set_hpn(options.hpn_disabled, options.hpn_buffer_size);
+
/*
* We don't want to listen forever unless the other side
* successfully authenticates itself. So we set up an alarm which is
@@ -2160,6 +2173,23 @@ main(int ac, char **av)
options.client_alive_count_max);
/* Start session. */
+
+ /* if we are using aes-ctr there can be issues in either a fork or sandbox
+ * so the initial aes-ctr is defined to point ot the original single process
+ * evp. After authentication we'll be past the fork and the sandboxed privsep
+ * so we repoint the define to the multithreaded evp. To start the threads we
+ * then force a rekey
+ */
+ CipherContext *ccsend;
+ ccsend = (CipherContext*)packet_get_send_context();
+
+ /* only rekey if necessary. If we don't do this gcm mode cipher breaks */
+ if (strstr(cipher_return_name((Cipher*)ccsend->cipher), "ctr")) {
+ debug ("Single to Multithreaded CTR cipher swap - server request");
+ cipher_reset_multithreaded();
+ packet_request_rekeying();
+ }
+
do_authenticated(authctxt);
/* The connection has been terminated. */
@@ -2442,6 +2472,10 @@ do_ssh2_kex(void)
if (options.ciphers != NULL) {
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
+ } else if (options.none_enabled == 1) {
+ debug ("WARNING: None cipher enabled");
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] =
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = KEX_ENCRYPT_INCLUDE_NONE;
}
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
diff --git a/sshd_config b/sshd_config
index e9045bc..33b0e4f 100644
--- a/sshd_config
+++ b/sshd_config
@@ -125,6 +125,20 @@ UsePrivilegeSeparation sandbox # Default for new installations.
# override default of no subsystems
Subsystem sftp /usr/libexec/sftp-server
+# the following are HPN related configuration options
+# tcp receive buffer polling. disable in non autotuning kernels
+#TcpRcvBufPoll yes
+
+# disable hpn performance boosts
+#HPNDisabled no
+
+# buffer size for hpn to non-hpn connections
+#HPNBufferSize 2048
+
+
+# allow the use of the none cipher
+#NoneEnabled no
+
# Example of overriding settings on a per-user basis
#Match User anoncvs
# X11Forwarding no
diff --git a/version.h b/version.h
index a1579ac..a888806 100644
--- a/version.h
+++ b/version.h
@@ -3,4 +3,6 @@
#define SSH_VERSION "OpenSSH_6.6"
#define SSH_PORTABLE "p1"
-#define SSH_RELEASE SSH_VERSION SSH_PORTABLE
+#define SSH_HPN "-hpn14v5"
+#define SSH_RELEASE SSH_VERSION SSH_PORTABLE SSH_HPN
+
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment