+ PUT_32BIT(pktout.data+biglen, crc);
+ PUT_32BIT(pktout.data, len);
+
+#if 0
+ debug(("Sending packet len=%d\r\n", biglen+4));
+ for (i = 0; i < biglen+4; i++)
+ debug((" %02x", (unsigned char)pktout.data[i]));
+ debug(("\r\n"));
+#endif
+ if (cipher)
+ cipher->encrypt(pktout.data+4, biglen);
+
+ sk_write(s, pktout.data, biglen+4);
+}
+
+/*
+ * Construct a packet with the specified contents and
+ * send it to the server.
+ */
+static void send_packet(int pkttype, ...)
+{
+ va_list args;
+ unsigned char *p, *argp, argchar;
+ unsigned long argint;
+ int pktlen, argtype, arglen;
+ Bignum bn;
+
+ pktlen = 0;
+ va_start(args, pkttype);
+ while ((argtype = va_arg(args, int)) != PKT_END) {
+ switch (argtype) {
+ case PKT_INT:
+ (void) va_arg(args, int);
+ pktlen += 4;
+ break;
+ case PKT_CHAR:
+ (void) va_arg(args, char);
+ pktlen++;
+ break;
+ case PKT_DATA:
+ (void) va_arg(args, unsigned char *);
+ arglen = va_arg(args, int);
+ pktlen += arglen;
+ break;
+ case PKT_STR:
+ argp = va_arg(args, unsigned char *);
+ arglen = strlen(argp);
+ pktlen += 4 + arglen;
+ break;
+ case PKT_BIGNUM:
+ bn = va_arg(args, Bignum);
+ pktlen += ssh1_bignum_length(bn);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ va_end(args);
+
+ s_wrpkt_start(pkttype, pktlen);
+ p = pktout.body;
+
+ va_start(args, pkttype);
+ while ((argtype = va_arg(args, int)) != PKT_END) {
+ switch (argtype) {
+ case PKT_INT:
+ argint = va_arg(args, int);
+ PUT_32BIT(p, argint);
+ p += 4;
+ break;
+ case PKT_CHAR:
+ argchar = va_arg(args, unsigned char);
+ *p = argchar;
+ p++;
+ break;
+ case PKT_DATA:
+ argp = va_arg(args, unsigned char *);
+ arglen = va_arg(args, int);
+ memcpy(p, argp, arglen);
+ p += arglen;
+ break;
+ case PKT_STR:
+ argp = va_arg(args, unsigned char *);
+ arglen = strlen(argp);
+ PUT_32BIT(p, arglen);
+ memcpy(p + 4, argp, arglen);
+ p += 4 + arglen;
+ break;
+ case PKT_BIGNUM:
+ bn = va_arg(args, Bignum);
+ p += ssh1_write_bignum(p, bn);
+ break;
+ }
+ }
+ va_end(args);
+
+ s_wrpkt();
+}
+
+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>
+static 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.
+ */
+static void ssh2_pkt_ensure(int length) {
+ if (pktout.maxlen < length) {
+ pktout.maxlen = length + 256;
+ pktout.data = (pktout.data == NULL ? smalloc(pktout.maxlen+APIEXTRA) :
+ srealloc(pktout.data, pktout.maxlen+APIEXTRA));
+ if (!pktout.data)
+ fatalbox("Out of memory");
+ }
+}
+static void ssh2_pkt_adddata(void *data, int len) {
+ pktout.length += len;
+ ssh2_pkt_ensure(pktout.length);
+ memcpy(pktout.data+pktout.length-len, data, len);
+}
+static void ssh2_pkt_addbyte(unsigned char byte) {
+ ssh2_pkt_adddata(&byte, 1);
+}
+static void ssh2_pkt_init(int pkt_type) {
+ pktout.length = 5;
+ ssh2_pkt_addbyte((unsigned char)pkt_type);
+}
+static void ssh2_pkt_addbool(unsigned char value) {
+ ssh2_pkt_adddata(&value, 1);
+}
+static void ssh2_pkt_adduint32(unsigned long value) {
+ unsigned char x[4];
+ PUT_32BIT(x, value);
+ ssh2_pkt_adddata(x, 4);
+}
+static void ssh2_pkt_addstring_start(void) {
+ ssh2_pkt_adduint32(0);
+ pktout.savedpos = pktout.length;
+}
+static void ssh2_pkt_addstring_str(char *data) {
+ ssh2_pkt_adddata(data, strlen(data));
+ PUT_32BIT(pktout.data + pktout.savedpos - 4,
+ pktout.length - pktout.savedpos);
+}
+static 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);
+}
+static void ssh2_pkt_addstring(char *data) {
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_str(data);
+}
+static char *ssh2_mpint_fmt(Bignum b, int *len) {
+ unsigned char *p;
+ int i, n = b[0];
+ p = smalloc(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;
+}
+static 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);
+ sfree(p);
+}
+static void ssh2_pkt_send(void) {
+ int cipherblk, maclen, padding, i;
+ static unsigned long outgoing_sequence = 0;
+
+ /*
+ * Compress packet payload.
+ */
+#if 0
+ debug(("Pre-compression payload:\r\n"));
+ for (i = 5; i < pktout.length; i++)
+ debug((" %02x", (unsigned char)pktout.data[i]));
+ debug(("\r\n"));
+#endif
+ {
+ unsigned char *newpayload;
+ int newlen;
+ if (cscomp && cscomp->compress(pktout.data+5, pktout.length-5,
+ &newpayload, &newlen)) {
+ pktout.length = 5;
+ ssh2_pkt_adddata(newpayload, newlen);
+ sfree(newpayload);
+ }
+ }
+
+ /*
+ * 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;
+ maclen = csmac ? csmac->len : 0;
+ ssh2_pkt_ensure(pktout.length + padding + maclen);
+ 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);
+
+ sk_write(s, 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"));
+ sfree(p);
+}
+#endif
+
+static void sha_mpint(SHA_State *s, Bignum b) {
+ unsigned char *p;
+ int len;
+ p = ssh2_mpint_fmt(b, &len);
+ sha_string(s, p, len);
+ sfree(p);
+}
+
+/*
+ * SSH2 packet decode functions.
+ */
+static 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;
+}
+static 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;
+}
+static 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]);
+ }
+ while (b[0] > 1 && b[b[0]] == 0) b[0]--;
+ return b;
+}
+
+static int do_ssh_init(unsigned char c) {
+ static char *vsp;
+ static char version[10];
+ static char vstring[80];
+ static char vlog[sizeof(vstring)+20];
+ static int i;
+
+ crBegin;
+
+ /* Search for the string "SSH-" in the input. */
+ 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') i = transS[i];
+ else if (c == 'H') i = transH[i];
+ else if (c == '-') i = transminus[i];
+ else i = 0;
+ if (i < 0)
+ break;
+ crReturn(1); /* get another character */
+ }
+
+ strcpy(vstring, "SSH-");
+ vsp = vstring+4;
+ i = 0;
+ while (1) {
+ crReturn(1); /* get another char */
+ 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;
+ }
+
+ ssh_agentfwd_enabled = FALSE;
+ rdpkt2_state.incoming_sequence = 0;
+
+ *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(&exhashbase);
+ /*
+ * Hash our version string and their version string.
+ */
+ sha_string(&exhashbase, verstring, strlen(verstring));
+ sha_string(&exhashbase, 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");
+ sk_write(s, 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");
+ sk_write(s, vstring, strlen(vstring));
+ ssh_protocol = ssh1_protocol;
+ ssh_version = 1;
+ s_rdpkt = ssh1_rdpkt;
+ }
+ ssh_state = SSH_STATE_BEFORE_SIZE;
+
+ crFinish(0);
+}
+
+static void ssh_gotdata(unsigned char *data, int datalen)
+{
+ crBegin;
+
+ /*
+ * To begin with, feed the characters one by one to the
+ * protocol initialisation / selection function do_ssh_init().
+ * When that returns 0, we're done with the initial greeting
+ * exchange and can move on to packet discipline.
+ */
+ while (1) {
+ int ret;
+ if (datalen == 0)
+ crReturnV; /* more data please */
+ ret = do_ssh_init(*data);
+ data++; datalen--;
+ if (ret == 0)
+ break;
+ }
+
+ /*
+ * We emerge from that loop when the initial negotiation is
+ * over and we have selected an s_rdpkt function. Now pass
+ * everything to s_rdpkt, and then pass the resulting packets
+ * to the proper protocol handler.
+ */
+ if (datalen == 0)
+ crReturnV;
+ while (1) {
+ while (datalen > 0) {
+ if ( s_rdpkt(&data, &datalen) == 0 ) {
+ ssh_protocol(NULL, 0, 1);
+ if (ssh_state == SSH_STATE_CLOSED) {
+ return;
+ }
+ }
+ }
+ crReturnV;
+ }
+ crFinishV;
+}
+
+static int ssh_receive(Socket skt, int urgent, char *data, int len) {
+ if (!len) {
+ /* Connection has closed. */
+ ssh_state = SSH_STATE_CLOSED;
+ sk_close(s);
+ s = NULL;
+ return 0;
+ }
+ ssh_gotdata (data, len);
+ if (ssh_state == SSH_STATE_CLOSED) {
+ if (s) {
+ sk_close(s);
+ s = NULL;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Connect to specified host and port.
+ * Returns an error message, or NULL on success.
+ * Also places the canonical host name into `realhost'.
+ */
+static char *connect_to_host(char *host, int port, char **realhost)
+{
+ SockAddr addr;
+ char *err;
+#ifdef FWHACK
+ char *FWhost;
+ int FWport;
+#endif
+
+ savedhost = smalloc(1+strlen(host));
+ if (!savedhost)
+ fatalbox("Out of memory");
+ strcpy(savedhost, host);
+
+ if (port < 0)
+ port = 22; /* default ssh port */
+ savedport = port;
+
+#ifdef FWHACK
+ FWhost = host;
+ FWport = port;
+ host = FWSTR;
+ port = 23;
+#endif
+
+ /*
+ * Try to find host.
+ */
+ addr = sk_namelookup(host, realhost);
+ if ( (err = sk_addr_error(addr)) )
+ return err;
+
+#ifdef FWHACK
+ *realhost = FWhost;
+#endif
+
+ /*
+ * Open socket.
+ */
+ s = sk_new(addr, port, 0, ssh_receive);
+ if ( (err = sk_socket_error(s)) )
+ return err;
+
+#ifdef FWHACK
+ sk_write(s, "connect ", 8);
+ sk_write(s, FWhost, strlen(FWhost));
+ {
+ char buf[20];
+ sprintf(buf, " %d\n", FWport);
+ sk_write(s, buf, strlen(buf));
+ }
+#endif
+
+ return NULL;
+}
+
+/*
+ * 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;
+ static char username[100];
+
+ 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);
+
+ /*
+ * Log the host key fingerprint.
+ */
+ {
+ char logmsg[80];
+ logevent("Host key fingerprint is:");
+ strcpy(logmsg, " ");
+ hostkey.comment = NULL;
+ rsa_fingerprint(logmsg+strlen(logmsg), sizeof(logmsg)-strlen(logmsg),
+ &hostkey);
+ 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);
+
+ for (i=0; i<32; i++)
+ session_key[i] = random_byte();
+
+ len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
+
+ rsabuf = smalloc(len);
+ if (!rsabuf)
+ fatalbox("Out of memory");
+
+ /*
+ * Verify the host key.
+ */
+ {
+ /*
+ * First format the key into a string.
+ */
+ int len = rsastr_len(&hostkey);
+ char fingerprint[100];
+ char *keystr = smalloc(len);
+ if (!keystr)
+ fatalbox("Out of memory");
+ rsastr_fmt(keystr, &hostkey);
+ rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
+ verify_ssh_host_key(savedhost, savedport, "rsa", keystr, fingerprint);
+ sfree(keystr);
+ }
+
+ for (i=0; i<32; i++) {
+ rsabuf[i] = session_key[i];
+ if (i < 16)
+ rsabuf[i] ^= session_id[i];
+ }
+
+ if (hostkey.bytes > servkey.bytes) {
+ rsaencrypt(rsabuf, 32, &servkey);
+ rsaencrypt(rsabuf, servkey.bytes, &hostkey);
+ } else {
+ rsaencrypt(rsabuf, 32, &hostkey);
+ rsaencrypt(rsabuf, hostkey.bytes, &servkey);
+ }
+
+ logevent("Encrypted session key");
+
+ cipher_type = cfg.cipher == CIPHER_BLOWFISH ? SSH_CIPHER_BLOWFISH :
+ cfg.cipher == CIPHER_DES ? SSH_CIPHER_DES :
+ SSH_CIPHER_3DES;
+ if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
+ c_write("Selected cipher not supported, falling back to 3DES\r\n", 53);
+ cipher_type = SSH_CIPHER_3DES;
+ }
+ switch (cipher_type) {
+ case SSH_CIPHER_3DES: logevent("Using 3DES encryption"); break;
+ case SSH_CIPHER_DES: logevent("Using single-DES encryption"); break;
+ case SSH_CIPHER_BLOWFISH: logevent("Using Blowfish encryption"); break;
+ }
+
+ send_packet(SSH1_CMSG_SESSION_KEY,
+ PKT_CHAR, cipher_type,
+ PKT_DATA, cookie, 8,
+ PKT_CHAR, (len*8) >> 8, PKT_CHAR, (len*8) & 0xFF,
+ PKT_DATA, rsabuf, len,
+ PKT_INT, 0,
+ PKT_END);
+
+ logevent("Trying to enable encryption...");
+
+ sfree(rsabuf);
+
+ cipher = cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
+ cipher_type == SSH_CIPHER_DES ? &ssh_des :
+ &ssh_3des;
+ cipher->sesskey(session_key);
+
+ crWaitUntil(ispkt);
+
+ if (pktin.type != SSH1_SMSG_SUCCESS) {
+ bombout(("Encryption not successfully enabled"));
+ crReturn(0);
+ }
+
+ logevent("Successfully started encryption");
+
+ fflush(stdout);
+ {
+ static int pos = 0;
+ static char c;
+ if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
+ c_write("login as: ", 10);
+ ssh_send_ok = 1;
+ while (pos >= 0) {
+ crWaitUntil(!ispkt);
+ while (inlen--) switch (c = *in++) {
+ case 10: case 13:
+ username[pos] = 0;
+ pos = -1;
+ break;
+ case 8: case 127:
+ if (pos > 0) {
+ c_write("\b \b", 3);
+ pos--;
+ }
+ break;
+ case 21: case 27:
+ while (pos > 0) {
+ c_write("\b \b", 3);
+ pos--;
+ }
+ break;
+ case 3: case 4:
+ random_save_seed();
+ exit(0);
+ break;
+ default:
+ if (((c >= ' ' && c <= '~') ||
+ ((unsigned char)c >= 160)) && pos < 40) {
+ username[pos++] = c;
+ c_write(&c, 1);
+ }
+ break;
+ }
+ }
+ c_write("\r\n", 2);
+ username[strcspn(username, "\n\r")] = '\0';
+ } else {
+ strncpy(username, cfg.username, 99);
+ username[99] = '\0';
+ }
+
+ send_packet(SSH1_CMSG_USER, PKT_STR, username, PKT_END);
+ {
+ char userlog[22+sizeof(username)];
+ sprintf(userlog, "Sent username \"%s\"", username);
+ logevent(userlog);
+ if (flags & FLAG_INTERACTIVE &&
+ (!((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)))) {
+ strcat(userlog, "\r\n");
+ c_write(userlog, strlen(userlog));
+ }
+ }
+ }
+
+ crWaitUntil(ispkt);
+
+ tried_publickey = 0;
+
+ while (pktin.type == SSH1_SMSG_FAILURE) {
+ static char password[100];
+ static char prompt[200];
+ static int pos;
+ static char c;
+ static int pwpkt_type;
+ /*
+ * Show password prompt, having first obtained it via a TIS
+ * or CryptoCard exchange if we're doing TIS or CryptoCard
+ * authentication.
+ */
+ pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
+ if (agent_exists()) {
+ /*
+ * Attempt RSA authentication using Pageant.
+ */
+ static unsigned char request[5], *response, *p;
+ static int responselen;
+ static int i, nkeys;
+ static int authed = FALSE;
+ void *r;
+
+ logevent("Pageant is running. Requesting keys.");
+
+ /* Request the keys held by the agent. */
+ PUT_32BIT(request, 1);
+ request[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES;
+ agent_query(request, 5, &r, &responselen);
+ response = (unsigned char *)r;
+ if (response) {
+ p = response + 5;
+ nkeys = GET_32BIT(p); p += 4;
+ { char buf[64]; sprintf(buf, "Pageant has %d keys", nkeys);
+ logevent(buf); }
+ for (i = 0; i < nkeys; i++) {
+ static struct RSAKey key;
+ static Bignum challenge;
+ static char *commentp;
+ static int commentlen;
+
+ { char buf[64]; sprintf(buf, "Trying Pageant key #%d", i);
+ logevent(buf); }
+ p += 4;
+ p += ssh1_read_bignum(p, &key.exponent);
+ p += ssh1_read_bignum(p, &key.modulus);
+ commentlen = GET_32BIT(p); p += 4;
+ commentp = p; p += commentlen;
+ send_packet(SSH1_CMSG_AUTH_RSA,
+ PKT_BIGNUM, key.modulus, PKT_END);
+ crWaitUntil(ispkt);
+ if (pktin.type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
+ logevent("Key refused");
+ continue;
+ }
+ logevent("Received RSA challenge");
+ ssh1_read_bignum(pktin.body, &challenge);
+ {
+ char *agentreq, *q, *ret;
+ int len, retlen;
+ len = 1 + 4; /* message type, bit count */
+ len += ssh1_bignum_length(key.exponent);
+ len += ssh1_bignum_length(key.modulus);
+ len += ssh1_bignum_length(challenge);
+ len += 16; /* session id */
+ len += 4; /* response format */
+ agentreq = smalloc(4 + len);
+ PUT_32BIT(agentreq, len);
+ q = agentreq + 4;
+ *q++ = SSH_AGENTC_RSA_CHALLENGE;
+ PUT_32BIT(q, ssh1_bignum_bitcount(key.modulus));
+ q += 4;
+ q += ssh1_write_bignum(q, key.exponent);
+ q += ssh1_write_bignum(q, key.modulus);
+ q += ssh1_write_bignum(q, challenge);
+ memcpy(q, session_id, 16); q += 16;
+ PUT_32BIT(q, 1); /* response format */
+ agent_query(agentreq, len+4, &ret, &retlen);
+ sfree(agentreq);
+ if (ret) {
+ if (ret[4] == SSH_AGENT_RSA_RESPONSE) {
+ logevent("Sending Pageant's response");
+ send_packet(SSH1_CMSG_AUTH_RSA_RESPONSE,
+ PKT_DATA, ret+5, 16, PKT_END);
+ sfree(ret);
+ crWaitUntil(ispkt);
+ if (pktin.type == SSH1_SMSG_SUCCESS) {
+ logevent("Pageant's response accepted");
+ if (flags & FLAG_VERBOSE) {
+ c_write("Authenticated using RSA key \"",
+ 29);
+ c_write(commentp, commentlen);
+ c_write("\" from agent\r\n", 14);
+ }
+ authed = TRUE;
+ } else
+ logevent("Pageant's response not accepted");
+ } else {
+ logevent("Pageant failed to answer challenge");
+ sfree(ret);
+ }
+ } else {
+ logevent("No reply received from Pageant");
+ }
+ }
+ freebn(key.exponent);
+ freebn(key.modulus);
+ freebn(challenge);
+ if (authed)
+ break;
+ }
+ }
+ if (authed)
+ break;
+ }
+ if (*cfg.keyfile && !tried_publickey)
+ pwpkt_type = SSH1_CMSG_AUTH_RSA;
+
+ if (pktin.type == SSH1_SMSG_FAILURE &&
+ cfg.try_tis_auth &&
+ (supported_auths_mask & (1<<SSH1_AUTH_TIS))) {
+ pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
+ logevent("Requested TIS authentication");
+ send_packet(SSH1_CMSG_AUTH_TIS, PKT_END);
+ crWaitUntil(ispkt);
+ if (pktin.type != SSH1_SMSG_AUTH_TIS_CHALLENGE) {
+ logevent("TIS authentication declined");
+ if (flags & FLAG_INTERACTIVE)
+ c_write("TIS authentication refused.\r\n", 29);
+ } else {
+ int challengelen = ((pktin.body[0] << 24) |
+ (pktin.body[1] << 16) |
+ (pktin.body[2] << 8) |
+ (pktin.body[3]));
+ logevent("Received TIS challenge");
+ if (challengelen > sizeof(prompt)-1)
+ challengelen = sizeof(prompt)-1; /* prevent overrun */
+ memcpy(prompt, pktin.body+4, challengelen);
+ prompt[challengelen] = '\0';
+ }
+ }
+ if (pktin.type == SSH1_SMSG_FAILURE &&
+ cfg.try_tis_auth &&
+ (supported_auths_mask & (1<<SSH1_AUTH_CCARD))) {
+ pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
+ logevent("Requested CryptoCard authentication");
+ send_packet(SSH1_CMSG_AUTH_CCARD, PKT_END);
+ crWaitUntil(ispkt);
+ if (pktin.type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
+ logevent("CryptoCard authentication declined");
+ c_write("CryptoCard authentication refused.\r\n", 29);
+ } else {
+ int challengelen = ((pktin.body[0] << 24) |
+ (pktin.body[1] << 16) |
+ (pktin.body[2] << 8) |
+ (pktin.body[3]));
+ logevent("Received CryptoCard challenge");
+ if (challengelen > sizeof(prompt)-1)
+ challengelen = sizeof(prompt)-1; /* prevent overrun */
+ memcpy(prompt, pktin.body+4, challengelen);
+ strncpy(prompt + challengelen, "\r\nResponse : ",
+ sizeof(prompt)-challengelen);
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+ }
+ if (pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
+ sprintf(prompt, "%.90s@%.90s's password: ",
+ username, savedhost);
+ }
+ if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
+ char *comment = NULL;
+ if (flags & FLAG_VERBOSE)
+ c_write("Trying public key authentication.\r\n", 35);
+ if (!rsakey_encrypted(cfg.keyfile, &comment)) {
+ if (flags & FLAG_VERBOSE)
+ c_write("No passphrase required.\r\n", 25);
+ goto tryauth;
+ }
+ sprintf(prompt, "Passphrase for key \"%.100s\": ", comment);
+ sfree(comment);
+ }
+
+ if (ssh_get_password) {
+ if (!ssh_get_password(prompt, password, sizeof(password))) {
+ /*
+ * get_password failed to get a password (for
+ * example because one was supplied on the command
+ * line which has already failed to work).
+ * Terminate.
+ */
+ logevent("No more passwords to try");
+ ssh_state = SSH_STATE_CLOSED;
+ crReturn(1);
+ }
+ } else {
+ c_write(prompt, strlen(prompt));
+ pos = 0;
+ ssh_send_ok = 1;
+ while (pos >= 0) {
+ crWaitUntil(!ispkt);
+ while (inlen--) switch (c = *in++) {
+ case 10: case 13:
+ password[pos] = 0;
+ pos = -1;
+ break;
+ case 8: case 127:
+ if (pos > 0)
+ pos--;
+ break;
+ case 21: case 27:
+ pos = 0;
+ break;
+ case 3: case 4:
+ random_save_seed();
+ exit(0);
+ break;
+ default:
+ if (((c >= ' ' && c <= '~') ||
+ ((unsigned char)c >= 160)) && pos < sizeof(password))
+ password[pos++] = c;
+ break;
+ }
+ }
+ c_write("\r\n", 2);
+ }
+
+ tryauth:
+ if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
+ /*
+ * Try public key authentication with the specified
+ * key file.
+ */
+ static struct RSAKey pubkey;
+ static Bignum challenge, response;
+ static int i;
+ static unsigned char buffer[32];
+
+ tried_publickey = 1;
+ i = loadrsakey(cfg.keyfile, &pubkey, NULL, password);
+ if (i == 0) {
+ c_write("Couldn't load public key from ", 30);
+ c_write(cfg.keyfile, strlen(cfg.keyfile));
+ c_write(".\r\n", 3);
+ continue; /* go and try password */
+ }
+ if (i == -1) {
+ c_write("Wrong passphrase.\r\n", 19);
+ tried_publickey = 0;
+ continue; /* try again */
+ }
+
+ /*
+ * Send a public key attempt.
+ */
+ send_packet(SSH1_CMSG_AUTH_RSA,
+ PKT_BIGNUM, pubkey.modulus, PKT_END);
+
+ crWaitUntil(ispkt);
+ if (pktin.type == SSH1_SMSG_FAILURE) {
+ c_write("Server refused our public key.\r\n", 32);
+ continue; /* go and try password */
+ }
+ if (pktin.type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
+ bombout(("Bizarre response to offer of public key"));
+ crReturn(0);
+ }
+ ssh1_read_bignum(pktin.body, &challenge);
+ response = rsadecrypt(challenge, &pubkey);
+ freebn(pubkey.private_exponent); /* burn the evidence */
+
+ for (i = 0; i < 32; i += 2) {
+ buffer[i] = response[16-i/2] >> 8;
+ buffer[i+1] = response[16-i/2] & 0xFF;
+ }
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, buffer, 32);
+ MD5Update(&md5c, session_id, 16);
+ MD5Final(buffer, &md5c);
+
+ send_packet(SSH1_CMSG_AUTH_RSA_RESPONSE,
+ PKT_DATA, buffer, 16, PKT_END);
+
+ crWaitUntil(ispkt);
+ if (pktin.type == SSH1_SMSG_FAILURE) {
+ if (flags & FLAG_VERBOSE)
+ c_write("Failed to authenticate with our public key.\r\n",
+ 45);
+ continue; /* go and try password */
+ } else if (pktin.type != SSH1_SMSG_SUCCESS) {
+ bombout(("Bizarre response to RSA authentication response"));
+ crReturn(0);
+ }
+
+ break; /* we're through! */
+ } else {
+ send_packet(pwpkt_type, PKT_STR, password, PKT_END);
+ }
+ logevent("Sent password");
+ memset(password, 0, strlen(password));
+ crWaitUntil(ispkt);
+ if (pktin.type == SSH1_SMSG_FAILURE) {
+ if (flags & FLAG_VERBOSE)
+ c_write("Access denied\r\n", 15);
+ logevent("Authentication refused");
+ } else if (pktin.type == SSH1_MSG_DISCONNECT) {
+ logevent("Received disconnect request");
+ ssh_state = SSH_STATE_CLOSED;
+ crReturn(1);
+ } else if (pktin.type != SSH1_SMSG_SUCCESS) {
+ bombout(("Strange packet received, type %d", pktin.type));
+ crReturn(0);
+ }
+ }
+
+ logevent("Authentication successful");
+
+ crFinish(1);
+}
+
+void sshfwd_close(struct ssh_channel *c) {
+ if (c) {
+ if (ssh_version == 1) {
+ send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, PKT_END);
+ } else {
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(c->remoteid);
+ ssh2_pkt_send();
+ }
+ c->closes = 1;
+ if (c->type == CHAN_X11) {
+ c->u.x11.s = NULL;
+ logevent("X11 connection terminated");
+ }
+ }
+}
+
+void sshfwd_write(struct ssh_channel *c, char *buf, int len) {
+ if (ssh_version == 1) {
+ send_packet(SSH1_MSG_CHANNEL_DATA,
+ PKT_INT, c->remoteid,
+ PKT_INT, len,
+ PKT_DATA, buf, len,
+ PKT_END);
+ } else {
+ ssh2_add_channel_data(c, buf, len);
+ ssh2_try_send(c);
+ }
+}
+
+static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
+ crBegin;
+
+ random_init();
+
+ while (!do_ssh1_login(in, inlen, ispkt)) {
+ crReturnV;
+ }
+ if (ssh_state == SSH_STATE_CLOSED)
+ crReturnV;
+
+ if (cfg.agentfwd && agent_exists()) {
+ logevent("Requesting agent forwarding");
+ send_packet(SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END);
+ do { crReturnV; } while (!ispkt);
+ if (pktin.type != SSH1_SMSG_SUCCESS && pktin.type != SSH1_SMSG_FAILURE) {
+ bombout(("Protocol confusion"));
+ crReturnV;
+ } else if (pktin.type == SSH1_SMSG_FAILURE) {
+ logevent("Agent forwarding refused");
+ } else {
+ logevent("Agent forwarding enabled");
+ ssh_agentfwd_enabled = TRUE;
+ }
+ }