#include "putty.h"
#include "tree234.h"
#include "ssh.h"
+#ifndef NO_GSSAPI
+#include "sshgssc.h"
#include "sshgss.h"
+#endif
#ifndef FALSE
#define FALSE 0
#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.
*
* - OUR_V2_BIGWIN is the window size we advertise for the only
* channel in a simple connection. It must be <= INT_MAX.
+ *
+ * - OUR_V2_MAXPKT is the official "maximum packet size" we send
+ * to the remote side. This actually has nothing to do with the
+ * size of the _packet_, but is instead a limit on the amount
+ * of data we're willing to receive in a single SSH2 channel
+ * data message.
+ *
+ * - OUR_V2_PACKETLIMIT is actually the maximum size of SSH
+ * _packet_ we're prepared to cope with. It must be a multiple
+ * of the cipher block size, and must be at least 35000.
*/
#define SSH1_BUFFER_LIMIT 32768
#define OUR_V2_WINSIZE 16384
#define OUR_V2_BIGWIN 0x7fffffff
#define OUR_V2_MAXPKT 0x4000UL
+#define OUR_V2_PACKETLIMIT 0x9000UL
/* Maximum length of passwords/passphrases (arbitrary) */
#define SSH_MAX_PASSWORD_LEN 100
return 0;
}
const static struct ssh_compress ssh_comp_none = {
- "none",
+ "none", NULL,
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
ssh_comp_none_disable, NULL
* 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.
Pkt_KCtx pkt_kctx;
Pkt_ACtx pkt_actx;
- void *x11auth;
+ struct X11Display *x11disp;
int version;
int conn_throttle_count;
* Fully qualified host name, which we need if doing GSSAPI.
*/
char *fullhostname;
+
+#ifndef NO_GSSAPI
+ /*
+ * GSSAPI libraries for this session.
+ */
+ struct ssh_gss_liblist *gsslibs;
+#endif
};
#define logevent(s) logevent(ssh->frontend, s)
st->cipherblk = 8;
if (st->cipherblk < 8)
st->cipherblk = 8;
+ st->maclen = ssh->scmac ? ssh->scmac->len : 0;
- st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char);
+ if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_IS_CBC) &&
+ ssh->scmac) {
+ /*
+ * When dealing with a CBC-mode cipher, we want to avoid the
+ * possibility of an attacker's tweaking the ciphertext stream
+ * so as to cause us to feed the same block to the block
+ * cipher more than once and thus leak information
+ * (VU#958563). The way we do this is not to take any
+ * decisions on the basis of anything we've decrypted until
+ * we've verified it with a MAC. That includes the packet
+ * length, so we just read data and check the MAC repeatedly,
+ * and when the MAC passes, see if the length we've got is
+ * plausible.
+ */
- /*
- * Acquire and decrypt the first block of the packet. This will
- * contain the length and padding details.
- */
- for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) {
- while ((*datalen) == 0)
- crReturn(NULL);
- st->pktin->data[st->i] = *(*data)++;
- (*datalen)--;
- }
+ /* May as well allocate the whole lot now. */
+ st->pktin->data = snewn(OUR_V2_PACKETLIMIT + st->maclen + APIEXTRA,
+ unsigned char);
- if (ssh->sccipher)
- ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->pktin->data, st->cipherblk);
+ /* Read an amount corresponding to the MAC. */
+ for (st->i = 0; st->i < st->maclen; st->i++) {
+ while ((*datalen) == 0)
+ crReturn(NULL);
+ st->pktin->data[st->i] = *(*data)++;
+ (*datalen)--;
+ }
- /*
- * Now get the length and padding figures.
- */
- st->len = GET_32BIT(st->pktin->data);
- st->pad = st->pktin->data[4];
+ st->packetlen = 0;
+ {
+ unsigned char seq[4];
+ ssh->scmac->start(ssh->sc_mac_ctx);
+ PUT_32BIT(seq, st->incoming_sequence);
+ ssh->scmac->bytes(ssh->sc_mac_ctx, seq, 4);
+ }
- /*
- * _Completely_ silly lengths should be stomped on before they
- * do us any more damage.
- */
- if (st->len < 0 || st->len > 35000 || st->pad < 4 ||
- st->len - st->pad < 1 || (st->len + 4) % st->cipherblk != 0) {
- bombout(("Incoming packet was garbled on decryption"));
- ssh_free_packet(st->pktin);
- crStop(NULL);
- }
+ for (;;) { /* Once around this loop per cipher block. */
+ /* Read another cipher-block's worth, and tack it onto the end. */
+ for (st->i = 0; st->i < st->cipherblk; st->i++) {
+ while ((*datalen) == 0)
+ crReturn(NULL);
+ st->pktin->data[st->packetlen+st->maclen+st->i] = *(*data)++;
+ (*datalen)--;
+ }
+ /* Decrypt one more block (a little further back in the stream). */
+ ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
+ st->pktin->data + st->packetlen,
+ st->cipherblk);
+ /* Feed that block to the MAC. */
+ ssh->scmac->bytes(ssh->sc_mac_ctx,
+ st->pktin->data + st->packetlen, st->cipherblk);
+ st->packetlen += st->cipherblk;
+ /* See if that gives us a valid packet. */
+ if (ssh->scmac->verresult(ssh->sc_mac_ctx,
+ st->pktin->data + st->packetlen) &&
+ (st->len = GET_32BIT(st->pktin->data)) + 4 == st->packetlen)
+ break;
+ if (st->packetlen >= OUR_V2_PACKETLIMIT) {
+ bombout(("No valid incoming packet found"));
+ ssh_free_packet(st->pktin);
+ crStop(NULL);
+ }
+ }
+ st->pktin->maxlen = st->packetlen + st->maclen;
+ st->pktin->data = sresize(st->pktin->data,
+ st->pktin->maxlen + APIEXTRA,
+ unsigned char);
+ } else {
+ st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char);
- /*
- * This enables us to deduce the payload length.
- */
- st->payload = st->len - st->pad - 1;
+ /*
+ * Acquire and decrypt the first block of the packet. This will
+ * contain the length and padding details.
+ */
+ for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) {
+ while ((*datalen) == 0)
+ crReturn(NULL);
+ st->pktin->data[st->i] = *(*data)++;
+ (*datalen)--;
+ }
- st->pktin->length = st->payload + 5;
+ if (ssh->sccipher)
+ ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
+ st->pktin->data, st->cipherblk);
- /*
- * So now we can work out the total packet length.
- */
- st->packetlen = st->len + 4;
- st->maclen = ssh->scmac ? ssh->scmac->len : 0;
+ /*
+ * Now get the length figure.
+ */
+ st->len = GET_32BIT(st->pktin->data);
- /*
- * Allocate memory for the rest of the packet.
- */
- st->pktin->maxlen = st->packetlen + st->maclen;
- st->pktin->data = sresize(st->pktin->data,
- st->pktin->maxlen + APIEXTRA,
- unsigned char);
+ /*
+ * _Completely_ silly lengths should be stomped on before they
+ * do us any more damage.
+ */
+ if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT ||
+ (st->len + 4) % st->cipherblk != 0) {
+ bombout(("Incoming packet was garbled on decryption"));
+ ssh_free_packet(st->pktin);
+ crStop(NULL);
+ }
- /*
- * Read and decrypt the remainder of the packet.
- */
- for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen;
- st->i++) {
- while ((*datalen) == 0)
- crReturn(NULL);
- st->pktin->data[st->i] = *(*data)++;
- (*datalen)--;
- }
- /* Decrypt everything _except_ the MAC. */
- if (ssh->sccipher)
- ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->pktin->data + st->cipherblk,
- st->packetlen - st->cipherblk);
+ /*
+ * So now we can work out the total packet length.
+ */
+ st->packetlen = st->len + 4;
- st->pktin->encrypted_len = st->packetlen;
+ /*
+ * Allocate memory for the rest of the packet.
+ */
+ st->pktin->maxlen = st->packetlen + st->maclen;
+ st->pktin->data = sresize(st->pktin->data,
+ st->pktin->maxlen + APIEXTRA,
+ unsigned char);
- /*
- * Check the MAC.
- */
- if (ssh->scmac
- && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data, st->len + 4,
- st->incoming_sequence)) {
- bombout(("Incorrect MAC received on packet"));
+ /*
+ * Read and decrypt the remainder of the packet.
+ */
+ for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen;
+ st->i++) {
+ while ((*datalen) == 0)
+ crReturn(NULL);
+ st->pktin->data[st->i] = *(*data)++;
+ (*datalen)--;
+ }
+ /* Decrypt everything _except_ the MAC. */
+ if (ssh->sccipher)
+ ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
+ st->pktin->data + st->cipherblk,
+ st->packetlen - st->cipherblk);
+
+ /*
+ * Check the MAC.
+ */
+ if (ssh->scmac
+ && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data,
+ st->len + 4, st->incoming_sequence)) {
+ bombout(("Incorrect MAC received on packet"));
+ ssh_free_packet(st->pktin);
+ crStop(NULL);
+ }
+ }
+ /* Get and sanity-check the amount of random padding. */
+ st->pad = st->pktin->data[4];
+ if (st->pad < 4 || st->len - st->pad < 1) {
+ bombout(("Invalid padding length on received packet"));
ssh_free_packet(st->pktin);
crStop(NULL);
}
+ /*
+ * This enables us to deduce the payload length.
+ */
+ st->payload = st->len - st->pad - 1;
+
+ st->pktin->length = st->payload + 5;
+ st->pktin->encrypted_len = st->packetlen;
st->pktin->sequence = st->incoming_sequence++;
{
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.
* 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);
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");
+ }
}
/*
x11_close(c->u.x11.s);
break;
case CHAN_SOCKDATA:
+ case CHAN_SOCKDATA_DORMANT:
pfd_close(c->u.pfd.s);
break;
}
del234(ssh->portfwds, pf); /* moving next one to index 0 */
free_portfwd(pf);
}
+ freetree234(ssh->portfwds);
+ ssh->portfwds = NULL;
}
return ret;
fflush(stdout); /* FIXME eh? */
{
- if (!*ssh->cfg.username) {
+ if (!get_remote_username(&ssh->cfg, s->username,
+ sizeof(s->username))) {
int ret; /* need not be kept over crReturn */
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
memcpy(s->username, s->cur_prompt->prompts[0]->result,
lenof(s->username));
free_prompts(s->cur_prompt);
- } else {
- strncpy(s->username, ssh->cfg.username, sizeof(s->username));
- s->username[sizeof(s->username)-1] = '\0';
}
send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END);
sfree(s->response);
if (s->publickey_blob && !s->tried_publickey)
logevent("Configured key file not in Pageant");
- }
+ } else {
+ logevent("Failed to get reply from Pageant");
+ }
if (s->authed)
break;
}
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
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");
rpf = del234(ssh->rportfwds, pf);
assert(rpf == pf);
+ pf->pfrec->remote = NULL;
free_rportfwd(pf);
}
}
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;
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;
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,
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);
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;
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);
}
}
- if (ssh->cfg.x11_forward) {
- char proto[20], data[64];
+ 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->x11auth = x11_invent_auth(proto, sizeof(proto),
- data, sizeof(data), ssh->cfg.x11_auth);
- x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display);
/*
* 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,
*/
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;
int n_preferred_ciphers;
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
const struct ssh_compress *preferred_comp;
+ int userauth_succeeded; /* for delayed compression */
+ int pending_compression;
int got_session_id, activated_authconn;
struct Packet *pktout;
int dlgret;
s->cscomp_tobe = s->sccomp_tobe = NULL;
s->got_session_id = s->activated_authconn = FALSE;
+ s->userauth_succeeded = FALSE;
+ s->pending_compression = FALSE;
/*
* Be prepared to work around the buggy MAC problem.
if (i < s->nmacs - 1)
ssh2_pkt_addstring_str(s->pktout, ",");
}
- /* List client->server compression algorithms. */
- ssh2_pkt_addstring_start(s->pktout);
- assert(lenof(compressions) > 1);
- ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
- for (i = 0; i < lenof(compressions); i++) {
- const struct ssh_compress *c = compressions[i];
- if (c != s->preferred_comp) {
+ /* List client->server compression algorithms,
+ * then server->client compression algorithms. (We use the
+ * same set twice.) */
+ for (j = 0; j < 2; j++) {
+ ssh2_pkt_addstring_start(s->pktout);
+ assert(lenof(compressions) > 1);
+ /* Prefer non-delayed versions */
+ ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
+ /* We don't even list delayed versions of algorithms until
+ * they're allowed to be used, to avoid a race. See the end of
+ * this function. */
+ if (s->userauth_succeeded && s->preferred_comp->delayed_name) {
ssh2_pkt_addstring_str(s->pktout, ",");
- ssh2_pkt_addstring_str(s->pktout, c->name);
+ ssh2_pkt_addstring_str(s->pktout,
+ s->preferred_comp->delayed_name);
}
- }
- /* List server->client compression algorithms. */
- ssh2_pkt_addstring_start(s->pktout);
- assert(lenof(compressions) > 1);
- ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
- for (i = 0; i < lenof(compressions); i++) {
- const struct ssh_compress *c = compressions[i];
- if (c != s->preferred_comp) {
- ssh2_pkt_addstring_str(s->pktout, ",");
- ssh2_pkt_addstring_str(s->pktout, c->name);
+ for (i = 0; i < lenof(compressions); i++) {
+ const struct ssh_compress *c = compressions[i];
+ if (c != s->preferred_comp) {
+ ssh2_pkt_addstring_str(s->pktout, ",");
+ ssh2_pkt_addstring_str(s->pktout, c->name);
+ if (s->userauth_succeeded && c->delayed_name) {
+ ssh2_pkt_addstring_str(s->pktout, ",");
+ ssh2_pkt_addstring_str(s->pktout, c->delayed_name);
+ }
+ }
}
}
/* List client->server languages. Empty list. */
if (in_commasep_string(c->name, str, len)) {
s->cscomp_tobe = c;
break;
+ } else if (in_commasep_string(c->delayed_name, str, len)) {
+ if (s->userauth_succeeded) {
+ s->cscomp_tobe = c;
+ break;
+ } else {
+ s->pending_compression = TRUE; /* try this later */
+ }
}
}
ssh_pkt_getstring(pktin, &str, &len); /* server->client compression */
if (in_commasep_string(c->name, str, len)) {
s->sccomp_tobe = c;
break;
+ } else if (in_commasep_string(c->delayed_name, str, len)) {
+ if (s->userauth_succeeded) {
+ s->sccomp_tobe = c;
+ break;
+ } else {
+ s->pending_compression = TRUE; /* try this later */
+ }
}
}
+ if (s->pending_compression) {
+ logevent("Server supports delayed compression; "
+ "will try this later");
+ }
ssh_pkt_getstring(pktin, &str, &len); /* client->server language */
ssh_pkt_getstring(pktin, &str, &len); /* server->client language */
s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok;
* start.
*
* We _also_ go back to the start if we see pktin==NULL and
- * inlen==-1, because this is a special signal meaning
+ * inlen negative, because this is a special signal meaning
* `initiate client-driven rekey', and `in' contains a message
* giving the reason for the rekey.
+ *
+ * inlen==-1 means always initiate a rekey;
+ * inlen==-2 means that userauth has completed successfully and
+ * we should consider rekeying (for delayed compression).
*/
while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) ||
- (!pktin && inlen == -1))) {
+ (!pktin && inlen < 0))) {
wait_for_rekey:
crReturn(1);
}
if (pktin) {
logevent("Server initiated key re-exchange");
} else {
+ if (inlen == -2) {
+ /*
+ * authconn has seen a USERAUTH_SUCCEEDED. Time to enable
+ * delayed compression, if it's available.
+ *
+ * draft-miller-secsh-compression-delayed-00 says that you
+ * negotiate delayed compression in the first key exchange, and
+ * both sides start compressing when the server has sent
+ * USERAUTH_SUCCESS. This has a race condition -- the server
+ * can't know when the client has seen it, and thus which incoming
+ * packets it should treat as compressed.
+ *
+ * Instead, we do the initial key exchange without offering the
+ * delayed methods, but note if the server offers them; when we
+ * get here, if a delayed method was available that was higher
+ * on our list than what we got, we initiate a rekey in which we
+ * _do_ list the delayed methods (and hopefully get it as a
+ * result). Subsequent rekeys will do the same.
+ */
+ assert(!s->userauth_succeeded); /* should only happen once */
+ s->userauth_succeeded = TRUE;
+ if (!s->pending_compression)
+ /* Can't see any point rekeying. */
+ goto wait_for_rekey; /* this is utterly horrid */
+ /* else fall through to rekey... */
+ s->pending_compression = FALSE;
+ }
/*
+ * Now we've decided to rekey.
+ *
* Special case: if the server bug is set that doesn't
* allow rekeying, we give a different log message and
* continue waiting. (If such a server _initiates_ a rekey,
schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
ssh2_timer, ssh);
}
- goto wait_for_rekey; /* this is utterly horrid */
+ goto wait_for_rekey; /* this is still utterly horrid */
} else {
logeventf(ssh, "Initiating key re-exchange (%s)", (char *)in);
}
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)
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;
+ }
}
/*
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;
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);
}
}
* 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);
}
}
if (typelen == 3 && !memcmp(type, "x11", 3)) {
char *addrstr;
+ const char *x11err;
ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
addrstr = snewn(peeraddrlen+1, char);
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 ((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");
}
/*
- * Buffer banner messages for later display at some convenient point.
+ * Buffer banner messages for later display at some convenient point,
+ * if we're going to display them.
*/
static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
{
/* Arbitrary limit to prevent unbounded inflation of buffer */
- if (bufchain_size(&ssh->banner) <= 131072) {
+ if (ssh->cfg.ssh_show_banner &&
+ bufchain_size(&ssh->banner) <= 131072) {
char *banner = NULL;
int size = 0;
ssh_pkt_getstring(pktin, &banner, &size);
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;
+ int we_are_in, userauth_success;
prompts_t *cur_prompt;
int num_prompts;
char username[100];
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);
crBegin(ssh->do_ssh2_authconn_crstate);
s->done_service_req = FALSE;
- s->we_are_in = FALSE;
+ s->we_are_in = s->userauth_success = FALSE;
+#ifndef NO_GSSAPI
s->tried_gssapi = FALSE;
+#endif
if (!ssh->cfg.ssh_no_userauth) {
/*
s->nkeys = 0;
}
}
+ } else {
+ logevent("Failed to get reply from Pageant");
}
}
* with change_username turned off we don't try to get
* it again.
*/
- } else if (!*ssh->cfg.username) {
+ } else if (!get_remote_username(&ssh->cfg, s->username,
+ sizeof(s->username))) {
int ret; /* need not be kept over crReturn */
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
free_prompts(s->cur_prompt);
} else {
char *stuff;
- strncpy(s->username, ssh->cfg.username, sizeof(s->username));
- s->username[sizeof(s->username)-1] = '\0';
if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
stuff = dupprintf("Using username \"%s\".\r\n", s->username);
c_write_str(ssh, stuff);
}
while (1) {
+ char *methods = NULL;
+ int methlen = 0;
+
/*
* Wait for the result of the last authentication request.
*/
}
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
logevent("Access granted");
- s->we_are_in = TRUE;
+ s->we_are_in = s->userauth_success = TRUE;
break;
}
* 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)) {
/*
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
+#ifndef NO_GSSAPI
+ if (!ssh->gsslibs)
+ ssh->gsslibs = ssh_gss_setup(&ssh->cfg);
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) &&
+ ssh->gsslibs->nlibraries > 0;
#endif
}
/* GSSAPI Authentication */
- int micoffset;
+ int micoffset, len;
+ char *data;
Ssh_gss_buf mic;
s->type = AUTH_TYPE_GSSAPI;
s->tried_gssapi = TRUE;
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 < ssh->gsslibs->nlibraries; j++)
+ if (ssh->gsslibs->libraries[j].id == want_id) {
+ s->gsslib = &ssh->gsslibs->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);
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);
/* length of OID + 2 */
- ssh2_pkt_adduint32(s->pktout, s->gss_buf.len + 2);
+ ssh2_pkt_adduint32(s->pktout, s->gss_buf.length + 2);
ssh2_pkt_addbyte(s->pktout, SSH2_GSS_OIDTYPE);
/* length of OID */
- ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.len);
+ ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.length);
- ssh_pkt_adddata(s->pktout, s->gss_buf.data, s->gss_buf.len);
+ ssh_pkt_adddata(s->pktout, s->gss_buf.value,
+ s->gss_buf.length);
ssh2_pkt_send(ssh, s->pktout);
crWaitUntilV(pktin);
if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) {
/* 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) ) {
+ ssh_pkt_getstring(pktin, &data, &len);
+ s->gss_rcvtok.value = data;
+ s->gss_rcvtok.length = len;
+ if (s->gss_rcvtok.length != s->gss_buf.length + 2 ||
+ ((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE ||
+ ((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length ||
+ memcmp((char *)s->gss_rcvtok.value + 2,
+ s->gss_buf.value,s->gss_buf.length) ) {
logevent("GSSAPI authentication - wrong response from server");
continue;
}
/* 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");
}
/* 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 */
- s->gss_rcvtok.len = s->gss_sndtok.len = 0;
- s->gss_rcvtok.data = s->gss_sndtok.data = NULL;
+ 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) {
- logevent(s->gss_buf.data);
- sfree(s->gss_buf.data);
+ 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);
}
break;
/* Client and server now exchange tokens until GSSAPI
* no longer says CONTINUE_NEEDED */
- if (s->gss_sndtok.len != 0) {
+ if (s->gss_sndtok.length != 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);
+ 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) {
s->gss_stat = SSH_GSS_FAILURE;
break;
}
- ssh_pkt_getstring(pktin,&s->gss_rcvtok.data,&s->gss_rcvtok.len);
+ ssh_pkt_getstring(pktin, &data, &len);
+ s->gss_rcvtok.value = data;
+ s->gss_rcvtok.length = 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);
+ 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");
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;
+ 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.data, mic.len);
+ 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) {
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.
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).
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;
}
if (s->agent_response)
sfree(s->agent_response);
+ if (s->userauth_success) {
+ /*
+ * We've just received USERAUTH_SUCCESS, and we haven't sent any
+ * packets since. Signal the transport layer to consider enacting
+ * delayed compression.
+ *
+ * (Relying on we_are_in is not sufficient, as
+ * draft-miller-secsh-compression-delayed is quite clear that it
+ * triggers on USERAUTH_SUCCESS specifically, and we_are_in can
+ * become set for other reasons.)
+ */
+ do_ssh2_transport(ssh, "enabling delayed compression", -2, NULL);
+ }
+
/*
* Now the connection protocol has started, one way or another.
*/
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
/*
* Potentially enable X11 forwarding.
*/
- if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) {
- char proto[20], data[64];
+ 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->x11auth = x11_invent_auth(proto, sizeof(proto),
- data, sizeof(data), ssh->cfg.x11_auth);
- x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display);
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,
* 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);
* 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);
}
}
/* log the debug message */
char *msg;
int msglen;
- int always_display;
- /* XXX maybe we should actually take notice of this */
- always_display = ssh2_pkt_getbool(pktin);
+ /* XXX maybe we should actually take notice of the return value */
+ ssh2_pkt_getbool(pktin);
ssh_pkt_getstring(pktin, &msg, &msglen);
logeventf(ssh, "Remote debug message: %.*s", msglen, msg);
ssh->fallback_cmd = 0;
ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
- ssh->x11auth = NULL;
+ ssh->x11disp = NULL;
ssh->v1_compressing = FALSE;
ssh->v2_outgoing_sequence = 0;
ssh->ssh1_rdpkt_crstate = 0;
ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);
ssh->kex_in_progress = FALSE;
+#ifndef NO_GSSAPI
+ ssh->gsslibs = NULL;
+#endif
+
p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
if (p != NULL)
return p;
x11_close(c->u.x11.s);
break;
case CHAN_SOCKDATA:
+ case CHAN_SOCKDATA_DORMANT:
if (c->u.pfd.s != NULL)
pfd_close(c->u.pfd.s);
break;
if (ssh->rportfwds) {
while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)
- sfree(pf);
+ free_rportfwd(pf);
freetree234(ssh->rportfwds);
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);
if (ssh->pinger)
pinger_free(ssh->pinger);
bufchain_clear(&ssh->queued_incoming_data);
+#ifndef NO_GSSAPI
+ if (ssh->gsslibs)
+ ssh_gss_cleanup(ssh->gsslibs);
+#endif
sfree(ssh);
random_unref();
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[] = {
{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;
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 */
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) {