+ 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;
+ }
+ }
+
+ if (cfg.x11_forward) {
+ char proto[20], data[64];
+ logevent("Requesting X11 forwarding");
+ x11_invent_auth(proto, sizeof(proto), data, sizeof(data));
+ send_packet(SSH1_CMSG_X11_REQUEST_FORWARDING,
+ PKT_STR, proto, PKT_STR, data,
+ PKT_INT, 0,
+ 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("X11 forwarding refused");
+ } else {
+ logevent("X11 forwarding enabled");
+ ssh_X11_fwd_enabled = TRUE;
+ }
+ }
+
+ if (!cfg.nopty) {
+ send_packet(SSH1_CMSG_REQUEST_PTY,
+ PKT_STR, cfg.termtype,
+ PKT_INT, rows, PKT_INT, cols,
+ PKT_INT, 0, PKT_INT, 0,
+ PKT_CHAR, 0,
+ PKT_END);
+ ssh_state = SSH_STATE_INTERMED;
+ 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) {
+ c_write("Server refused to allocate pty\r\n", 32);
+ ssh_editing = ssh_echoing = 1;
+ }
+ logevent("Allocated pty");
+ } else {
+ ssh_editing = ssh_echoing = 1;
+ }
+
+ if (cfg.compression) {
+ send_packet(SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, 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) {
+ c_write("Server refused to compress\r\n", 32);
+ }
+ logevent("Started compression");
+ ssh1_compressing = TRUE;
+ zlib_compress_init();
+ zlib_decompress_init();
+ }
+
+ if (*cfg.remote_cmd)
+ send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cfg.remote_cmd, PKT_END);
+ else
+ send_packet(SSH1_CMSG_EXEC_SHELL, PKT_END);
+ logevent("Started session");
+
+ ssh_state = SSH_STATE_SESSION;
+ if (size_needed)
+ ssh_size();
+ if (eof_needed)
+ ssh_special(TS_EOF);
+
+ ldisc_send(NULL, 0); /* cause ldisc to notice changes */
+ ssh_send_ok = 1;
+ ssh_channels = newtree234(ssh_channelcmp);
+ while (1) {
+ crReturnV;
+ if (ispkt) {
+ if (pktin.type == SSH1_SMSG_STDOUT_DATA ||
+ pktin.type == SSH1_SMSG_STDERR_DATA) {
+ long len = GET_32BIT(pktin.body);
+ from_backend(pktin.type == SSH1_SMSG_STDERR_DATA,
+ pktin.body+4, len);
+ } else if (pktin.type == SSH1_MSG_DISCONNECT) {
+ ssh_state = SSH_STATE_CLOSED;
+ logevent("Received disconnect request");
+ crReturnV;
+ } else if (pktin.type == SSH1_SMSG_X11_OPEN) {
+ /* Remote side is trying to open a channel to talk to our
+ * X-Server. Give them back a local channel number. */
+ unsigned i;
+ struct ssh_channel *c, *d;
+ enum234 e;
+
+ logevent("Received X11 connect request");
+ /* Refuse if X11 forwarding is disabled. */
+ if (!ssh_X11_fwd_enabled) {
+ send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
+ PKT_INT, GET_32BIT(pktin.body),
+ PKT_END);
+ logevent("Rejected X11 connect request");
+ } else {
+ c = smalloc(sizeof(struct ssh_channel));
+
+ if ( x11_init(&c->u.x11.s, cfg.x11_display, c) != NULL ) {
+ logevent("opening X11 forward connection failed");
+ sfree(c);
+ send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
+ PKT_INT, GET_32BIT(pktin.body),
+ PKT_END);
+ } else {
+ logevent("opening X11 forward connection succeeded");
+ for (i=1, d = first234(ssh_channels, &e); d; d = next234(&e)) {
+ if (d->localid > i)
+ break; /* found a free number */
+ i = d->localid + 1;
+ }
+ c->remoteid = GET_32BIT(pktin.body);
+ c->localid = i;
+ c->closes = 0;
+ c->type = CHAN_X11; /* identify channel type */
+ add234(ssh_channels, c);
+ send_packet(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
+ PKT_INT, c->remoteid, PKT_INT, c->localid,
+ PKT_END);
+ logevent("Opened X11 forward channel");
+ }
+ }
+ } else if (pktin.type == SSH1_SMSG_AGENT_OPEN) {
+ /* Remote side is trying to open a channel to talk to our
+ * agent. Give them back a local channel number. */
+ unsigned i;
+ struct ssh_channel *c;
+ enum234 e;
+
+ /* Refuse if agent forwarding is disabled. */
+ if (!ssh_agentfwd_enabled) {
+ send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
+ PKT_INT, GET_32BIT(pktin.body),
+ PKT_END);
+ } else {
+ i = 1;
+ for (c = first234(ssh_channels, &e); c; c = next234(&e)) {
+ if (c->localid > i)
+ break; /* found a free number */
+ i = c->localid + 1;
+ }
+ c = smalloc(sizeof(struct ssh_channel));
+ c->remoteid = GET_32BIT(pktin.body);
+ c->localid = i;
+ c->closes = 0;
+ c->type = CHAN_AGENT; /* identify channel type */
+ c->u.a.lensofar = 0;
+ add234(ssh_channels, c);
+ send_packet(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
+ PKT_INT, c->remoteid, PKT_INT, c->localid,
+ PKT_END);
+ }
+ } else if (pktin.type == SSH1_MSG_CHANNEL_CLOSE ||
+ pktin.type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION) {
+ /* Remote side closes a channel. */
+ unsigned i = GET_32BIT(pktin.body);
+ struct ssh_channel *c;
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (c) {
+ int closetype;
+ closetype = (pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
+ send_packet(pktin.type, PKT_INT, c->remoteid, PKT_END);
+ if ((c->closes == 0) && (c->type == CHAN_X11)) {
+ logevent("X11 connection closed");
+ assert(c->u.x11.s != NULL);
+ x11_close(c->u.x11.s);
+ c->u.x11.s = NULL;
+ }
+ c->closes |= closetype;
+ if (c->closes == 3) {
+ del234(ssh_channels, c);
+ sfree(c);
+ }
+ }
+ } else if (pktin.type == SSH1_MSG_CHANNEL_DATA) {
+ /* Data sent down one of our channels. */
+ int i = GET_32BIT(pktin.body);
+ int len = GET_32BIT(pktin.body+4);
+ unsigned char *p = pktin.body+8;
+ struct ssh_channel *c;
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (c) {
+ switch(c->type) {
+ case CHAN_X11:
+ x11_send(c->u.x11.s, p, len);
+ break;
+ case CHAN_AGENT:
+ /* Data for an agent message. Buffer it. */
+ while (len > 0) {
+ if (c->u.a.lensofar < 4) {
+ int l = min(4 - c->u.a.lensofar, len);
+ memcpy(c->u.a.msglen + c->u.a.lensofar, p, l);
+ p += l; len -= l; c->u.a.lensofar += l;
+ }
+ if (c->u.a.lensofar == 4) {
+ c->u.a.totallen = 4 + GET_32BIT(c->u.a.msglen);
+ c->u.a.message = smalloc(c->u.a.totallen);
+ memcpy(c->u.a.message, c->u.a.msglen, 4);
+ }
+ if (c->u.a.lensofar >= 4 && len > 0) {
+ int l = min(c->u.a.totallen - c->u.a.lensofar, len);
+ memcpy(c->u.a.message + c->u.a.lensofar, p, l);
+ p += l; len -= l; c->u.a.lensofar += l;
+ }
+ if (c->u.a.lensofar == c->u.a.totallen) {
+ void *reply, *sentreply;
+ int replylen;
+ agent_query(c->u.a.message, c->u.a.totallen,
+ &reply, &replylen);
+ if (reply)
+ sentreply = reply;
+ else {
+ /* Fake SSH_AGENT_FAILURE. */
+ sentreply = "\0\0\0\1\5";
+ replylen = 5;
+ }
+ send_packet(SSH1_MSG_CHANNEL_DATA,
+ PKT_INT, c->remoteid,
+ PKT_INT, replylen,
+ PKT_DATA, sentreply, replylen,
+ PKT_END);
+ if (reply)
+ sfree(reply);
+ sfree(c->u.a.message);
+ c->u.a.lensofar = 0;
+ }
+ }
+ break;
+ }
+ }
+ } else if (pktin.type == SSH1_SMSG_SUCCESS) {
+ /* may be from EXEC_SHELL on some servers */
+ } else if (pktin.type == SSH1_SMSG_FAILURE) {
+ /* may be from EXEC_SHELL on some servers
+ * if no pty is available or in other odd cases. Ignore */
+ } else if (pktin.type == SSH1_SMSG_EXIT_STATUS) {
+ send_packet(SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
+ } else {
+ bombout(("Strange packet received: type %d", pktin.type));
+ crReturnV;
+ }
+ } else {
+ while (inlen > 0) {
+ int len = min(inlen, 512);
+ send_packet(SSH1_CMSG_STDIN_DATA,
+ PKT_INT, len, PKT_DATA, in, len, PKT_END);
+ in += len;
+ inlen -= len;
+ }
+ }
+ }
+
+ crFinishV;
+}
+
+/*
+ * Utility routine for decoding comma-separated strings in KEXINIT.
+ */
+static int in_commasep_string(char *needle, char *haystack, int haylen) {
+ int needlen = strlen(needle);
+ while (1) {
+ /*
+ * Is it at the start of the string?
+ */
+ if (haylen >= needlen && /* haystack is long enough */
+ !memcmp(needle, haystack, needlen) && /* initial match */
+ (haylen == needlen || haystack[needlen] == ',')
+ /* either , or EOS follows */
+ )
+ return 1;
+ /*
+ * If not, search for the next comma and resume after that.
+ * If no comma found, terminate.
+ */
+ while (haylen > 0 && *haystack != ',')
+ haylen--, haystack++;
+ if (haylen == 0)
+ return 0;
+ haylen--, haystack++; /* skip over comma itself */
+ }
+}
+
+/*
+ * SSH2 key creation method.
+ */
+static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr, char *keyspace) {
+ SHA_State s;
+ /* First 20 bytes. */
+ SHA_Init(&s);
+ sha_mpint(&s, K);
+ SHA_Bytes(&s, H, 20);
+ SHA_Bytes(&s, &chr, 1);
+ SHA_Bytes(&s, sessid, 20);
+ SHA_Final(&s, keyspace);
+ /* Next 20 bytes. */
+ SHA_Init(&s);
+ sha_mpint(&s, K);
+ SHA_Bytes(&s, H, 20);
+ SHA_Bytes(&s, keyspace, 20);
+ SHA_Final(&s, keyspace+20);
+}
+
+/*
+ * Handle the SSH2 transport layer.
+ */
+static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
+{
+ static int i, len;
+ static char *str;
+ static Bignum e, f, K;
+ static const struct ssh_mac **maclist;
+ static int nmacs;
+ static const struct ssh_cipher *cscipher_tobe = NULL;
+ static const struct ssh_cipher *sccipher_tobe = NULL;
+ static const struct ssh_mac *csmac_tobe = NULL;
+ static const struct ssh_mac *scmac_tobe = NULL;
+ static const struct ssh_compress *cscomp_tobe = NULL;
+ static const struct ssh_compress *sccomp_tobe = NULL;
+ static char *hostkeydata, *sigdata, *keystr, *fingerprint;
+ static int hostkeylen, siglen;
+ static void *hkey; /* actual host key */
+ static unsigned char exchange_hash[20];
+ static unsigned char first_exchange_hash[20];
+ static unsigned char keyspace[40];
+ static const struct ssh_cipher *preferred_cipher;
+ static const struct ssh_compress *preferred_comp;
+ static int first_kex;
+
+ crBegin;
+ random_init();
+ first_kex = 1;
+
+ /*
+ * Set up the preferred cipher and compression.
+ */
+ if (cfg.cipher == CIPHER_BLOWFISH) {
+ preferred_cipher = &ssh_blowfish_ssh2;
+ } else if (cfg.cipher == CIPHER_DES) {
+ logevent("Single DES not supported in SSH2; using 3DES");
+ preferred_cipher = &ssh_3des_ssh2;
+ } else if (cfg.cipher == CIPHER_3DES) {
+ preferred_cipher = &ssh_3des_ssh2;
+ } else {
+ /* Shouldn't happen, but we do want to initialise to _something_. */
+ preferred_cipher = &ssh_3des_ssh2;
+ }
+ if (cfg.compression)
+ preferred_comp = &ssh_zlib;
+ else
+ preferred_comp = &ssh_comp_none;
+
+ /*
+ * Be prepared to work around the buggy MAC problem.
+ */
+ if (cfg.buggymac)
+ maclist = buggymacs, nmacs = lenof(buggymacs);
+ else
+ maclist = macs, nmacs = lenof(macs);
+
+ begin_key_exchange:
+ /*
+ * Construct and send our key exchange packet.
+ */
+ ssh2_pkt_init(SSH2_MSG_KEXINIT);
+ for (i = 0; i < 16; i++)
+ ssh2_pkt_addbyte((unsigned char)random_byte());
+ /* List key exchange algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(kex_algs); i++) {
+ ssh2_pkt_addstring_str(kex_algs[i]->name);
+ if (i < lenof(kex_algs)-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server host key algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(hostkey_algs); i++) {
+ ssh2_pkt_addstring_str(hostkey_algs[i]->name);
+ if (i < lenof(hostkey_algs)-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server encryption algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(ciphers)+1; i++) {
+ const struct ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
+ ssh2_pkt_addstring_str(c->name);
+ if (i < lenof(ciphers))
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server->client encryption algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(ciphers)+1; i++) {
+ const struct ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
+ ssh2_pkt_addstring_str(c->name);
+ if (i < lenof(ciphers))
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server MAC algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < nmacs; i++) {
+ ssh2_pkt_addstring_str(maclist[i]->name);
+ if (i < nmacs-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server->client MAC algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < nmacs; i++) {
+ ssh2_pkt_addstring_str(maclist[i]->name);
+ if (i < nmacs-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server compression algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(compressions)+1; i++) {
+ const struct ssh_compress *c = i==0 ? preferred_comp : compressions[i-1];
+ ssh2_pkt_addstring_str(c->name);
+ if (i < lenof(compressions))
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server->client compression algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(compressions)+1; i++) {
+ const struct ssh_compress *c = i==0 ? preferred_comp : compressions[i-1];
+ ssh2_pkt_addstring_str(c->name);
+ if (i < lenof(compressions))
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server languages. Empty list. */
+ ssh2_pkt_addstring_start();
+ /* List server->client languages. Empty list. */
+ ssh2_pkt_addstring_start();
+ /* First KEX packet does _not_ follow, because we're not that brave. */
+ ssh2_pkt_addbool(FALSE);
+ /* Reserved. */
+ ssh2_pkt_adduint32(0);