*/
#define SSH2_PKTCTX_DHGROUP 0x0001
#define SSH2_PKTCTX_DHGEX 0x0002
+#define SSH2_PKTCTX_KEX_MASK 0x000F
#define SSH2_PKTCTX_PUBLICKEY 0x0010
#define SSH2_PKTCTX_PASSWORD 0x0020
#define SSH2_PKTCTX_KBDINTER 0x0040
#define BUG_CHOKES_ON_RSA 8
#define BUG_SSH2_RSA_PADDING 16
#define BUG_SSH2_DERIVEKEY 32
-/* 64 was BUG_SSH2_DH_GEX, now spare */
+#define BUG_SSH2_REKEY 64
#define BUG_SSH2_PK_SESSIONID 128
#define translate(x) if (type == x) return #x
#define SSH1_BUFFER_LIMIT 32768
#define SSH_MAX_BACKLOG 32768
#define OUR_V2_WINSIZE 16384
+#define OUR_V2_MAXPKT 0x4000UL
const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
static int ssh_sendbuffer(void *handle);
-static void ssh_do_close(Ssh ssh);
+static int ssh_do_close(Ssh ssh, int notify_exit);
static unsigned long ssh_pkt_getuint32(struct Packet *pkt);
static int ssh2_pkt_getbool(struct Packet *pkt);
static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
tree234 *channels; /* indexed by local id */
struct ssh_channel *mainchan; /* primary session channel */
int exitcode;
+ int close_expected;
tree234 *rportfwds, *portfwds;
#define bombout(msg) \
do { \
char *text = dupprintf msg; \
- ssh_do_close(ssh); \
+ ssh_do_close(ssh, FALSE); \
logevent(text); \
connection_fatal(ssh->frontend, "%s", text); \
sfree(text); \
if (!ssh->kex_in_progress &&
ssh->max_data_size != 0 &&
ssh->outgoing_data_size > ssh->max_data_size)
- do_ssh2_transport(ssh, "Initiating key re-exchange "
- "(too much data sent)", -1, NULL);
+ do_ssh2_transport(ssh, "too much data sent", -1, NULL);
ssh_free_packet(pkt);
}
if (!ssh->kex_in_progress &&
ssh->max_data_size != 0 &&
ssh->outgoing_data_size > ssh->max_data_size)
- do_ssh2_transport(ssh, "Initiating key re-exchange "
- "(too much data sent)", -1, NULL);
+ do_ssh2_transport(ssh, "too much data sent", -1, NULL);
ssh->deferred_data_size = 0;
}
ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID;
logevent("We believe remote version has SSH2 public-key-session-ID bug");
}
+
+ if (ssh->cfg.sshbug_rekey2 == FORCE_ON ||
+ (ssh->cfg.sshbug_rekey2 == AUTO &&
+ (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)))) {
+ /*
+ * These versions have the SSH2 rekey bug.
+ */
+ ssh->remote_bugs |= BUG_SSH2_REKEY;
+ logevent("We believe remote version has SSH2 rekey bug");
+ }
}
/*
crFinishV;
}
-static void ssh_do_close(Ssh ssh)
+static int ssh_do_close(Ssh ssh, int notify_exit)
{
- int i;
+ int i, ret = 0;
struct ssh_channel *c;
ssh->state = SSH_STATE_CLOSED;
if (ssh->s) {
sk_close(ssh->s);
ssh->s = NULL;
- notify_remote_exit(ssh->frontend);
+ if (notify_exit)
+ notify_remote_exit(ssh->frontend);
+ else
+ ret = 1;
}
/*
* Now we must shut down any port and X forwardings going
sfree(c);
}
}
+
+ return ret;
}
static int ssh_closing(Plug plug, const char *error_msg, int error_code,
int calling_back)
{
Ssh ssh = (Ssh) plug;
- ssh_do_close(ssh);
+ int need_notify = ssh_do_close(ssh, FALSE);
+
+ if (!error_msg && !ssh->close_expected) {
+ error_msg = "Server unexpectedly closed network connection";
+ }
+
if (error_msg) {
/* A socket error has occurred. */
logevent(error_msg);
connection_fatal(ssh->frontend, "%s", error_msg);
} else {
- /* Otherwise, the remote side closed the connection normally. */
+ logevent("Server closed network connection");
}
+ if (need_notify)
+ notify_remote_exit(ssh->frontend);
return 0;
}
Ssh ssh = (Ssh) plug;
ssh_gotdata(ssh, (unsigned char *)data, len);
if (ssh->state == SSH_STATE_CLOSED) {
- ssh_do_close(ssh);
+ ssh_do_close(ssh, TRUE);
return 0;
}
return 1;
* Terminate.
*/
logevent("No username provided. Abandoning session.");
+ ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
crStop(1);
}
PKT_END);
logevent("Unable to authenticate");
connection_fatal(ssh->frontend, "Unable to authenticate");
+ ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
crStop(1);
}
* encrypted packet, we close the session once
* we've sent EXIT_CONFIRMATION.
*/
+ ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
}
s->maclist = macs, s->nmacs = lenof(macs);
begin_key_exchange:
+ ssh->pkt_ctx &= ~SSH2_PKTCTX_KEX_MASK;
{
int i, j, commalist_started;
*/
while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) ||
(!pktin && inlen == -1))) {
+ wait_for_rekey:
crReturn(1);
}
if (pktin) {
logevent("Server initiated key re-exchange");
} else {
- logevent((char *)in);
+ /*
+ * 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,
+ * we process it anyway!)
+ */
+ if ((ssh->remote_bugs & BUG_SSH2_REKEY)) {
+ logeventf(ssh, "Server bug prevents key re-exchange (%s)",
+ (char *)in);
+ /* Reset the counters, so that at least this message doesn't
+ * hit the event log _too_ often. */
+ ssh->outgoing_data_size = 0;
+ ssh->incoming_data_size = 0;
+ if (ssh->cfg.ssh_rekey_time != 0) {
+ ssh->next_rekey =
+ schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
+ ssh2_timer, ssh);
+ }
+ goto wait_for_rekey; /* this is utterly horrid */
+ } else {
+ logeventf(ssh, "Initiating key re-exchange (%s)", (char *)in);
+ }
}
goto begin_key_exchange;
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);
}
}
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);
return;
}
ssh2_pkt_adduint32(pktout, c->remoteid);
ssh2_pkt_adduint32(pktout, c->localid);
ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);
- ssh2_pkt_adduint32(pktout, 0x4000UL); /* our max pkt size */
+ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */
ssh2_pkt_send(ssh, pktout);
}
}
* Terminate.
*/
logevent("No username provided. Abandoning session.");
+ ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
crStopV;
}
}
}
- if (!s->method && s->can_keyb_inter && !s->kbd_inter_refused) {
+ if (!s->method && s->can_keyb_inter && !s->kbd_inter_refused &&
+ !s->kbd_inter_running) {
s->method = AUTH_KEYBOARD_INTERACTIVE;
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
logevent("Unable to authenticate");
connection_fatal(ssh->frontend,
"Unable to authenticate");
+ ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
crStopV;
}
" 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);
crStopV;
}
ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
- ssh2_pkt_adduint32(s->pktout, 0x4000UL); /* our max pkt size */
+ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */
ssh2_pkt_send(ssh, s->pktout);
crWaitUntilV(pktin);
if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
if (!ssh->kex_in_progress && ssh->cfg.ssh_rekey_time != 0 &&
now - ssh->next_rekey >= 0) {
- do_ssh2_transport(ssh, "Initiating key re-exchange (timeout)",
- -1, NULL);
+ do_ssh2_transport(ssh, "timeout", -1, NULL);
}
}
if (!ssh->kex_in_progress &&
ssh->max_data_size != 0 &&
ssh->incoming_data_size > ssh->max_data_size)
- do_ssh2_transport(ssh, "Initiating key re-exchange "
- "(too much data received)", -1, NULL);
+ do_ssh2_transport(ssh, "too much data received", -1, NULL);
}
if (pktin && ssh->packet_dispatch[pktin->type]) {
ssh->kex_ctx = NULL;
ssh->hostkey = NULL;
ssh->exitcode = -1;
+ ssh->close_expected = FALSE;
ssh->state = SSH_STATE_PREPACKET;
ssh->size_needed = FALSE;
ssh->eof_needed = FALSE;
ssh->crcda_ctx = NULL;
}
if (ssh->s)
- ssh_do_close(ssh);
+ ssh_do_close(ssh, TRUE);
expire_timer_context(ssh);
if (ssh->pinger)
pinger_free(ssh->pinger);
long now = GETTICKCOUNT();
if (new_next - now < 0) {
- rekeying = "Initiating key re-exchange (timeout shortened)";
+ rekeying = "timeout shortened";
} else {
ssh->next_rekey = schedule_timer(new_next - now, ssh2_timer, ssh);
}
ssh->max_data_size != 0) {
if (ssh->outgoing_data_size > ssh->max_data_size ||
ssh->incoming_data_size > ssh->max_data_size)
- rekeying = "Initiating key re-exchange (data limit lowered)";
+ rekeying = "data limit lowered";
}
if (ssh->cfg.compression != cfg->compression) {
- rekeying = "Initiating key re-exchange (compression setting changed)";
+ rekeying = "compression setting changed";
rekey_mandatory = TRUE;
}
if (ssh->cfg.ssh2_des_cbc != cfg->ssh2_des_cbc ||
memcmp(ssh->cfg.ssh_cipherlist, cfg->ssh_cipherlist,
sizeof(ssh->cfg.ssh_cipherlist))) {
- rekeying = "Initiating key re-exchange (cipher settings changed)";
+ rekeying = "cipher settings changed";
rekey_mandatory = TRUE;
}
}
} else if (code == TS_REKEY) {
if (!ssh->kex_in_progress && ssh->version == 2) {
- do_ssh2_transport(ssh, "Initiating key re-exchange at"
- " user request", -1, NULL);
+ do_ssh2_transport(ssh, "at user request", -1, NULL);
}
} else if (code == TS_BRK) {
if (ssh->state == SSH_STATE_CLOSED
ssh2_pkt_adduint32(pktout, c->localid);
c->v.v2.locwindow = OUR_V2_WINSIZE;
ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */
- ssh2_pkt_adduint32(pktout, 0x4000UL); /* our max pkt size */
+ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */
ssh2_pkt_addstring(pktout, hostname);
ssh2_pkt_adduint32(pktout, port);
/*