diff --git a/HPN-README b/HPN-README new file mode 100644 index 00000000..466e6951 --- /dev/null +++ b/HPN-README @@ -0,0 +1,153 @@ +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. + + +NONE MAC: +Starting with HPN 15v1 users will have the option to disable HMAC (message +authentication ciphers) when using the NONE cipher. You must enable the following: +NoneEnabled, NoneSwitch, and NoneMacEnabled. If all three are not enabled the None MAC +will be automatically disabled. In tests the use of the None MAC improved throuput by +more than 30%. + +ex: scp -oNoneSwitch=yes -oNoneEnabled=yes -oNoneMacEnabled=yes file host:~ + +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. + +NoneMacEnabled=[yes/no] client/server + Enable or disable the use of the None MAC. When this is enabled ssh +will *not* provide data integrity of any data being transmitted between hosts. Use +with caution as it, unlike just using NoneEnabled, doesn't provide data integrity and +protection against man-in-the-middle attacks. As with NoneEnabled all authentication +remains encrypted and integrity is ensured. Default is no. + +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. + +DisableMTAES=[yes/no] client/server + Switch the encryption cipher being used from the multithreaded MT-AES-CTR cipher +back to the stock single-threaded AES-CTR cipher. Useful on modern processors with +AES-NI instructions which make the stock single-threaded AES-CTR cipher faster than +the multithreaded MT-AES-CTR cipher. Set to no by default. + +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. + Allan Jude provided the code for the NoneMac and buffer normalization. + This work was financed, in part, by Cisco System, Inc., the National + Library of Medicine, and the National Science Foundation. diff --git a/channels.c b/channels.c index b60d56c4..0e363c15 100644 --- a/channels.c +++ b/channels.c @@ -220,6 +220,10 @@ static int rdynamic_connect_finish(struct ssh *, Channel *); /* Setup helper */ static void channel_handler_init(struct ssh_channels *sc); + +static int hpn_disabled = 0; +static int hpn_buffer_size = 2 * 1024 * 1024; + /* -- channel core */ void @@ -395,6 +399,7 @@ channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd, c->local_window = window; c->local_window_max = window; c->local_maxpacket = maxpack; + c->dynamic_window = 0; c->remote_name = xstrdup(remote_name); c->ctl_chan = -1; c->delayed = 1; /* prevent call to channel_post handler */ @@ -1082,6 +1087,28 @@ channel_pre_connecting(struct ssh *ssh, Channel *c, FD_SET(c->sock, writeset); } +static int +channel_tcpwinsz(struct ssh *ssh) +{ + u_int32_t tcpwinsz = 0; + socklen_t optsz = sizeof(tcpwinsz); + int ret = -1; + + /* if we aren't on a socket return 128KB */ + if (!ssh_packet_connection_is_on_socket(ssh)) + return 128 * 1024; + + ret = getsockopt(ssh_packet_get_connection_in(ssh), + SOL_SOCKET, SO_RCVBUF, &tcpwinsz, &optsz); + /* return no more than SSHBUF_SIZE_MAX (currently 256MB) */ + if ((ret == 0) && tcpwinsz > SSHBUF_SIZE_MAX) + tcpwinsz = SSHBUF_SIZE_MAX; + + debug2("tcpwinsz: tcp connection %d, Receive window: %d", + ssh_packet_get_connection_in(ssh), tcpwinsz); + return tcpwinsz; +} + static void channel_pre_open(struct ssh *ssh, Channel *c, fd_set *readset, fd_set *writeset) @@ -2120,22 +2147,32 @@ channel_check_window(struct ssh *ssh, Channel *c) if (c->type == SSH_CHANNEL_OPEN && !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && - ((c->local_window_max - c->local_window > - c->local_maxpacket*3) || + ((ssh_packet_is_interactive(ssh) && + c->local_window_max - c->local_window > c->local_maxpacket*3) || c->local_window < c->local_window_max/2) && c->local_consumed > 0) { + u_int addition = 0; + u_int32_t tcpwinsz = channel_tcpwinsz(ssh); + /* adjust max window size if we are in a dynamic environment */ + if (c->dynamic_window && (tcpwinsz > c->local_window_max)) { + /* grow the window somewhat aggressively to maintain pressure */ + addition = 1.5 * (tcpwinsz - c->local_window_max); + c->local_window_max += addition; + debug("Channel: Window growth to %d by %d bytes", c->local_window_max, addition); + } if (!c->have_remote_id) fatal_f("channel %d: no remote id", c->self); if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || - (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_consumed + addition)) != 0 || (r = sshpkt_send(ssh)) != 0) { fatal_fr(r, "channel %i", c->self); } - debug2("channel %d: window %d sent adjust %d", c->self, - c->local_window, c->local_consumed); - c->local_window += c->local_consumed; + debug2("channel %d: window %d sent adjust %d", + c->self, c->local_window, + c->local_consumed + addition); + c->local_window += c->local_consumed + addition; c->local_consumed = 0; } return 1; @@ -3302,6 +3339,15 @@ channel_fwd_bind_addr(struct ssh *ssh, 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_tcpip(struct ssh *ssh, int type, struct Forward *fwd, int *allocated_listen_port, @@ -3442,9 +3488,11 @@ channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, } /* Allocate a channel number for the socket. */ + /* explicitly test for hpn disabled option. if true use smaller window size */ c = channel_new(ssh, "port listener", type, sock, sock, -1, - CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, - 0, "port listener", 1); + hpn_disabled ? CHAN_TCP_WINDOW_DEFAULT : hpn_buffer_size, + CHAN_TCP_PACKET_DEFAULT, + 0, "port listener", 1); c->path = xstrdup(host); c->host_port = fwd->connect_port; c->listening_addr = addr == NULL ? NULL : xstrdup(addr); @@ -4612,8 +4660,9 @@ x11_create_display_inet(struct ssh *ssh, int x11_display_offset, sock = socks[n]; nc = channel_new(ssh, "x11 listener", SSH_CHANNEL_X11_LISTENER, sock, sock, -1, - CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, - 0, "X11 inet listener", 1); + hpn_disabled ? CHAN_X11_WINDOW_DEFAULT : 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 74e9b3f8..a409f367 100644 --- a/channels.h +++ b/channels.h @@ -158,6 +158,7 @@ struct Channel { u_int local_window_max; u_int local_consumed; u_int local_maxpacket; + int dynamic_window; int extended_usage; int single_connection; @@ -221,7 +222,7 @@ struct Channel { #define CHAN_LOCAL 0x10 /* Read buffer size */ -#define CHAN_RBUF (16*1024) +#define CHAN_RBUF CHAN_SES_PACKET_DEFAULT /* Maximum channel input buffer size */ #define CHAN_INPUT_MAX (16*1024*1024) @@ -352,4 +353,7 @@ void chan_rcvd_ieof(struct ssh *, Channel *); void chan_write_failed(struct ssh *, Channel *); void chan_obuf_empty(struct ssh *, Channel *); +/* hpn handler */ +void channel_set_hpn(int, int); + #endif diff --git a/cipher.c b/cipher.c index 639511cf..dd902029 100644 --- a/cipher.c +++ b/cipher.c @@ -226,7 +226,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->flags & CFLAG_INTERNAL) != 0) { + if (c == NULL || ((c->flags & CFLAG_INTERNAL) != 0 && + (c->flags & CFLAG_NONE) != 0)) { free(cipher_list); return 0; } diff --git a/clientloop.c b/clientloop.c index 70f492f8..5503af1d 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1578,9 +1578,11 @@ client_request_x11(struct ssh *ssh, const char *request_type, int rchan) sock = x11_connect_display(ssh); if (sock < 0) return NULL; - c = channel_new(ssh, "x11", - SSH_CHANNEL_X11_OPEN, sock, sock, -1, - CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); + c = channel_new(ssh, "x11", + SSH_CHANNEL_X11_OPEN, sock, sock, -1, + /* again is this really necessary for X11? */ + options.hpn_disabled ? CHAN_TCP_WINDOW_DEFAULT : options.hpn_buffer_size, + CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); c->force_drain = 1; return c; } @@ -1608,9 +1610,10 @@ client_request_agent(struct ssh *ssh, const char *request_type, int rchan) return NULL; } c = channel_new(ssh, "authentication agent connection", - SSH_CHANNEL_OPEN, sock, sock, -1, - CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, - "authentication agent connection", 1); + SSH_CHANNEL_OPEN, sock, sock, -1, + options.hpn_disabled ? CHAN_X11_WINDOW_DEFAULT : options.hpn_buffer_size, + CHAN_TCP_PACKET_DEFAULT, 0, + "authentication agent connection", 1); c->force_drain = 1; return c; } @@ -1635,10 +1638,13 @@ client_request_tun_fwd(struct ssh *ssh, int tun_mode, } debug("Tunnel forwarding using interface %s", ifname); - c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, - CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); + c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, + options.hpn_disabled ? CHAN_TCP_WINDOW_DEFAULT : 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(ssh, c->self, sys_tun_infilter, diff --git a/compat.c b/compat.c index 69befa96..90b5f338 100644 --- a/compat.c +++ b/compat.c @@ -149,6 +149,14 @@ compat_banner(struct ssh *ssh, const char *version) debug_f("match: %s pat %s compat 0x%08x", version, check[i].pat, check[i].bugs); ssh->compat = check[i].bugs; + /* Check to see if the remote side is OpenSSH and not HPN */ + /* TODO: need to use new method to test for this */ + if (strstr(version, "OpenSSH") != NULL) { + if (strstr(version, "hpn") == NULL) { + ssh->compat |= SSH_BUG_LARGEWINDOW; + debug("Remote is NON-HPN aware"); + } + } return; } } diff --git a/compat.h b/compat.h index c197fafc..ea2e17a7 100644 --- a/compat.h +++ b/compat.h @@ -57,6 +57,7 @@ #define SSH_BUG_CURVE25519PAD 0x10000000 #define SSH_BUG_HOSTKEYS 0x20000000 #define SSH_BUG_DHGEX_LARGE 0x40000000 +#define SSH_BUG_LARGEWINDOW 0x80000000 struct ssh; diff --git a/defines.h b/defines.h index d6a1d014..911c5120 100644 --- a/defines.h +++ b/defines.h @@ -848,7 +848,7 @@ struct winsize { #endif #ifndef SSH_IOBUFSZ -# define SSH_IOBUFSZ 8192 +# define SSH_IOBUFSZ 32*1024 #endif /* diff --git a/digest-openssl.c b/digest-openssl.c index e073a807..fc0257e3 100644 --- a/digest-openssl.c +++ b/digest-openssl.c @@ -61,6 +61,7 @@ const struct ssh_digest digests[] = { { SSH_DIGEST_SHA256, "SHA256", 32, EVP_sha256 }, { SSH_DIGEST_SHA384, "SHA384", 48, EVP_sha384 }, { SSH_DIGEST_SHA512, "SHA512", 64, EVP_sha512 }, + { SSH_DIGEST_NULL, "NONEMAC", 0, EVP_md_null}, { -1, NULL, 0, NULL }, }; diff --git a/digest.h b/digest.h index 274574d0..dbe10f02 100644 --- a/digest.h +++ b/digest.h @@ -27,7 +27,8 @@ #define SSH_DIGEST_SHA256 2 #define SSH_DIGEST_SHA384 3 #define SSH_DIGEST_SHA512 4 -#define SSH_DIGEST_MAX 5 +#define SSH_DIGEST_NULL 5 +#define SSH_DIGEST_MAX 6 struct sshbuf; struct ssh_digest_ctx; diff --git a/kex.c b/kex.c index 30425ab8..9d40e813 100644 --- a/kex.c +++ b/kex.c @@ -890,6 +890,10 @@ kex_choose_conf(struct ssh *ssh) int nenc, nmac, ncomp; u_int mode, ctos, need, dh_need, authlen; int r, first_kex_follows; + int auth_flag = 0; + + auth_flag = packet_authentication_state(ssh); + debug("AUTH STATE IS %d", auth_flag); debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) @@ -960,6 +964,19 @@ kex_choose_conf(struct ssh *ssh) peer[ncomp] = NULL; goto out; } + debug("REQUESTED ENC.NAME is '%s'", newkeys->enc.name); + debug("REQUESTED MAC.NAME is '%s'", newkeys->mac.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."); + ssh->none = 1; + } + else + fatal("Pre-authentication none cipher requests are not allowed."); + if (newkeys->mac.name != NULL && strcmp(newkeys->mac.name, "none") == 0) + debug("Requesting: NONEMAC. Authflag is %d", auth_flag); + } debug("kex: %s cipher: %s MAC: %s compression: %s", ctos ? "client->server" : "server->client", newkeys->enc.name, diff --git a/mac.c b/mac.c index f3dda669..e39777fa 100644 --- a/mac.c +++ b/mac.c @@ -63,6 +63,7 @@ static const struct macalg macs[] = { { "hmac-sha2-512", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 }, { "hmac-md5", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 }, { "hmac-md5-96", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 0 }, + { "none", SSH_DIGEST, SSH_DIGEST_NULL, 0, 0, 0, 0 }, { "umac-64@openssh.com", SSH_UMAC, 0, 0, 128, 64, 0 }, { "umac-128@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 0 }, diff --git a/packet.c b/packet.c index 4bd8b4ec..22b81f2d 100644 --- a/packet.c +++ b/packet.c @@ -245,7 +245,7 @@ ssh_alloc_session_state(void) TAILQ_INIT(&ssh->public_keys); state->connection_in = -1; state->connection_out = -1; - state->max_packet_size = 32768; + state->max_packet_size = CHAN_SES_PACKET_DEFAULT; state->packet_timeout_ms = -1; state->p_send.packets = state->p_read.packets = 0; state->initialized = 1; @@ -942,10 +942,19 @@ ssh_set_newkeys(struct ssh *ssh, int mode) * 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; + + /* we really don't need to rekey if we are using the none cipher + * but there isn't a good way to disable it entirely that I can find + * and using a blocksize larger that 16 doesn't work (dunno why) + * so this seems to be a good limit for now - CJR 10/16/2020*/ + if (ssh->none == 1) { + *max_blocks = (u_int64_t)1 << (16*2); + } else { + 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 = MINIMUM(*max_blocks, state->rekey_limit / enc->block_size); @@ -954,6 +963,24 @@ ssh_set_newkeys(struct ssh *ssh, int mode) return 0; } +/* this supports the forced rekeying required for the NONE cipher */ +int rekey_requested = 0; +void +packet_request_rekeying(void) +{ + rekey_requested = 1; +} + +/* used to determine if pre or post auth when rekeying for aes-ctr + * and none cipher switch */ +int +packet_authentication_state(const struct ssh *ssh) +{ + struct session_state *state = ssh->state; + + return state->after_authentication; +} + #define MAX_PACKETS (1U<<31) static int ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) @@ -980,6 +1007,13 @@ ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) if (state->p_send.packets == 0 && state->p_read.packets == 0) return 0; + /* used to force rekeying when called for by the none + * cipher switch methods -cjr */ + if (rekey_requested == 1) { + rekey_requested = 0; + return 1; + } + /* Time-based rekeying */ if (state->rekey_interval != 0 && (int64_t)state->rekey_time + state->rekey_interval <= monotime()) @@ -1317,7 +1351,7 @@ 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; fd_set *setp; - char buf[8192]; + char buf[SSH_IOBUFSZ]; struct timeval timeout, start, *timeoutp = NULL; DBG(debug("packet_read()")); diff --git a/packet.h b/packet.h index c2544bd9..f2a33b7b 100644 --- a/packet.h +++ b/packet.h @@ -86,6 +86,9 @@ struct ssh { /* APP data */ void *app_data; + + /* track that we are in a none cipher/mac state */ + int none; }; typedef int (ssh_packet_hook_fn)(struct ssh *, struct sshbuf *, @@ -155,6 +158,10 @@ int ssh_packet_inc_alive_timeouts(struct ssh *); int ssh_packet_set_maxsize(struct ssh *, u_int); u_int ssh_packet_get_maxsize(struct ssh *); +/* for forced packet rekeying post auth */ +void packet_request_rekeying(void); +int packet_authentication_state(const struct ssh *); + int ssh_packet_get_state(struct ssh *, struct sshbuf *); int ssh_packet_set_state(struct ssh *, struct sshbuf *); diff --git a/readconf.c b/readconf.c index 724974b7..b55c71d0 100644 --- a/readconf.c +++ b/readconf.c @@ -67,6 +67,7 @@ #include "uidswap.h" #include "myproposal.h" #include "digest.h" +#include "sshbuf.h" /* Format of the configuration file: @@ -166,6 +167,8 @@ typedef enum { oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oRemoteCommand, + oTcpRcvBufPoll, oTcpRcvBuf, oHPNDisabled, oHPNBufferSize, + oNoneEnabled, oNoneMacEnabled, oNoneSwitch, oVisualHostKey, oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, @@ -297,6 +300,9 @@ static struct { { "kexalgorithms", oKexAlgorithms }, { "ipqos", oIPQoS }, { "requesttty", oRequestTTY }, + { "noneenabled", oNoneEnabled }, + { "nonemacenabled", oNoneMacEnabled }, + { "noneswitch", oNoneSwitch }, { "proxyusefdpass", oProxyUseFdpass }, { "canonicaldomains", oCanonicalDomains }, { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal }, @@ -317,6 +323,11 @@ static struct { { "securitykeyprovider", oSecurityKeyProvider }, { "knownhostscommand", oKnownHostsCommand }, + { "tcprcvbufpoll", oTcpRcvBufPoll }, + { "tcprcvbuf", oTcpRcvBuf }, + { "hpndisabled", oHPNDisabled }, + { "hpnbuffersize", oHPNBufferSize }, + { NULL, oBadOption } }; @@ -1091,6 +1102,42 @@ 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; + + case oNoneMacEnabled: + 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; @@ -1324,6 +1371,10 @@ parse_int: *intptr = value; break; + case oTcpRcvBuf: + intptr = &options->tcp_rcv_buf; + goto parse_int; + case oCiphers: arg = strdelim(&s); if (!arg || *arg == '\0') { @@ -2262,6 +2313,13 @@ 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->nonemac_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; @@ -2426,6 +2484,41 @@ 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->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 SSHBUF_SIZE_MAX (currently 256MB) */ + if (options->hpn_buffer_size > (SSHBUF_SIZE_MAX / 1024)) { + options->hpn_buffer_size = SSHBUF_SIZE_MAX; + debug("User requested buffer larger than 256MB. Request reverted to 256MB"); + } 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->none_switch == -1) + options->none_switch = 0; + if (options->none_enabled == -1) + options->none_enabled = 0; + if (options->none_enabled == 0 && options->none_switch > 0) { + fprintf(stderr, "NoneEnabled must be enabled to use the None Switch option. None cipher disabled.\n"); + options->none_enabled = 0; + } + if (options->nonemac_enabled == -1) + options->nonemac_enabled = 0; + if (options->nonemac_enabled > 0 && (options->none_enabled == 0 || + options->none_switch == 0)) { + fprintf(stderr, "None MAC can only be used with the None cipher. None MAC disabled.\n"); + options->nonemac_enabled = 0; + } if (options->control_master == -1) options->control_master = 0; if (options->control_persist == -1) { diff --git a/readconf.h b/readconf.h index 2fba866e..3846562b 100644 --- a/readconf.h +++ b/readconf.h @@ -51,6 +51,10 @@ typedef struct { int strict_host_key_checking; /* Strict host key checking. */ int compression; /* Compress packets in both directions. */ 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 */ SyslogFacility log_facility; /* Facility for system logging. */ @@ -120,7 +124,11 @@ typedef struct { int enable_ssh_keysign; int64_t rekey_limit; + int none_switch; /* Use none cipher */ + int none_enabled; /* Allow none cipher to be used */ + int nonemac_enabled; /* Allow none MAC to be used */ int rekey_interval; + int no_host_authentication_for_localhost; int identities_only; int server_alive_interval; diff --git a/regress/integrity.sh b/regress/integrity.sh index bc030cb7..33be7288 100644 --- a/regress/integrity.sh +++ b/regress/integrity.sh @@ -21,6 +21,12 @@ macs="$macs `${SSH} -Q cipher-auth`" cmd="$SUDO env SSH_SK_HELPER="$SSH_SK_HELPER" sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy" for m in $macs; do + # the none mac is now valid but tests against it will succeed when we expect it to + # fail. so we need to explicity remove it from the list of macs returned. + if [ "$m" = "none" ]; then + continue + fi + trace "test $tid: mac $m" elen=0 epad=0 diff --git a/servconf.c b/servconf.c index 9695583a..f82cf006 100644 --- a/servconf.c +++ b/servconf.c @@ -70,6 +70,7 @@ #include "auth.h" #include "myproposal.h" #include "digest.h" +#include "sshbuf.h" static void add_listen_addr(ServerOptions *, const char *, const char *, int); @@ -190,6 +191,11 @@ initialize_server_options(ServerOptions *options) options->authorized_principals_file = NULL; options->authorized_principals_command = NULL; options->authorized_principals_command_user = NULL; + options->tcp_rcv_buf_poll = -1; + options->hpn_disabled = -1; + options->hpn_buffer_size = -1; + options->none_enabled = -1; + options->nonemac_enabled = -1; options->ip_qos_interactive = -1; options->ip_qos_bulk = -1; options->version_addendum = NULL; @@ -275,6 +281,10 @@ void fill_default_server_options(ServerOptions *options) { u_int i; + /* needed for hpn socket tests */ + int sock; + int socksize; + int socksizelen = sizeof(int); /* Portable-specific options */ if (options->use_pam == -1) @@ -424,6 +434,49 @@ 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->nonemac_enabled == -1) + options->nonemac_enabled = 0; + if (options->nonemac_enabled > 0 && options->none_enabled == 0) { + debug ("Attempted to enabled None MAC without setting None Enabled to true. None MAC disabled."); + options->nonemac_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 in case 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 SSHBUF_SIZE_MAX (currently 256MB) */ + if (options->hpn_buffer_size > (SSHBUF_SIZE_MAX / 1024)) { + options->hpn_buffer_size = SSHBUF_SIZE_MAX; + } 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_DSCP_AF21; if (options->ip_qos_bulk == -1) @@ -496,6 +549,8 @@ typedef enum { sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, sAddressFamily, sPrintMotd, sPrintLastLog, sIgnoreRhosts, + sNoneEnabled, sNoneMacEnabled, + sTcpRcvBufPoll, sHPNDisabled, sHPNBufferSize, sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive, sPermitUserEnvironment, sAllowTcpForwarding, sCompression, @@ -660,6 +715,11 @@ static struct { { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, + { "hpndisabled", sHPNDisabled, SSHCFG_ALL }, + { "hpnbuffersize", sHPNBufferSize, SSHCFG_ALL }, + { "tcprcvbufpoll", sTcpRcvBufPoll, SSHCFG_ALL }, + { "noneenabled", sNoneEnabled, SSHCFG_ALL }, + { "nonemacenabled", sNoneMacEnabled, SSHCFG_ALL }, { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, { "include", sInclude, SSHCFG_ALL }, { "ipqos", sIPQoS, SSHCFG_ALL }, @@ -718,6 +778,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; } @@ -1461,12 +1522,33 @@ process_server_config_line_depth(ServerOptions *options, char *line, multistate_ptr = multistate_ignore_rhosts; goto parse_multistate; + + 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; parse_flag: multistate_ptr = multistate_flag; goto parse_multistate; + case sNoneEnabled: + intptr = &options->none_enabled; + goto parse_flag; + + case sNoneMacEnabled: + intptr = &options->none_enabled; + goto parse_flag; + case sHostbasedAuthentication: intptr = &options->hostbased_authentication; goto parse_flag; diff --git a/servconf.h b/servconf.h index 4f4fd9ba..c42d6a10 100644 --- a/servconf.h +++ b/servconf.h @@ -200,6 +200,12 @@ 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 nonemac_enabled; /* Enable NONE MAC switch */ int permit_tun; diff --git a/serverloop.c b/serverloop.c index 306658cb..d4309903 100644 --- a/serverloop.c +++ b/serverloop.c @@ -322,7 +322,7 @@ static int process_input(struct ssh *ssh, fd_set *readset, int connection_in) { int r, len; - char buf[16384]; + char buf[SSH_IOBUFSZ]; /* Read and buffer any input data from the client. */ if (FD_ISSET(connection_in, readset)) { @@ -608,7 +608,8 @@ server_request_tun(struct ssh *ssh) debug("Tunnel forwarding using interface %s", ifname); c = channel_new(ssh, "tun", SSH_CHANNEL_OPEN, sock, sock, -1, - CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); + options.hpn_disabled ? CHAN_TCP_WINDOW_DEFAULT : options.hpn_buffer_size, + CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; #if defined(SSH_TUN_FILTER) if (mode == SSH_TUNMODE_POINTOPOINT) @@ -659,6 +660,8 @@ server_request_session(struct ssh *ssh) c = channel_new(ssh, "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(ssh, c); diff --git a/session.c b/session.c index 4b155f91..f2449be3 100644 --- a/session.c +++ b/session.c @@ -223,6 +223,7 @@ auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw) goto authsock_err; /* Allocate a channel for the authentication agent socket. */ + /* this shouldn't matter if its hpn or not - cjr */ nc = channel_new(ssh, "auth socket", SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, @@ -2250,10 +2251,11 @@ session_set_fds(struct ssh *ssh, Session *s, */ if (s->chanid == -1) fatal("no channel for session %d", s->self); - channel_set_fds(ssh, s->chanid, - fdout, fdin, fderr, - ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, - 1, is_tty, CHAN_SES_WINDOW_DEFAULT); + channel_set_fds(ssh, s->chanid, + fdout, fdin, fderr, + ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, + 1, is_tty, + options.hpn_disabled ? CHAN_SES_WINDOW_DEFAULT : options.hpn_buffer_size); } /* diff --git a/sftp.1 b/sftp.1 index a1a63730..9f40f28b 100644 --- a/sftp.1 +++ b/sftp.1 @@ -296,7 +296,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 fb3c08d1..89bebbb2 100644 --- a/sftp.c +++ b/sftp.c @@ -71,7 +71,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-keygen.c b/ssh-keygen.c index cfb5f115..36a6e519 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -2971,7 +2971,7 @@ do_download_sk(const char *skprovider, const char *device) freezero(pin, strlen(pin)); error_r(r, "Unable to load resident keys"); return -1; - } + } if (nkeys == 0) logit("No keys to download"); if (pin != NULL) diff --git a/ssh.c b/ssh.c index 53330da5..27b9770e 100644 --- a/ssh.c +++ b/ssh.c @@ -1027,6 +1027,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); @@ -2052,6 +2056,78 @@ ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg) NULL, fileno(stdin), command, environ); } +static void +hpn_options_init(struct ssh *ssh) +{ + /* + * 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 (ssh->compat & SSH_BUG_LARGEWINDOW) { + debug("HPN to Non-HPN Connection"); + } else { + int sock, socksize; + socklen_t socksizelen; + if (options.tcp_rcv_buf_poll <= 0) { + sock = socket(AF_INET, SOCK_STREAM, 0); + socksizelen = sizeof(socksize); + 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) { + socksizelen = sizeof(options.tcp_rcv_buf); + setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + &options.tcp_rcv_buf, socksizelen); + } + socksizelen = sizeof(socksize); + 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); + + channel_set_hpn(options.hpn_disabled, options.hpn_buffer_size); +} + /* open new channel for a session */ static int ssh_session2_open(struct ssh *ssh) @@ -2078,9 +2154,11 @@ ssh_session2_open(struct ssh *ssh) if (!isatty(err)) set_nonblock(err); - window = CHAN_SES_WINDOW_DEFAULT; + window = options.hpn_buffer_size; + packetmax = CHAN_SES_PACKET_DEFAULT; if (tty_flag) { + window = CHAN_SES_WINDOW_DEFAULT; window >>= 1; packetmax >>= 1; } @@ -2089,6 +2167,11 @@ ssh_session2_open(struct ssh *ssh) 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_f("channel_new: %d", c->self); channel_send_open(ssh, c->self); @@ -2105,6 +2188,13 @@ ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo) int r, id = -1; char *cp, *tun_fwd_ifname = NULL; + /* + * We need to initialize this early because the forwarding logic below + * might open channels that use the hpn buffer sizes. We can't send a + * window of -1 (the default) to the server as it breaks things. + */ + hpn_options_init(ssh); + /* XXX should be pre-session */ if (!options.control_persist) ssh_init_stdio_forwarding(ssh); diff --git a/sshbuf.h b/sshbuf.h index 2ad0e61b..84bbdc63 100644 --- a/sshbuf.h +++ b/sshbuf.h @@ -28,7 +28,7 @@ # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ -#define SSHBUF_SIZE_MAX 0x8000000 /* Hard maximum size */ +#define SSHBUF_SIZE_MAX 0xF000000 /* Hard maximum size 256MB */ #define SSHBUF_REFS_MAX 0x100000 /* Max child buffers */ #define SSHBUF_MAX_BIGNUM (16384 / 8) /* Max bignum *bytes* */ #define SSHBUF_MAX_ECPOINT ((528 * 2 / 8) + 1) /* Max EC point *bytes* */ diff --git a/sshconnect.c b/sshconnect.c index 74f9e767..4654df66 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -342,6 +342,30 @@ check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs, } #endif +/* + * 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 socket for use as the ssh connection. */ @@ -364,6 +388,9 @@ ssh_create_socket(struct addrinfo *ai) } fcntl(sock, F_SETFD, FD_CLOEXEC); + if (options.tcp_rcv_buf > 0) + ssh_set_socket_recvbuf(sock); + /* Use interactive QOS (if specified) until authentication completed */ if (options.ip_qos_interactive != INT_MAX) set_sock_tos(sock, options.ip_qos_interactive); diff --git a/sshconnect2.c b/sshconnect2.c index 059c9480..2a18d8e1 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -85,6 +85,13 @@ extern char *client_version_string; extern char *server_version_string; extern Options options; +/* + * 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 */ @@ -212,6 +219,8 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port, return ret; } +static char *myproposal[PROPOSAL_MAX]; +static const char *myproposal_default[PROPOSAL_MAX] = { KEX_CLIENT }; void ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port, const struct ssh_conn_info *cinfo) @@ -220,6 +229,8 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port, char *s, *all_key; int r, use_known_hosts_order = 0; + memcpy(&myproposal, &myproposal_default, sizeof(myproposal)); + xxx_host = host; xxx_hostaddr = hostaddr; xxx_conn_info = cinfo; @@ -489,6 +500,34 @@ ssh_userauth2(struct ssh *ssh, const char *local_user, if (!authctxt.success) fatal("Authentication failed."); + + /* + * 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..."); + memcpy(&myproposal, &myproposal_default, sizeof(myproposal)); + myproposal[PROPOSAL_ENC_ALGS_STOC] = "none"; + myproposal[PROPOSAL_ENC_ALGS_CTOS] = "none"; + fprintf(stderr, "WARNING: ENABLED NONE CIPHER!!!\n"); + /* NONEMAC can only be used in context of the NONE CIPHER */ + if (options.nonemac_enabled == 1) { + myproposal[PROPOSAL_MAC_ALGS_STOC] = "none"; + myproposal[PROPOSAL_MAC_ALGS_CTOS] = "none"; + fprintf(stderr, "WARNING: ENABLED NONE MAC\n"); + } + kex_prop2buf(ssh->kex->my, myproposal); + packet_request_rekeying(); + } 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"); + } + } + debug("Authentication succeeded (%s).", authctxt.method->name); } diff --git a/sshd.c b/sshd.c index 6277e6d6..d66fa41a 100644 --- a/sshd.c +++ b/sshd.c @@ -1031,6 +1031,8 @@ listen_on_addrs(struct listenaddr *la) int ret, listen_sock; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + int socksize; + int socksizelen = sizeof(int); for (ai = la->addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) @@ -1076,6 +1078,11 @@ listen_on_addrs(struct listenaddr *la) 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) == -1) { error("Bind to port %s on %s failed: %.200s.", @@ -1727,6 +1734,19 @@ main(int ac, char **av) /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); + if (options.none_enabled == 1) { + char *old_ciphers = options.ciphers; + xasprintf(&options.ciphers, "%s,none", old_ciphers); + free(old_ciphers); + + /* only enable the none MAC in context of the none cipher -cjr */ + if (options.nonemac_enabled == 1) { + char *old_macs = options.macs; + xasprintf(&options.macs, "%s,none", old_macs); + free(old_macs); + } + } + /* challenge-response is implemented via keyboard interactive */ if (options.challenge_response_authentication) options.kbd_interactive_authentication = 1; @@ -2166,6 +2186,9 @@ main(int ac, char **av) rdomain == NULL ? "" : "\""); free(laddr); + /* 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 @@ -2343,6 +2366,12 @@ do_ssh2_kex(struct ssh *ssh) struct kex *kex; int r; + if (options.none_enabled == 1) + debug("WARNING: None cipher enabled"); + + if (options.nonemac_enabled == 1) + debug("WARNING: None MAC enabled"); + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(ssh, options.kex_algorithms); myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal(ssh, diff --git a/sshd_config b/sshd_config index 19b7c91a..cdd889b2 100644 --- a/sshd_config +++ b/sshd_config @@ -108,6 +108,22 @@ AuthorizedKeysFile .ssh/authorized_keys # 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 + +# allow the use of the none MAC +#NoneMacEnabled no + # Example of overriding settings on a per-user basis #Match User anoncvs # X11Forwarding no diff --git a/version.h b/version.h index 6b4fa372..332fb486 100644 --- a/version.h +++ b/version.h @@ -3,4 +3,5 @@ #define SSH_VERSION "OpenSSH_8.5" #define SSH_PORTABLE "p1" -#define SSH_RELEASE SSH_VERSION SSH_PORTABLE +#define SSH_HPN "-hpn15v2" +#define SSH_RELEASE SSH_VERSION SSH_PORTABLE SSH_HPN