X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/9916cc1e8d26f1caef36fc1ee525819e6ca62f42..63f305ff4329c3d56ad2d5b22dbf20873fd6f627:/ssh.c diff --git a/ssh.c b/ssh.c index 6a882b79..401706c4 100644 --- a/ssh.c +++ b/ssh.c @@ -12,7 +12,10 @@ #include "putty.h" #include "tree234.h" #include "ssh.h" +#ifndef NO_GSSAPI +#include "sshgssc.h" #include "sshgss.h" +#endif #ifndef FALSE #define FALSE 0 @@ -192,6 +195,7 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_REKEY 64 #define BUG_SSH2_PK_SESSIONID 128 #define BUG_SSH2_MAXPKT 256 +#define BUG_CHOKES_ON_SSH2_IGNORE 512 /* * Codes for terminal modes. @@ -586,6 +590,17 @@ struct ssh_channel { * A channel is completely finished with when all four bits are set. */ int closes; + + /* + * This flag indicates that a close is pending on the outgoing + * side of the channel: that is, wherever we're getting the data + * for this channel has sent us some data followed by EOF. We + * can't actually close the channel until we've finished sending + * the data, so we set this flag instead to remind us to + * initiate the closing process once our buffer is clear. + */ + int pending_close; + /* * True if this channel is causing the underlying connection to be * throttled. @@ -2009,7 +2024,8 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore) { int len; if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) && - ssh->deferred_len == 0 && !noignore) { + ssh->deferred_len == 0 && !noignore && + !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { /* * Interpose an SSH_MSG_IGNORE to ensure that user data don't * get encrypted with a known IV. @@ -2139,7 +2155,8 @@ static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt, * unavailable, we don't do this trick at all, because we * gain nothing by it.) */ - if (ssh->cscipher) { + if (ssh->cscipher && + !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { int stringlen, i; stringlen = (256 - ssh->deferred_len); @@ -2506,6 +2523,15 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs |= BUG_SSH2_MAXPKT; logevent("We believe remote version ignores SSH-2 maximum packet size"); } + + if (ssh->cfg.sshbug_ignore2 == FORCE_ON) { + /* + * Servers that don't support SSH2_MSG_IGNORE. Currently, + * none detected automatically. + */ + ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE; + logevent("We believe remote version has SSH-2 ignore bug"); + } } /* @@ -2851,6 +2877,8 @@ static int ssh_do_close(Ssh ssh, int notify_exit) del234(ssh->portfwds, pf); /* moving next one to index 0 */ free_portfwd(pf); } + freetree234(ssh->portfwds); + ssh->portfwds = NULL; } return ret; @@ -4136,7 +4164,7 @@ void sshfwd_close(struct ssh_channel *c) if (ssh->state == SSH_STATE_CLOSED) return; - if (c && !c->closes) { + if (!c->closes) { /* * If halfopen is true, we have sent * CHANNEL_OPEN for this channel, but it hasn't even been @@ -4148,14 +4176,42 @@ void sshfwd_close(struct ssh_channel *c) if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, PKT_END); + c->closes = 1; /* sent MSG_CLOSE */ } else { - struct Packet *pktout; - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(pktout, c->remoteid); - ssh2_pkt_send(ssh, pktout); + int bytes_to_send = bufchain_size(&c->v.v2.outbuffer); + if (bytes_to_send > 0) { + /* + * If we still have unsent data in our outgoing + * buffer for this channel, we can't actually + * initiate a close operation yet or that data + * will be lost. Instead, set the pending_close + * flag so that when we do clear the buffer + * we'll start closing the channel. + */ + char logmsg[160] = {'\0'}; + sprintf( + logmsg, + "Forwarded port pending to be closed : " + "%d bytes remaining", + bytes_to_send); + logevent(logmsg); + + c->pending_close = TRUE; + } else { + /* + * No locally buffered data, so we can send the + * close message immediately. + */ + struct Packet *pktout; + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_send(ssh, pktout); + c->closes = 1; /* sent MSG_CLOSE */ + logevent("Nothing left to send, closing channel"); + } } } - c->closes = 1; /* sent MSG_CLOSE */ + if (c->type == CHAN_X11) { c->u.x11.s = NULL; logevent("Forwarded X11 connection terminated"); @@ -4294,6 +4350,7 @@ static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx) rpf = del234(ssh->rportfwds, pf); assert(rpf == pf); + pf->pfrec->remote = NULL; free_rportfwd(pf); } } @@ -4425,12 +4482,19 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) epfrec = add234(ssh->portfwds, pfrec); if (epfrec != pfrec) { + if (epfrec->status == DESTROY) { + /* + * We already have a port forwarding up and running + * with precisely these parameters. Hence, no need + * to do anything; simply re-tag the existing one + * as KEEP. + */ + epfrec->status = KEEP; + } /* - * We already have a port forwarding with precisely - * these parameters. Hence, no need to do anything; - * simply tag the existing one as KEEP. + * Anything else indicates that there was a duplicate + * in our input, which we'll silently ignore. */ - epfrec->status = KEEP; free_portfwd(pfrec); } else { pfrec->status = CREATE; @@ -4463,6 +4527,8 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) logeventf(ssh, "Cancelling %s", message); sfree(message); + /* epf->remote or epf->local may be NULL if setting up a + * forwarding failed. */ if (epf->remote) { struct ssh_rportfwd *rpf = epf->remote; struct Packet *pktout; @@ -4672,6 +4738,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->pending_close = FALSE; c->throttling_conn = 0; c->type = CHAN_X11; /* identify channel type */ add234(ssh->channels, c); @@ -4701,6 +4768,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->pending_close = FALSE; c->throttling_conn = 0; c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; @@ -4755,6 +4823,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->pending_close = FALSE; c->throttling_conn = 0; c->type = CHAN_SOCKDATA; /* identify channel type */ add234(ssh->channels, c); @@ -4994,10 +5063,10 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } } - if (ssh->cfg.x11_forward) { + if (ssh->cfg.x11_forward && + (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, + ssh->cfg.x11_auth, &ssh->cfg))) { logevent("Requesting X11 forwarding"); - 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, @@ -6321,7 +6390,7 @@ static int ssh2_try_send(struct ssh_channel *c) return bufchain_size(&c->v.v2.outbuffer); } -static void ssh2_try_send_and_unthrottle(struct ssh_channel *c) +static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c) { int bufsize; if (c->closes) @@ -6345,6 +6414,19 @@ static void ssh2_try_send_and_unthrottle(struct ssh_channel *c) break; } } + + /* + * If we've emptied the channel's output buffer and there's a + * pending close event, start the channel-closing procedure. + */ + if (c->pending_close && bufchain_size(&c->v.v2.outbuffer) == 0) { + struct Packet *pktout; + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_send(ssh, pktout); + c->closes = 1; + c->pending_close = FALSE; + } } /* @@ -6355,6 +6437,7 @@ static void ssh2_channel_init(struct ssh_channel *c) Ssh ssh = c->ssh; c->localid = alloc_channel_id(ssh); c->closes = 0; + c->pending_close = FALSE; 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; @@ -6538,7 +6621,7 @@ static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) return; if (!c->closes) { c->v.v2.remwindow += ssh_pkt_getuint32(pktin); - ssh2_try_send_and_unthrottle(c); + ssh2_try_send_and_unthrottle(ssh, c); } } @@ -6659,11 +6742,13 @@ static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) * wrap up and close the channel ourselves. */ x11_close(c->u.x11.s); + c->u.x11.s = NULL; sshfwd_close(c); } else if (c->type == CHAN_AGENT) { sshfwd_close(c); } else if (c->type == CHAN_SOCKDATA) { pfd_close(c->u.pfd.s); + c->u.pfd.s = NULL; sshfwd_close(c); } } @@ -7011,6 +7096,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) if (typelen == 3 && !memcmp(type, "x11", 3)) { char *addrstr; + const char *x11err; ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen); addrstr = snewn(peeraddrlen+1, char); @@ -7023,8 +7109,9 @@ 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->x11disp, c, - addrstr, peerport, &ssh->cfg) != NULL) { + else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c, + addrstr, peerport, &ssh->cfg)) != NULL) { + logeventf(ssh, "Local X11 connection failed: %s", x11err); error = "Unable to open an X11 connection"; } else { logevent("Opening X11 forward connection succeeded"); @@ -7154,8 +7241,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int done_service_req; int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter; int tried_pubkey_config, done_agent; +#ifndef NO_GSSAPI int can_gssapi; int tried_gssapi; +#endif int kbd_inter_refused; int we_are_in; prompts_t *cur_prompt; @@ -7179,11 +7268,14 @@ 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; +#ifndef NO_GSSAPI + struct ssh_gss_library *gsslib; 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; +#endif }; crState(do_ssh2_authconn_state); @@ -7191,7 +7283,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->done_service_req = FALSE; s->we_are_in = FALSE; +#ifndef NO_GSSAPI s->tried_gssapi = FALSE; +#endif if (!ssh->cfg.ssh_no_userauth) { /* @@ -7444,6 +7538,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } while (1) { + char *methods = NULL; + int methlen = 0; + /* * Wait for the result of the last authentication request. */ @@ -7493,8 +7590,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * helpfully try next. */ if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) { - char *methods; - int methlen; ssh_pkt_getstring(pktin, &methods, &methlen); if (!ssh2_pkt_getbool(pktin)) { /* @@ -7551,9 +7646,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->can_keyb_inter = ssh->cfg.try_ki_auth && in_commasep_string("keyboard-interactive", methods, methlen); #ifndef NO_GSSAPI + ssh_gss_init(); s->can_gssapi = ssh->cfg.try_gssapi_auth && - in_commasep_string("gssapi-with-mic", methods, methlen) && - ssh_gss_init(); + in_commasep_string("gssapi-with-mic", methods, methlen) && + n_ssh_gss_libraries > 0; #endif } @@ -7896,6 +7992,35 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->gotit = TRUE; ssh->pkt_actx = SSH2_PKTCTX_GSSAPI; + /* + * Pick the highest GSS library on the preference + * list. + */ + { + int i, j; + s->gsslib = NULL; + for (i = 0; i < ngsslibs; i++) { + int want_id = ssh->cfg.ssh_gsslist[i]; + for (j = 0; j < n_ssh_gss_libraries; j++) + if (ssh_gss_libraries[j].id == want_id) { + s->gsslib = &ssh_gss_libraries[j]; + goto got_gsslib; /* double break */ + } + } + got_gsslib: + /* + * We always expect to have found something in + * the above loop: we only came here if there + * was at least one viable GSS library, and the + * preference list should always mention + * everything and only change the order. + */ + assert(s->gsslib); + } + + if (s->gsslib->gsslogmsg) + logevent(s->gsslib->gsslogmsg); + /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); ssh2_pkt_addstring(s->pktout, s->username); @@ -7903,7 +8028,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring(s->pktout, "gssapi-with-mic"); /* add mechanism info */ - ssh_gss_indicate_mech(&s->gss_buf); + s->gsslib->indicate_mech(s->gsslib, &s->gss_buf); /* number of GSSAPI mechanisms */ ssh2_pkt_adduint32(s->pktout,1); @@ -7939,8 +8064,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } /* now start running */ - s->gss_stat = ssh_gss_import_name(ssh->fullhostname, - &s->gss_srv_name); + s->gss_stat = s->gsslib->import_name(s->gsslib, + 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"); @@ -7950,30 +8076,34 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } /* fetch TGT into GSS engine */ - s->gss_stat = ssh_gss_acquire_cred(&s->gss_ctx); + s->gss_stat = s->gsslib->acquire_cred(s->gsslib, &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); + s->gsslib->release_name(s->gsslib, &s->gss_srv_name); continue; } /* initial tokens are empty */ SSH_GSS_CLEAR_BUF(&s->gss_rcvtok); + SSH_GSS_CLEAR_BUF(&s->gss_sndtok); /* 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); + s->gss_stat = s->gsslib->init_sec_context + (s->gsslib, + &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) { + if (s->gsslib->display_status(s->gsslib, s->gss_ctx, + &s->gss_buf) == SSH_GSS_OK) { logevent(s->gss_buf.value); sfree(s->gss_buf.value); } @@ -7990,7 +8120,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_pkt_addstring_start(s->pktout); ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.value,s->gss_sndtok.length); ssh2_pkt_send(ssh, s->pktout); - ssh_gss_free_tok(&s->gss_sndtok); + s->gsslib->free_tok(s->gsslib, &s->gss_sndtok); } if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) { @@ -8007,8 +8137,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } 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); + s->gsslib->release_name(s->gsslib, &s->gss_srv_name); + s->gsslib->release_cred(s->gsslib, &s->gss_ctx); continue; } logevent("GSSAPI authentication loop finished OK"); @@ -8027,17 +8157,17 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->gss_buf.value = (char *)s->pktout->data + micoffset; s->gss_buf.length = s->pktout->length - micoffset; - ssh_gss_get_mic(s->gss_ctx, &s->gss_buf, &mic); + s->gsslib->get_mic(s->gsslib, 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.value, mic.length); ssh2_pkt_send(ssh, s->pktout); - ssh_gss_free_mic(&mic); + s->gsslib->free_mic(s->gsslib, &mic); s->gotit = FALSE; - ssh_gss_release_name(&s->gss_srv_name); - ssh_gss_release_cred(&s->gss_ctx); + s->gsslib->release_name(s->gsslib, &s->gss_srv_name); + s->gsslib->release_cred(s->gsslib, &s->gss_ctx); continue; #endif } else if (s->can_keyb_inter && !s->kbd_inter_refused) { @@ -8092,23 +8222,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_pkt_getstring(pktin, &lang, &lang_len); s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; - if (name_len) { - /* FIXME: better prefix to distinguish from - * local prompts? */ - s->cur_prompt->name = - dupprintf("SSH server: %.*s", name_len, name); - s->cur_prompt->name_reqd = TRUE; - } else { - s->cur_prompt->name = - dupstr("SSH server authentication"); - s->cur_prompt->name_reqd = FALSE; - } - /* FIXME: ugly to print "Using..." in prompt _every_ - * time round. Can this be done more subtly? */ - s->cur_prompt->instruction = - dupprintf("Using keyboard-interactive authentication.%s%.*s", - inst_len ? "\n" : "", inst_len, inst); - s->cur_prompt->instr_reqd = TRUE; /* * Get any prompt(s) from the packet. @@ -8132,6 +8245,33 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, echo, SSH_MAX_PASSWORD_LEN); } + if (name_len) { + /* FIXME: better prefix to distinguish from + * local prompts? */ + s->cur_prompt->name = + dupprintf("SSH server: %.*s", name_len, name); + s->cur_prompt->name_reqd = TRUE; + } else { + s->cur_prompt->name = + dupstr("SSH server authentication"); + s->cur_prompt->name_reqd = FALSE; + } + /* We add a prefix to try to make it clear that a prompt + * has come from the server. + * FIXME: ugly to print "Using..." in prompt _every_ + * time round. Can this be done more subtly? */ + /* Special case: for reasons best known to themselves, + * some servers send k-i requests with no prompts and + * nothing to display. Keep quiet in this case. */ + if (s->num_prompts || name_len || inst_len) { + s->cur_prompt->instruction = + dupprintf("Using keyboard-interactive authentication.%s%.*s", + inst_len ? "\n" : "", inst_len, inst); + s->cur_prompt->instr_reqd = TRUE; + } else { + s->cur_prompt->instr_reqd = FALSE; + } + /* * Display any instructions, and get the user's * response(s). @@ -8413,11 +8553,16 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, sfree(s->password); } else { + char *str = dupprintf("No supported authentication methods available" + " (server sent: %.*s)", + methlen, methods); - ssh_disconnect(ssh, NULL, + ssh_disconnect(ssh, str, "No supported authentication methods available", SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, FALSE); + sfree(str); + crStopV; } @@ -8549,7 +8694,7 @@ 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) { + if (ssh->mainchan && ssh->cfg.ssh_simple) { /* * This message indicates to the server that we promise * not to try to run any other channel in parallel with @@ -8566,10 +8711,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Potentially enable X11 forwarding. */ - if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) { + if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward && + (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display, + ssh->cfg.x11_auth, &ssh->cfg))) { logevent("Requesting X11 forwarding"); - 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"); @@ -8856,7 +9001,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Try to send data on all channels if we can. */ for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) - ssh2_try_send_and_unthrottle(c); + ssh2_try_send_and_unthrottle(ssh, c); } } @@ -9210,7 +9355,7 @@ static void ssh_free(void *handle) if (ssh->rportfwds) { while ((pf = delpos234(ssh->rportfwds, 0)) != NULL) - sfree(pf); + free_rportfwd(pf); freetree234(ssh->rportfwds); ssh->rportfwds = NULL; } @@ -9394,8 +9539,10 @@ static const struct telnet_special *ssh_get_specials(void *handle) static const struct telnet_special ssh1_ignore_special[] = { {"IGNORE message", TS_NOP} }; - static const struct telnet_special ssh2_transport_specials[] = { + static const struct telnet_special ssh2_ignore_special[] = { {"IGNORE message", TS_NOP}, + }; + static const struct telnet_special ssh2_rekey_special[] = { {"Repeat key exchange", TS_REKEY}, }; static const struct telnet_special ssh2_session_specials[] = { @@ -9420,7 +9567,8 @@ static const struct telnet_special *ssh_get_specials(void *handle) {NULL, TS_EXITMENU} }; /* XXX review this length for any changes: */ - static struct telnet_special ssh_specials[lenof(ssh2_transport_specials) + + static struct telnet_special ssh_specials[lenof(ssh2_ignore_special) + + lenof(ssh2_rekey_special) + lenof(ssh2_session_specials) + lenof(specials_end)]; Ssh ssh = (Ssh) handle; @@ -9439,7 +9587,10 @@ static const struct telnet_special *ssh_get_specials(void *handle) if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) ADD_SPECIALS(ssh1_ignore_special); } else if (ssh->version == 2) { - ADD_SPECIALS(ssh2_transport_specials); + if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) + ADD_SPECIALS(ssh2_ignore_special); + if (!(ssh->remote_bugs & BUG_SSH2_REKEY)) + ADD_SPECIALS(ssh2_rekey_special); if (ssh->mainchan) ADD_SPECIALS(ssh2_session_specials); } /* else we're not ready yet */ @@ -9489,9 +9640,11 @@ static void ssh_special(void *handle, Telnet_Special code) if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END); } else { - pktout = ssh2_pkt_init(SSH2_MSG_IGNORE); - ssh2_pkt_addstring_start(pktout); - ssh2_pkt_send_noqueue(ssh, pktout); + if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { + pktout = ssh2_pkt_init(SSH2_MSG_IGNORE); + ssh2_pkt_addstring_start(pktout); + ssh2_pkt_send_noqueue(ssh, pktout); + } } } else if (code == TS_REKEY) { if (!ssh->kex_in_progress && ssh->version == 2) {