- if (ssh_get_line) {
- if (!ssh_get_line(prompt, password, sizeof(password), TRUE)) {
- /*
- * get_line 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_str(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_str("\r\n");
- }
-
- 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, password);
- if (i == 0) {
- c_write_str("Couldn't load public key from ");
- c_write_str(cfg.keyfile);
- c_write_str(".\r\n");
- continue; /* go and try password */
- }
- if (i == -1) {
- c_write_str("Wrong passphrase.\r\n");
- 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_str("Server refused our public key.\r\n");
- 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++) {
- buffer[i] = bignum_byte(response, 31-i);
- }
-
- 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_str("Failed to authenticate with our public key.\r\n");
- 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 {
- if (pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
- /*
- * Defence against traffic analysis: we send a
- * whole bunch of packets containing strings of
- * different lengths. One of these strings is the
- * password, in a SSH1_CMSG_AUTH_PASSWORD packet.
- * The others are all random data in
- * SSH1_MSG_IGNORE packets. This way a passive
- * listener can't tell which is the password, and
- * hence can't deduce the password length.
- *
- * Anybody with a password length greater than 16
- * bytes is going to have enough entropy in their
- * password that a listener won't find it _that_
- * much help to know how long it is. So what we'll
- * do is:
- *
- * - if password length < 16, we send 15 packets
- * containing string lengths 1 through 15
- *
- * - otherwise, we let N be the nearest multiple
- * of 8 below the password length, and send 8
- * packets containing string lengths N through
- * N+7. This won't obscure the order of
- * magnitude of the password length, but it will
- * introduce a bit of extra uncertainty.
- *
- * A few servers (the old 1.2.18 through 1.2.22)
- * can't deal with SSH1_MSG_IGNORE. For these
- * servers, we need an alternative defence. We make
- * use of the fact that the password is interpreted
- * as a C string: so we can append a NUL, then some
- * random data.
- */
- if (ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) {
- char string[64];
- char *s;
- int len;
-
- len = strlen(password);
- if (len < sizeof(string)) {
- s = string;
- strcpy(string, password);
- len++; /* cover the zero byte */
- while (len < sizeof(string)) {
- string[len++] = (char)random_byte();
- }
- } else {
- s = password;
- }
- send_packet(pwpkt_type, PKT_INT, len,
- PKT_DATA, s, len, PKT_END);
- } else {
- int bottom, top, pwlen, i;
- char *randomstr;
-
- pwlen = strlen(password);
- if (pwlen < 16) {
- bottom = 0; /* zero length passwords are OK! :-) */
- top = 15;
- } else {
- bottom = pwlen &~ 7;
- top = bottom + 7;
- }
-
- assert(pwlen >= bottom && pwlen <= top);
-
- randomstr = smalloc(top+1);
-
- for (i = bottom; i <= top; i++) {
- if (i == pwlen)
- defer_packet(pwpkt_type, PKT_STR, password, PKT_END);
- else {
- for (j = 0; j < i; j++) {
- do {
- randomstr[j] = random_byte();
- } while (randomstr[j] == '\0');
- }
- randomstr[i] = '\0';
- defer_packet(SSH1_MSG_IGNORE,
- PKT_STR, randomstr, PKT_END);
- }
- }
- ssh_pkt_defersend();
- }
- } 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_str("Access denied\r\n");
+ 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] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
+ agent_query(request, 5, &r, &responselen);
+ response = (unsigned char *) r;
+ if (response && responselen >= 5 &&
+ response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
+ p = response + 5;
+ nkeys = GET_32BIT(p);
+ p += 4;
+ {
+ char buf[64];
+ sprintf(buf, "Pageant has %d SSH1 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;
+ void *vret;
+ 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++ = SSH1_AGENTC_RSA_CHALLENGE;
+ PUT_32BIT(q, 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, &vret, &retlen);
+ ret = vret;
+ sfree(agentreq);
+ if (ret) {
+ if (ret[4] == SSH1_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_str
+ ("Authenticated using RSA key \"");
+ c_write(commentp, commentlen);
+ c_write_str("\" from agent\r\n");
+ }
+ 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 (cfg.try_tis_auth &&
+ (supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
+ !tis_auth_refused) {
+ 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_str("TIS authentication refused.\r\n");
+ tis_auth_refused = 1;
+ continue;
+ } 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 heuristic comes from OpenSSH */
+ strncpy(prompt + challengelen,
+ memchr(prompt, '\n', challengelen) ?
+ "": "\r\nResponse: ",
+ (sizeof prompt) - challengelen);
+ prompt[(sizeof prompt) - 1] = '\0';
+ }
+ }
+ if (cfg.try_tis_auth &&
+ (supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
+ !ccard_auth_refused) {
+ 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_str("CryptoCard authentication refused.\r\n");
+ ccard_auth_refused = 1;
+ continue;
+ } 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,
+ memchr(prompt, '\n', 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_str("Trying public key authentication.\r\n");
+ if (!rsakey_encrypted(cfg.keyfile, &comment)) {
+ if (flags & FLAG_VERBOSE)
+ c_write_str("No passphrase required.\r\n");
+ goto tryauth;
+ }
+ sprintf(prompt, "Passphrase for key \"%.100s\": ", comment);
+ sfree(comment);
+ }
+
+ /*
+ * Show password prompt, having first obtained it via a TIS
+ * or CryptoCard exchange if we're doing TIS or CryptoCard
+ * authentication.
+ */
+ if (ssh_get_line) {
+ if (!ssh_get_line(prompt, password, sizeof(password), TRUE)) {
+ /*
+ * get_line failed to get a password (for example
+ * because one was supplied on the command line
+ * which has already failed to work). Terminate.
+ */
+ send_packet(SSH1_MSG_DISCONNECT,
+ PKT_STR, "No more passwords available to try",
+ PKT_END);
+ connection_fatal("Unable to authenticate");
+ ssh_state = SSH_STATE_CLOSED;
+ crReturn(1);
+ }
+ } else {
+ /* Prompt may have come from server. We've munged it a bit, so
+ * we know it to be zero-terminated at least once. */
+ c_write_untrusted(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 (pos < sizeof(password)-1)
+ password[pos++] = c;
+ break;
+ }
+ }
+ c_write_str("\r\n");
+ }
+
+ 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, password);
+ if (i == 0) {
+ c_write_str("Couldn't load private key from ");
+ c_write_str(cfg.keyfile);
+ c_write_str(".\r\n");
+ continue; /* go and try password */
+ }
+ if (i == -1) {
+ c_write_str("Wrong passphrase.\r\n");
+ 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_str("Server refused our public key.\r\n");
+ 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++) {
+ buffer[i] = bignum_byte(response, 31 - i);
+ }
+
+ 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_str
+ ("Failed to authenticate with our public key.\r\n");
+ 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 {
+ if (pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
+ /*
+ * Defence against traffic analysis: we send a
+ * whole bunch of packets containing strings of
+ * different lengths. One of these strings is the
+ * password, in a SSH1_CMSG_AUTH_PASSWORD packet.
+ * The others are all random data in
+ * SSH1_MSG_IGNORE packets. This way a passive
+ * listener can't tell which is the password, and
+ * hence can't deduce the password length.
+ *
+ * Anybody with a password length greater than 16
+ * bytes is going to have enough entropy in their
+ * password that a listener won't find it _that_
+ * much help to know how long it is. So what we'll
+ * do is:
+ *
+ * - if password length < 16, we send 15 packets
+ * containing string lengths 1 through 15
+ *
+ * - otherwise, we let N be the nearest multiple
+ * of 8 below the password length, and send 8
+ * packets containing string lengths N through
+ * N+7. This won't obscure the order of
+ * magnitude of the password length, but it will
+ * introduce a bit of extra uncertainty.
+ *
+ * A few servers (the old 1.2.18 through 1.2.22)
+ * can't deal with SSH1_MSG_IGNORE. For these
+ * servers, we need an alternative defence. We make
+ * use of the fact that the password is interpreted
+ * as a C string: so we can append a NUL, then some
+ * random data.
+ *
+ * One server (a Cisco one) can deal with neither
+ * SSH1_MSG_IGNORE _nor_ a padded password string.
+ * For this server we are left with no defences
+ * against password length sniffing.
+ */
+ if (!(ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
+ /*
+ * The server can deal with SSH1_MSG_IGNORE, so
+ * we can use the primary defence.
+ */
+ int bottom, top, pwlen, i;
+ char *randomstr;
+
+ pwlen = strlen(password);
+ if (pwlen < 16) {
+ bottom = 0; /* zero length passwords are OK! :-) */
+ top = 15;
+ } else {
+ bottom = pwlen & ~7;
+ top = bottom + 7;
+ }
+
+ assert(pwlen >= bottom && pwlen <= top);
+
+ randomstr = smalloc(top + 1);
+
+ for (i = bottom; i <= top; i++) {
+ if (i == pwlen)
+ defer_packet(pwpkt_type, PKT_STR, password,
+ PKT_END);
+ else {
+ for (j = 0; j < i; j++) {
+ do {
+ randomstr[j] = random_byte();
+ } while (randomstr[j] == '\0');
+ }
+ randomstr[i] = '\0';
+ defer_packet(SSH1_MSG_IGNORE,
+ PKT_STR, randomstr, PKT_END);
+ }
+ }
+ logevent("Sending password with camouflage packets");
+ ssh_pkt_defersend();
+ }
+ else if (!(ssh_remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
+ /*
+ * The server can't deal with SSH1_MSG_IGNORE
+ * but can deal with padded passwords, so we
+ * can use the secondary defence.
+ */
+ char string[64];
+ char *s;
+ int len;
+
+ len = strlen(password);
+ if (len < sizeof(string)) {
+ s = string;
+ strcpy(string, password);
+ len++; /* cover the zero byte */
+ while (len < sizeof(string)) {
+ string[len++] = (char) random_byte();
+ }
+ } else {
+ s = password;
+ }
+ logevent("Sending length-padded password");
+ send_packet(pwpkt_type, PKT_INT, len,
+ PKT_DATA, s, len, PKT_END);
+ } else {
+ /*
+ * The server has _both_
+ * BUG_CHOKES_ON_SSH1_IGNORE and
+ * BUG_NEEDS_SSH1_PLAIN_PASSWORD. There is
+ * therefore nothing we can do.
+ */
+ int len;
+ len = strlen(password);
+ logevent("Sending unpadded password");
+ send_packet(pwpkt_type, PKT_INT, len,
+ PKT_DATA, password, len, PKT_END);
+ }
+ } 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_str("Access denied\r\n");