+ 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");
+ 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 && !c->closes) {
+ 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("Forwarded X11 connection terminated");
+ } else if (c->type == CHAN_SOCKDATA) {
+ c->u.pfd.s = NULL;
+ logevent("Forwarded port closed");
+ }
+ }
+}
+
+int 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);
+ /*
+ * In SSH1 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
+ * _everything_ will be throttled as a whole.
+ */
+ return 0;
+ } else {
+ ssh2_add_channel_data(c, buf, len);
+ return ssh2_try_send(c);
+ }