X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=ssh.c;h=288616fd901c6756bac358f98e5618220ed9f283;hb=af1460d6e5044a3344aaacd15c91cfdcb58578e7;hp=1a069c26a3e2dc8ccfcbf8013f4ab3974c95184f;hpb=5d9a9a7bdf6f227a2a592d57ea71b69d0111e341;p=PuTTY.git diff --git a/ssh.c b/ssh.c index 1a069c26..288616fd 100644 --- a/ssh.c +++ b/ssh.c @@ -364,6 +364,7 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen, struct Packet *pktin); static void ssh2_channel_check_close(struct ssh_channel *c); static void ssh_channel_destroy(struct ssh_channel *c); +static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin); /* * Buffer management constants. There are several of these for @@ -1834,6 +1835,15 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, const unsigned char **data, } } + /* + * RFC 4253 doesn't explicitly say that completely empty packets + * with no type byte are forbidden, so treat them as deserving + * an SSH_MSG_UNIMPLEMENTED. + */ + if (st->pktin->length <= 5) { /* == 5 we hope, but robustness */ + ssh2_msg_something_unimplemented(ssh, st->pktin); + crStop(NULL); + } /* * pktin->body and pktin->length should identify the semantic * content of the packet, excluding the initial type byte. @@ -3007,6 +3017,10 @@ static void ssh_send_verstring(Ssh ssh, const char *protoname, char *svers) } ssh_fix_verstring(verstring + strlen(protoname)); +#ifdef FUZZING + /* FUZZING make PuTTY insecure, so make live use difficult. */ + verstring[0] = 'I'; +#endif if (ssh->version == 2) { size_t len; @@ -3549,35 +3563,20 @@ static void ssh_sent(Plug plug, int bufsize) ssh_throttle_all(ssh, 0, bufsize); } -/* - * Connect to specified host and port. - * Returns an error message, or NULL on success. - * Also places the canonical host name into `realhost'. It must be - * freed by the caller. - */ -static const char *connect_to_host(Ssh ssh, const char *host, int port, - char **realhost, int nodelay, int keepalive) +static void ssh_hostport_setup(const char *host, int port, Conf *conf, + char **savedhost, int *savedport, + char **loghost_ret) { - static const struct plug_function_table fn_table = { - ssh_socket_log, - ssh_closing, - ssh_receive, - ssh_sent, - NULL - }; + char *loghost = conf_get_str(conf, CONF_loghost); + if (loghost_ret) + *loghost_ret = loghost; - SockAddr addr; - const char *err; - char *loghost; - int addressfamily, sshprot; - - loghost = conf_get_str(ssh->conf, CONF_loghost); if (*loghost) { char *tmphost; char *colon; tmphost = dupstr(loghost); - ssh->savedport = 22; /* default ssh port */ + *savedport = 22; /* default ssh port */ /* * A colon suffix on the hostname string also lets us affect @@ -3588,17 +3587,58 @@ static const char *connect_to_host(Ssh ssh, const char *host, int port, if (colon && colon == host_strchr(tmphost, ':')) { *colon++ = '\0'; if (*colon) - ssh->savedport = atoi(colon); + *savedport = atoi(colon); } - ssh->savedhost = host_strduptrim(tmphost); + *savedhost = host_strduptrim(tmphost); sfree(tmphost); } else { - ssh->savedhost = host_strduptrim(host); + *savedhost = host_strduptrim(host); if (port < 0) port = 22; /* default ssh port */ - ssh->savedport = port; + *savedport = port; } +} + +static int ssh_test_for_upstream(const char *host, int port, Conf *conf) +{ + char *savedhost; + int savedport; + int ret; + + random_ref(); /* platform may need this to determine share socket name */ + ssh_hostport_setup(host, port, conf, &savedhost, &savedport, NULL); + ret = ssh_share_test_for_upstream(savedhost, savedport, conf); + sfree(savedhost); + random_unref(); + + return ret; +} + +/* + * Connect to specified host and port. + * Returns an error message, or NULL on success. + * Also places the canonical host name into `realhost'. It must be + * freed by the caller. + */ +static const char *connect_to_host(Ssh ssh, const char *host, int port, + char **realhost, int nodelay, int keepalive) +{ + static const struct plug_function_table fn_table = { + ssh_socket_log, + ssh_closing, + ssh_receive, + ssh_sent, + NULL + }; + + SockAddr addr; + const char *err; + char *loghost; + int addressfamily, sshprot; + + ssh_hostport_setup(host, port, ssh->conf, + &ssh->savedhost, &ssh->savedport, &loghost); ssh->fn = &fn_table; /* make 'ssh' usable as a Plug */ @@ -4014,6 +4054,9 @@ static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen, "rsa", keystr, fingerprint, ssh_dialog_callback, ssh); sfree(keystr); +#ifdef FUZZING + s->dlgret = 1; +#endif if (s->dlgret < 0) { do { crReturn(0); @@ -6127,16 +6170,24 @@ static void ssh2_pkt_addstring_commasep(struct Packet *pkt, const char *data) /* - * SSH-2 key creation method. - * (Currently assumes 2 lots of any hash are sufficient to generate - * keys/IVs for any cipher/MAC. SSH2_MKKEY_ITERS documents this assumption.) + * SSH-2 key derivation (RFC 4253 section 7.2). */ -#define SSH2_MKKEY_ITERS (2) -static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr, - unsigned char *keyspace) +static unsigned char *ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, + char chr, int keylen) { const struct ssh_hash *h = ssh->kex->hash; - void *s; + int keylen_padded; + unsigned char *key; + void *s, *s2; + + if (keylen == 0) + return NULL; + + /* Round up to the next multiple of hash length. */ + keylen_padded = ((keylen + h->hlen - 1) / h->hlen) * h->hlen; + + key = snewn(keylen_padded, unsigned char); + /* First hlen bytes. */ s = h->init(); if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY)) @@ -6144,14 +6195,33 @@ static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr, h->bytes(s, H, h->hlen); h->bytes(s, &chr, 1); h->bytes(s, ssh->v2_session_id, ssh->v2_session_id_len); - h->final(s, keyspace); - /* Next hlen bytes. */ - s = h->init(); - if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY)) - hash_mpint(h, s, K); - h->bytes(s, H, h->hlen); - h->bytes(s, keyspace, h->hlen); - h->final(s, keyspace + h->hlen); + h->final(s, key); + + /* Subsequent blocks of hlen bytes. */ + if (keylen_padded > h->hlen) { + int offset; + + s = h->init(); + if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY)) + hash_mpint(h, s, K); + h->bytes(s, H, h->hlen); + + for (offset = h->hlen; offset < keylen_padded; offset += h->hlen) { + h->bytes(s, key + offset - h->hlen, h->hlen); + s2 = h->copy(s); + h->final(s2, key + offset); + } + + h->free(s); + } + + /* Now clear any extra bytes of key material beyond the length + * we're officially returning, because the caller won't know to + * smemclr those. */ + if (keylen_padded > keylen) + smemclr(key + keylen, keylen_padded - keylen); + + return key; } /* @@ -6197,6 +6267,7 @@ static struct kexinit_algorithm *ssh2_kexinit_addalg(struct kexinit_algorithm return &list[i]; } assert(!"No space in KEXINIT list"); + return NULL; } /* @@ -6340,6 +6411,9 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, case CIPHER_ARCFOUR: s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_arcfour; break; + case CIPHER_CHACHA20: + s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_ccp; + break; case CIPHER_WARN: /* Flag for later. Don't bother if it's the last in * the list. */ @@ -6420,6 +6494,11 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, /* List encryption algorithms (client->server then server->client). */ for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) { warn = FALSE; +#ifdef FUZZING + alg = ssh2_kexinit_addalg(s->kexlists[k], "none"); + alg->u.cipher.cipher = NULL; + alg->u.cipher.warn = warn; +#endif /* FUZZING */ for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) warn = TRUE; @@ -6433,6 +6512,11 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, } /* List MAC algorithms (client->server then server->client). */ for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) { +#ifdef FUZZING + alg = ssh2_kexinit_addalg(s->kexlists[j], "none"); + alg->u.mac.mac = NULL; + alg->u.mac.etm = FALSE; +#endif /* FUZZING */ for (i = 0; i < s->nmacs; i++) { alg = ssh2_kexinit_addalg(s->kexlists[j], s->maclist[i]->name); alg->u.mac.mac = s->maclist[i]; @@ -6537,6 +6621,24 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, bombout(("KEXINIT packet was incomplete")); crStopV; } + + /* If we've already selected a cipher which requires a + * particular MAC, then just select that, and don't even + * bother looking through the server's KEXINIT string for + * MACs. */ + if (i == KEXLIST_CSMAC && s->cscipher_tobe && + s->cscipher_tobe->required_mac) { + s->csmac_tobe = s->cscipher_tobe->required_mac; + s->csmac_etm_tobe = !!(s->csmac_tobe->etm_name); + goto matched; + } + if (i == KEXLIST_SCMAC && s->sccipher_tobe && + s->sccipher_tobe->required_mac) { + s->scmac_tobe = s->sccipher_tobe->required_mac; + s->scmac_etm_tobe = !!(s->scmac_tobe->etm_name); + goto matched; + } + for (j = 0; j < MAXKEXLIST; j++) { struct kexinit_algorithm *alg = &s->kexlists[i][j]; if (alg->name == NULL) break; @@ -6582,16 +6684,6 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, matched:; } - /* If the cipher over-rides the mac, then pick it */ - if (s->cscipher_tobe && s->cscipher_tobe->required_mac) { - s->csmac_tobe = s->cscipher_tobe->required_mac; - s->csmac_etm_tobe = !!(s->csmac_tobe->etm_name); - } - if (s->sccipher_tobe && s->sccipher_tobe->required_mac) { - s->scmac_tobe = s->sccipher_tobe->required_mac; - s->scmac_etm_tobe = !!(s->scmac_tobe->etm_name); - } - if (s->pending_compression) { logevent("Server supports delayed compression; " "will try this later"); @@ -6697,8 +6789,8 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, { int csbits, scbits; - csbits = s->cscipher_tobe->keylen; - scbits = s->sccipher_tobe->keylen; + csbits = s->cscipher_tobe ? s->cscipher_tobe->real_keybits : 0; + scbits = s->sccipher_tobe ? s->sccipher_tobe->real_keybits : 0; s->nbits = (csbits > scbits ? csbits : scbits); } /* The keys only have hlen-bit entropy, since they're based on @@ -6775,6 +6867,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, } set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */ ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); + if (!s->hostkeydata) { + bombout(("unable to parse key exchange reply packet")); + crStopV; + } s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata, s->hostkeylen); s->f = ssh2_pkt_getmp(pktin); @@ -6783,6 +6879,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, crStopV; } ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); + if (!s->sigdata) { + bombout(("unable to parse key exchange reply packet")); + crStopV; + } { const char *err = dh_validate_f(ssh->kex_ctx, s->f); @@ -6854,6 +6954,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, } ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); + if (!s->hostkeydata) { + bombout(("unable to parse ECDH reply packet")); + crStopV; + } hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen); s->hkey = ssh->hostkey->newkey(ssh->hostkey, s->hostkeydata, s->hostkeylen); @@ -6876,6 +6980,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, char *keydata; int keylen; ssh_pkt_getstring(pktin, &keydata, &keylen); + if (!keydata) { + bombout(("unable to parse ECDH reply packet")); + crStopV; + } hash_string(ssh->kex->hash, ssh->exhash, keydata, keylen); s->K = ssh_ecdhkex_getkey(s->eckey, keydata, keylen); if (!s->K) { @@ -6886,6 +6994,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, } ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); + if (!s->sigdata) { + bombout(("unable to parse key exchange reply packet")); + crStopV; + } ssh_ecdhkex_freekey(s->eckey); } else { @@ -6903,6 +7015,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, } ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen); + if (!s->hostkeydata) { + bombout(("unable to parse RSA public key packet")); + crStopV; + } hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen); s->hkey = ssh->hostkey->newkey(ssh->hostkey, @@ -6911,6 +7027,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, { char *keydata; ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen); + if (!keydata) { + bombout(("unable to parse RSA public key packet")); + crStopV; + } s->rsakeydata = snewn(s->rsakeylen, char); memcpy(s->rsakeydata, keydata, s->rsakeylen); } @@ -6987,6 +7107,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, } ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen); + if (!s->sigdata) { + bombout(("unable to parse signature packet")); + crStopV; + } sfree(s->rsakeydata); } @@ -7006,8 +7130,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen, (char *)s->exchange_hash, ssh->kex->hash->hlen)) { +#ifndef FUZZING bombout(("Server's host key did not match the signature supplied")); crStopV; +#endif } s->keystr = ssh->hostkey->fmtkey(s->hkey); @@ -7032,6 +7158,9 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, ssh->hostkey->keytype, s->keystr, s->fingerprint, ssh_dialog_callback, ssh); +#ifdef FUZZING + s->dlgret = 1; +#endif if (s->dlgret < 0) { do { crReturnV; @@ -7064,8 +7193,10 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, * the one we saw before. */ if (strcmp(ssh->hostkey_str, s->keystr)) { +#ifndef FUZZING bombout(("Host key was different in repeat key exchange")); crStopV; +#endif } sfree(s->keystr); } @@ -7099,13 +7230,14 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, if (ssh->cs_cipher_ctx) ssh->cscipher->free_context(ssh->cs_cipher_ctx); ssh->cscipher = s->cscipher_tobe; - ssh->cs_cipher_ctx = ssh->cscipher->make_context(); + if (ssh->cscipher) ssh->cs_cipher_ctx = ssh->cscipher->make_context(); if (ssh->cs_mac_ctx) ssh->csmac->free_context(ssh->cs_mac_ctx); ssh->csmac = s->csmac_tobe; ssh->csmac_etm = s->csmac_etm_tobe; - ssh->cs_mac_ctx = ssh->csmac->make_context(ssh->cs_cipher_ctx); + if (ssh->csmac) + ssh->cs_mac_ctx = ssh->csmac->make_context(ssh->cs_cipher_ctx); if (ssh->cs_comp_ctx) ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx); @@ -7116,29 +7248,39 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, * Set IVs on client-to-server keys. Here we use the exchange * hash from the _first_ key exchange. */ - { - unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS]; - assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); - ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace); - assert((ssh->cscipher->keylen+7) / 8 <= - ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); - ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace); - assert(ssh->cscipher->blksize <= - ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); - ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace); - assert(ssh->csmac->len <= - ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); - ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace); - smemclr(keyspace, sizeof(keyspace)); - } - - logeventf(ssh, "Initialised %.200s client->server encryption", - ssh->cscipher->text_name); - logeventf(ssh, "Initialised %.200s client->server MAC algorithm%s", - ssh->csmac->text_name, - ssh->csmac_etm ? " (in ETM mode)" : ""); + if (ssh->cscipher) { + unsigned char *key; + + key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'C', + ssh->cscipher->padded_keybytes); + ssh->cscipher->setkey(ssh->cs_cipher_ctx, key); + smemclr(key, ssh->cscipher->padded_keybytes); + sfree(key); + + key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'A', + ssh->cscipher->blksize); + ssh->cscipher->setiv(ssh->cs_cipher_ctx, key); + smemclr(key, ssh->cscipher->blksize); + sfree(key); + } + if (ssh->csmac) { + unsigned char *key; + + key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'E', + ssh->csmac->keylen); + ssh->csmac->setkey(ssh->cs_mac_ctx, key); + smemclr(key, ssh->csmac->keylen); + sfree(key); + } + + if (ssh->cscipher) + logeventf(ssh, "Initialised %.200s client->server encryption", + ssh->cscipher->text_name); + if (ssh->csmac) + logeventf(ssh, "Initialised %.200s client->server MAC algorithm%s%s", + ssh->csmac->text_name, + ssh->csmac_etm ? " (in ETM mode)" : "", + ssh->cscipher->required_mac ? " (required by cipher)" : ""); if (ssh->cscomp->text_name) logeventf(ssh, "Initialised %s compression", ssh->cscomp->text_name); @@ -7166,14 +7308,18 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, */ if (ssh->sc_cipher_ctx) ssh->sccipher->free_context(ssh->sc_cipher_ctx); - ssh->sccipher = s->sccipher_tobe; - ssh->sc_cipher_ctx = ssh->sccipher->make_context(); + if (ssh->sccipher) { + ssh->sccipher = s->sccipher_tobe; + ssh->sc_cipher_ctx = ssh->sccipher->make_context(); + } if (ssh->sc_mac_ctx) ssh->scmac->free_context(ssh->sc_mac_ctx); - ssh->scmac = s->scmac_tobe; - ssh->scmac_etm = s->scmac_etm_tobe; - ssh->sc_mac_ctx = ssh->scmac->make_context(ssh->sc_cipher_ctx); + if (ssh->scmac) { + ssh->scmac = s->scmac_tobe; + ssh->scmac_etm = s->scmac_etm_tobe; + ssh->sc_mac_ctx = ssh->scmac->make_context(ssh->sc_cipher_ctx); + } if (ssh->sc_comp_ctx) ssh->sccomp->decompress_cleanup(ssh->sc_comp_ctx); @@ -7184,28 +7330,38 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen, * Set IVs on server-to-client keys. Here we use the exchange * hash from the _first_ key exchange. */ - { - unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS]; - assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); - ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace); - assert((ssh->sccipher->keylen+7) / 8 <= - ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); - ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace); - assert(ssh->sccipher->blksize <= - ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); - ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace); - assert(ssh->scmac->len <= - ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); - ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace); - smemclr(keyspace, sizeof(keyspace)); - } - logeventf(ssh, "Initialised %.200s server->client encryption", - ssh->sccipher->text_name); - logeventf(ssh, "Initialised %.200s server->client MAC algorithm%s", - ssh->scmac->text_name, - ssh->scmac_etm ? " (in ETM mode)" : ""); + if (ssh->sccipher) { + unsigned char *key; + + key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'D', + ssh->sccipher->padded_keybytes); + ssh->sccipher->setkey(ssh->sc_cipher_ctx, key); + smemclr(key, ssh->sccipher->padded_keybytes); + sfree(key); + + key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'B', + ssh->sccipher->blksize); + ssh->sccipher->setiv(ssh->sc_cipher_ctx, key); + smemclr(key, ssh->sccipher->blksize); + sfree(key); + } + if (ssh->scmac) { + unsigned char *key; + + key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'F', + ssh->scmac->keylen); + ssh->scmac->setkey(ssh->sc_mac_ctx, key); + smemclr(key, ssh->scmac->keylen); + sfree(key); + } + if (ssh->sccipher) + logeventf(ssh, "Initialised %.200s server->client encryption", + ssh->sccipher->text_name); + if (ssh->scmac) + logeventf(ssh, "Initialised %.200s server->client MAC algorithm%s%s", + ssh->scmac->text_name, + ssh->scmac_etm ? " (in ETM mode)" : "", + ssh->sccipher->required_mac ? " (required by cipher)" : ""); if (ssh->sccomp->text_name) logeventf(ssh, "Initialised %s decompression", ssh->sccomp->text_name); @@ -7780,7 +7936,7 @@ static void ssh_check_termination(Ssh ssh) { if (ssh->version == 2 && !conf_get_int(ssh->conf, CONF_ssh_no_shell) && - count234(ssh->channels) == 0 && + (ssh->channels && count234(ssh->channels) == 0) && !(ssh->connshare && share_ndownstreams(ssh->connshare) > 0)) { /* * We used to send SSH_MSG_DISCONNECT here, because I'd @@ -9321,11 +9477,20 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen, s->can_keyb_inter = conf_get_int(ssh->conf, CONF_try_ki_auth) && in_commasep_string("keyboard-interactive", methods, methlen); #ifndef NO_GSSAPI - if (!ssh->gsslibs) - ssh->gsslibs = ssh_gss_setup(ssh->conf); - s->can_gssapi = conf_get_int(ssh->conf, CONF_try_gssapi_auth) && - in_commasep_string("gssapi-with-mic", methods, methlen) && - ssh->gsslibs->nlibraries > 0; + if (conf_get_int(ssh->conf, CONF_try_gssapi_auth) && + in_commasep_string("gssapi-with-mic", methods, methlen)) { + /* Try loading the GSS libraries and see if we + * have any. */ + if (!ssh->gsslibs) + ssh->gsslibs = ssh_gss_setup(ssh->conf); + s->can_gssapi = (ssh->gsslibs->nlibraries > 0); + } else { + /* No point in even bothering to try to load the + * GSS libraries, if the user configuration and + * server aren't both prepared to attempt GSSAPI + * auth in the first place. */ + s->can_gssapi = FALSE; + } #endif } @@ -10519,7 +10684,8 @@ static void do_ssh2_authconn(Ssh ssh, const 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(ssh, c); + if (c->type != CHAN_SHARING) + ssh2_try_send_and_unthrottle(ssh, c); } } @@ -11543,6 +11709,7 @@ Backend ssh_backend = { ssh_provide_logctx, ssh_unthrottle, ssh_cfg_info, + ssh_test_for_upstream, "ssh", PROT_SSH, 22