#include <winsock.h>
#include "putty.h"
+#include "tree234.h"
#include "ssh.h"
#include "scp.h"
if (!(flags & FLAG_CONNECTION) && (flags & FLAG_VERBOSE)) \
fprintf(stderr, "%s\n", s); }
-#define SSH1_MSG_DISCONNECT 1
-#define SSH1_SMSG_PUBLIC_KEY 2
-#define SSH1_CMSG_SESSION_KEY 3
-#define SSH1_CMSG_USER 4
-#define SSH1_CMSG_AUTH_RSA 6
-#define SSH1_SMSG_AUTH_RSA_CHALLENGE 7
-#define SSH1_CMSG_AUTH_RSA_RESPONSE 8
-#define SSH1_CMSG_AUTH_PASSWORD 9
-#define SSH1_CMSG_REQUEST_PTY 10
-#define SSH1_CMSG_WINDOW_SIZE 11
-#define SSH1_CMSG_EXEC_SHELL 12
-#define SSH1_CMSG_EXEC_CMD 13
-#define SSH1_SMSG_SUCCESS 14
-#define SSH1_SMSG_FAILURE 15
-#define SSH1_CMSG_STDIN_DATA 16
-#define SSH1_SMSG_STDOUT_DATA 17
-#define SSH1_SMSG_STDERR_DATA 18
-#define SSH1_CMSG_EOF 19
-#define SSH1_SMSG_EXIT_STATUS 20
-#define SSH1_CMSG_EXIT_CONFIRMATION 33
-#define SSH1_MSG_IGNORE 32
-#define SSH1_MSG_DEBUG 36
-#define SSH1_CMSG_AUTH_TIS 39
-#define SSH1_SMSG_AUTH_TIS_CHALLENGE 40
-#define SSH1_CMSG_AUTH_TIS_RESPONSE 41
-
-#define SSH1_AUTH_TIS 5
-
-#define SSH2_MSG_DISCONNECT 1
-#define SSH2_MSG_IGNORE 2
-#define SSH2_MSG_UNIMPLEMENTED 3
-#define SSH2_MSG_DEBUG 4
-#define SSH2_MSG_SERVICE_REQUEST 5
-#define SSH2_MSG_SERVICE_ACCEPT 6
-#define SSH2_MSG_KEXINIT 20
-#define SSH2_MSG_NEWKEYS 21
-#define SSH2_MSG_KEXDH_INIT 30
-#define SSH2_MSG_KEXDH_REPLY 31
-#define SSH2_MSG_USERAUTH_REQUEST 50
-#define SSH2_MSG_USERAUTH_FAILURE 51
-#define SSH2_MSG_USERAUTH_SUCCESS 52
-#define SSH2_MSG_USERAUTH_BANNER 53
-#define SSH2_MSG_USERAUTH_PK_OK 60
-#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60
-#define SSH2_MSG_GLOBAL_REQUEST 80
-#define SSH2_MSG_REQUEST_SUCCESS 81
-#define SSH2_MSG_REQUEST_FAILURE 82
-#define SSH2_MSG_CHANNEL_OPEN 90
-#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91
-#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92
-#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93
-#define SSH2_MSG_CHANNEL_DATA 94
-#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95
-#define SSH2_MSG_CHANNEL_EOF 96
-#define SSH2_MSG_CHANNEL_CLOSE 97
-#define SSH2_MSG_CHANNEL_REQUEST 98
-#define SSH2_MSG_CHANNEL_SUCCESS 99
-#define SSH2_MSG_CHANNEL_FAILURE 100
-
-#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1
-#define SSH2_OPEN_CONNECT_FAILED 2
-#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3
-#define SSH2_OPEN_RESOURCE_SHORTAGE 4
-#define SSH2_EXTENDED_DATA_STDERR 1
+#define SSH1_MSG_DISCONNECT 1 /* 0x1 */
+#define SSH1_SMSG_PUBLIC_KEY 2 /* 0x2 */
+#define SSH1_CMSG_SESSION_KEY 3 /* 0x3 */
+#define SSH1_CMSG_USER 4 /* 0x4 */
+#define SSH1_CMSG_AUTH_RSA 6 /* 0x6 */
+#define SSH1_SMSG_AUTH_RSA_CHALLENGE 7 /* 0x7 */
+#define SSH1_CMSG_AUTH_RSA_RESPONSE 8 /* 0x8 */
+#define SSH1_CMSG_AUTH_PASSWORD 9 /* 0x9 */
+#define SSH1_CMSG_REQUEST_PTY 10 /* 0xa */
+#define SSH1_CMSG_WINDOW_SIZE 11 /* 0xb */
+#define SSH1_CMSG_EXEC_SHELL 12 /* 0xc */
+#define SSH1_CMSG_EXEC_CMD 13 /* 0xd */
+#define SSH1_SMSG_SUCCESS 14 /* 0xe */
+#define SSH1_SMSG_FAILURE 15 /* 0xf */
+#define SSH1_CMSG_STDIN_DATA 16 /* 0x10 */
+#define SSH1_SMSG_STDOUT_DATA 17 /* 0x11 */
+#define SSH1_SMSG_STDERR_DATA 18 /* 0x12 */
+#define SSH1_CMSG_EOF 19 /* 0x13 */
+#define SSH1_SMSG_EXIT_STATUS 20 /* 0x14 */
+#define SSH1_MSG_CHANNEL_OPEN_CONFIRMATION 21 /* 0x15 */
+#define SSH1_MSG_CHANNEL_OPEN_FAILURE 22 /* 0x16 */
+#define SSH1_MSG_CHANNEL_DATA 23 /* 0x17 */
+#define SSH1_MSG_CHANNEL_CLOSE 24 /* 0x18 */
+#define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* 0x19 */
+#define SSH1_CMSG_AGENT_REQUEST_FORWARDING 30 /* 0x1e */
+#define SSH1_SMSG_AGENT_OPEN 31 /* 0x1f */
+#define SSH1_CMSG_EXIT_CONFIRMATION 33 /* 0x21 */
+#define SSH1_MSG_IGNORE 32 /* 0x20 */
+#define SSH1_MSG_DEBUG 36 /* 0x24 */
+#define SSH1_CMSG_AUTH_TIS 39 /* 0x27 */
+#define SSH1_SMSG_AUTH_TIS_CHALLENGE 40 /* 0x28 */
+#define SSH1_CMSG_AUTH_TIS_RESPONSE 41 /* 0x29 */
+#define SSH1_CMSG_AUTH_CCARD 70 /* 0x46 */
+#define SSH1_SMSG_AUTH_CCARD_CHALLENGE 71 /* 0x47 */
+#define SSH1_CMSG_AUTH_CCARD_RESPONSE 72 /* 0x48 */
+
+#define SSH1_AUTH_TIS 5 /* 0x5 */
+#define SSH1_AUTH_CCARD 16 /* 0x10 */
+
+#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 /* 0x1 */
+#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 /* 0x2 */
+#define SSH_AGENTC_RSA_CHALLENGE 3 /* 0x3 */
+#define SSH_AGENT_RSA_RESPONSE 4 /* 0x4 */
+#define SSH_AGENT_FAILURE 5 /* 0x5 */
+#define SSH_AGENT_SUCCESS 6 /* 0x6 */
+#define SSH_AGENTC_ADD_RSA_IDENTITY 7 /* 0x7 */
+#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 /* 0x8 */
+
+#define SSH2_MSG_DISCONNECT 1 /* 0x1 */
+#define SSH2_MSG_IGNORE 2 /* 0x2 */
+#define SSH2_MSG_UNIMPLEMENTED 3 /* 0x3 */
+#define SSH2_MSG_DEBUG 4 /* 0x4 */
+#define SSH2_MSG_SERVICE_REQUEST 5 /* 0x5 */
+#define SSH2_MSG_SERVICE_ACCEPT 6 /* 0x6 */
+#define SSH2_MSG_KEXINIT 20 /* 0x14 */
+#define SSH2_MSG_NEWKEYS 21 /* 0x15 */
+#define SSH2_MSG_KEXDH_INIT 30 /* 0x1e */
+#define SSH2_MSG_KEXDH_REPLY 31 /* 0x1f */
+#define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */
+#define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */
+#define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */
+#define SSH2_MSG_USERAUTH_BANNER 53 /* 0x35 */
+#define SSH2_MSG_USERAUTH_PK_OK 60 /* 0x3c */
+#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 /* 0x3c */
+#define SSH2_MSG_GLOBAL_REQUEST 80 /* 0x50 */
+#define SSH2_MSG_REQUEST_SUCCESS 81 /* 0x51 */
+#define SSH2_MSG_REQUEST_FAILURE 82 /* 0x52 */
+#define SSH2_MSG_CHANNEL_OPEN 90 /* 0x5a */
+#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 /* 0x5b */
+#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 /* 0x5c */
+#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 /* 0x5d */
+#define SSH2_MSG_CHANNEL_DATA 94 /* 0x5e */
+#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 /* 0x5f */
+#define SSH2_MSG_CHANNEL_EOF 96 /* 0x60 */
+#define SSH2_MSG_CHANNEL_CLOSE 97 /* 0x61 */
+#define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */
+#define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */
+#define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */
+
+#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 /* 0x1 */
+#define SSH2_OPEN_CONNECT_FAILED 2 /* 0x2 */
+#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 /* 0x3 */
+#define SSH2_OPEN_RESOURCE_SHORTAGE 4 /* 0x4 */
+
+#define SSH2_EXTENDED_DATA_STDERR 1 /* 0x1 */
#define GET_32BIT(cp) \
(((unsigned long)(unsigned char)(cp)[0] << 24) | \
extern struct ssh_mac ssh_sha1;
-SHA_State exhash;
+static SHA_State exhash;
static void nullmac_key(unsigned char *key) { }
static void nullmac_generate(unsigned char *blk, int len, unsigned long seq) { }
static char *savedhost;
static int ssh_send_ok;
+/*
+ * 2-3-4 tree storing channels.
+ */
+struct ssh_channel {
+ unsigned remoteid, localid;
+ int type;
+ int closes;
+ union {
+ struct ssh_agent_channel {
+ unsigned char *message;
+ unsigned char msglen[4];
+ int lensofar, totallen;
+ } a;
+ struct ssh2_data_channel {
+ unsigned char *outbuffer;
+ unsigned outbuflen, outbufsize;
+ unsigned remwindow, remmaxpkt;
+ } v2;
+ } u;
+};
+static tree234 *ssh_channels; /* indexed by local id */
+static int ssh_channelcmp(void *av, void *bv) {
+ struct ssh_channel *a = (struct ssh_channel *)av;
+ struct ssh_channel *b = (struct ssh_channel *)bv;
+ if (a->localid < b->localid) return -1;
+ if (a->localid > b->localid) return +1;
+ return 0;
+}
+static int ssh_channelfind(void *av, void *bv) {
+ unsigned *a = (unsigned *)av;
+ struct ssh_channel *b = (struct ssh_channel *)bv;
+ if (*a < b->localid) return -1;
+ if (*a > b->localid) return +1;
+ return 0;
+}
+
+static struct ssh_channel *mainchan; /* primary session channel */
+
static enum {
SSH_STATE_BEFORE_SIZE,
SSH_STATE_INTERMED,
if (pktin.type == SSH1_SMSG_STDOUT_DATA ||
pktin.type == SSH1_SMSG_STDERR_DATA ||
pktin.type == SSH1_MSG_DEBUG ||
- pktin.type == SSH1_SMSG_AUTH_TIS_CHALLENGE) {
+ pktin.type == SSH1_SMSG_AUTH_TIS_CHALLENGE ||
+ pktin.type == SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
long strlen = GET_32BIT(pktin.body);
if (strlen + 4 != pktin.length)
fatalbox("Received data packet with bogus string length");
/*
* Adjust memory allocation if packet is too big.
*/
- if (pktin.maxlen < packetlen) {
- pktin.maxlen = packetlen;
- pktin.data = (pktin.data == NULL ? malloc(packetlen+APIEXTRA) :
- realloc(pktin.data, packetlen+APIEXTRA));
+ if (pktin.maxlen < packetlen+maclen) {
+ pktin.maxlen = packetlen+maclen;
+ pktin.data = (pktin.data == NULL ? malloc(pktin.maxlen+APIEXTRA) :
+ realloc(pktin.data, pktin.maxlen+APIEXTRA));
if (!pktin.data)
fatalbox("Out of memory");
}
unsigned long argint;
int pktlen, argtype, arglen;
Bignum bn;
- int i;
pktlen = 0;
va_start(args, pkttype);
break;
case PKT_BIGNUM:
bn = va_arg(args, Bignum);
- i = 16 * bn[0] - 1;
- while ( i > 0 && (bn[i/16+1] >> (i%16)) == 0 )
- i--;
- pktlen += 2 + (i+7)/8;
+ pktlen += ssh1_bignum_length(bn);
break;
default:
assert(0);
break;
case PKT_BIGNUM:
bn = va_arg(args, Bignum);
- i = 16 * bn[0] - 1;
- while ( i > 0 && (bn[i/16+1] >> (i%16)) == 0 )
- i--;
- *p++ = (i >> 8) & 0xFF;
- *p++ = i & 0xFF;
- i = (i + 7) / 8;
- while (i-- > 0) {
- if (i % 2)
- *p++ = bn[i/2+1] >> 8;
- else
- *p++ = bn[i/2+1] & 0xFF;
- }
+ p += ssh1_write_bignum(p, bn);
break;
}
}
#include <stdio.h>
void sha_string(SHA_State *s, void *str, int len) {
unsigned char lenblk[4];
-static FILE *fp;
PUT_32BIT(lenblk, len);
-if (!fp) fp = fopen("h:\\statham\\windows\\putty\\data","wb");
-fwrite(lenblk, 4, 1, fp);
SHA_Bytes(s, lenblk, 4);
-fwrite(str, len, 1, fp);
-fflush(fp);
SHA_Bytes(s, str, len);
}
vlog[strcspn(vlog, "\r\n")] = '\0';
logevent(vlog);
- if (ssh_versioncmp(version, "2.0" /* FIXME: "1.99" */ ) >= 0) {
+ /*
+ * 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.
*/
static char username[100];
static int pos = 0;
static char c;
- if (!(flags & FLAG_CONNECTION) && !*cfg.username) {
+ if ((flags & FLAG_CONNECTION) && !*cfg.username) {
c_write("login as: ", 10);
while (pos >= 0) {
crWaitUntil(!ispkt);
static int pwpkt_type;
/*
* Show password prompt, having first obtained it via a TIS
- * exchange if we're doing TIS authentication.
+ * 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 = malloc(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);
+ free(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);
+ free(ret);
+ crWaitUntil(ispkt);
+ if (pktin.type == SSH1_SMSG_SUCCESS) {
+ logevent("Pageant's response accepted");
+ 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");
+ free(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;
c_write(pktin.body+4, challengelen);
}
}
+ 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");
+ c_write(pktin.body+4, challengelen);
+ c_write("\r\nResponse : ", 13);
+ }
+ }
if (pwpkt_type == SSH1_CMSG_AUTH_PASSWORD)
c_write("password: ", 10);
if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
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) {
+ fatalbox("Protocol confusion");
+ } else if (pktin.type == SSH1_SMSG_FAILURE) {
+ logevent("Agent forwarding refused");
+ } else
+ logevent("Agent forwarding enabled");
+ }
+
if (!cfg.nopty) {
send_packet(SSH1_CMSG_REQUEST_PTY,
PKT_STR, cfg.termtype,
ssh_size();
ssh_send_ok = 1;
+ ssh_channels = newtree234(ssh_channelcmp);
while (1) {
crReturnV;
if (ispkt) {
} else if (pktin.type == SSH1_MSG_DISCONNECT) {
ssh_state = SSH_STATE_CLOSED;
logevent("Received disconnect request");
+ } 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 = 1;
+ struct ssh_channel *c;
+ enum234 e;
+ for (c = first234(ssh_channels, &e); c; c = next234(&e)) {
+ if (c->localid > i)
+ break; /* found a free number */
+ i = c->localid + 1;
+ }
+ c = malloc(sizeof(struct ssh_channel));
+ c->remoteid = GET_32BIT(pktin.body);
+ c->localid = i;
+ c->closes = 0;
+ c->type = SSH1_SMSG_AGENT_OPEN; /* identify channel type */
+ 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);
+ c->closes |= closetype;
+ if (c->closes == 3) {
+ del234(ssh_channels, c);
+ free(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 SSH1_SMSG_AGENT_OPEN:
+ /* 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 = malloc(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)
+ free(reply);
+ free(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) {
}
/*
- * SSH2: remote identifier for the main session channel.
- */
-static unsigned long ssh_remote_channel;
-
-/*
* Handle the SSH2 userauth and connection layers.
*/
static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
/*
* So now create a channel with a session in it.
*/
+ mainchan = malloc(sizeof(struct ssh_channel));
+ mainchan->localid = 100; /* as good as any */
ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
ssh2_pkt_addstring("session");
- ssh2_pkt_adduint32(100); /* as good as any */
- ssh2_pkt_adduint32(0xFFFFFFFFUL); /* very big window which we ignore */
- ssh2_pkt_adduint32(0xFFFFFFFFUL); /* very big max pkt size */
+ ssh2_pkt_adduint32(mainchan->localid);
+ ssh2_pkt_adduint32(0x7FFFFFFFUL); /* our window size */
+ ssh2_pkt_adduint32(0x4000UL); /* our max pkt size */
ssh2_pkt_send();
crWaitUntilV(ispkt);
if (pktin.type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
fatalbox("Server refused to open a session");
/* FIXME: error data comes back in FAILURE packet */
}
- if (ssh2_pkt_getuint32() != 100) {
+ if (ssh2_pkt_getuint32() != mainchan->localid) {
fatalbox("Server's channel confirmation cited wrong channel");
}
- ssh_remote_channel = ssh2_pkt_getuint32();
- remote_winsize = ssh2_pkt_getuint32();
- remote_maxpkt = ssh2_pkt_getuint32();
+ mainchan->remoteid = ssh2_pkt_getuint32();
+ mainchan->u.v2.remwindow = ssh2_pkt_getuint32();
+ mainchan->u.v2.remmaxpkt = ssh2_pkt_getuint32();
+ mainchan->u.v2.outbuffer = NULL;
+ mainchan->u.v2.outbuflen = mainchan->u.v2.outbufsize = 0;
logevent("Opened channel for session");
/*
* Now allocate a pty for the session.
*/
ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
- ssh2_pkt_adduint32(ssh_remote_channel); /* recipient channel */
+ ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
ssh2_pkt_addstring("pty-req");
ssh2_pkt_addbool(1); /* want reply */
ssh2_pkt_addstring(cfg.termtype);
* Start a shell.
*/
ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
- ssh2_pkt_adduint32(ssh_remote_channel); /* recipient channel */
+ ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
ssh2_pkt_addstring("shell");
ssh2_pkt_addbool(1); /* want reply */
ssh2_pkt_send();
- do { /* FIXME: pay attention to these */
+ do {
crWaitUntilV(ispkt);
+ if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ }
} while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
*/
ssh_send_ok = 1;
while (1) {
+ static int try_send;
crReturnV;
+ try_send = FALSE;
if (ispkt) {
if (pktin.type == SSH2_MSG_CHANNEL_DATA ||
pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
char *data;
int length;
- if (ssh2_pkt_getuint32() != 100)
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
continue; /* wrong channel */
if (pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
ssh2_pkt_getuint32() != SSH2_EXTENDED_DATA_STDERR)
continue; /* extended but not stderr */
ssh2_pkt_getstring(&data, &length);
- if (data)
+ if (data) {
c_write(data, length);
+ /*
+ * Enlarge the window again at the remote side,
+ * just in case it ever runs down and they fail
+ * to send us any more data.
+ */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_adduint32(length);
+ ssh2_pkt_send();
+ }
} else if (pktin.type == SSH2_MSG_DISCONNECT) {
ssh_state = SSH_STATE_CLOSED;
- logevent("Received disconnect request");
+ logevent("Received disconnect message");
} else if (pktin.type == SSH2_MSG_CHANNEL_REQUEST) {
continue; /* exit status et al; ignore (FIXME?) */
+ } else if (pktin.type == SSH2_MSG_CHANNEL_EOF) {
+ continue; /* remote sends EOF; ignore */
+ } else if (pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_send();
+ /* FIXME: mark the channel as closed */
+ if (1 /* FIXME: "all channels are closed" */) {
+ logevent("All channels closed. Disconnecting");
+ ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+ ssh2_pkt_send();
+ }
+ continue; /* remote sends close; ignore (FIXME) */
} else if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
- continue; /* ignore for now (FIXME!) */
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ try_send = TRUE;
} else {
fatalbox("Strange packet received: type %d", pktin.type);
}
} else {
- /* FIXME: for now, ignore window size */
- ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
- ssh2_pkt_adduint32(ssh_remote_channel);
- ssh2_pkt_addstring_start();
- ssh2_pkt_addstring_data(in, inlen);
- ssh2_pkt_send();
+ /*
+ * We have spare data. Add it to the channel buffer.
+ */
+ if (mainchan->u.v2.outbufsize <
+ mainchan->u.v2.outbuflen + inlen) {
+ mainchan->u.v2.outbufsize =
+ mainchan->u.v2.outbuflen + inlen + 1024;
+ mainchan->u.v2.outbuffer = srealloc(mainchan->u.v2.outbuffer,
+ mainchan->u.v2.outbufsize);
+ }
+ memcpy(mainchan->u.v2.outbuffer + mainchan->u.v2.outbuflen,
+ in, inlen);
+ mainchan->u.v2.outbuflen += inlen;
+ try_send = TRUE;
}
+ if (try_send) {
+ /*
+ * Try to send data on the channel if we can. (FIXME:
+ * on _all_ channels.)
+ */
+ while (mainchan->u.v2.remwindow > 0 &&
+ mainchan->u.v2.outbuflen > 0) {
+ unsigned len = mainchan->u.v2.remwindow;
+ if (len > mainchan->u.v2.outbuflen)
+ len = mainchan->u.v2.outbuflen;
+ if (len > mainchan->u.v2.remmaxpkt)
+ len = mainchan->u.v2.remmaxpkt;
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
+ ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_data(mainchan->u.v2.outbuffer, len);
+ ssh2_pkt_send();
+ mainchan->u.v2.outbuflen -= len;
+ memmove(mainchan->u.v2.outbuffer, mainchan->u.v2.outbuffer+len,
+ mainchan->u.v2.outbuflen);
+ mainchan->u.v2.remwindow -= len;
+ }
+ }
}
crFinishV;
send_packet(SSH1_CMSG_EOF, PKT_END);
} else {
ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
- ssh2_pkt_adduint32(ssh_remote_channel);
+ ssh2_pkt_adduint32(mainchan->remoteid);
ssh2_pkt_send();
}
} else {