const struct plug_function_table *fn;
/* the above field _must_ be first in the structure */
- SHA_State exhash, exhashbase;
+ char *v_c, *v_s;
+ SHA_State exhash;
Socket s;
struct ssh_channel *mainchan; /* primary session channel */
int exitcode;
int close_expected;
+ int clean_exit;
tree234 *rportfwds, *portfwds;
* _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);
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);
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");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
- connection_fatal(ssh->frontend, "Unable to authenticate");
+ 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 */
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;
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
}
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);
}
}
+ SHA_Init(&ssh->exhash);
+ sha_string(&ssh->exhash, ssh->v_c, strlen(ssh->v_c));
+ sha_string(&ssh->exhash, ssh->v_s, strlen(ssh->v_s));
+ sha_string(&ssh->exhash, s->our_kexinit, s->our_kexinitlen);
+ sfree(s->our_kexinit);
+ if (pktin->length > 5)
+ sha_string(&ssh->exhash, pktin->data + 5, pktin->length - 5);
+
+
if (s->ignorepkt) /* first_kex_packet_follows */
crWaitUntil(pktin); /* Ignore packet */
}
}
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 */
* 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);
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
- connection_fatal(ssh->frontend, "%s", buf);
+ 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 {
* 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 {
* 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");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
- connection_fatal(ssh->frontend,
- "Unable to authenticate");
+ ssh_disconnect(ssh, NULL, "Unable to authenticate",
+ SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ FALSE);
crStopV;
}
} else {
}
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->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;
}
/*
- * 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').
*/