*
* - SSH1_BUFFER_LIMIT is the amount of backlog that must build up
* on a local data stream before we throttle the whole SSH
- * connection (in SSH1 only). Throttling the whole connection is
+ * connection (in SSH-1 only). Throttling the whole connection is
* pretty drastic so we set this high in the hope it won't
* happen very often.
*
* - SSH_MAX_BACKLOG is the amount of backlog that must build up
* on the SSH connection itself before we defensively throttle
* _all_ local data streams. This is pretty drastic too (though
- * thankfully unlikely in SSH2 since the window mechanism should
+ * thankfully unlikely in SSH-2 since the window mechanism should
* ensure that the server never has any need to throttle its end
* of the connection), so we set this high as well.
*
- * - OUR_V2_WINSIZE is the maximum window size we present on SSH2
+ * - OUR_V2_WINSIZE is the maximum window size we present on SSH-2
* channels.
*/
Ssh ssh; /* pointer back to main context */
unsigned remoteid, localid;
int type;
+ /* True if we opened this channel but server hasn't confirmed. */
+ int halfopen;
/*
- * In SSH1, this value contains four bits:
+ * In SSH-1, this value contains four bits:
*
* 1 We have sent SSH1_MSG_CHANNEL_CLOSE.
* 2 We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
};
/*
- * 2-3-4 tree storing remote->local port forwardings. SSH 1 and SSH
- * 2 use this structure in different ways, reflecting SSH 2's
+ * 2-3-4 tree storing remote->local port forwardings. SSH-1 and SSH-2
+ * use this structure in different ways, reflecting SSH-2's
* altogether saner approach to port forwarding.
*
- * In SSH 1, you arrange a remote forwarding by sending the server
+ * In SSH-1, you arrange a remote forwarding by sending the server
* the remote port number, and the local destination host:port.
* When a connection comes in, the server sends you back that
* host:port pair, and you connect to it. This is a ready-made
* host:port pairs we _are_ trying to forward to, and reject a
* connection request from the server if it's not in the list.
*
- * In SSH 2, each side of the connection minds its own business and
+ * In SSH-2, each side of the connection minds its own business and
* doesn't send unnecessary information to the other. You arrange a
* remote forwarding by sending the server just the remote port
* number. When a connection comes in, the server tells you which
* of its ports was connected to; and _you_ have to remember what
* local host:port pair went with that port number.
*
- * Hence, in SSH 1 this structure is indexed by destination
- * host:port pair, whereas in SSH 2 it is indexed by source port.
+ * Hence, in SSH-1 this structure is indexed by destination
+ * host:port pair, whereas in SSH-2 it is indexed by source port.
*/
struct ssh_portfwd; /* forward declaration */
struct Packet {
long length;
+ long forcepad; /* Force padding to at least this length */
int type;
unsigned long sequence;
unsigned char *data;
unsigned char *body;
long savedpos;
long maxlen;
- long encrypted_len; /* for SSH2 total-size counting */
+ long encrypted_len; /* for SSH-2 total-size counting */
/*
* State associated with packet logging
struct logblank_t *blanks;
};
-static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen,
+static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
struct Packet *pktin);
-static void ssh2_protocol(Ssh ssh, unsigned char *in, int inlen,
+static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
struct Packet *pktin);
static void ssh1_protocol_setup(Ssh ssh);
static void ssh2_protocol_setup(Ssh ssh);
static int ssh2_pkt_getbool(struct Packet *pkt);
static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
static void ssh2_timer(void *ctx, long now);
-static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen,
+static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
struct Packet *pktin);
struct rdpkt1_state_tag {
int overall_bufsize;
int throttled_all;
int v1_stdout_throttling;
- int v2_outgoing_sequence;
+ unsigned long v2_outgoing_sequence;
int ssh1_rdpkt_crstate;
int ssh2_rdpkt_crstate;
struct rdpkt1_state_tag rdpkt1_state;
struct rdpkt2_state_tag rdpkt2_state;
- /* ssh1 and ssh2 use this for different things, but both use it */
+ /* SSH-1 and SSH-2 use this for different things, but both use it */
int protocol_initial_phase_done;
- void (*protocol) (Ssh ssh, unsigned char *in, int inlen,
+ void (*protocol) (Ssh ssh, void *vin, int inlen,
struct Packet *pkt);
struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);
Config cfg;
/*
- * Used to transfer data back from async agent callbacks.
+ * Used to transfer data back from async callbacks.
*/
void *agent_response;
int agent_response_len;
+ int user_response;
+
+ /*
+ * The SSH connection can be set as `frozen', meaning we are
+ * not currently accepting incoming data from the network. This
+ * is slightly more serious than setting the _socket_ as
+ * frozen, because we may already have had data passed to us
+ * from the network which we need to delay processing until
+ * after the freeze is lifted, so we also need a bufchain to
+ * store that data.
+ */
+ int frozen;
+ bufchain queued_incoming_data;
/*
* Dispatch table for packet types that we may have to deal
}
/*
- * SSH2 packet construction functions.
+ * SSH-2 packet construction functions.
*/
static void ssh2_pkt_ensure(struct Packet *pkt, int length)
{
{
struct Packet *pkt = ssh_new_packet();
pkt->length = 5;
+ pkt->forcepad = 0;
ssh2_pkt_addbyte(pkt, (unsigned char) pkt_type);
return pkt;
}
}
/*
- * Construct an SSH2 final-form packet: compress it, encrypt it,
+ * Construct an SSH-2 final-form packet: compress it, encrypt it,
* put the MAC on it. Final packet, ready to be sent, is stored in
* pkt->data. Total length is returned.
*/
/*
* Add padding. At least four bytes, and must also bring total
* length (minus MAC) up to a multiple of the block size.
+ * If pkt->forcepad is set, make sure the packet is at least that size
+ * after padding.
*/
cipherblk = ssh->cscipher ? ssh->cscipher->blksize : 8; /* block size */
cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
padding = 4;
+ if (pkt->length + padding < pkt->forcepad)
+ padding = pkt->forcepad - pkt->length;
padding +=
(cipherblk - (pkt->length + padding) % cipherblk) % cipherblk;
+ assert(padding <= 255);
maclen = ssh->csmac ? ssh->csmac->len : 0;
ssh2_pkt_ensure(pkt, pkt->length + padding + maclen);
pkt->data[4] = padding;
*/
/*
- * Send an SSH2 packet immediately, without queuing or deferring.
+ * Send an SSH-2 packet immediately, without queuing or deferring.
*/
static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt)
{
}
/*
- * Defer an SSH2 packet.
+ * Defer an SSH-2 packet.
*/
static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt)
{
}
/*
- * Queue an SSH2 packet.
+ * Queue an SSH-2 packet.
*/
static void ssh2_pkt_queue(Ssh ssh, struct Packet *pkt)
{
ssh2_pkt_send_noqueue(ssh, pkt);
}
+#if 0 /* disused */
/*
* Either queue or defer a packet, depending on whether queueing is
* set.
else
ssh2_pkt_defer_noqueue(ssh, pkt);
}
+#endif
/*
* Send the whole deferred data block constructed by
- * ssh2_pkt_defer() or SSH1's defer_packet().
+ * ssh2_pkt_defer() or SSH-1's defer_packet().
*
* The expected use of the defer mechanism is that you call
* ssh2_pkt_defer() a few times, then call ssh_pkt_defersend(). If
}
/*
- * Send all queued SSH2 packets. We send them by means of
+ * Send all queued SSH-2 packets. We send them by means of
* ssh2_pkt_defer_noqueue(), in case they included a pair of
* packets that needed to be lumped together.
*/
}
/*
- * Packet decode functions for both SSH1 and SSH2.
+ * Packet decode functions for both SSH-1 and SSH-2.
*/
static unsigned long ssh_pkt_getuint32(struct Packet *pkt)
{
}
/*
- * Helper function to add an SSH2 signature blob to a packet.
+ * Helper function to add an SSH-2 signature blob to a packet.
* Expects to be shown the public key blob as well as the signature
* blob. Normally works just like ssh2_pkt_addstring, but will
* fiddle with the signature packet if necessary for
* sniffing.
*/
ssh->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
- logevent("We believe remote version has SSH1 ignore bug");
+ logevent("We believe remote version has SSH-1 ignore bug");
}
if (ssh->cfg.sshbug_plainpw1 == FORCE_ON ||
* the password.
*/
ssh->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
- logevent("We believe remote version needs a plain SSH1 password");
+ logevent("We believe remote version needs a plain SSH-1 password");
}
if (ssh->cfg.sshbug_rsa1 == FORCE_ON ||
* an AUTH_RSA message.
*/
ssh->remote_bugs |= BUG_CHOKES_ON_RSA;
- logevent("We believe remote version can't handle RSA authentication");
+ logevent("We believe remote version can't handle SSH-1 RSA authentication");
}
if (ssh->cfg.sshbug_hmac2 == FORCE_ON ||
* These versions have the HMAC bug.
*/
ssh->remote_bugs |= BUG_SSH2_HMAC;
- logevent("We believe remote version has SSH2 HMAC bug");
+ logevent("We believe remote version has SSH-2 HMAC bug");
}
if (ssh->cfg.sshbug_derivekey2 == FORCE_ON ||
* generate the keys).
*/
ssh->remote_bugs |= BUG_SSH2_DERIVEKEY;
- logevent("We believe remote version has SSH2 key-derivation bug");
+ logevent("We believe remote version has SSH-2 key-derivation bug");
}
if (ssh->cfg.sshbug_rsapad2 == FORCE_ON ||
(wc_match("OpenSSH_2.[5-9]*", imp) ||
wc_match("OpenSSH_3.[0-2]*", imp)))) {
/*
- * These versions have the SSH2 RSA padding bug.
+ * These versions have the SSH-2 RSA padding bug.
*/
ssh->remote_bugs |= BUG_SSH2_RSA_PADDING;
- logevent("We believe remote version has SSH2 RSA padding bug");
+ logevent("We believe remote version has SSH-2 RSA padding bug");
}
if (ssh->cfg.sshbug_pksessid2 == FORCE_ON ||
(ssh->cfg.sshbug_pksessid2 == AUTO &&
wc_match("OpenSSH_2.[0-2]*", imp))) {
/*
- * These versions have the SSH2 session-ID bug in
+ * These versions have the SSH-2 session-ID bug in
* public-key authentication.
*/
ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID;
- logevent("We believe remote version has SSH2 public-key-session-ID bug");
+ logevent("We believe remote version has SSH-2 public-key-session-ID bug");
}
if (ssh->cfg.sshbug_rekey2 == FORCE_ON ||
wc_match("Sun_SSH_1.0", imp) ||
wc_match("Sun_SSH_1.0.1", imp)))) {
/*
- * These versions have the SSH2 rekey bug.
+ * These versions have the SSH-2 rekey bug.
*/
ssh->remote_bugs |= BUG_SSH2_REKEY;
- logevent("We believe remote version has SSH2 rekey bug");
+ logevent("We believe remote version has SSH-2 rekey bug");
}
}
s->vstring[s->vslen] = 0;
s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */
- {
- char *vlog;
- vlog = snewn(20 + s->vslen, char);
- sprintf(vlog, "Server version: %s", s->vstring);
- logevent(vlog);
- sfree(vlog);
- }
+ logeventf(ssh, "Server version: %s", s->vstring);
ssh_detect_bugs(ssh, s->vstring);
/*
strcspn(s->vstring, "\015\012"));
/*
- * Initialise SSHv2 protocol.
+ * Initialise SSH-2 protocol.
*/
ssh->protocol = ssh2_protocol;
ssh2_protocol_setup(ssh);
ssh->s_rdpkt = ssh2_rdpkt;
} else {
/*
- * Initialise SSHv1 protocol.
+ * Initialise SSH-1 protocol.
*/
ssh->protocol = ssh1_protocol;
ssh1_protocol_setup(ssh);
crFinish(0);
}
+static void ssh_process_incoming_data(Ssh ssh,
+ unsigned char **data, int *datalen)
+{
+ struct Packet *pktin = ssh->s_rdpkt(ssh, data, datalen);
+ if (pktin) {
+ ssh->protocol(ssh, NULL, 0, pktin);
+ ssh_free_packet(pktin);
+ }
+}
+
+static void ssh_queue_incoming_data(Ssh ssh,
+ unsigned char **data, int *datalen)
+{
+ bufchain_add(&ssh->queued_incoming_data, *data, *datalen);
+ *data += *datalen;
+ *datalen = 0;
+}
+
+static void ssh_process_queued_incoming_data(Ssh ssh)
+{
+ void *vdata;
+ unsigned char *data;
+ int len, origlen;
+
+ while (!ssh->frozen && bufchain_size(&ssh->queued_incoming_data)) {
+ bufchain_prefix(&ssh->queued_incoming_data, &vdata, &len);
+ data = vdata;
+ origlen = len;
+
+ while (!ssh->frozen && len > 0)
+ ssh_process_incoming_data(ssh, &data, &len);
+
+ if (origlen > len)
+ bufchain_consume(&ssh->queued_incoming_data, origlen - len);
+ }
+}
+
+static void ssh_set_frozen(Ssh ssh, int frozen)
+{
+ if (ssh->s)
+ sk_set_frozen(ssh->s, frozen);
+ ssh->frozen = frozen;
+}
+
static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
{
crBegin(ssh->ssh_gotdata_crstate);
*/
if (datalen == 0)
crReturnV;
+
+ /*
+ * Process queued data if there is any.
+ */
+ ssh_process_queued_incoming_data(ssh);
+
while (1) {
while (datalen > 0) {
- struct Packet *pktin = ssh->s_rdpkt(ssh, &data, &datalen);
- if (pktin) {
- ssh->protocol(ssh, NULL, 0, pktin);
- ssh_free_packet(pktin);
- }
+ if (ssh->frozen)
+ ssh_queue_incoming_data(ssh, &data, &datalen);
+
+ ssh_process_incoming_data(ssh, &data, &datalen);
+
if (ssh->state == SSH_STATE_CLOSED)
return;
}
static int ssh_do_close(Ssh ssh, int notify_exit)
{
- int i, ret = 0;
+ int ret = 0;
struct ssh_channel *c;
ssh->state = SSH_STATE_CLOSED;
ret = 1;
}
/*
- * Now we must shut down any port and X forwardings going
+ * Now we must shut down any port- and X-forwarded channels going
* through this connection.
*/
if (ssh->channels) {
- for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) {
+ while (NULL != (c = index234(ssh->channels, 0))) {
switch (c->type) {
case CHAN_X11:
x11_close(c->u.x11.s);
pfd_close(c->u.pfd.s);
break;
}
- del234(ssh->channels, c);
+ del234(ssh->channels, c); /* moving next one to index 0 */
if (ssh->version == 2)
bufchain_clear(&c->v.v2.outbuffer);
sfree(c);
}
}
+ /*
+ * Go through port-forwardings, and close any associated
+ * listening sockets.
+ */
+ if (ssh->portfwds) {
+ struct ssh_portfwd *pf;
+ while (NULL != (pf = index234(ssh->portfwds, 0))) {
+ /* Dispose of any listening socket. */
+ if (pf->local)
+ pfd_terminate(pf->local);
+ del234(ssh->portfwds, pf); /* moving next one to index 0 */
+ free_portfwd(pf);
+ }
+ }
return ret;
}
+static void ssh_log(Plug plug, int type, SockAddr addr, int port,
+ const char *error_msg, int error_code)
+{
+ Ssh ssh = (Ssh) plug;
+ char addrbuf[256], *msg;
+
+ sk_getaddr(addr, addrbuf, lenof(addrbuf));
+
+ if (type == 0)
+ msg = dupprintf("Connecting to %s port %d", addrbuf, port);
+ else
+ msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
+
+ logevent(msg);
+ sfree(msg);
+}
+
static int ssh_closing(Plug plug, const char *error_msg, int error_code,
int calling_back)
{
error_msg = "Server unexpectedly closed network connection";
}
+ if (need_notify)
+ notify_remote_exit(ssh->frontend);
+
if (error_msg) {
/* A socket error has occurred. */
logevent(error_msg);
} else {
logevent("Server closed network connection");
}
- if (need_notify)
- notify_remote_exit(ssh->frontend);
return 0;
}
char **realhost, int nodelay, int keepalive)
{
static const struct plug_function_table fn_table = {
+ ssh_log,
ssh_closing,
ssh_receive,
ssh_sent,
/*
* Open socket.
*/
- {
- char addrbuf[100];
- sk_getaddr(addr, addrbuf, 100);
- logeventf(ssh, "Connecting to %s port %d", addrbuf, port);
- }
ssh->fn = &fn_table;
ssh->s = new_connection(addr, *realhost, port,
0, 1, nodelay, keepalive, (Plug) ssh, &ssh->cfg);
ssh->v1_throttle_count += adjust;
assert(ssh->v1_throttle_count >= 0);
if (ssh->v1_throttle_count && !old_count) {
- sk_set_frozen(ssh->s, 1);
+ ssh_set_frozen(ssh, 1);
} else if (!ssh->v1_throttle_count && old_count) {
- sk_set_frozen(ssh->s, 0);
+ ssh_set_frozen(ssh, 0);
}
}
/*
* Username and password input, abstracted off into routines
- * reusable in several places - even between SSH1 and SSH2.
+ * reusable in several places - even between SSH-1 and SSH-2.
*/
/* Set up a username or password input loop on a given buffer. */
do_ssh2_authconn(ssh, NULL, -1, NULL);
}
+static void ssh_dialog_callback(void *sshv, int ret)
+{
+ Ssh ssh = (Ssh) sshv;
+
+ ssh->user_response = ret;
+
+ if (ssh->version == 1)
+ do_ssh1_login(ssh, NULL, -1, NULL);
+ else
+ do_ssh2_transport(ssh, NULL, -1, NULL);
+
+ /*
+ * This may have unfrozen the SSH connection, so do a
+ * queued-data run.
+ */
+ ssh_process_queued_incoming_data(ssh);
+}
+
static void ssh_agentf_callback(void *cv, void *reply, int replylen)
{
struct ssh_channel *c = (struct ssh_channel *)cv;
Bignum challenge;
char *commentp;
int commentlen;
+ int dlgret;
};
crState(do_ssh1_login_state);
ptr = ssh_pkt_getdata(pktin, 8);
if (!ptr) {
- bombout(("SSH1 public key packet stopped before random cookie"));
+ bombout(("SSH-1 public key packet stopped before random cookie"));
crStop(0);
}
memcpy(cookie, ptr, 8);
if (!ssh1_pkt_getrsakey(pktin, &servkey, &s->keystr1) ||
!ssh1_pkt_getrsakey(pktin, &hostkey, &s->keystr2)) {
- bombout(("Failed to read SSH1 public keys from public key packet"));
+ bombout(("Failed to read SSH-1 public keys from public key packet"));
crStop(0);
}
*/
if (hostkey.bits > hostkey.bytes * 8 ||
servkey.bits > servkey.bytes * 8) {
- bombout(("SSH1 public keys were badly formatted"));
+ bombout(("SSH-1 public keys were badly formatted"));
crStop(0);
}
fatalbox("Out of memory");
rsastr_fmt(keystr, &hostkey);
rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
- verify_ssh_host_key(ssh->frontend,
- ssh->savedhost, ssh->savedport, "rsa", keystr,
- fingerprint);
+
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = verify_ssh_host_key(ssh->frontend,
+ ssh->savedhost, ssh->savedport,
+ "rsa", keystr, fingerprint,
+ ssh_dialog_callback, ssh);
sfree(keystr);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while waiting"
+ " for user host key response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+
+ if (s->dlgret == 0) {
+ ssh->close_expected = TRUE;
+ ssh_closing((Plug)ssh, NULL, 0, 0);
+ crStop(0);
+ }
}
for (i = 0; i < 32; i++) {
ret = rsaencrypt(s->rsabuf, hostkey.bytes, &servkey);
}
if (!ret) {
- bombout(("SSH1 public key encryptions failed due to bad formatting"));
+ bombout(("SSH-1 public key encryptions failed due to bad formatting"));
crStop(0);
}
warn = 1;
} else if (next_cipher == CIPHER_AES) {
/* XXX Probably don't need to mention this. */
- logevent("AES not supported in SSH1, skipping");
+ logevent("AES not supported in SSH-1, skipping");
} else {
switch (next_cipher) {
case CIPHER_3DES: s->cipher_type = SSH_CIPHER_3DES;
}
if (!cipher_chosen) {
if ((s->supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0)
- bombout(("Server violates SSH 1 protocol by not "
+ bombout(("Server violates SSH-1 protocol by not "
"supporting 3DES encryption"));
else
/* shouldn't happen */
/* Warn about chosen cipher if necessary. */
if (warn) {
- sk_set_frozen(ssh->s, 1);
- askalg(ssh->frontend, "cipher", cipher_string);
- sk_set_frozen(ssh->s, 0);
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = askalg(ssh->frontend, "cipher", cipher_string,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while waiting"
+ " for user response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh->close_expected = TRUE;
+ ssh_closing((Plug)ssh, NULL, 0, 0);
+ crStop(0);
+ }
}
}
s->p = s->response + 5;
s->nkeys = GET_32BIT(s->p);
s->p += 4;
- {
- char buf[64];
- sprintf(buf, "Pageant has %d SSH1 keys", s->nkeys);
- logevent(buf);
- }
+ logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys);
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
- {
- char buf[64];
- sprintf(buf, "Trying Pageant key #%d", s->keyi);
- logevent(buf);
- }
+ logeventf(ssh, "Trying Pageant key #%d", s->keyi);
if (s->publickey_blob &&
!memcmp(s->p, s->publickey_blob,
s->publickey_bloblen)) {
bombout(("TIS challenge packet was badly formed"));
crStop(0);
}
+ c_write_str(ssh, "Using TIS authentication.\r\n");
logevent("Received TIS challenge");
if (challengelen > sizeof(s->prompt) - 1)
challengelen = sizeof(s->prompt) - 1;/* prevent overrun */
bombout(("CryptoCard challenge packet was badly formed"));
crStop(0);
}
+ c_write_str(ssh, "Using CryptoCard authentication.\r\n");
logevent("Received CryptoCard challenge");
if (challengelen > sizeof(s->prompt) - 1)
challengelen = sizeof(s->prompt) - 1;/* prevent overrun */
if (s->pwpkt_type == SSH1_CMSG_AUTH_RSA) {
char *comment = NULL;
int type;
- char msgbuf[256];
if (flags & FLAG_VERBOSE)
c_write_str(ssh, "Trying public key authentication.\r\n");
logeventf(ssh, "Trying public key \"%s\"",
filename_to_str(&ssh->cfg.keyfile));
type = key_type(&ssh->cfg.keyfile);
if (type != SSH_KEYTYPE_SSH1) {
- sprintf(msgbuf, "Key is of wrong type (%s)",
- key_type_to_str(type));
- logevent(msgbuf);
- c_write_str(ssh, msgbuf);
+ char *msg = dupprintf("Key is of wrong type (%s)",
+ key_type_to_str(type));
+ logevent(msg);
+ c_write_str(ssh, msg);
c_write_str(ssh, "\r\n");
+ sfree(msg);
s->tried_publickey = 1;
continue;
}
{
Ssh ssh = c->ssh;
- if (ssh->state != SSH_STATE_SESSION) {
- assert(ssh->state == SSH_STATE_CLOSED);
+ if (ssh->state == SSH_STATE_CLOSED)
return;
- }
if (c && !c->closes) {
/*
- * If the channel's remoteid is -1, we have sent
+ * If halfopen is true, we have sent
* CHANNEL_OPEN for this channel, but it hasn't even been
* acknowledged by the server. So we must set a close flag
* on it now, and then when the server acks the channel
* open, we can close it then.
*/
- if (((int)c->remoteid) != -1) {
+ if (!c->halfopen) {
if (ssh->version == 1) {
send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
PKT_END);
{
Ssh ssh = c->ssh;
- if (ssh->state != SSH_STATE_SESSION) {
- assert(ssh->state == SSH_STATE_CLOSED);
+ if (ssh->state == SSH_STATE_CLOSED)
return 0;
- }
if (ssh->version == 1) {
send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
PKT_INT, len, PKT_DATA, buf, len,
PKTT_OTHER, PKT_END);
/*
- * In SSH1 we can return 0 here - implying that forwarded
+ * In SSH-1 we can return 0 here - implying that forwarded
* connections are never individually throttled - because
* the only circumstance that can cause throttling will be
* the whole SSH connection backing up, in which case
{
Ssh ssh = c->ssh;
- if (ssh->state != SSH_STATE_SESSION) {
- assert(ssh->state == SSH_STATE_CLOSED);
+ if (ssh->state == SSH_STATE_CLOSED)
return;
- }
if (ssh->version == 1) {
if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) {
portfwd_strptr++;
sports[n] = '\0';
if (ssh->version == 1 && type == 'R') {
- logeventf(ssh, "SSH1 cannot handle remote source address "
+ logeventf(ssh, "SSH-1 cannot handle remote source address "
"spec \"%s\"; ignoring", sports);
} else
strcpy(saddr, sports);
n = 0;
}
- if (n < 255) sports[n++] = *portfwd_strptr++;
+ if (n < lenof(sports)-1) sports[n++] = *portfwd_strptr++;
}
sports[n] = 0;
if (type != 'D') {
portfwd_strptr++;
n = 0;
while (*portfwd_strptr && *portfwd_strptr != ':') {
- if (n < 255) host[n++] = *portfwd_strptr++;
+ if (n < lenof(host)-1) host[n++] = *portfwd_strptr++;
}
host[n] = 0;
if (*portfwd_strptr == ':')
portfwd_strptr++;
n = 0;
while (*portfwd_strptr) {
- if (n < 255) dports[n++] = *portfwd_strptr++;
+ if (n < lenof(dports)-1) dports[n++] = *portfwd_strptr++;
}
dports[n] = 0;
portfwd_strptr++;
}
} else {
while (*portfwd_strptr) portfwd_strptr++;
+ host[0] = 0;
+ dports[0] = 0;
dport = dserv = -1;
portfwd_strptr++; /* eat the NUL and move to next one */
}
if (ssh->version == 1) {
/*
* We cannot cancel listening ports on the
- * server side in SSH1! There's no message
+ * server side in SSH-1! There's no message
* to support it. Instead, we simply remove
* the rportfwd record from the local end
* so that any connections the server tries
if (epf->saddr) {
ssh2_pkt_addstring(pktout, epf->saddr);
} else if (ssh->cfg.rport_acceptall) {
+ /* XXX: ssh->cfg.rport_acceptall may not represent
+ * what was used to open the original connection,
+ * since it's reconfigurable. */
ssh2_pkt_addstring(pktout, "0.0.0.0");
} else {
ssh2_pkt_addstring(pktout, "127.0.0.1");
if (epf->type == 'L') {
const char *err = pfd_addforward(epf->daddr, epf->dport,
epf->saddr, epf->sport,
- ssh, &ssh->cfg,
+ ssh, cfg,
&epf->local,
epf->addressfamily);
} else if (epf->type == 'D') {
const char *err = pfd_addforward(NULL, -1,
epf->saddr, epf->sport,
- ssh, &ssh->cfg,
+ ssh, cfg,
&epf->local,
epf->addressfamily);
ssh2_pkt_addbool(pktout, 1);/* want reply */
if (epf->saddr) {
ssh2_pkt_addstring(pktout, epf->saddr);
- } else if (ssh->cfg.rport_acceptall) {
+ } else if (cfg->rport_acceptall) {
ssh2_pkt_addstring(pktout, "0.0.0.0");
} else {
ssh2_pkt_addstring(pktout, "127.0.0.1");
logevent
("Opening X11 forward connection succeeded");
c->remoteid = remoteid;
+ c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
c->v.v1.throttling = 0;
c = snew(struct ssh_channel);
c->ssh = ssh;
c->remoteid = remoteid;
+ c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
c->v.v1.throttling = 0;
struct ssh_rportfwd pf, *pfp;
int remoteid;
int hostsize, port;
- char *host, buf[1024];
+ char *host;
const char *e;
c = snew(struct ssh_channel);
c->ssh = ssh;
pfp = find234(ssh->rportfwds, &pf, NULL);
if (pfp == NULL) {
- sprintf(buf, "Rejected remote port open request for %s:%d",
- pf.dhost, port);
- logevent(buf);
+ logeventf(ssh, "Rejected remote port open request for %s:%d",
+ pf.dhost, port);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
PKT_INT, remoteid, PKT_END);
} else {
- sprintf(buf, "Received remote port open request for %s:%d",
- pf.dhost, port);
- logevent(buf);
+ logeventf(ssh, "Received remote port open request for %s:%d",
+ pf.dhost, port);
e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port,
c, &ssh->cfg, pfp->pfrec->addressfamily);
if (e != NULL) {
- char buf[256];
- sprintf(buf, "Port open failed: %s", e);
- logevent(buf);
+ logeventf(ssh, "Port open failed: %s", e);
sfree(c);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
PKT_INT, remoteid, PKT_END);
} else {
c->remoteid = remoteid;
+ c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
c->v.v1.throttling = 0;
c = find234(ssh->channels, &remoteid, ssh_channelfind);
if (c && c->type == CHAN_SOCKDATA_DORMANT) {
c->remoteid = localid;
+ c->halfopen = FALSE;
c->type = CHAN_SOCKDATA;
c->v.v1.throttling = 0;
pfd_confirm(c->u.pfd.s);
unsigned i = ssh_pkt_getuint32(pktin);
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
- if (c && ((int)c->remoteid) != -1) {
+ if (c && !c->halfopen) {
int closetype;
closetype =
(pktin->type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
/* Data sent down one of our channels. */
int i = ssh_pkt_getuint32(pktin);
char *p;
- unsigned int len;
+ int len;
struct ssh_channel *c;
ssh_pkt_getstring(pktin, &p, &len);
static void ssh1_smsg_exit_status(Ssh ssh, struct Packet *pktin)
{
- char buf[100];
ssh->exitcode = ssh_pkt_getuint32(pktin);
- sprintf(buf, "Server sent command exit status %d",
- ssh->exitcode);
- logevent(buf);
+ logeventf(ssh, "Server sent command exit status %d", ssh->exitcode);
send_packet(ssh, SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
/*
* In case `helpful' firewalls or proxies tack
/*
* Start the shell or command.
*
- * Special case: if the first-choice command is an SSH2
+ * Special case: if the first-choice command is an SSH-2
* subsystem (hence not usable here) and the second choice
* exists, we fall straight back to that.
*/
{
char *cmd = ssh->cfg.remote_cmd_ptr;
+
+ if (!cmd) cmd = ssh->cfg.remote_cmd;
if (ssh->cfg.ssh_subsys && ssh->cfg.remote_cmd_ptr2) {
cmd = ssh->cfg.remote_cmd_ptr2;
}
/*
- * Handle the top-level SSH2 protocol.
+ * Handle the top-level SSH-2 protocol.
*/
static void ssh1_msg_debug(Ssh ssh, struct Packet *pktin)
{
- char *buf, *msg;
+ char *msg;
int msglen;
ssh_pkt_getstring(pktin, &msg, &msglen);
- buf = dupprintf("Remote debug message: %.*s", msglen, msg);
- logevent(buf);
- sfree(buf);
+ logeventf(ssh, "Remote debug message: %.*s", msglen, msg);
}
static void ssh1_msg_disconnect(Ssh ssh, struct Packet *pktin)
bombout(("Server sent disconnect message:\n\"%.*s\"", msglen, msg));
}
-void ssh_msg_ignore(Ssh ssh, struct Packet *pktin)
+static void ssh_msg_ignore(Ssh ssh, struct Packet *pktin)
{
/* Do nothing, because we're ignoring it! Duhh. */
}
ssh->packet_dispatch[SSH1_MSG_DEBUG] = ssh1_msg_debug;
}
-static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen,
+static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
struct Packet *pktin)
{
+ unsigned char *in=(unsigned char*)vin;
if (ssh->state == SSH_STATE_CLOSED)
return;
}
/*
- * SSH2 key creation method.
+ * Similar routine for checking whether we have the first string in a list.
+ */
+static int first_in_commasep_string(char *needle, char *haystack, int haylen)
+{
+ int needlen;
+ if (!needle || !haystack) /* protect against null pointers */
+ return 0;
+ needlen = strlen(needle);
+ /*
+ * Is it at the start of the string?
+ */
+ if (haylen >= needlen && /* haystack is long enough */
+ !memcmp(needle, haystack, needlen) && /* initial match */
+ (haylen == needlen || haystack[needlen] == ',')
+ /* either , or EOS follows */
+ )
+ return 1;
+ return 0;
+}
+
+
+/*
+ * SSH-2 key creation method.
*/
static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H,
unsigned char *sessid, char chr,
}
/*
- * Handle the SSH2 transport layer.
+ * Handle the SSH-2 transport layer.
*/
-static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen,
+static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
struct Packet *pktin)
{
+ unsigned char *in = (unsigned char *)vin;
struct do_ssh2_transport_state {
- int nbits, pbits, warn;
+ int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher;
Bignum p, g, e, f, K;
int kex_init_value, kex_reply_value;
const struct ssh_mac **maclist;
const struct ssh_compress *preferred_comp;
int got_session_id, activated_authconn;
struct Packet *pktout;
+ int dlgret;
+ int guessok;
+ int ignorepkt;
};
crState(do_ssh2_transport_state);
* to.
*/
{
- char *str;
+ char *str, *preferred;
int i, j, len;
if (pktin->type != SSH2_MSG_KEXINIT) {
s->scmac_tobe = NULL;
s->cscomp_tobe = NULL;
s->sccomp_tobe = NULL;
+ s->warn_kex = s->warn_cscipher = s->warn_sccipher = FALSE;
+
pktin->savedpos += 16; /* skip garbage cookie */
ssh_pkt_getstring(pktin, &str, &len); /* key exchange algorithms */
- s->warn = 0;
+
+ preferred = NULL;
for (i = 0; i < s->n_preferred_kex; i++) {
const struct ssh_kex *k = s->preferred_kex[i];
if (!k) {
- s->warn = 1;
- } else if (in_commasep_string(k->name, str, len)) {
- ssh->kex = k;
+ s->warn_kex = TRUE;
+ } else {
+ if (!preferred) preferred = k->name;
+ if (in_commasep_string(k->name, str, len))
+ ssh->kex = k;
}
- if (ssh->kex) {
- if (s->warn) {
- sk_set_frozen(ssh->s, 1);
- askalg(ssh->frontend, "key-exchange algorithm",
- ssh->kex->name);
- sk_set_frozen(ssh->s, 0);
- }
+ if (ssh->kex)
break;
- }
}
if (!ssh->kex) {
bombout(("Couldn't agree a key exchange algorithm (available: %s)",
str ? str : "(null)"));
crStop(0);
}
+ /*
+ * Note that the server's guess is considered wrong if it doesn't match
+ * the first algorithm in our list, even if it's still the algorithm
+ * we end up using.
+ */
+ s->guessok = first_in_commasep_string(preferred, str, len);
ssh_pkt_getstring(pktin, &str, &len); /* host key algorithms */
for (i = 0; i < lenof(hostkey_algs); i++) {
if (in_commasep_string(hostkey_algs[i]->name, str, len)) {
break;
}
}
+ s->guessok = s->guessok &&
+ first_in_commasep_string(hostkey_algs[0]->name, str, len);
ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */
- s->warn = 0;
for (i = 0; i < s->n_preferred_ciphers; i++) {
const struct ssh2_ciphers *c = s->preferred_ciphers[i];
if (!c) {
- s->warn = 1;
+ s->warn_cscipher = TRUE;
} else {
for (j = 0; j < c->nciphers; j++) {
if (in_commasep_string(c->list[j]->name, str, len)) {
}
}
}
- if (s->cscipher_tobe) {
- if (s->warn) {
- sk_set_frozen(ssh->s, 1);
- askalg(ssh->frontend, "client-to-server cipher",
- s->cscipher_tobe->name);
- sk_set_frozen(ssh->s, 0);
- }
+ if (s->cscipher_tobe)
break;
- }
}
if (!s->cscipher_tobe) {
bombout(("Couldn't agree a client-to-server cipher (available: %s)",
}
ssh_pkt_getstring(pktin, &str, &len); /* server->client cipher */
- s->warn = 0;
for (i = 0; i < s->n_preferred_ciphers; i++) {
const struct ssh2_ciphers *c = s->preferred_ciphers[i];
if (!c) {
- s->warn = 1;
+ s->warn_sccipher = TRUE;
} else {
for (j = 0; j < c->nciphers; j++) {
if (in_commasep_string(c->list[j]->name, str, len)) {
}
}
}
- if (s->sccipher_tobe) {
- if (s->warn) {
- sk_set_frozen(ssh->s, 1);
- askalg(ssh->frontend, "server-to-client cipher",
- s->sccipher_tobe->name);
- sk_set_frozen(ssh->s, 0);
- }
+ if (s->sccipher_tobe)
break;
- }
}
if (!s->sccipher_tobe) {
bombout(("Couldn't agree a server-to-client cipher (available: %s)",
break;
}
}
+ 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;
+
+ if (s->warn_kex) {
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",
+ ssh->kex->name,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while"
+ " waiting for user response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh->close_expected = TRUE;
+ ssh_closing((Plug)ssh, NULL, 0, 0);
+ crStop(0);
+ }
+ }
+
+ if (s->warn_cscipher) {
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = askalg(ssh->frontend,
+ "client-to-server cipher",
+ s->cscipher_tobe->name,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while"
+ " waiting for user response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh->close_expected = TRUE;
+ ssh_closing((Plug)ssh, NULL, 0, 0);
+ crStop(0);
+ }
+ }
+
+ if (s->warn_sccipher) {
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = askalg(ssh->frontend,
+ "server-to-client cipher",
+ s->sccipher_tobe->name,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while"
+ " waiting for user response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh->close_expected = TRUE;
+ ssh_closing((Plug)ssh, NULL, 0, 0);
+ crStop(0);
+ }
+ }
+
+ if (s->ignorepkt) /* first_kex_packet_follows */
+ crWaitUntil(pktin); /* Ignore packet */
}
/*
/*
* Now generate and send e for Diffie-Hellman.
*/
+ set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
s->pktout = ssh2_pkt_init(s->kex_init_value);
ssh2_pkt_addmp(s->pktout, s->e);
ssh2_pkt_send_noqueue(ssh, s->pktout);
+ set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
crWaitUntil(pktin);
if (pktin->type != s->kex_reply_value) {
bombout(("expected key exchange reply packet from server"));
crStop(0);
}
+ set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
s->f = ssh2_pkt_getmp(pktin);
if (!s->f) {
s->K = dh_find_K(ssh->kex_ctx, s->f);
+ /* We assume everything from now on will be quick, and it might
+ * 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);
*/
s->keystr = ssh->hostkey->fmtkey(s->hkey);
s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
- sk_set_frozen(ssh->s, 1);
- verify_ssh_host_key(ssh->frontend,
- ssh->savedhost, ssh->savedport, ssh->hostkey->keytype,
- s->keystr, s->fingerprint);
- sk_set_frozen(ssh->s, 0);
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = verify_ssh_host_key(ssh->frontend,
+ ssh->savedhost, ssh->savedport,
+ ssh->hostkey->keytype, s->keystr,
+ s->fingerprint,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while waiting"
+ " for user host key response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh->close_expected = TRUE;
+ ssh_closing((Plug)ssh, NULL, 0, 0);
+ crStop(0);
+ }
if (!s->got_session_id) { /* don't bother logging this in rekeys */
logevent("Host key fingerprint is:");
logevent(s->fingerprint);
}
/*
- * Add data to an SSH2 channel output buffer.
+ * Add data to an SSH-2 channel output buffer.
*/
static void ssh2_add_channel_data(struct ssh_channel *c, char *buf,
int len)
}
/*
- * Attempt to send data on an SSH2 channel.
+ * Attempt to send data on an SSH-2 channel.
*/
static int ssh2_try_send(struct ssh_channel *c)
{
}
/*
- * Potentially enlarge the window on an SSH2 channel.
+ * Potentially enlarge the window on an SSH-2 channel.
*/
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
{
if (c->closes != 0)
return;
- if (newwin > c->v.v2.locwindow) {
+ /*
+ * Only send a WINDOW_ADJUST if there's significantly more window
+ * available than the other end thinks there is. This saves us
+ * sending a WINDOW_ADJUST for every character in a shell session.
+ *
+ * "Significant" is arbitrarily defined as half the window size.
+ */
+ if (newwin > c->v.v2.locwindow * 2) {
struct Packet *pktout;
pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
{
char *data;
- unsigned int length;
+ int length;
unsigned i = ssh_pkt_getuint32(pktin);
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
struct Packet *pktout;
c = find234(ssh->channels, &i, ssh_channelfind);
- if (!c || ((int)c->remoteid) == -1) {
+ if (!c || c->halfopen) {
bombout(("Received CHANNEL_CLOSE for %s channel %d\n",
c ? "half-open" : "nonexistent", i));
return;
/*
* We used to send SSH_MSG_DISCONNECT here,
* because I'd believed that _every_ conforming
- * SSH2 connection had to end with a disconnect
+ * SSH-2 connection had to end with a disconnect
* being sent by at least one side; apparently
* I was wrong and it's perfectly OK to
* unceremoniously slam the connection shut
if (c->type != CHAN_SOCKDATA_DORMANT)
return; /* dunno why they're confirming this */
c->remoteid = ssh_pkt_getuint32(pktin);
+ c->halfopen = FALSE;
c->type = CHAN_SOCKDATA;
c->v.v2.remwindow = ssh_pkt_getuint32(pktin);
c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
unsigned reason_code;
char *reason_string;
int reason_length;
- char *message;
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
if (!c)
if (reason_code >= lenof(reasons))
reason_code = 0; /* ensure reasons[reason_code] in range */
ssh_pkt_getstring(pktin, &reason_string, &reason_length);
- message = dupprintf("Forwarded connection refused by"
- " server: %s [%.*s]", reasons[reason_code],
- reason_length, reason_string);
- logevent(message);
- sfree(message);
+ logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
+ reasons[reason_code], reason_length, reason_string);
pfd_close(c->u.pfd.s);
if (q >= 0 && q+4 <= len) { \
q = q + 4 + GET_32BIT(p+q); \
if (q >= 0 && q+4 <= len && \
- (q = q + 4 + GET_32BIT(p+q)) && q == len) \
+ ((q = q + 4 + GET_32BIT(p+q))!= 0) && q == len) \
result = TRUE; \
} \
} while(0)
}
c->remoteid = remid;
+ c->halfopen = FALSE;
if (error) {
pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_FAILURE);
ssh2_pkt_adduint32(pktout, c->remoteid);
}
/*
- * Handle the SSH2 userauth and connection layers.
+ * Handle the SSH-2 userauth and connection layers.
*/
static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
struct Packet *pktin)
s->p = s->response + 5;
s->nkeys = GET_32BIT(s->p);
s->p += 4;
- {
- char buf[64];
- sprintf(buf, "Pageant has %d SSH2 keys", s->nkeys);
- logevent(buf);
- }
+ logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
void *vret;
- {
- char buf[64];
- sprintf(buf, "Trying Pageant key #%d", s->keyi);
- logevent(buf);
- }
+ logeventf(ssh, "Trying Pageant key #%d", s->keyi);
s->pklen = GET_32BIT(s->p);
s->p += 4;
if (s->publickey_blob &&
continue;
}
+ c_write_str(ssh, "Using keyboard-interactive authentication.\r\n");
s->kbd_inter_running = TRUE;
s->curr_prompt = 0;
}
}
} else if (s->method == AUTH_PASSWORD) {
/*
- * We send the password packet lumped tightly together with
- * an SSH_MSG_IGNORE packet. The IGNORE packet contains a
- * string long enough to make the total length of the two
- * packets constant. This should ensure that a passive
- * listener doing traffic analyis can't work out the length
- * of the password.
+ * We pad out the password packet to 256 bytes to make
+ * it harder for an attacker to find the length of the
+ * user's password.
*
- * For this to work, we need an assumption about the
- * maximum length of the password packet. I think 256 is
- * pretty conservative. Anyone using a password longer than
- * that probably doesn't have much to worry about from
+ * Anyone using a password longer than 256 bytes
+ * probably doesn't have much to worry about from
* people who find out how long their password is!
*/
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout->forcepad = 256;
ssh2_pkt_addstring(s->pktout, s->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */
ssh2_pkt_addstring(s->pktout, "password");
ssh2_pkt_addstring(s->pktout, s->password);
memset(s->password, 0, sizeof(s->password));
end_log_omission(ssh, s->pktout);
- ssh2_pkt_defer(ssh, s->pktout);
- /*
- * We'll include a string that's an exact multiple of the
- * cipher block size. If the cipher is NULL for some
- * reason, we don't do this trick at all because we gain
- * nothing by it.
- */
- if (ssh->cscipher) {
- int stringlen, i;
-
- stringlen = (256 - ssh->deferred_len);
- stringlen += ssh->cscipher->blksize - 1;
- stringlen -= (stringlen % ssh->cscipher->blksize);
- if (ssh->cscomp) {
- /*
- * Temporarily disable actual compression,
- * so we can guarantee to get this string
- * exactly the length we want it. The
- * compression-disabling routine should
- * return an integer indicating how many
- * bytes we should adjust our string length
- * by.
- */
- stringlen -=
- ssh->cscomp->disable_compression(ssh->cs_comp_ctx);
- }
- s->pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
- ssh2_pkt_addstring_start(s->pktout);
- for (i = 0; i < stringlen; i++) {
- char c = (char) random_byte();
- ssh2_pkt_addstring_data(s->pktout, &c, 1);
- }
- ssh2_pkt_defer(ssh, s->pktout);
- }
- ssh_pkt_defersend(ssh);
+ ssh2_pkt_send(ssh, s->pktout);
logevent("Sent password");
s->type = AUTH_TYPE_PASSWORD;
} else if (s->method == AUTH_KEYBOARD_INTERACTIVE) {
if (s->curr_prompt == 0) {
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
+ s->pktout->forcepad = 256;
ssh2_pkt_adduint32(s->pktout, s->num_prompts);
}
if (s->need_pw) { /* only add pw if we just got one! */
crStopV;
}
ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
+ ssh->mainchan->halfopen = FALSE;
ssh->mainchan->type = CHAN_MAINSESSION;
ssh->mainchan->closes = 0;
ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
} else {
subsys = ssh->cfg.ssh_subsys;
cmd = ssh->cfg.remote_cmd_ptr;
+ if (!cmd) cmd = ssh->cfg.remote_cmd;
}
s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
}
/*
- * Handlers for SSH2 messages that might arrive at any moment.
+ * Handlers for SSH-2 messages that might arrive at any moment.
*/
-void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin)
{
/* log reason code in disconnect message */
char *buf, *msg;
sfree(buf);
}
-void ssh2_msg_debug(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin)
{
/* log the debug message */
- char *buf, *msg;
+ char *msg;
int msglen;
int always_display;
always_display = ssh2_pkt_getbool(pktin);
ssh_pkt_getstring(pktin, &msg, &msglen);
- buf = dupprintf("Remote debug message: %.*s", msglen, msg);
- logevent(buf);
- sfree(buf);
+ logeventf(ssh, "Remote debug message: %.*s", msglen, msg);
}
-void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin)
{
struct Packet *pktout;
pktout = ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED);
}
/*
- * Handle the top-level SSH2 protocol.
+ * Handle the top-level SSH-2 protocol.
*/
static void ssh2_protocol_setup(Ssh ssh)
{
* These special message types we install handlers for.
*/
ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect;
- ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with ssh1 */
+ ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with SSH-1 */
ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug;
}
}
}
-static void ssh2_protocol(Ssh ssh, unsigned char *in, int inlen,
+static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
struct Packet *pktin)
{
+ unsigned char *in = (unsigned char *)vin;
if (ssh->state == SSH_STATE_CLOSED)
return;
ssh->queueing = FALSE;
ssh->qhead = ssh->qtail = NULL;
ssh->deferred_rekey_reason = NULL;
+ bufchain_init(&ssh->queued_incoming_data);
+ ssh->frozen = FALSE;
*backend_handle = ssh;
expire_timer_context(ssh);
if (ssh->pinger)
pinger_free(ssh->pinger);
+ bufchain_clear(&ssh->queued_incoming_data);
sfree(ssh);
random_unref();
if (ssh->state == SSH_STATE_CLOSED
|| ssh->state == SSH_STATE_PREPACKET) return;
if (ssh->version == 1) {
- logevent("Unable to send BREAK signal in SSH1");
+ logevent("Unable to send BREAK signal in SSH-1");
} else if (ssh->mainchan) {
pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);
c->ssh = ssh;
if (c) {
- c->remoteid = -1; /* to be set when open confirmed */
+ c->halfopen = TRUE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */