+static int ssh_versioncmp(char *a, char *b) {
+ char *ae, *be;
+ unsigned long av, bv;
+
+ av = strtoul(a, &ae, 10);
+ bv = strtoul(b, &be, 10);
+ if (av != bv) return (av < bv ? -1 : +1);
+ if (*ae == '.') ae++;
+ if (*be == '.') be++;
+ av = strtoul(ae, &ae, 10);
+ bv = strtoul(be, &be, 10);
+ if (av != bv) return (av < bv ? -1 : +1);
+ return 0;
+}
+
+
+/*
+ * Utility routine for putting an SSH-protocol `string' into a SHA
+ * state.
+ */
+#include <stdio.h>
+void sha_string(SHA_State *s, void *str, int len) {
+ unsigned char lenblk[4];
+ PUT_32BIT(lenblk, len);
+ SHA_Bytes(s, lenblk, 4);
+ SHA_Bytes(s, str, len);
+}
+
+/*
+ * SSH2 packet construction functions.
+ */
+void ssh2_pkt_adddata(void *data, int len) {
+ pktout.length += len;
+ if (pktout.maxlen < pktout.length) {
+ pktout.maxlen = pktout.length + 256;
+ pktout.data = (pktout.data == NULL ? malloc(pktout.maxlen+APIEXTRA) :
+ realloc(pktout.data, pktout.maxlen+APIEXTRA));
+ if (!pktout.data)
+ fatalbox("Out of memory");
+ }
+ memcpy(pktout.data+pktout.length-len, data, len);
+}
+void ssh2_pkt_addbyte(unsigned char byte) {
+ ssh2_pkt_adddata(&byte, 1);
+}
+void ssh2_pkt_init(int pkt_type) {
+ pktout.length = 5;
+ ssh2_pkt_addbyte((unsigned char)pkt_type);
+}
+void ssh2_pkt_addbool(unsigned char value) {
+ ssh2_pkt_adddata(&value, 1);
+}
+void ssh2_pkt_adduint32(unsigned long value) {
+ unsigned char x[4];
+ PUT_32BIT(x, value);
+ ssh2_pkt_adddata(x, 4);
+}
+void ssh2_pkt_addstring_start(void) {
+ ssh2_pkt_adduint32(0);
+ pktout.savedpos = pktout.length;
+}
+void ssh2_pkt_addstring_str(char *data) {
+ ssh2_pkt_adddata(data, strlen(data));
+ PUT_32BIT(pktout.data + pktout.savedpos - 4,
+ pktout.length - pktout.savedpos);
+}
+void ssh2_pkt_addstring_data(char *data, int len) {
+ ssh2_pkt_adddata(data, len);
+ PUT_32BIT(pktout.data + pktout.savedpos - 4,
+ pktout.length - pktout.savedpos);
+}
+void ssh2_pkt_addstring(char *data) {
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_str(data);
+}
+char *ssh2_mpint_fmt(Bignum b, int *len) {
+ unsigned char *p;
+ int i, n = b[0];
+ p = malloc(n * 2 + 1);
+ if (!p)
+ fatalbox("out of memory");
+ p[0] = 0;
+ for (i = 0; i < n; i++) {
+ p[i*2+1] = (b[n-i] >> 8) & 0xFF;
+ p[i*2+2] = (b[n-i] ) & 0xFF;
+ }
+ i = 0;
+ while (p[i] == 0 && (p[i+1] & 0x80) == 0)
+ i++;
+ memmove(p, p+i, n*2+1-i);
+ *len = n*2+1-i;
+ return p;
+}
+void ssh2_pkt_addmp(Bignum b) {
+ unsigned char *p;
+ int len;
+ p = ssh2_mpint_fmt(b, &len);
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_data(p, len);
+ free(p);
+}
+void ssh2_pkt_send(void) {
+ int cipherblk, maclen, padding, i;
+ static unsigned long outgoing_sequence = 0;
+
+ /*
+ * Add padding. At least four bytes, and must also bring total
+ * length (minus MAC) up to a multiple of the block size.
+ */
+ cipherblk = cipher ? cipher->blksize : 8; /* block size */
+ cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
+ padding = 4;
+ padding += (cipherblk - (pktout.length + padding) % cipherblk) % cipherblk;
+ pktout.data[4] = padding;
+ for (i = 0; i < padding; i++)
+ pktout.data[pktout.length + i] = random_byte();
+ PUT_32BIT(pktout.data, pktout.length + padding - 4);
+ if (csmac)
+ csmac->generate(pktout.data, pktout.length + padding,
+ outgoing_sequence);
+ outgoing_sequence++; /* whether or not we MACed */
+
+#if 0
+ debug(("Sending packet len=%d\r\n", pktout.length+padding));
+ for (i = 0; i < pktout.length+padding; i++)
+ debug((" %02x", (unsigned char)pktout.data[i]));
+ debug(("\r\n"));
+#endif
+
+ if (cscipher)
+ cscipher->encrypt(pktout.data, pktout.length + padding);
+ maclen = csmac ? csmac->len : 0;
+
+ s_write(pktout.data, pktout.length + padding + maclen);
+}
+
+#if 0
+void bndebug(char *string, Bignum b) {
+ unsigned char *p;
+ int i, len;
+ p = ssh2_mpint_fmt(b, &len);
+ debug(("%s", string));
+ for (i = 0; i < len; i++)
+ debug((" %02x", p[i]));
+ debug(("\r\n"));
+ free(p);
+}
+#endif
+
+void sha_mpint(SHA_State *s, Bignum b) {
+ unsigned char *p;
+ int len;
+ p = ssh2_mpint_fmt(b, &len);
+ sha_string(s, p, len);
+ free(p);
+}
+
+/*
+ * SSH2 packet decode functions.
+ */
+unsigned long ssh2_pkt_getuint32(void) {
+ unsigned long value;
+ if (pktin.length - pktin.savedpos < 4)
+ return 0; /* arrgh, no way to decline (FIXME?) */
+ value = GET_32BIT(pktin.data+pktin.savedpos);
+ pktin.savedpos += 4;
+ return value;
+}
+void ssh2_pkt_getstring(char **p, int *length) {
+ *p = NULL;
+ if (pktin.length - pktin.savedpos < 4)
+ return;
+ *length = GET_32BIT(pktin.data+pktin.savedpos);
+ pktin.savedpos += 4;
+ if (pktin.length - pktin.savedpos < *length)
+ return;
+ *p = pktin.data+pktin.savedpos;
+ pktin.savedpos += *length;
+}
+Bignum ssh2_pkt_getmp(void) {
+ char *p;
+ int i, j, length;
+ Bignum b;
+
+ ssh2_pkt_getstring(&p, &length);
+ if (!p)
+ return NULL;
+ if (p[0] & 0x80) {
+ bombout(("internal error: Can't handle negative mpints"));
+ return NULL;
+ }
+ b = newbn((length+1)/2);
+ for (i = 0; i < length; i++) {
+ j = length - 1 - i;
+ if (j & 1)
+ b[j/2+1] |= ((unsigned char)p[i]) << 8;
+ else
+ b[j/2+1] |= ((unsigned char)p[i]);
+ }
+ return b;
+}
+
+static int do_ssh_init(void) {
+ char c, *vsp;
+ char version[10];
+ char vstring[80];
+ char vlog[sizeof(vstring)+20];
+ int i;
+
+#ifdef FWHACK
+ i = 0;
+ while (s_read(&c, 1) == 1) {
+ if (c == 'S' && i < 2) i++;
+ else if (c == 'S' && i == 2) i = 2;
+ else if (c == 'H' && i == 2) break;
+ else i = 0;
+ }
+#else
+ if (s_read(&c,1) != 1 || c != 'S') return 0;
+ if (s_read(&c,1) != 1 || c != 'S') return 0;
+ if (s_read(&c,1) != 1 || c != 'H') return 0;
+#endif
+ strcpy(vstring, "SSH-");
+ vsp = vstring+4;
+ if (s_read(&c,1) != 1 || c != '-') return 0;
+ i = 0;
+ while (1) {
+ if (s_read(&c,1) != 1)
+ return 0;
+ if (vsp < vstring+sizeof(vstring)-1)
+ *vsp++ = c;
+ if (i >= 0) {
+ if (c == '-') {
+ version[i] = '\0';
+ i = -1;
+ } else if (i < sizeof(version)-1)
+ version[i++] = c;
+ }
+ else if (c == '\n')
+ break;
+ }
+
+ *vsp = 0;
+ sprintf(vlog, "Server version: %s", vstring);
+ vlog[strcspn(vlog, "\r\n")] = '\0';
+ logevent(vlog);
+
+ /*
+ * Server version "1.99" means we can choose whether we use v1
+ * or v2 protocol. Choice is based on cfg.sshprot.
+ */
+ if (ssh_versioncmp(version, cfg.sshprot == 1 ? "2.0" : "1.99") >= 0) {
+ /*
+ * This is a v2 server. Begin v2 protocol.
+ */
+ char *verstring = "SSH-2.0-PuTTY";
+ SHA_Init(&exhash);
+ /*
+ * Hash our version string and their version string.
+ */
+ sha_string(&exhash, verstring, strlen(verstring));
+ sha_string(&exhash, vstring, strcspn(vstring, "\r\n"));
+ sprintf(vstring, "%s\n", verstring);
+ sprintf(vlog, "We claim version: %s", verstring);
+ logevent(vlog);
+ logevent("Using SSH protocol version 2");
+ s_write(vstring, strlen(vstring));
+ ssh_protocol = ssh2_protocol;
+ ssh_version = 2;
+ s_rdpkt = ssh2_rdpkt;
+ } else {
+ /*
+ * This is a v1 server. Begin v1 protocol.
+ */
+ sprintf(vstring, "SSH-%s-PuTTY\n",
+ (ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5"));
+ sprintf(vlog, "We claim version: %s", vstring);
+ vlog[strcspn(vlog, "\r\n")] = '\0';
+ logevent(vlog);
+ logevent("Using SSH protocol version 1");
+ s_write(vstring, strlen(vstring));
+ ssh_protocol = ssh1_protocol;
+ ssh_version = 1;
+ s_rdpkt = ssh1_rdpkt;
+ }
+ ssh_send_ok = 0;
+ return 1;
+}
+
+/*
+ * Handle the key exchange and user authentication phases.
+ */
+static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
+{
+ int i, j, len;
+ unsigned char *rsabuf, *keystr1, *keystr2;
+ unsigned char cookie[8];
+ struct RSAKey servkey, hostkey;
+ struct MD5Context md5c;
+ static unsigned long supported_ciphers_mask, supported_auths_mask;
+ static int tried_publickey;
+ static unsigned char session_id[16];
+ int cipher_type;
+
+ crBegin;
+
+ if (!ispkt) crWaitUntil(ispkt);
+
+ if (pktin.type != SSH1_SMSG_PUBLIC_KEY) {
+ bombout(("Public key packet not received"));
+ crReturn(0);
+ }
+
+ logevent("Received public keys");
+
+ memcpy(cookie, pktin.body, 8);
+
+ i = makekey(pktin.body+8, &servkey, &keystr1, 0);
+ j = makekey(pktin.body+8+i, &hostkey, &keystr2, 0);
+
+ /*
+ * Hash the host key and print the hash in the log box. Just as
+ * a last resort in case the registry's host key checking is
+ * compromised, we'll allow the user some ability to verify
+ * host keys by eye.
+ */
+ MD5Init(&md5c);
+ MD5Update(&md5c, keystr2, hostkey.bytes);
+ MD5Final(session_id, &md5c);
+ {
+ char logmsg[80];
+ int i;
+ logevent("Host key MD5 is:");
+ strcpy(logmsg, " ");
+ for (i = 0; i < 16; i++)
+ sprintf(logmsg+strlen(logmsg), "%02x", session_id[i]);
+ logevent(logmsg);
+ }
+
+ supported_ciphers_mask = GET_32BIT(pktin.body+12+i+j);
+ supported_auths_mask = GET_32BIT(pktin.body+16+i+j);
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, keystr2, hostkey.bytes);
+ MD5Update(&md5c, keystr1, servkey.bytes);
+ MD5Update(&md5c, pktin.body, 8);
+ MD5Final(session_id, &md5c);