X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/48406e6b01ff451976b1ef042b723c117e6c40f2..8def70c3ec6f81f95673c0de67a75b5a6b2e9e1c:/ssh.c diff --git a/ssh.c b/ssh.c index 809f4af4..d30626dc 100644 --- a/ssh.c +++ b/ssh.c @@ -12,6 +12,7 @@ #include "putty.h" #include "tree234.h" #include "ssh.h" +#include "sshgss.h" #ifndef FALSE #define FALSE 0 @@ -62,6 +63,10 @@ #define SSH1_SMSG_AUTH_CCARD_CHALLENGE 71 /* 0x47 */ #define SSH1_CMSG_AUTH_CCARD_RESPONSE 72 /* 0x48 */ +#define SSH1_AUTH_RHOSTS 1 /* 0x1 */ +#define SSH1_AUTH_RSA 2 /* 0x2 */ +#define SSH1_AUTH_PASSWORD 3 /* 0x3 */ +#define SSH1_AUTH_RHOSTS_RSA 4 /* 0x4 */ #define SSH1_AUTH_TIS 5 /* 0x5 */ #define SSH1_AUTH_CCARD 16 /* 0x10 */ @@ -108,19 +113,30 @@ #define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */ #define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */ #define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */ +#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 +#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 +#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 +#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 +#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 +#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 /* * Packet type contexts, so that ssh2_pkt_type can correctly decode * the ambiguous type numbers back into the correct type strings. */ -#define SSH2_PKTCTX_DHGROUP 0x0001 -#define SSH2_PKTCTX_DHGEX 0x0002 -#define SSH2_PKTCTX_RSAKEX 0x0004 -#define SSH2_PKTCTX_KEX_MASK 0x000F -#define SSH2_PKTCTX_PUBLICKEY 0x0010 -#define SSH2_PKTCTX_PASSWORD 0x0020 -#define SSH2_PKTCTX_KBDINTER 0x0040 -#define SSH2_PKTCTX_AUTH_MASK 0x00F0 +typedef enum { + SSH2_PKTCTX_NOKEX, + SSH2_PKTCTX_DHGROUP, + SSH2_PKTCTX_DHGEX, + SSH2_PKTCTX_RSAKEX +} Pkt_KCtx; +typedef enum { + SSH2_PKTCTX_NOAUTH, + SSH2_PKTCTX_PUBLICKEY, + SSH2_PKTCTX_PASSWORD, + SSH2_PKTCTX_GSSAPI, + SSH2_PKTCTX_KBDINTER +} Pkt_ACtx; #define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 /* 0x1 */ #define SSH2_DISCONNECT_PROTOCOL_ERROR 2 /* 0x2 */ @@ -175,11 +191,12 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_DERIVEKEY 32 #define BUG_SSH2_REKEY 64 #define BUG_SSH2_PK_SESSIONID 128 +#define BUG_SSH2_MAXPKT 256 /* * Codes for terminal modes. * Most of these are the same in SSH-1 and SSH-2. - * This list is derived from draft-ietf-secsh-connect-25 and + * This list is derived from RFC 4254 and * SSH-1 RFC-1.2.31. */ static const struct { @@ -281,7 +298,8 @@ static unsigned int ssh_tty_parse_boolean(char *s) } #define translate(x) if (type == x) return #x -#define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x +#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x +#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x static char *ssh1_pkt_type(int type) { translate(SSH1_MSG_DISCONNECT); @@ -327,8 +345,14 @@ static char *ssh1_pkt_type(int type) translate(SSH1_CMSG_AUTH_CCARD_RESPONSE); return "unknown"; } -static char *ssh2_pkt_type(int pkt_ctx, int type) +static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type) { + translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI); translate(SSH2_MSG_DISCONNECT); translate(SSH2_MSG_IGNORE); translate(SSH2_MSG_UNIMPLEMENTED); @@ -337,23 +361,23 @@ static char *ssh2_pkt_type(int pkt_ctx, int type) translate(SSH2_MSG_SERVICE_ACCEPT); translate(SSH2_MSG_KEXINIT); translate(SSH2_MSG_NEWKEYS); - translatec(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP); - translatec(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP); - translatec(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX); - translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX); - translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX); - translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX); - translatec(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX); - translatec(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX); - translatec(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX); + translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP); + translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP); + translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX); + translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX); + translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX); + translatek(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX); + translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX); + translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX); + translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX); translate(SSH2_MSG_USERAUTH_REQUEST); translate(SSH2_MSG_USERAUTH_FAILURE); translate(SSH2_MSG_USERAUTH_SUCCESS); translate(SSH2_MSG_USERAUTH_BANNER); - translatec(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY); - translatec(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD); - translatec(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER); - translatec(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER); + translatea(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY); + translatea(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD); + translatea(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER); + translatea(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER); translate(SSH2_MSG_GLOBAL_REQUEST); translate(SSH2_MSG_REQUEST_SUCCESS); translate(SSH2_MSG_REQUEST_FAILURE); @@ -465,11 +489,15 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * * - OUR_V2_WINSIZE is the maximum window size we present on SSH-2 * channels. + * + * - OUR_V2_BIGWIN is the window size we advertise for the only + * channel in a simple connection. It must be <= INT_MAX. */ #define SSH1_BUFFER_LIMIT 32768 #define SSH_MAX_BACKLOG 32768 #define OUR_V2_WINSIZE 16384 +#define OUR_V2_BIGWIN 0x7fffffff #define OUR_V2_MAXPKT 0x4000UL /* Maximum length of passwords/passphrases (arbitrary) */ @@ -520,6 +548,14 @@ enum { /* channel types */ }; /* + * little structure to keep track of outstanding WINDOW_ADJUSTs + */ +struct winadj { + struct winadj *next; + unsigned size; +}; + +/* * 2-3-4 tree storing channels. */ struct ssh_channel { @@ -539,14 +575,29 @@ struct ssh_channel { * A channel is completely finished with when all four bits are set. */ int closes; + /* + * True if this channel is causing the underlying connection to be + * throttled. + */ + int throttling_conn; union { - struct ssh1_data_channel { - int throttling; - } v1; struct ssh2_data_channel { bufchain outbuffer; unsigned remwindow, remmaxpkt; - unsigned locwindow; + /* locwindow is signed so we can cope with excess data. */ + int locwindow, locmaxwin; + /* + * remlocwin is the amount of local window that we think + * the remote end had available to it after it sent the + * last data packet or window adjust ack. + */ + int remlocwin; + /* + * These store the list of window adjusts that haven't + * been acked. + */ + struct winadj *winadj_head, *winadj_tail; + enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state; } v2; } v; union { @@ -653,7 +704,7 @@ static void ssh_special(void *handle, Telnet_Special); static int ssh2_try_send(struct ssh_channel *c); static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len); static void ssh_throttle_all(Ssh ssh, int enable, int bufsize); -static void ssh2_set_window(struct ssh_channel *c, unsigned newwin); +static void ssh2_set_window(struct ssh_channel *c, int newwin); static int ssh_sendbuffer(void *handle); static int ssh_do_close(Ssh ssh, int notify_exit); static unsigned long ssh_pkt_getuint32(struct Packet *pkt); @@ -770,12 +821,13 @@ struct ssh_tag { bufchain banner; /* accumulates banners during do_ssh2_authconn */ - int pkt_ctx; + Pkt_KCtx pkt_kctx; + Pkt_ACtx pkt_actx; - void *x11auth; + struct X11Display *x11disp; int version; - int v1_throttle_count; + int conn_throttle_count; int overall_bufsize; int throttled_all; int v1_stdout_throttling; @@ -858,6 +910,11 @@ struct ssh_tag { int kex_in_progress; long next_rekey, last_rekey; char *deferred_rekey_reason; /* points to STATIC string; don't free */ + + /* + * Fully qualified host name, which we need if doing GSSAPI. + */ + char *fullhostname; }; #define logevent(s) logevent(ssh->frontend, s) @@ -1215,9 +1272,9 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen) /* "Session data" packets - omit the data field */ if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) || (st->pktin->type == SSH1_SMSG_STDERR_DATA)) { - do_blank = TRUE; blank_prefix = 0; - } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) { do_blank = TRUE; blank_prefix = 4; + } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) { + do_blank = TRUE; blank_prefix = 8; } if (do_blank) { blank.offset = blank_prefix; @@ -1230,7 +1287,7 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen) PKT_INCOMING, st->pktin->type, ssh1_pkt_type(st->pktin->type), st->pktin->body, st->pktin->length, - nblanks, &blank); + nblanks, &blank, NULL); } crFinish(st->pktin); @@ -1375,9 +1432,9 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) int do_blank = FALSE, blank_prefix = 0; /* "Session data" packets - omit the data field */ if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) { - do_blank = TRUE; blank_prefix = 4; - } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) { do_blank = TRUE; blank_prefix = 8; + } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) { + do_blank = TRUE; blank_prefix = 12; } if (do_blank) { blank.offset = blank_prefix; @@ -1387,9 +1444,10 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) } } log_packet(ssh->logctx, PKT_INCOMING, st->pktin->type, - ssh2_pkt_type(ssh->pkt_ctx, st->pktin->type), + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, + st->pktin->type), st->pktin->data+6, st->pktin->length-6, - nblanks, &blank); + nblanks, &blank, &st->pktin->sequence); } crFinish(st->pktin); @@ -1414,7 +1472,7 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12], ssh1_pkt_type(pkt->data[12]), pkt->body, pkt->length - (pkt->body - pkt->data), - pkt->nblanks, pkt->blanks); + pkt->nblanks, pkt->blanks, NULL); sfree(pkt->blanks); pkt->blanks = NULL; pkt->nblanks = 0; @@ -1453,7 +1511,9 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) static int s_write(Ssh ssh, void *data, int len) { - log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL); + if (ssh->logctx) + log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, + 0, NULL, NULL); return sk_write(ssh->s, (char *)data, len); } @@ -1734,9 +1794,9 @@ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt) if (ssh->logctx) log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5], - ssh2_pkt_type(ssh->pkt_ctx, pkt->data[5]), + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]), pkt->body, pkt->length - (pkt->body - pkt->data), - pkt->nblanks, pkt->blanks); + pkt->nblanks, pkt->blanks, &ssh->v2_outgoing_sequence); sfree(pkt->blanks); pkt->blanks = NULL; pkt->nblanks = 0; @@ -2352,6 +2412,17 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs |= BUG_SSH2_REKEY; logevent("We believe remote version has SSH-2 rekey bug"); } + + if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON || + (ssh->cfg.sshbug_maxpkt2 == AUTO && + (wc_match("1.36_sshlib GlobalSCAPE", imp) || + wc_match("1.36 sshlib: GlobalScape", imp)))) { + /* + * This version ignores our makpkt and needs to be throttled. + */ + ssh->remote_bugs |= BUG_SSH2_MAXPKT; + logevent("We believe remote version ignores SSH-2 maximum packet size"); + } } /* @@ -2377,6 +2448,47 @@ static void ssh_fix_verstring(char *str) } } +/* + * Send an appropriate SSH version string. + */ +static void ssh_send_verstring(Ssh ssh, char *svers) +{ + char *verstring; + + if (ssh->version == 2) { + /* + * Construct a v2 version string. + */ + verstring = dupprintf("SSH-2.0-%s\015\012", sshver); + } else { + /* + * Construct a v1 version string. + */ + verstring = dupprintf("SSH-%s-%s\012", + (ssh_versioncmp(svers, "1.5") <= 0 ? + svers : "1.5"), + sshver); + } + + ssh_fix_verstring(verstring); + + if (ssh->version == 2) { + size_t len; + /* + * Record our version string. + */ + len = strcspn(verstring, "\015\012"); + ssh->v_c = snewn(len + 1, char); + memcpy(ssh->v_c, verstring, len); + ssh->v_c[len] = 0; + } + + logeventf(ssh, "We claim version: %.*s", + strcspn(verstring, "\015\012"), verstring); + s_write(ssh, verstring, strlen(verstring)); + sfree(verstring); +} + static int do_ssh_init(Ssh ssh, unsigned char c) { struct do_ssh_init_state { @@ -2455,65 +2567,43 @@ static int do_ssh_init(Ssh ssh, unsigned char c) crStop(0); } - { - char *verstring; + if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) + ssh->version = 2; + else + ssh->version = 1; - if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) { - /* - * Construct a v2 version string. - */ - verstring = dupprintf("SSH-2.0-%s\015\012", sshver); - ssh->version = 2; - } else { - /* - * Construct a v1 version string. - */ - verstring = dupprintf("SSH-%s-%s\012", - (ssh_versioncmp(s->version, "1.5") <= 0 ? - s->version : "1.5"), - sshver); - ssh->version = 1; - } + logeventf(ssh, "Using SSH protocol version %d", ssh->version); - ssh_fix_verstring(verstring); + /* Send the version string, if we haven't already */ + if (ssh->cfg.sshprot != 3) + ssh_send_verstring(ssh, s->version); - if (ssh->version == 2) { - size_t len; - /* - * Hash our version string and their version string. - */ - len = strcspn(verstring, "\015\012"); - ssh->v_c = snewn(len + 1, char); - memcpy(ssh->v_c, verstring, len); - ssh->v_c[len] = 0; - len = strcspn(s->vstring, "\015\012"); - ssh->v_s = snewn(len + 1, char); - memcpy(ssh->v_s, s->vstring, len); - ssh->v_s[len] = 0; + if (ssh->version == 2) { + size_t len; + /* + * Record their version string. + */ + len = strcspn(s->vstring, "\015\012"); + ssh->v_s = snewn(len + 1, char); + memcpy(ssh->v_s, s->vstring, len); + ssh->v_s[len] = 0; - /* - * Initialise SSH-2 protocol. - */ - ssh->protocol = ssh2_protocol; - ssh2_protocol_setup(ssh); - ssh->s_rdpkt = ssh2_rdpkt; - } else { - /* - * Initialise SSH-1 protocol. - */ - ssh->protocol = ssh1_protocol; - ssh1_protocol_setup(ssh); - ssh->s_rdpkt = ssh1_rdpkt; - } - logeventf(ssh, "We claim version: %.*s", - strcspn(verstring, "\015\012"), verstring); - s_write(ssh, verstring, strlen(verstring)); - sfree(verstring); - if (ssh->version == 2) - do_ssh2_transport(ssh, NULL, -1, NULL); + /* + * Initialise SSH-2 protocol. + */ + ssh->protocol = ssh2_protocol; + ssh2_protocol_setup(ssh); + ssh->s_rdpkt = ssh2_rdpkt; + } else { + /* + * Initialise SSH-1 protocol. + */ + ssh->protocol = ssh1_protocol; + ssh1_protocol_setup(ssh); + ssh->s_rdpkt = ssh1_rdpkt; } - - logeventf(ssh, "Using SSH protocol version %d", ssh->version); + if (ssh->version == 2) + do_ssh2_transport(ssh, NULL, -1, NULL); update_specials_menu(ssh->frontend); ssh->state = SSH_STATE_BEFORE_SIZE; @@ -2573,7 +2663,9 @@ static void ssh_set_frozen(Ssh ssh, int frozen) static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen) { /* Log raw data, if we're in that mode. */ - log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL); + if (ssh->logctx) + log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, + 0, NULL, NULL); crBegin(ssh->ssh_gotdata_crstate); @@ -2766,12 +2858,30 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, SockAddr addr; const char *err; - ssh->savedhost = snewn(1 + strlen(host), char); - strcpy(ssh->savedhost, host); + if (*ssh->cfg.loghost) { + char *colon; - if (port < 0) - port = 22; /* default ssh port */ - ssh->savedport = port; + ssh->savedhost = dupstr(ssh->cfg.loghost); + ssh->savedport = 22; /* default ssh port */ + + /* + * A colon suffix on savedhost also lets us affect + * savedport. + * + * (FIXME: do something about IPv6 address literals here.) + */ + colon = strrchr(ssh->savedhost, ':'); + if (colon) { + *colon++ = '\0'; + if (*colon) + ssh->savedport = atoi(colon); + } + } else { + ssh->savedhost = dupstr(host); + if (port < 0) + port = 22; /* default ssh port */ + ssh->savedport = port; + } /* * Try to find host. @@ -2785,6 +2895,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, sk_addr_free(addr); return err; } + ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */ /* * Open socket. @@ -2798,20 +2909,39 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, return err; } + /* + * If the SSH version number's fixed, set it now, and if it's SSH-2, + * send the version string too. + */ + if (ssh->cfg.sshprot == 0) + ssh->version = 1; + if (ssh->cfg.sshprot == 3) { + ssh->version = 2; + ssh_send_verstring(ssh, NULL); + } + + /* + * loghost, if configured, overrides realhost. + */ + if (*ssh->cfg.loghost) { + sfree(*realhost); + *realhost = dupstr(ssh->cfg.loghost); + } + return NULL; } /* * Throttle or unthrottle the SSH connection. */ -static void ssh1_throttle(Ssh ssh, int adjust) +static void ssh_throttle_conn(Ssh ssh, int adjust) { - int old_count = ssh->v1_throttle_count; - ssh->v1_throttle_count += adjust; - assert(ssh->v1_throttle_count >= 0); - if (ssh->v1_throttle_count && !old_count) { + int old_count = ssh->conn_throttle_count; + ssh->conn_throttle_count += adjust; + assert(ssh->conn_throttle_count >= 0); + if (ssh->conn_throttle_count && !old_count) { ssh_set_frozen(ssh, 1); - } else if (!ssh->v1_throttle_count && old_count) { + } else if (!ssh->conn_throttle_count && old_count) { ssh_set_frozen(ssh, 0); } } @@ -2899,8 +3029,8 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen) } else { send_packet(ssh, SSH1_MSG_CHANNEL_DATA, PKT_INT, c->remoteid, - PKTT_DATA, PKT_INT, replylen, + PKTT_DATA, PKT_DATA, sentreply, replylen, PKTT_OTHER, PKT_END); @@ -3021,6 +3151,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->v1_remote_protoflags = ssh_pkt_getuint32(pktin); s->supported_ciphers_mask = ssh_pkt_getuint32(pktin); s->supported_auths_mask = ssh_pkt_getuint32(pktin); + if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) + s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA); ssh->v1_local_protoflags = ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED; @@ -3274,7 +3406,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, crWaitUntil(pktin); - if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) { + if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) { /* We must not attempt PK auth. Pretend we've already tried it. */ s->tried_publickey = s->tried_agent = 1; } else { @@ -3731,6 +3863,10 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } } if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) { + if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) { + bombout(("No supported authentication methods available")); + crStop(0); + } s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ", @@ -3960,8 +4096,7 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len) if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_CHANNEL_DATA, PKT_INT, c->remoteid, - PKTT_DATA, - PKT_INT, len, PKT_DATA, buf, len, + PKT_INT, len, PKTT_DATA, PKT_DATA, buf, len, PKTT_OTHER, PKT_END); /* * In SSH-1 we can return 0 here - implying that forwarded @@ -3980,17 +4115,20 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len) void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) { Ssh ssh = c->ssh; + int buflimit; if (ssh->state == SSH_STATE_CLOSED) return; if (ssh->version == 1) { - if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) { - c->v.v1.throttling = 0; - ssh1_throttle(ssh, -1); - } + buflimit = SSH1_BUFFER_LIMIT; } else { - ssh2_set_window(c, OUR_V2_WINSIZE - bufsize); + buflimit = c->v.v2.locmaxwin; + ssh2_set_window(c, bufsize < buflimit ? buflimit - bufsize : 0); + } + if (c->throttling_conn && bufsize <= buflimit) { + c->throttling_conn = 0; + ssh_throttle_conn(ssh, -1); } } @@ -4419,7 +4557,7 @@ static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin) string, stringlen); if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) { ssh->v1_stdout_throttling = 1; - ssh1_throttle(ssh, +1); + ssh_throttle_conn(ssh, +1); } } @@ -4440,8 +4578,8 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin) c = snew(struct ssh_channel); c->ssh = ssh; - if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c, - ssh->x11auth, NULL, -1, &ssh->cfg) != NULL) { + if (x11_init(&c->u.x11.s, ssh->x11disp, c, + NULL, -1, &ssh->cfg) != NULL) { logevent("Opening X11 forward connection failed"); sfree(c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE, @@ -4453,7 +4591,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_X11; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, @@ -4482,7 +4620,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; add234(ssh->channels, c); @@ -4536,7 +4674,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_SOCKDATA; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, @@ -4558,7 +4696,7 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) c->remoteid = localid; c->halfopen = FALSE; c->type = CHAN_SOCKDATA; - c->v.v1.throttling = 0; + c->throttling_conn = 0; pfd_confirm(c->u.pfd.s); } @@ -4694,9 +4832,9 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) bufsize = 0; /* agent channels never back up */ break; } - if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) { - c->v.v1.throttling = 1; - ssh1_throttle(ssh, +1); + if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) { + c->throttling_conn = 1; + ssh_throttle_conn(ssh, +1); } } } @@ -4776,11 +4914,9 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } if (ssh->cfg.x11_forward) { - char proto[20], data[64]; logevent("Requesting X11 forwarding"); - ssh->x11auth = x11_invent_auth(proto, sizeof(proto), - data, sizeof(data), ssh->cfg.x11_auth); - x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display); + ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, + ssh->cfg.x11_auth, &ssh->cfg); /* * Note that while we blank the X authentication data here, we don't * take any special action to blank the start of an X11 channel, @@ -4790,14 +4926,19 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, */ if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) { send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING, - PKT_STR, proto, - PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, - PKT_INT, x11_get_screen_number(ssh->cfg.x11_display), + PKT_STR, ssh->x11disp->remoteauthprotoname, + PKTT_PASSWORD, + PKT_STR, ssh->x11disp->remoteauthdatastring, + PKTT_OTHER, + PKT_INT, ssh->x11disp->screennum, PKT_END); } else { send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING, - PKT_STR, proto, - PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, PKT_END); + PKT_STR, ssh->x11disp->remoteauthprotoname, + PKTT_PASSWORD, + PKT_STR, ssh->x11disp->remoteauthdatastring, + PKTT_OTHER, + PKT_END); } do { crReturnV; @@ -4932,8 +5073,8 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } else { while (inlen > 0) { int len = min(inlen, 512); - send_packet(ssh, SSH1_CMSG_STDIN_DATA, PKTT_DATA, - PKT_INT, len, PKT_DATA, in, len, + send_packet(ssh, SSH1_CMSG_STDIN_DATA, + PKT_INT, len, PKTT_DATA, PKT_DATA, in, len, PKTT_OTHER, PKT_END); in += len; inlen -= len; @@ -5148,7 +5289,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, s->maclist = macs, s->nmacs = lenof(macs); begin_key_exchange: - ssh->pkt_ctx &= ~SSH2_PKTCTX_KEX_MASK; + ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; { int i, j, commalist_started; @@ -5597,7 +5738,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ if (!ssh->kex->pdata) { logevent("Doing Diffie-Hellman group exchange"); - ssh->pkt_ctx |= SSH2_PKTCTX_DHGEX; + ssh->pkt_kctx = SSH2_PKTCTX_DHGEX; /* * Work out how big a DH group we will need to allow that * much data. @@ -5622,7 +5763,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT; s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY; } else { - ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP; + ssh->pkt_kctx = SSH2_PKTCTX_DHGROUP; ssh->kex_ctx = dh_setup_group(ssh->kex); s->kex_init_value = SSH2_MSG_KEXDH_INIT; s->kex_reply_value = SSH2_MSG_KEXDH_REPLY; @@ -5681,7 +5822,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } else { logeventf(ssh, "Doing RSA key exchange with hash %s", ssh->kex->hash->text_name); - ssh->pkt_ctx |= SSH2_PKTCTX_RSAKEX; + ssh->pkt_kctx = SSH2_PKTCTX_RSAKEX; /* * RSA key exchange. First expect a KEXRSA_PUBKEY packet * from the server. @@ -5756,7 +5897,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET); ssh2_pkt_addstring_start(s->pktout); - ssh2_pkt_addstring_data(s->pktout, outstr, outstrlen); + ssh2_pkt_addstring_data(s->pktout, (char *)outstr, outstrlen); ssh2_pkt_send_noqueue(ssh, s->pktout); hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen); @@ -6083,8 +6224,8 @@ static int ssh2_try_send(struct ssh_channel *c) len = c->v.v2.remmaxpkt; pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA); ssh2_pkt_adduint32(pktout, c->remoteid); - dont_log_data(ssh, pktout, PKTLOG_OMIT); ssh2_pkt_addstring_start(pktout); + dont_log_data(ssh, pktout, PKTLOG_OMIT); ssh2_pkt_addstring_data(pktout, data, len); end_log_omission(ssh, pktout); ssh2_pkt_send(ssh, pktout); @@ -6126,9 +6267,25 @@ static void ssh2_try_send_and_unthrottle(struct ssh_channel *c) } /* + * Set up most of a new ssh_channel for SSH-2. + */ +static void ssh2_channel_init(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + c->localid = alloc_channel_id(ssh); + c->closes = 0; + c->throttling_conn = FALSE; + c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin = + ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL; + c->v.v2.throttle_state = UNTHROTTLED; + bufchain_init(&c->v.v2.outbuffer); +} + +/* * Potentially enlarge the window on an SSH-2 channel. */ -static void ssh2_set_window(struct ssh_channel *c, unsigned newwin) +static void ssh2_set_window(struct ssh_channel *c, int newwin) { Ssh ssh = c->ssh; @@ -6141,15 +6298,68 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin) return; /* + * If the remote end has a habit of ignoring maxpkt, limit the + * window so that it has no choice (assuming it doesn't ignore the + * window as well). + */ + if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT) + newwin = OUR_V2_MAXPKT; + + + /* * Only send a WINDOW_ADJUST if there's significantly more window * available than the other end thinks there is. This saves us * sending a WINDOW_ADJUST for every character in a shell session. * * "Significant" is arbitrarily defined as half the window size. */ - if (newwin > c->v.v2.locwindow * 2) { + if (newwin / 2 >= c->v.v2.locwindow) { struct Packet *pktout; + struct winadj *wa; + /* + * In order to keep track of how much window the client + * actually has available, we'd like it to acknowledge each + * WINDOW_ADJUST. We can't do that directly, so we accompany + * it with a CHANNEL_REQUEST that has to be acknowledged. + * + * This is only necessary if we're opening the window wide. + * If we're not, then throughput is being constrained by + * something other than the maximum window size anyway. + * + * We also only send this if the main channel has finished its + * initial CHANNEL_REQUESTs and installed the default + * CHANNEL_FAILURE handler, so as not to risk giving it + * unexpected CHANNEL_FAILUREs. + */ + if (newwin == c->v.v2.locmaxwin && + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) { + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org"); + ssh2_pkt_addbool(pktout, TRUE); + ssh2_pkt_send(ssh, pktout); + + /* + * CHANNEL_FAILURE doesn't come with any indication of + * what message caused it, so we have to keep track of the + * outstanding CHANNEL_REQUESTs ourselves. + */ + wa = snew(struct winadj); + wa->size = newwin - c->v.v2.locwindow; + wa->next = NULL; + if (!c->v.v2.winadj_head) + c->v.v2.winadj_head = wa; + else + c->v.v2.winadj_tail->next = wa; + c->v.v2.winadj_tail = wa; + if (c->v.v2.throttle_state != UNTHROTTLED) + c->v.v2.throttle_state = UNTHROTTLING; + } else { + /* Pretend the WINDOW_ADJUST was acked immediately. */ + c->v.v2.remlocwin = newwin; + c->v.v2.throttle_state = THROTTLED; + } pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow); @@ -6158,12 +6368,94 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin) } } +/* + * Find the channel associated with a message. If there's no channel, + * or it's not properly open, make a noise about it and return NULL. + */ +static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin) +{ + unsigned localid = ssh_pkt_getuint32(pktin); + struct ssh_channel *c; + + c = find234(ssh->channels, &localid, ssh_channelfind); + if (!c || + (c->halfopen && pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION && + pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) { + char *buf = dupprintf("Received %s for %s channel %u", + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, + pktin->type), + c ? "half-open" : "nonexistent", localid); + ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + sfree(buf); + return NULL; + } + return c; +} + +static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin) +{ + /* + * This should never get called. All channel requests are either + * sent with want_reply false or are sent before this handler gets + * installed. + */ + struct ssh_channel *c; + struct winadj *wa; + + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + wa = c->v.v2.winadj_head; + if (wa) + ssh_disconnect(ssh, NULL, "Received SSH_MSG_CHANNEL_SUCCESS for " + "\"winadj@putty.projects.tartarus.org\"", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + else + ssh_disconnect(ssh, NULL, + "Received unsolicited SSH_MSG_CHANNEL_SUCCESS", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); +} + +static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) +{ + /* + * The only time this should get called is for "winadj@putty" + * messages sent above. All other channel requests are either + * sent with want_reply false or are sent before this handler gets + * installed. + */ + struct ssh_channel *c; + struct winadj *wa; + + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + wa = c->v.v2.winadj_head; + if (!wa) { + ssh_disconnect(ssh, NULL, + "Received unsolicited SSH_MSG_CHANNEL_FAILURE", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + return; + } + c->v.v2.winadj_head = wa->next; + c->v.v2.remlocwin += wa->size; + sfree(wa); + /* + * winadj messages are only sent when the window is fully open, so + * if we get an ack of one, we know any pending unthrottle is + * complete. + */ + if (c->v.v2.throttle_state == UNTHROTTLING) + c->v.v2.throttle_state = UNTHROTTLED; +} + static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); - if (c && !c->closes) { + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + if (!c->closes) { c->v.v2.remwindow += ssh_pkt_getuint32(pktin); ssh2_try_send_and_unthrottle(c); } @@ -6173,11 +6465,10 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) { char *data; int length; - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA && ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR) return; /* extended but not stderr */ @@ -6185,6 +6476,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) if (data) { int bufsize = 0; c->v.v2.locwindow -= length; + c->v.v2.remlocwin -= length; switch (c->type) { case CHAN_MAINSESSION: bufsize = @@ -6242,22 +6534,43 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) break; } /* + * If it looks like the remote end hit the end of its window, + * and we didn't want it to do that, think about using a + * larger window. + */ + if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED && + c->v.v2.locmaxwin < 0x40000000) + c->v.v2.locmaxwin += OUR_V2_WINSIZE; + /* * If we are not buffering too much data, * enlarge the window again at the remote side. + * If we are buffering too much, we may still + * need to adjust the window if the server's + * sent excess data. + */ + ssh2_set_window(c, bufsize < c->v.v2.locmaxwin ? + c->v.v2.locmaxwin - bufsize : 0); + /* + * If we're either buffering way too much data, or if we're + * buffering anything at all and we're in "simple" mode, + * throttle the whole channel. */ - if (bufsize < OUR_V2_WINSIZE) - ssh2_set_window(c, OUR_V2_WINSIZE - bufsize); + if ((bufsize > c->v.v2.locmaxwin || + (ssh->cfg.ssh_simple && bufsize > 0)) && + !c->throttling_conn) { + c->throttling_conn = 1; + ssh_throttle_conn(ssh, +1); + } } } static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type == CHAN_X11) { /* @@ -6276,16 +6589,12 @@ static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct Packet *pktout; - c = find234(ssh->channels, &i, ssh_channelfind); - if (!c || c->halfopen) { - bombout(("Received CHANNEL_CLOSE for %s channel %d\n", - c ? "half-open" : "nonexistent", i)); + c = ssh2_channel_msg(ssh, pktin); + if (!c) return; - } /* Do pre-close processing on the channel. */ switch (c->type) { case CHAN_MAINSESSION: @@ -6338,13 +6647,12 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct Packet *pktout; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type != CHAN_SOCKDATA_DORMANT) return; /* dunno why they're confirming this */ c->remoteid = ssh_pkt_getuint32(pktin); @@ -6376,14 +6684,13 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) "Unknown channel type", "Resource shortage", }; - unsigned i = ssh_pkt_getuint32(pktin); unsigned reason_code; char *reason_string; int reason_length; struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type != CHAN_SOCKDATA_DORMANT) return; /* dunno why they're failing this */ @@ -6402,31 +6709,19 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) { - unsigned localid; char *type; int typelen, want_reply; int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */ struct ssh_channel *c; struct Packet *pktout; - localid = ssh_pkt_getuint32(pktin); + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; ssh_pkt_getstring(pktin, &type, &typelen); want_reply = ssh2_pkt_getbool(pktin); /* - * First, check that the channel exists. Otherwise, - * we can instantly disconnect with a rude message. - */ - c = find234(ssh->channels, &localid, ssh_channelfind); - if (!c) { - char *buf = dupprintf("Received channel request for nonexistent" - " channel %d", localid); - ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); - sfree(buf); - return; - } - - /* * Having got the channel number, we now look at * the request type string to see if it's something * we recognise. @@ -6453,7 +6748,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) int msglen = 0, core = FALSE; /* ICK: older versions of OpenSSH (e.g. 3.4p1) * provide an `int' for the signal, despite its - * having been a `string' in the drafts since at + * having been a `string' in the drafts of RFC 4254 since at * least 2001. (Fixed in session.c 1.147.) Try to * infer which we can safely parse it as. */ { @@ -6496,7 +6791,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) fmt_sig = dupprintf(" %d", signum); ssh->exitcode = 128 + signum; } else { - /* As per the drafts. */ + /* As per RFC 4254. */ char *sig; int siglen; ssh_pkt_getstring(pktin, &sig, &siglen); @@ -6647,9 +6942,8 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) if (!ssh->X11_fwd_enabled) error = "X11 forwarding is not enabled"; - else if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c, - ssh->x11auth, addrstr, peerport, - &ssh->cfg) != NULL) { + else if (x11_init(&c->u.x11.s, ssh->x11disp, c, + addrstr, peerport, &ssh->cfg) != NULL) { error = "Unable to open an X11 connection"; } else { logevent("Opening X11 forward connection succeeded"); @@ -6711,12 +7005,9 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) logeventf(ssh, "Rejected channel open: %s", error); sfree(c); } else { - c->localid = alloc_channel_id(ssh); - c->closes = 0; - c->v.v2.locwindow = OUR_V2_WINSIZE; + ssh2_channel_init(c); c->v.v2.remwindow = winsize; c->v.v2.remmaxpkt = pktsize; - bufchain_init(&c->v.v2.outbuffer); add234(ssh->channels, c); pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); ssh2_pkt_adduint32(pktout, c->remoteid); @@ -6775,12 +7066,15 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, AUTH_TYPE_PUBLICKEY_OFFER_LOUD, AUTH_TYPE_PUBLICKEY_OFFER_QUIET, AUTH_TYPE_PASSWORD, + AUTH_TYPE_GSSAPI, AUTH_TYPE_KEYBOARD_INTERACTIVE, AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET } type; int done_service_req; int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter; int tried_pubkey_config, done_agent; + int can_gssapi; + int tried_gssapi; int kbd_inter_refused; int we_are_in; prompts_t *cur_prompt; @@ -6804,6 +7098,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int try_send; int num_env, env_left, env_ok; struct Packet *pktout; + Ssh_gss_ctx gss_ctx; + Ssh_gss_buf gss_buf; + Ssh_gss_buf gss_rcvtok, gss_sndtok; + Ssh_gss_name gss_srv_name; + Ssh_gss_stat gss_stat; }; crState(do_ssh2_authconn_state); @@ -6811,6 +7110,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->done_service_req = FALSE; s->we_are_in = FALSE; + s->tried_gssapi = FALSE; + if (!ssh->cfg.ssh_no_userauth) { /* * Request userauth protocol, and await a response to it. @@ -7037,7 +7338,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * just in case it succeeds, and (b) so that we know what * authentication methods we can usefully try next. */ - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; + ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, s->username); @@ -7098,7 +7399,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, break; } - if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) { + if (pktin->type != SSH2_MSG_USERAUTH_FAILURE && s->type != AUTH_TYPE_GSSAPI) { bombout(("Strange packet received during authentication: " "type %d", pktin->type)); crStopV; @@ -7169,9 +7470,14 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, in_commasep_string("password", methods, methlen); s->can_keyb_inter = ssh->cfg.try_ki_auth && in_commasep_string("keyboard-interactive", methods, methlen); +#ifndef NO_GSSAPI + s->can_gssapi = ssh->cfg.try_gssapi_auth && + in_commasep_string("gssapi-with-mic", methods, methlen) && + ssh_gss_init(); +#endif } - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; + ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; if (s->can_pubkey && !s->done_agent && s->nkeys) { @@ -7179,8 +7485,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Attempt public-key authentication using a key from Pageant. */ - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; - ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY; + ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY; logeventf(ssh, "Trying Pageant key #%d", s->keyi); @@ -7327,8 +7632,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct ssh2_userkey *key; /* not live over crReturn */ char *passphrase; /* not live over crReturn */ - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; - ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY; + ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY; s->tried_pubkey_config = TRUE; @@ -7499,6 +7803,157 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, key->alg->freekey(key->data); } +#ifndef NO_GSSAPI + } else if (s->can_gssapi && !s->tried_gssapi) { + + /* GSSAPI Authentication */ + + int micoffset; + Ssh_gss_buf mic; + s->type = AUTH_TYPE_GSSAPI; + s->tried_gssapi = TRUE; + s->gotit = TRUE; + ssh->pkt_actx = SSH2_PKTCTX_GSSAPI; + + /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */ + s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); + ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, "ssh-connection"); + ssh2_pkt_addstring(s->pktout, "gssapi-with-mic"); + + /* add mechanism info */ + ssh_gss_indicate_mech(&s->gss_buf); + + /* number of GSSAPI mechanisms */ + ssh2_pkt_adduint32(s->pktout,1); + + /* length of OID + 2 */ + ssh2_pkt_adduint32(s->pktout, s->gss_buf.len + 2); + ssh2_pkt_addbyte(s->pktout, SSH2_GSS_OIDTYPE); + + /* length of OID */ + ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.len); + + ssh_pkt_adddata(s->pktout, s->gss_buf.data, s->gss_buf.len); + ssh2_pkt_send(ssh, s->pktout); + crWaitUntilV(pktin); + if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) { + logevent("GSSAPI authentication request refused"); + continue; + } + + /* check returned packet ... */ + + ssh_pkt_getstring(pktin,&s->gss_rcvtok.data,&s->gss_rcvtok.len); + if (s->gss_rcvtok.len != s->gss_buf.len + 2 || + s->gss_rcvtok.data[0] != SSH2_GSS_OIDTYPE || + s->gss_rcvtok.data[1] != s->gss_buf.len || + memcmp(s->gss_rcvtok.data+2,s->gss_buf.data,s->gss_buf.len) ) { + logevent("GSSAPI authentication - wrong response from server"); + continue; + } + + /* now start running */ + s->gss_stat = ssh_gss_import_name(ssh->fullhostname, + &s->gss_srv_name); + if (s->gss_stat != SSH_GSS_OK) { + if (s->gss_stat == SSH_GSS_BAD_HOST_NAME) + logevent("GSSAPI import name failed - Bad service name"); + else + logevent("GSSAPI import name failed"); + continue; + } + + /* fetch TGT into GSS engine */ + s->gss_stat = ssh_gss_acquire_cred(&s->gss_ctx); + + if (s->gss_stat != SSH_GSS_OK) { + logevent("GSSAPI authentication failed to get credentials"); + ssh_gss_release_name(&s->gss_srv_name); + continue; + } + + /* initial tokens are empty */ + s->gss_rcvtok.len = s->gss_sndtok.len = 0; + s->gss_rcvtok.data = s->gss_sndtok.data = NULL; + + /* now enter the loop */ + do { + s->gss_stat = ssh_gss_init_sec_context(&s->gss_ctx, + s->gss_srv_name, + ssh->cfg.gssapifwd, + &s->gss_rcvtok, + &s->gss_sndtok); + + if (s->gss_stat!=SSH_GSS_S_COMPLETE && + s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) { + logevent("GSSAPI authentication initialisation failed"); + + if (ssh_gss_display_status(s->gss_ctx,&s->gss_buf) == SSH_GSS_OK) { + logevent(s->gss_buf.data); + sfree(s->gss_buf.data); + } + + break; + } + logevent("GSSAPI authentication initialised"); + + /* Client and server now exchange tokens until GSSAPI + * no longer says CONTINUE_NEEDED */ + + if (s->gss_sndtok.len != 0) { + s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + ssh_pkt_addstring_start(s->pktout); + ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.data,s->gss_sndtok.len); + ssh2_pkt_send(ssh, s->pktout); + ssh_gss_free_tok(&s->gss_sndtok); + } + + if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) { + crWaitUntilV(pktin); + if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_TOKEN) { + logevent("GSSAPI authentication - bad server response"); + s->gss_stat = SSH_GSS_FAILURE; + break; + } + ssh_pkt_getstring(pktin,&s->gss_rcvtok.data,&s->gss_rcvtok.len); + } + } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED); + + if (s->gss_stat != SSH_GSS_OK) { + ssh_gss_release_name(&s->gss_srv_name); + ssh_gss_release_cred(&s->gss_ctx); + continue; + } + logevent("GSSAPI authentication loop finished OK"); + + /* Now send the MIC */ + + s->pktout = ssh2_pkt_init(0); + micoffset = s->pktout->length; + ssh_pkt_addstring_start(s->pktout); + ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len); + ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST); + ssh_pkt_addstring(s->pktout, s->username); + ssh_pkt_addstring(s->pktout, "ssh-connection"); + ssh_pkt_addstring(s->pktout, "gssapi-with-mic"); + + s->gss_buf.data = (char *)s->pktout->data + micoffset; + s->gss_buf.len = s->pktout->length - micoffset; + + ssh_gss_get_mic(s->gss_ctx, &s->gss_buf, &mic); + s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC); + ssh_pkt_addstring_start(s->pktout); + ssh_pkt_addstring_data(s->pktout, mic.data, mic.len); + ssh2_pkt_send(ssh, s->pktout); + ssh_gss_free_mic(&mic); + + s->gotit = FALSE; + + ssh_gss_release_name(&s->gss_srv_name); + ssh_gss_release_cred(&s->gss_ctx); + continue; +#endif } else if (s->can_keyb_inter && !s->kbd_inter_refused) { /* @@ -7507,8 +7962,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE; - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; - ssh->pkt_ctx |= SSH2_PKTCTX_KBDINTER; + ssh->pkt_actx = SSH2_PKTCTX_KBDINTER; s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, s->username); @@ -7571,7 +8025,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt->instr_reqd = TRUE; /* - * Get the prompts from the packet. + * Get any prompt(s) from the packet. */ s->num_prompts = ssh_pkt_getuint32(pktin); for (i = 0; i < s->num_prompts; i++) { @@ -7593,9 +8047,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } /* - * Get the user's responses. + * Display any instructions, and get the user's + * response(s). */ - if (s->num_prompts) { + { int ret; /* not live over crReturn */ ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { @@ -7617,7 +8072,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } /* - * Send the responses to the server. + * Send the response(s) to the server. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE); ssh2_pkt_adduint32(s->pktout, s->num_prompts); @@ -7650,8 +8105,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ret; /* not live over crReturn */ int changereq_first_time; /* not live over crReturn */ - ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK; - ssh->pkt_ctx |= SSH2_PKTCTX_PASSWORD; + ssh->pkt_actx = SSH2_PKTCTX_PASSWORD; s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; @@ -7921,14 +8375,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; - ssh->mainchan->localid = alloc_channel_id(ssh); + ssh2_channel_init(ssh->mainchan); logeventf(ssh, "Opening direct-tcpip channel to %s:%d in place of session", ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "direct-tcpip"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host); @@ -7955,10 +8408,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); ssh->mainchan->halfopen = FALSE; ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->closes = 0; ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - bufchain_init(&ssh->mainchan->v.v2.outbuffer); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened direct-tcpip channel"); @@ -7966,11 +8417,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } else { ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; - ssh->mainchan->localid = alloc_channel_id(ssh); + ssh2_channel_init(ssh->mainchan); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "session"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_send(ssh, s->pktout); @@ -7987,10 +8437,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); ssh->mainchan->halfopen = FALSE; ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->closes = 0; ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - bufchain_init(&ssh->mainchan->v.v2.outbuffer); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened channel for session"); @@ -8015,21 +8463,33 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_channel_open; + if (ssh->cfg.ssh_simple) { + /* + * This message indicates to the server that we promise + * not to try to run any other channel in parallel with + * this one, so it's safe for it to advertise a very large + * window and leave the flow control to TCP. + */ + s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); + ssh2_pkt_addstring(s->pktout, "simple@putty.projects.tartarus.org"); + ssh2_pkt_addbool(s->pktout, 0); /* no reply */ + ssh2_pkt_send(ssh, s->pktout); + } + /* * Potentially enable X11 forwarding. */ if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) { - char proto[20], data[64]; logevent("Requesting X11 forwarding"); - ssh->x11auth = x11_invent_auth(proto, sizeof(proto), - data, sizeof(data), ssh->cfg.x11_auth); - x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display); + ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, + ssh->cfg.x11_auth, &ssh->cfg); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); ssh2_pkt_addstring(s->pktout, "x11-req"); ssh2_pkt_addbool(s->pktout, 1); /* want reply */ ssh2_pkt_addbool(s->pktout, 0); /* many connections */ - ssh2_pkt_addstring(s->pktout, proto); + ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthprotoname); /* * Note that while we blank the X authentication data here, we don't * take any special action to blank the start of an X11 channel, @@ -8038,9 +8498,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * cookie into the log. */ dont_log_password(ssh, s->pktout, PKTLOG_BLANK); - ssh2_pkt_addstring(s->pktout, data); + ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthdatastring); end_log_omission(ssh, s->pktout); - ssh2_pkt_adduint32(s->pktout, x11_get_screen_number(ssh->cfg.x11_display)); + ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum); ssh2_pkt_send(ssh, s->pktout); crWaitUntilV(pktin); @@ -8270,6 +8730,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_special(ssh, TS_EOF); /* + * All the initial channel requests are done, so install the default + * failure handler. + */ + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_success; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure; + + /* * Transfer data! */ if (ssh->ldisc) @@ -8317,7 +8784,7 @@ static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin) { /* log reason code in disconnect message */ char *buf, *msg; - int nowlen, reason, msglen; + int reason, msglen; reason = ssh_pkt_getuint32(pktin); ssh_pkt_getstring(pktin, &msg, &msglen); @@ -8331,14 +8798,14 @@ static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin) } logevent(buf); sfree(buf); - buf = dupprintf("Disconnection message text: %n%.*s", - &nowlen, msglen, msg); + buf = dupprintf("Disconnection message text: %.*s", + msglen, msg); logevent(buf); - bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"", + bombout(("Server sent disconnect message\ntype %d (%s):\n\"%.*s\"", reason, (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ? ssh2_disconnect_reasons[reason] : "unknown", - buf+nowlen)); + msglen, msg)); sfree(buf); } @@ -8522,8 +8989,9 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->deferred_len = 0; ssh->deferred_size = 0; ssh->fallback_cmd = 0; - ssh->pkt_ctx = 0; - ssh->x11auth = NULL; + ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; + ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; + ssh->x11disp = NULL; ssh->v1_compressing = FALSE; ssh->v2_outgoing_sequence = 0; ssh->ssh1_rdpkt_crstate = 0; @@ -8569,7 +9037,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->send_ok = 0; ssh->editing = 0; ssh->echoing = 0; - ssh->v1_throttle_count = 0; + ssh->conn_throttle_count = 0; ssh->overall_bufsize = 0; ssh->fallback_cmd = 0; @@ -8661,14 +9129,15 @@ static void ssh_free(void *handle) ssh->rportfwds = NULL; } sfree(ssh->deferred_send_data); - if (ssh->x11auth) - x11_free_auth(ssh->x11auth); + if (ssh->x11disp) + x11_free_display(ssh->x11disp); sfree(ssh->do_ssh_init_state); sfree(ssh->do_ssh1_login_state); sfree(ssh->do_ssh2_transport_state); sfree(ssh->do_ssh2_authconn_state); sfree(ssh->v_c); sfree(ssh->v_s); + sfree(ssh->fullhostname); if (ssh->crcda_ctx) { crcda_free_context(ssh->crcda_ctx); ssh->crcda_ctx = NULL; @@ -8846,7 +9315,7 @@ static const struct telnet_special *ssh_get_specials(void *handle) static const struct telnet_special ssh2_session_specials[] = { {NULL, TS_SEP}, {"Break", TS_BRK}, - /* These are the signal names defined by draft-ietf-secsh-connect-23. + /* These are the signal names defined by RFC 4254. * They include all the ISO C signals, but are a subset of the POSIX * required signals. */ {"SIGINT (Interrupt)", TS_SIGINT}, @@ -8995,17 +9464,13 @@ void *new_sock_channel(void *handle, Socket s) Ssh ssh = (Ssh) handle; struct ssh_channel *c; c = snew(struct ssh_channel); - c->ssh = ssh; - if (c) { - c->halfopen = TRUE; - c->localid = alloc_channel_id(ssh); - c->closes = 0; - c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ - c->u.pfd.s = s; - bufchain_init(&c->v.v2.outbuffer); - add234(ssh->channels, c); - } + c->ssh = ssh; + ssh2_channel_init(c); + c->halfopen = TRUE; + c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ + c->u.pfd.s = s; + add234(ssh->channels, c); return c; } @@ -9016,13 +9481,27 @@ void *new_sock_channel(void *handle, Socket s) static void ssh_unthrottle(void *handle, int bufsize) { Ssh ssh = (Ssh) handle; + int buflimit; + if (ssh->version == 1) { if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) { ssh->v1_stdout_throttling = 0; - ssh1_throttle(ssh, -1); + ssh_throttle_conn(ssh, -1); } } else { - ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize); + if (ssh->mainchan) { + ssh2_set_window(ssh->mainchan, + bufsize < ssh->mainchan->v.v2.locmaxwin ? + ssh->mainchan->v.v2.locmaxwin - bufsize : 0); + if (ssh->cfg.ssh_simple) + buflimit = 0; + else + buflimit = ssh->mainchan->v.v2.locmaxwin; + if (ssh->mainchan->throttling_conn && bufsize <= buflimit) { + ssh->mainchan->throttling_conn = 0; + ssh_throttle_conn(ssh, -1); + } + } } } @@ -9045,7 +9524,6 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(pktout, "direct-tcpip"); ssh2_pkt_adduint32(pktout, c->localid); - c->v.v2.locwindow = OUR_V2_WINSIZE; ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(pktout, hostname); @@ -9146,5 +9624,7 @@ Backend ssh_backend = { ssh_provide_logctx, ssh_unthrottle, ssh_cfg_info, + "ssh", + PROT_SSH, 22 };