#define BUG_SSH2_RSA_PADDING 16
#define BUG_SSH2_DERIVEKEY 32
#define BUG_SSH2_DH_GEX 64
+#define BUG_SSH2_PK_SESSIONID 128
#define translate(x) if (type == x) return #x
#define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x
enum { PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM };
-/* Coroutine mechanics for the sillier bits of the code */
+/*
+ * Coroutine mechanics for the sillier bits of the code. If these
+ * macros look impenetrable to you, you might find it helpful to
+ * read
+ *
+ * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+ *
+ * which explains the theory behind these macros.
+ *
+ * In particular, if you are getting `case expression not constant'
+ * errors when building with MS Visual Studio, this is because MS's
+ * Edit and Continue debugging feature causes their compiler to
+ * violate ANSI C. To disable Edit and Continue debugging:
+ *
+ * - right-click ssh.c in the FileView
+ * - click Settings
+ * - select the C/C++ tab and the General category
+ * - under `Debug info:', select anything _other_ than `Program
+ * Database for Edit and Continue'.
+ */
#define crBegin(v) { int *crLine = &v; switch(v) { case 0:;
#define crState(t) \
struct t *s; \
return low + 1 + CHANNEL_NUMBER_OFFSET;
}
-static void c_write(Ssh ssh, char *buf, int len)
+static void c_write(Ssh ssh, const char *buf, int len)
{
if ((flags & FLAG_STDERR)) {
int i;
from_backend(ssh->frontend, 1, buf, len);
}
-static void c_write_untrusted(Ssh ssh, char *buf, int len)
+static void c_write_untrusted(Ssh ssh, const char *buf, int len)
{
int i;
for (i = 0; i < len; i++) {
}
}
-static void c_write_str(Ssh ssh, char *buf)
+static void c_write_str(Ssh ssh, const char *buf)
{
c_write(ssh, buf, strlen(buf));
}
static int s_wrpkt_prepare(Ssh ssh)
{
- int pad, len, biglen, i;
+ int pad, biglen, i;
unsigned long crc;
+#ifdef __SC__
+ /*
+ * XXX various versions of SC (including 8.8.4) screw up the
+ * register allocation in this function and use the same register
+ * (D6) for len and as a temporary, with predictable results. The
+ * following sledgehammer prevents this.
+ */
+ volatile
+#endif
+ int len;
ssh->pktout.body[-1] = ssh->pktout.type;
ssh->remote_bugs = 0;
- if (ssh->cfg.sshbug_ignore1 == BUG_ON ||
- (ssh->cfg.sshbug_ignore1 == BUG_AUTO &&
+ if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||
+ (ssh->cfg.sshbug_ignore1 == AUTO &&
(!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
!strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
!strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25")))) {
logevent("We believe remote version has SSH1 ignore bug");
}
- if (ssh->cfg.sshbug_plainpw1 == BUG_ON ||
- (ssh->cfg.sshbug_plainpw1 == BUG_AUTO &&
+ if (ssh->cfg.sshbug_plainpw1 == FORCE_ON ||
+ (ssh->cfg.sshbug_plainpw1 == AUTO &&
(!strcmp(imp, "Cisco-1.25")))) {
/*
* These versions need a plain password sent; they can't
logevent("We believe remote version needs a plain SSH1 password");
}
- if (ssh->cfg.sshbug_rsa1 == BUG_ON ||
- (ssh->cfg.sshbug_rsa1 == BUG_AUTO &&
+ if (ssh->cfg.sshbug_rsa1 == FORCE_ON ||
+ (ssh->cfg.sshbug_rsa1 == AUTO &&
(!strcmp(imp, "Cisco-1.25")))) {
/*
* These versions apparently have no clue whatever about
logevent("We believe remote version can't handle RSA authentication");
}
- if (ssh->cfg.sshbug_hmac2 == BUG_ON ||
- (ssh->cfg.sshbug_hmac2 == BUG_AUTO &&
+ if (ssh->cfg.sshbug_hmac2 == FORCE_ON ||
+ (ssh->cfg.sshbug_hmac2 == AUTO &&
(wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||
wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||
wc_match("2.1 *", imp)))) {
logevent("We believe remote version has SSH2 HMAC bug");
}
- if (ssh->cfg.sshbug_derivekey2 == BUG_ON ||
- (ssh->cfg.sshbug_derivekey2 == BUG_AUTO &&
- (wc_match("2.0.0*", imp) || wc_match("2.0.1[01]*", imp) ))) {
+ if (ssh->cfg.sshbug_derivekey2 == FORCE_ON ||
+ (ssh->cfg.sshbug_derivekey2 == AUTO &&
+ (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {
/*
* These versions have the key-derivation bug (failing to
* include the literal shared secret in the hashes that
logevent("We believe remote version has SSH2 key-derivation bug");
}
- if (ssh->cfg.sshbug_rsapad2 == BUG_ON ||
- (ssh->cfg.sshbug_rsapad2 == BUG_AUTO &&
+ if (ssh->cfg.sshbug_rsapad2 == FORCE_ON ||
+ (ssh->cfg.sshbug_rsapad2 == AUTO &&
(wc_match("OpenSSH_2.[5-9]*", imp) ||
wc_match("OpenSSH_3.[0-2]*", imp)))) {
/*
logevent("We believe remote version has SSH2 RSA padding bug");
}
- if (ssh->cfg.sshbug_dhgex2 == BUG_ON) {
+ 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
+ * public-key authentication.
+ */
+ ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID;
+ logevent("We believe remote version has SSH2 public-key-session-ID bug");
+ }
+
+ if (ssh->cfg.sshbug_dhgex2 == FORCE_ON) {
/*
* User specified the SSH2 DH GEX bug.
*/
* Try to find host.
*/
logeventf(ssh, "Looking up host \"%s\"", host);
- addr = name_lookup(host, port, realhost);
+ addr = name_lookup(host, port, realhost, &ssh->cfg);
if ((err = sk_addr_error(addr)) != NULL)
return err;
logeventf(ssh, "Connecting to %s port %d", addrbuf, port);
}
ssh->fn = &fn_table;
- ssh->s = new_connection(addr, *realhost, port, 0, 1, nodelay, (Plug) ssh);
+ ssh->s = new_connection(addr, *realhost, port,
+ 0, 1, nodelay, (Plug) ssh, &ssh->cfg);
if ((err = sk_socket_error(ssh->s)) != NULL) {
ssh->s = NULL;
return err;
return -1;
break;
default:
- if (((c >= ' ' && c <= '~') ||
+ /*
+ * This simplistic check for printability is disabled
+ * when we're doing password input, because some people
+ * have control characters in their passwords.o
+ */
+ if ((!ssh->userpass_input_echo ||
+ (c >= ' ' && c <= '~') ||
((unsigned char) c >= 160))
&& ssh->userpass_input_bufpos < ssh->userpass_input_buflen-1) {
ssh->userpass_input_buffer[ssh->userpass_input_bufpos++] = c;
}
s->tis_auth_refused = s->ccard_auth_refused = 0;
/* Load the public half of ssh->cfg.keyfile so we notice if it's in Pageant */
- if (*ssh->cfg.keyfile) {
- if (!rsakey_pubblob(ssh->cfg.keyfile,
+ if (!filename_is_null(ssh->cfg.keyfile)) {
+ if (!rsakey_pubblob(&ssh->cfg.keyfile,
&s->publickey_blob, &s->publickey_bloblen))
s->publickey_blob = NULL;
} else
if (s->authed)
break;
}
- if (*ssh->cfg.keyfile && !s->tried_publickey)
+ if (!filename_is_null(ssh->cfg.keyfile) && !s->tried_publickey)
s->pwpkt_type = SSH1_CMSG_AUTH_RSA;
if (ssh->cfg.try_tis_auth &&
char msgbuf[256];
if (flags & FLAG_VERBOSE)
c_write_str(ssh, "Trying public key authentication.\r\n");
- logeventf(ssh, "Trying public key \"%s\"", ssh->cfg.keyfile);
- type = key_type(ssh->cfg.keyfile);
+ 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));
s->tried_publickey = 1;
continue;
}
- if (!rsakey_encrypted(ssh->cfg.keyfile, &comment)) {
+ if (!rsakey_encrypted(&ssh->cfg.keyfile, &comment)) {
if (flags & FLAG_VERBOSE)
c_write_str(ssh, "No passphrase required.\r\n");
goto tryauth;
s->tried_publickey = 1;
{
- int ret = loadrsakey(ssh->cfg.keyfile, &s->key, s->password);
+ int ret = loadrsakey(&ssh->cfg.keyfile, &s->key, s->password);
if (ret == 0) {
c_write_str(ssh, "Couldn't load private key from ");
- c_write_str(ssh, ssh->cfg.keyfile);
+ c_write_str(ssh, filename_to_str(&ssh->cfg.keyfile));
c_write_str(ssh, ".\r\n");
continue; /* go and try password */
}
if (sport && dport) {
if (type == 'L') {
pfd_addforward(host, dport, *saddr ? saddr : NULL,
- sport, ssh, ssh->cfg.lport_acceptall);
+ sport, ssh, &ssh->cfg);
logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s"
" forwarding to %s:%.*s%.*s%d%.*s",
(int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
c->ssh = ssh;
if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c,
- ssh->x11auth, NULL, -1) != NULL) {
+ ssh->x11auth, NULL, -1, &ssh->cfg) != NULL) {
logevent("opening X11 forward connection failed");
sfree(c);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
sprintf(buf, "Received remote port open request for %s:%d",
host, port);
logevent(buf);
- e = pfd_newconnect(&c->u.pfd.s, host, port, c);
+ e = pfd_newconnect(&c->u.pfd.s, host, port, c, &ssh->cfg);
if (e != NULL) {
char buf[256];
sprintf(buf, "Port open failed: %s", e);
SHA_Final(&ssh->exhash, s->exchange_hash);
dh_cleanup(ssh->kex_ctx);
+ ssh->kex_ctx = NULL;
#if 0
debug(("Exchange hash is:\n"));
s->tried_keyb_inter = FALSE;
s->kbd_inter_running = FALSE;
/* Load the pub half of ssh->cfg.keyfile so we notice if it's in Pageant */
- if (*ssh->cfg.keyfile) {
+ if (!filename_is_null(ssh->cfg.keyfile)) {
int keytype;
- logeventf(ssh, "Reading private key file \"%.150s\"", ssh->cfg.keyfile);
- keytype = key_type(ssh->cfg.keyfile);
+ logeventf(ssh, "Reading private key file \"%.150s\"",
+ filename_to_str(&ssh->cfg.keyfile));
+ keytype = key_type(&ssh->cfg.keyfile);
if (keytype == SSH_KEYTYPE_SSH2) {
s->publickey_blob =
- ssh2_userkey_loadpub(ssh->cfg.keyfile, NULL,
+ ssh2_userkey_loadpub(&ssh->cfg.keyfile, NULL,
&s->publickey_bloblen);
} else {
char *msgbuf;
logeventf(ssh, "Unable to use this key file (%s)",
key_type_to_str(keytype));
msgbuf = dupprintf("Unable to use key file \"%.150s\""
- " (%s)\r\n", ssh->cfg.keyfile,
+ " (%s)\r\n",
+ filename_to_str(&ssh->cfg.keyfile),
key_type_to_str(keytype));
c_write_str(ssh, msgbuf);
sfree(msgbuf);
ssh2_pkt_addstring_data(ssh, s->pkblob, s->pklen);
s->siglen = ssh->pktout.length - 5 + 4 + 20;
+ if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
+ s->siglen -= 4;
s->len = 1; /* message type */
s->len += 4 + s->pklen; /* key blob */
s->len += 4 + s->siglen; /* data to sign */
PUT_32BIT(s->q, s->siglen);
s->q += 4;
/* Now the data to be signed... */
- PUT_32BIT(s->q, 20);
- s->q += 4;
+ if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
+ PUT_32BIT(s->q, 20);
+ s->q += 4;
+ }
memcpy(s->q, ssh->v2_session_id, 20);
s->q += 20;
memcpy(s->q, ssh->pktout.data + 5,
* willing to accept it.
*/
pub_blob =
- (unsigned char *)ssh2_userkey_loadpub(ssh->cfg.keyfile,
+ (unsigned char *)ssh2_userkey_loadpub(&ssh->cfg.keyfile,
&algorithm,
&pub_blob_len);
if (pub_blob) {
* Actually attempt a serious authentication using
* the key.
*/
- if (ssh2_userkey_encrypted(ssh->cfg.keyfile, &comment)) {
+ if (ssh2_userkey_encrypted(&ssh->cfg.keyfile, &comment)) {
sprintf(s->pwprompt,
"Passphrase for key \"%.100s\": ",
comment);
*/
struct ssh2_userkey *key;
- key = ssh2_load_userkey(ssh->cfg.keyfile, s->password);
+ key = ssh2_load_userkey(&ssh->cfg.keyfile, s->password);
if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {
if (key == SSH2_WRONG_PASSPHRASE) {
c_write_str(ssh, "Wrong passphrase\r\n");
} else {
unsigned char *pkblob, *sigblob, *sigdata;
int pkblob_len, sigblob_len, sigdata_len;
+ int p;
/*
* We have loaded the private key and the server
* outgoing packet.
*/
sigdata_len = ssh->pktout.length - 5 + 4 + 20;
+ if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
+ sigdata_len -= 4;
sigdata = smalloc(sigdata_len);
- PUT_32BIT(sigdata, 20);
- memcpy(sigdata + 4, ssh->v2_session_id, 20);
- memcpy(sigdata + 24, ssh->pktout.data + 5,
+ p = 0;
+ if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
+ PUT_32BIT(sigdata+p, 20);
+ p += 4;
+ }
+ memcpy(sigdata+p, ssh->v2_session_id, 20); p += 20;
+ memcpy(sigdata+p, ssh->pktout.data + 5,
ssh->pktout.length - 5);
+ p += ssh->pktout.length - 5;
+ assert(p == sigdata_len);
sigblob = key->alg->sign(key->data, (char *)sigdata,
sigdata_len, &sigblob_len);
ssh2_add_sigblob(ssh, pkblob, pkblob_len,
if (sport && dport) {
if (type == 'L') {
pfd_addforward(host, dport, *saddr ? saddr : NULL,
- sport, ssh, ssh->cfg.lport_acceptall);
+ sport, ssh, &ssh->cfg);
logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s"
" forwarding to %s:%.*s%.*s%d%.*s",
(int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
if (!ssh->X11_fwd_enabled)
error = "X11 forwarding is not enabled";
else if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c,
- ssh->x11auth, addrstr, port) != NULL) {
+ ssh->x11auth, addrstr, port,
+ &ssh->cfg) != NULL) {
error = "Unable to open an X11 connection";
} else {
c->type = CHAN_X11;
error = "Remote port is not recognised";
} else {
char *e = pfd_newconnect(&c->u.pfd.s, realpf->dhost,
- realpf->dport, c);
+ realpf->dport, c, &ssh->cfg);
logeventf(ssh, "Received remote port open request"
" for %s:%d", realpf->dhost, realpf->dport);
if (e != NULL) {
ssh->sccomp = NULL;
ssh->sc_comp_ctx = NULL;
ssh->kex = NULL;
+ ssh->kex_ctx = NULL;
ssh->hostkey = NULL;
ssh->exitcode = -1;
ssh->state = SSH_STATE_PREPACKET;
ssh->term_width = ssh->cfg.width;
ssh->term_height = ssh->cfg.height;
+ ssh->channels = NULL;
+ ssh->rportfwds = NULL;
+
ssh->send_ok = 0;
ssh->editing = 0;
ssh->echoing = 0;
return NULL;
}
+static void ssh_free(void *handle)
+{
+ Ssh ssh = (Ssh) handle;
+ struct ssh_channel *c;
+ struct ssh_rportfwd *pf;
+
+ if (ssh->v1_cipher_ctx)
+ ssh->cipher->free_context(ssh->v1_cipher_ctx);
+ if (ssh->cs_cipher_ctx)
+ ssh->cscipher->free_context(ssh->cs_cipher_ctx);
+ if (ssh->sc_cipher_ctx)
+ ssh->sccipher->free_context(ssh->sc_cipher_ctx);
+ if (ssh->cs_mac_ctx)
+ ssh->csmac->free_context(ssh->cs_mac_ctx);
+ if (ssh->sc_mac_ctx)
+ ssh->scmac->free_context(ssh->sc_mac_ctx);
+ if (ssh->cs_comp_ctx)
+ ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx);
+ if (ssh->sc_comp_ctx)
+ ssh->sccomp->compress_cleanup(ssh->sc_comp_ctx);
+ if (ssh->kex_ctx)
+ dh_cleanup(ssh->kex_ctx);
+ sfree(ssh->savedhost);
+
+ if (ssh->channels) {
+ while ((c = delpos234(ssh->channels, 0)) != NULL) {
+ switch (c->type) {
+ case CHAN_X11:
+ if (c->u.x11.s != NULL)
+ x11_close(c->u.x11.s);
+ break;
+ case CHAN_SOCKDATA:
+ if (c->u.pfd.s != NULL)
+ pfd_close(c->u.pfd.s);
+ break;
+ }
+ sfree(c);
+ }
+ freetree234(ssh->channels);
+ }
+
+ if (ssh->rportfwds) {
+ while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)
+ sfree(pf);
+ freetree234(ssh->rportfwds);
+ }
+ sfree(ssh->deferred_send_data);
+ if (ssh->x11auth)
+ x11_free_auth(ssh->x11auth);
+ sfree(ssh->do_ssh_init_state);
+ sfree(ssh->do_ssh1_login_state);
+ sfree(ssh->do_ssh2_transport_state);
+ sfree(ssh->do_ssh2_authconn_state);
+
+ if (ssh->s)
+ sk_close(ssh->s);
+ sfree(ssh);
+}
+
/*
* Reconfigure the SSH backend.
*
Backend ssh_backend = {
ssh_init,
+ ssh_free,
ssh_reconfig,
ssh_send,
ssh_sendbuffer,