static const char *const ssh2_disconnect_reasons[] = {
NULL,
- "SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT",
- "SSH_DISCONNECT_PROTOCOL_ERROR",
- "SSH_DISCONNECT_KEY_EXCHANGE_FAILED",
- "SSH_DISCONNECT_HOST_AUTHENTICATION_FAILED",
- "SSH_DISCONNECT_MAC_ERROR",
- "SSH_DISCONNECT_COMPRESSION_ERROR",
- "SSH_DISCONNECT_SERVICE_NOT_AVAILABLE",
- "SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED",
- "SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE",
- "SSH_DISCONNECT_CONNECTION_LOST",
- "SSH_DISCONNECT_BY_APPLICATION",
- "SSH_DISCONNECT_TOO_MANY_CONNECTIONS",
- "SSH_DISCONNECT_AUTH_CANCELLED_BY_USER",
- "SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE",
- "SSH_DISCONNECT_ILLEGAL_USER_NAME",
+ "host not allowed to connect",
+ "protocol error",
+ "key exchange failed",
+ "host authentication failed",
+ "mac error",
+ "compression error",
+ "service not available",
+ "protocol version not supported",
+ "host key not verifiable",
+ "connection lost",
+ "by application",
+ "too many connections",
+ "auth cancelled by user",
+ "no more auth methods available",
+ "illegal user name",
};
#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 /* 0x1 */
const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
const static struct ssh_mac *macs[] = {
- &ssh_sha1, &ssh_md5
+ &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
};
const static struct ssh_mac *buggymacs[] = {
- &ssh_sha1_buggy, &ssh_md5
+ &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
};
static void *ssh_comp_none_init(void)
const struct plug_function_table *fn;
/* the above field _must_ be first in the structure */
- SHA_State exhash, exhashbase;
+ char *v_c, *v_s;
+ void *exhash;
Socket s;
void *cs_comp_ctx, *sc_comp_ctx;
const struct ssh_kex *kex;
const struct ssh_signkey *hostkey;
- unsigned char v2_session_id[20];
+ unsigned char v2_session_id[32];
+ int v2_session_id_len;
void *kex_ctx;
char *savedhost;
struct ssh_channel *mainchan; /* primary session channel */
int exitcode;
int close_expected;
+ int clean_exit;
tree234 *rportfwds, *portfwds;
*/
int fallback_cmd;
+ bufchain banner; /* accumulates banners during do_ssh2_authconn */
/*
* Used for username and password input.
*/
* _Completely_ silly lengths should be stomped on before they
* do us any more damage.
*/
- if (st->len < 0 || st->pad < 0 || st->len + st->pad < 0) {
+ 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);
/*
* Utility routines for putting an SSH-protocol `string' and
- * `uint32' into a SHA state.
+ * `uint32' into a hash state.
*/
-static void sha_string(SHA_State * s, void *str, int len)
+static void hash_string(const struct ssh_hash *h, void *s, void *str, int len)
{
unsigned char lenblk[4];
PUT_32BIT(lenblk, len);
- SHA_Bytes(s, lenblk, 4);
- SHA_Bytes(s, str, len);
+ h->bytes(s, lenblk, 4);
+ h->bytes(s, str, len);
}
-static void sha_uint32(SHA_State * s, unsigned i)
+static void hash_uint32(const struct ssh_hash *h, void *s, unsigned i)
{
unsigned char intblk[4];
PUT_32BIT(intblk, i);
- SHA_Bytes(s, intblk, 4);
+ h->bytes(s, intblk, 4);
}
/*
unsigned char *p;
int i, n = (bignum_bitcount(b) + 7) / 8;
p = snewn(n + 1, unsigned char);
- if (!p)
- fatalbox("out of memory");
p[0] = 0;
for (i = 1; i <= n; i++)
p[i] = bignum_byte(b, n - i);
}
#endif
-static void sha_mpint(SHA_State * s, Bignum b)
+static void hash_mpint(const struct ssh_hash *h, void *s, Bignum b)
{
unsigned char *p;
int len;
p = ssh2_mpint_fmt(b, &len);
- sha_string(s, p, len);
+ hash_string(h, s, p, len);
sfree(p);
}
if (ssh->cfg.sshbug_rekey2 == FORCE_ON ||
(ssh->cfg.sshbug_rekey2 == AUTO &&
- (wc_match("OpenSSH_2.[0-4]*", imp) ||
+ (wc_match("DigiSSH_2.0", imp) ||
+ wc_match("OpenSSH_2.[0-4]*", imp) ||
wc_match("OpenSSH_2.5.[0-3]*", imp) ||
wc_match("Sun_SSH_1.0", imp) ||
- wc_match("Sun_SSH_1.0.1", imp)))) {
+ wc_match("Sun_SSH_1.0.1", imp) ||
+ /* All versions <= 1.2.6 (they changed their format in 1.2.7) */
+ wc_match("WeOnlyDo-*", imp)))) {
/*
* These versions have the SSH-2 rekey bug.
*/
crBegin(ssh->do_ssh_init_crstate);
- /* Search for the string "SSH-" in the input. */
- s->i = 0;
- while (1) {
- static const int transS[] = { 1, 2, 2, 1 };
- static const int transH[] = { 0, 0, 3, 0 };
- static const int transminus[] = { 0, 0, 0, -1 };
- if (c == 'S')
- s->i = transS[s->i];
- else if (c == 'H')
- s->i = transH[s->i];
- else if (c == '-')
- s->i = transminus[s->i];
- else
- s->i = 0;
- if (s->i < 0)
- break;
- crReturn(1); /* get another character */
+ /* Search for a line beginning with the string "SSH-" in the input. */
+ for (;;) {
+ if (c != 'S') goto no;
+ crReturn(1);
+ if (c != 'S') goto no;
+ crReturn(1);
+ if (c != 'H') goto no;
+ crReturn(1);
+ if (c != '-') goto no;
+ break;
+ no:
+ while (c != '\012')
+ crReturn(1);
+ crReturn(1);
}
s->vstrsize = 16;
ssh_fix_verstring(verstring);
if (ssh->version == 2) {
+ size_t len;
/*
* Hash our version string and their version string.
*/
- SHA_Init(&ssh->exhashbase);
- sha_string(&ssh->exhashbase, verstring,
- strcspn(verstring, "\015\012"));
- sha_string(&ssh->exhashbase, s->vstring,
- strcspn(s->vstring, "\015\012"));
-
+ 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;
+
/*
* Initialise SSH-2 protocol.
*/
* everything to s_rdpkt, and then pass the resulting packets
* to the proper protocol handler.
*/
- if (datalen == 0)
- crReturnV;
-
- /*
- * Process queued data if there is any.
- */
- ssh_process_queued_incoming_data(ssh);
while (1) {
- while (datalen > 0) {
- if (ssh->frozen)
+ while (bufchain_size(&ssh->queued_incoming_data) > 0 || datalen > 0) {
+ if (ssh->frozen) {
ssh_queue_incoming_data(ssh, &data, &datalen);
-
- ssh_process_incoming_data(ssh, &data, &datalen);
-
+ /* This uses up all data and cannot cause anything interesting
+ * to happen; indeed, for anything to happen at all, we must
+ * return, so break out. */
+ break;
+ } else if (bufchain_size(&ssh->queued_incoming_data) > 0) {
+ /* This uses up some or all data, and may freeze the
+ * session. */
+ ssh_process_queued_incoming_data(ssh);
+ } else {
+ /* This uses up some or all data, and may freeze the
+ * session. */
+ ssh_process_incoming_data(ssh, &data, &datalen);
+ }
+ /* FIXME this is probably EBW. */
if (ssh->state == SSH_STATE_CLOSED)
return;
}
+ /* We're out of data. Go and get some more. */
crReturnV;
}
crFinishV;
Ssh ssh = (Ssh) plug;
int need_notify = ssh_do_close(ssh, FALSE);
- if (!error_msg && !ssh->close_expected) {
- error_msg = "Server unexpectedly closed network connection";
+ if (!error_msg) {
+ if (!ssh->close_expected)
+ error_msg = "Server unexpectedly closed network connection";
+ else
+ error_msg = "Server closed network connection";
}
if (need_notify)
notify_remote_exit(ssh->frontend);
- if (error_msg) {
- /* A socket error has occurred. */
+ if (error_msg)
logevent(error_msg);
+ if (!ssh->close_expected || !ssh->clean_exit)
connection_fatal(ssh->frontend, "%s", error_msg);
- } else {
- logevent("Server closed network connection");
- }
return 0;
}
const char *err;
ssh->savedhost = snewn(1 + strlen(host), char);
- if (!ssh->savedhost)
- fatalbox("Out of memory");
strcpy(ssh->savedhost, host);
if (port < 0)
}
/*
+ * Client-initiated disconnection. Send a DISCONNECT if `wire_reason'
+ * non-NULL, otherwise just close the connection. `client_reason' == NULL
+ * => log `wire_reason'.
+ */
+static void ssh_disconnect(Ssh ssh, char *client_reason, char *wire_reason,
+ int code, int clean_exit)
+{
+ char *error;
+ if (!client_reason)
+ client_reason = wire_reason;
+ if (client_reason)
+ error = dupprintf("Disconnected: %s", client_reason);
+ else
+ error = dupstr("Disconnected");
+ if (wire_reason) {
+ if (ssh->version == 1) {
+ send_packet(ssh, SSH1_MSG_DISCONNECT, PKT_STR, wire_reason,
+ PKT_END);
+ } else if (ssh->version == 2) {
+ struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+ ssh2_pkt_adduint32(pktout, code);
+ ssh2_pkt_addstring(pktout, wire_reason);
+ ssh2_pkt_addstring(pktout, "en"); /* language tag */
+ ssh2_pkt_send_noqueue(ssh, pktout);
+ }
+ }
+ ssh->close_expected = TRUE;
+ ssh->clean_exit = clean_exit;
+ ssh_closing((Plug)ssh, error, 0, 0);
+ sfree(error);
+}
+
+/*
* Handle the key exchange and user authentication phases.
*/
static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
s->rsabuf = snewn(s->len, unsigned char);
- if (!s->rsabuf)
- fatalbox("Out of memory");
/*
* Verify the host key.
int len = rsastr_len(&hostkey);
char fingerprint[100];
char *keystr = snewn(len, char);
- if (!keystr)
- fatalbox("Out of memory");
rsastr_fmt(keystr, &hostkey);
rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at host key verification",
+ NULL, 0, TRUE);
crStop(0);
}
}
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
+ 0, TRUE);
crStop(0);
}
}
* get_line failed to get a username.
* Terminate.
*/
- logevent("No username provided. Abandoning session.");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
crStop(1);
}
} else {
* because one was supplied on the command line
* which has already failed to work). Terminate.
*/
- send_packet(ssh, SSH1_MSG_DISCONNECT,
- PKT_STR, "No more passwords available to try",
- PKT_END);
- logevent("Unable to authenticate");
- connection_fatal(ssh->frontend, "Unable to authenticate");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate", 0, FALSE);
crStop(1);
}
} else {
* encrypted packet, we close the session once
* we've sent EXIT_CONFIRMATION.
*/
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL, NULL, 0, TRUE);
}
/* Helper function to deal with sending tty modes for REQUEST_PTY */
/*
* SSH-2 key creation method.
*/
-static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H,
- unsigned char *sessid, char chr,
+static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr,
unsigned char *keyspace)
{
- SHA_State s;
- /* First 20 bytes. */
- SHA_Init(&s);
+ const struct ssh_hash *h = ssh->kex->hash;
+ void *s;
+ /* First hlen bytes. */
+ s = h->init();
if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
- sha_mpint(&s, K);
- SHA_Bytes(&s, H, 20);
- SHA_Bytes(&s, &chr, 1);
- SHA_Bytes(&s, sessid, 20);
- SHA_Final(&s, keyspace);
- /* Next 20 bytes. */
- SHA_Init(&s);
+ hash_mpint(h, s, K);
+ 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))
- sha_mpint(&s, K);
- SHA_Bytes(&s, H, 20);
- SHA_Bytes(&s, keyspace, 20);
- SHA_Final(&s, keyspace + 20);
+ hash_mpint(h, s, K);
+ h->bytes(s, H, h->hlen);
+ h->bytes(s, keyspace, h->hlen);
+ h->final(s, keyspace + h->hlen);
}
/*
struct do_ssh2_transport_state {
int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher;
Bignum p, g, e, f, K;
+ void *our_kexinit;
+ int our_kexinitlen;
int kex_init_value, kex_reply_value;
const struct ssh_mac **maclist;
int nmacs;
char *hostkeydata, *sigdata, *keystr, *fingerprint;
int hostkeylen, siglen;
void *hkey; /* actual host key */
- unsigned char exchange_hash[20];
+ unsigned char exchange_hash[32];
int n_preferred_kex;
- const struct ssh_kex *preferred_kex[KEX_MAX];
+ const struct ssh_kexes *preferred_kex[KEX_MAX];
int n_preferred_ciphers;
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
const struct ssh_compress *preferred_comp;
ssh2_pkt_addstring_start(s->pktout);
commalist_started = 0;
for (i = 0; i < s->n_preferred_kex; i++) {
- const struct ssh_kex *k = s->preferred_kex[i];
+ const struct ssh_kexes *k = s->preferred_kex[i];
if (!k) continue; /* warning flag */
- if (commalist_started)
- ssh2_pkt_addstring_str(s->pktout, ",");
- ssh2_pkt_addstring_str(s->pktout, s->preferred_kex[i]->name);
- commalist_started = 1;
+ for (j = 0; j < k->nkexes; j++) {
+ if (commalist_started)
+ ssh2_pkt_addstring_str(s->pktout, ",");
+ ssh2_pkt_addstring_str(s->pktout, k->list[j]->name);
+ commalist_started = 1;
+ }
}
/* List server host key algorithms. */
ssh2_pkt_addstring_start(s->pktout);
ssh2_pkt_adduint32(s->pktout, 0);
}
- ssh->exhash = ssh->exhashbase;
- sha_string(&ssh->exhash, s->pktout->data + 5, s->pktout->length - 5);
+ s->our_kexinitlen = s->pktout->length - 5;
+ s->our_kexinit = snewn(s->our_kexinitlen, unsigned char);
+ memcpy(s->our_kexinit, s->pktout->data + 5, s->our_kexinitlen);
ssh2_pkt_send_noqueue(ssh, s->pktout);
if (!pktin)
crWaitUntil(pktin);
- if (pktin->length > 5)
- sha_string(&ssh->exhash, pktin->data + 5, pktin->length - 5);
/*
* Now examine the other side's KEXINIT to see what we're up
preferred = NULL;
for (i = 0; i < s->n_preferred_kex; i++) {
- const struct ssh_kex *k = s->preferred_kex[i];
+ const struct ssh_kexes *k = s->preferred_kex[i];
if (!k) {
s->warn_kex = TRUE;
} else {
- if (!preferred) preferred = k->name;
- if (in_commasep_string(k->name, str, len))
- ssh->kex = k;
+ for (j = 0; j < k->nkexes; j++) {
+ if (!preferred) preferred = k->list[j]->name;
+ if (in_commasep_string(k->list[j]->name, str, len)) {
+ ssh->kex = k->list[j];
+ break;
+ }
+ }
}
if (ssh->kex)
break;
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at kex warning", NULL,
+ 0, TRUE);
crStop(0);
}
}
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
+ 0, TRUE);
crStop(0);
}
}
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
+ 0, TRUE);
crStop(0);
}
}
+ ssh->exhash = ssh->kex->hash->init();
+ hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c));
+ hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s));
+ hash_string(ssh->kex->hash, ssh->exhash,
+ s->our_kexinit, s->our_kexinitlen);
+ sfree(s->our_kexinit);
+ if (pktin->length > 5)
+ hash_string(ssh->kex->hash, ssh->exhash,
+ pktin->data + 5, pktin->length - 5);
+
if (s->ignorepkt) /* first_kex_packet_follows */
crWaitUntil(pktin); /* Ignore packet */
}
scbits = s->sccipher_tobe->keylen;
s->nbits = (csbits > scbits ? csbits : scbits);
}
- /* The keys only have 160-bit entropy, since they're based on
- * a SHA-1 hash. So cap the key size at 160 bits. */
- if (s->nbits > 160)
- s->nbits = 160;
+ /* The keys only have hlen-bit entropy, since they're based on
+ * a hash. So cap the key size at hlen bits. */
+ if (s->nbits > ssh->kex->hash->hlen * 8)
+ s->nbits = ssh->kex->hash->hlen * 8;
/*
* If we're doing Diffie-Hellman group exchange, start by
* involve user interaction. */
set_busy_status(ssh->frontend, BUSY_NOT);
- sha_string(&ssh->exhash, s->hostkeydata, s->hostkeylen);
- if (ssh->kex == &ssh_diffiehellman_gex) {
- sha_uint32(&ssh->exhash, s->pbits);
- sha_mpint(&ssh->exhash, s->p);
- sha_mpint(&ssh->exhash, s->g);
+ hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
+ if (!ssh->kex->pdata) {
+ hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->p);
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->g);
}
- sha_mpint(&ssh->exhash, s->e);
- sha_mpint(&ssh->exhash, s->f);
- sha_mpint(&ssh->exhash, s->K);
- SHA_Final(&ssh->exhash, s->exchange_hash);
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->K);
+ assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
+ ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
dh_cleanup(ssh->kex_ctx);
ssh->kex_ctx = NULL;
#if 0
debug(("Exchange hash is:\n"));
- dmemdump(s->exchange_hash, 20);
+ dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
#endif
s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
if (!s->hkey ||
!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
- (char *)s->exchange_hash, 20)) {
+ (char *)s->exchange_hash,
+ ssh->kex->hash->hlen)) {
bombout(("Server's host key did not match the signature supplied"));
crStop(0);
}
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at host key verification", NULL,
+ 0, TRUE);
crStop(0);
}
if (!s->got_session_id) { /* don't bother logging this in rekeys */
* authentication.
*/
if (!s->got_session_id) {
+ assert(sizeof(s->exchange_hash) <= sizeof(ssh->v2_session_id));
memcpy(ssh->v2_session_id, s->exchange_hash,
sizeof(s->exchange_hash));
+ ssh->v2_session_id_len = ssh->kex->hash->hlen;
+ assert(ssh->v2_session_id_len <= sizeof(ssh->v2_session_id));
s->got_session_id = TRUE;
}
*/
{
unsigned char keyspace[40];
- ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'C',keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace);
ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'A',keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace);
ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'E',keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace);
ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace);
}
*/
{
unsigned char keyspace[40];
- ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'D',keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace);
ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'B',keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace);
ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'F',keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace);
ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace);
}
logeventf(ssh, "Initialised %.200s server->client encryption",
*/
freebn(s->f);
freebn(s->K);
- if (ssh->kex == &ssh_diffiehellman_gex) {
+ if (!ssh->kex->pdata) {
freebn(s->g);
freebn(s->p);
}
* it would only confuse the layer above.
*/
if (s->activated_authconn) {
- crReturn(1);
+ crReturn(0);
}
s->activated_authconn = TRUE;
* not running in -N mode.)
*/
if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) {
- logevent("All channels closed. Disconnecting");
-#if 0
/*
* We used to send SSH_MSG_DISCONNECT here,
* because I'd believed that _every_ conforming
* this is more polite than sending a
* DISCONNECT. So now we don't.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "All open channels closed");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
-#endif
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);
}
}
*/
c = find234(ssh->channels, &localid, ssh_channelfind);
if (!c) {
- char buf[80];
- sprintf(buf, "Received channel request for nonexistent"
- " channel %d", localid);
- logevent(buf);
- pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(pktout, buf);
- ssh2_pkt_addstring(pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, pktout);
- connection_fatal(ssh->frontend, "%s", buf);
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ char *buf = dupprintf("Received channel request for nonexistent"
+ " channel %d", localid);
+ ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+ sfree(buf);
return;
}
}
}
} else if (typelen == 22 &&
- !memcmp(type, "auth-agent@openssh.com", 3)) {
+ !memcmp(type, "auth-agent@openssh.com", 22)) {
if (!ssh->agentfwd_enabled)
error = "Agent forwarding is not enabled";
else {
}
}
+/*
+ * Buffer banner messages for later display at some convenient point.
+ */
+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) {
+ char *banner = NULL;
+ int size = 0;
+ ssh_pkt_getstring(pktin, &banner, &size);
+ if (banner)
+ bufchain_add(&ssh->banner, banner, size);
+ }
+}
+
/* Helper function to deal with sending tty modes for "pty-req" */
static void ssh2_send_ttymode(void *data, char *mode, char *val)
{
*/
s->username[0] = '\0';
s->got_username = FALSE;
+ bufchain_init(&ssh->banner);
+ ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] =
+ ssh2_msg_userauth_banner;
while (!s->we_are_in) {
/*
* Get a username.
* get_line failed to get a username.
* Terminate.
*/
- logevent("No username provided. Abandoning session.");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
crStopV;
}
} else {
*/
if (!s->gotit)
crWaitUntilV(pktin);
- while (pktin->type == SSH2_MSG_USERAUTH_BANNER) {
- char *banner;
- int size;
+ /*
+ * Now is a convenient point to spew any banner material
+ * that we've accumulated. (This should ensure that when
+ * we exit the auth loop, we haven't any left to deal
+ * with.)
+ */
+ {
+ int size = bufchain_size(&ssh->banner);
/*
* Don't show the banner if we're operating in
* non-verbose non-interactive mode. (It's probably
* the banner will screw up processing on the
* output of (say) plink.)
*/
- if (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE)) {
- ssh_pkt_getstring(pktin, &banner, &size);
- if (banner)
- c_write_untrusted(ssh, banner, size);
+ if (size && (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) {
+ char *banner = snewn(size, char);
+ bufchain_fetch(&ssh->banner, banner, size);
+ c_write_untrusted(ssh, banner, size);
+ sfree(banner);
}
- crWaitUntilV(pktin);
+ bufchain_clear(&ssh->banner);
}
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
logevent("Access granted");
ssh2_pkt_addstring_start(s->pktout);
ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);
- s->siglen = s->pktout->length - 5 + 4 + 20;
+ s->siglen = s->pktout->length - 5 + 4 +
+ ssh->v2_session_id_len;
if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
s->siglen -= 4;
s->len = 1; /* message type */
s->q += 4;
/* Now the data to be signed... */
if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
- PUT_32BIT(s->q, 20);
+ PUT_32BIT(s->q, ssh->v2_session_id_len);
s->q += 4;
}
- memcpy(s->q, ssh->v2_session_id, 20);
- s->q += 20;
+ memcpy(s->q, ssh->v2_session_id,
+ ssh->v2_session_id_len);
+ s->q += ssh->v2_session_id_len;
memcpy(s->q, s->pktout->data + 5,
s->pktout->length - 5);
s->q += s->pktout->length - 5;
* command line which has already failed to
* work). Terminate.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout,SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "No more passwords available"
- " to try");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
- logevent("Unable to authenticate");
- connection_fatal(ssh->frontend,
- "Unable to authenticate");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate",
+ SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ FALSE);
crStopV;
}
} else {
* followed by everything so far placed in the
* outgoing packet.
*/
- sigdata_len = s->pktout->length - 5 + 4 + 20;
+ sigdata_len = s->pktout->length - 5 + 4 +
+ ssh->v2_session_id_len;
if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
sigdata_len -= 4;
sigdata = snewn(sigdata_len, unsigned char);
p = 0;
if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
- PUT_32BIT(sigdata+p, 20);
+ PUT_32BIT(sigdata+p, ssh->v2_session_id_len);
p += 4;
}
- memcpy(sigdata+p, ssh->v2_session_id, 20); p += 20;
+ memcpy(sigdata+p, ssh->v2_session_id,
+ ssh->v2_session_id_len);
+ p += ssh->v2_session_id_len;
memcpy(sigdata+p, s->pktout->data + 5,
s->pktout->length - 5);
p += s->pktout->length - 5;
}
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
} else {
- c_write_str(ssh, "No supported authentication methods"
- " left to try!\r\n");
- logevent("No supported authentications offered."
- " Disconnecting");
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "No supported authentication"
- " methods available");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL,
+ "No supported authentication methods available",
+ SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+ FALSE);
crStopV;
}
}
}
+ ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL;
/*
* Now the connection protocol has started, one way or another.
ssh->hostkey = NULL;
ssh->exitcode = -1;
ssh->close_expected = FALSE;
+ ssh->clean_exit = FALSE;
ssh->state = SSH_STATE_PREPACKET;
ssh->size_needed = FALSE;
ssh->eof_needed = FALSE;
ssh->do_ssh1_login_state = NULL;
ssh->do_ssh2_transport_state = NULL;
ssh->do_ssh2_authconn_state = NULL;
+ ssh->v_c = NULL;
+ ssh->v_s = NULL;
ssh->mainchan = NULL;
ssh->throttled_all = 0;
ssh->v1_stdout_throttling = 0;
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);
if (ssh->crcda_ctx) {
crcda_free_context(ssh->crcda_ctx);
ssh->crcda_ctx = NULL;
unsigned long old_max_data_size;
pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);
- ssh_setup_portfwd(ssh, cfg);
+ if (ssh->portfwds)
+ ssh_setup_portfwd(ssh, cfg);
if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time &&
cfg->ssh_rekey_time != 0) {
}
/*
- * Called to send data down the Telnet connection.
+ * Called to send data down the SSH connection.
*/
static int ssh_send(void *handle, char *buf, int len)
{
}
/*
- * Send Telnet special codes. TS_EOF is useful for `plink', so you
+ * Send special codes. TS_EOF is useful for `plink', so you
* can send an EOF and collect resulting output (e.g. `plink
* hostname sort').
*/