fprintf(stderr, "%s\n", s); }
#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \
- (s ? sk_close(s), s = NULL : (void)0), \
+ (s ? sk_close(s), s = NULL : 0), \
connection_fatal msg )
#define SSH1_MSG_DISCONNECT 1 /* 0x1 */
static struct Packet pktin = { 0, 0, NULL, NULL, 0 };
static struct Packet pktout = { 0, 0, NULL, NULL, 0 };
+static unsigned char *deferred_send_data = NULL;
+static int deferred_len = 0, deferred_size = 0;
static int ssh_version;
static void (*ssh_protocol)(unsigned char *in, int inlen, int ispkt);
}
static char *ssh2_mpint_fmt(Bignum b, int *len) {
unsigned char *p;
- int i, n = b[0];
- p = smalloc(n * 2 + 1);
+ int i, n = (ssh1_bignum_bitcount(b)+7)/8;
+ p = smalloc(n + 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;
- }
+ for (i = 1; i <= n; i++)
+ p[i] = bignum_byte(b, n-i);
i = 0;
- while (p[i] == 0 && (p[i+1] & 0x80) == 0)
+ while (i <= n && p[i] == 0 && (p[i+1] & 0x80) == 0)
i++;
- memmove(p, p+i, n*2+1-i);
- *len = n*2+1-i;
+ memmove(p, p+i, n+1-i);
+ *len = n+1-i;
return p;
}
static void ssh2_pkt_addmp(Bignum b) {
ssh2_pkt_addstring_data(p, len);
sfree(p);
}
-static void ssh2_pkt_send(void) {
+
+/*
+ * Construct an SSH2 final-form packet: compress it, encrypt it,
+ * put the MAC on it. Final packet, ready to be sent, is stored in
+ * pktout.data. Total length is returned.
+ */
+static int ssh2_pkt_construct(void) {
int cipherblk, maclen, padding, i;
static unsigned long outgoing_sequence = 0;
if (cscipher)
cscipher->encrypt(pktout.data, pktout.length + padding);
- sk_write(s, pktout.data, pktout.length + padding + maclen);
+ /* Ready-to-send packet starts at pktout.data. We return length. */
+ return pktout.length + padding + maclen;
+}
+
+/*
+ * Construct and send an SSH2 packet immediately.
+ */
+static void ssh2_pkt_send(void) {
+ int len = ssh2_pkt_construct();
+ sk_write(s, pktout.data, len);
+}
+
+/*
+ * Construct an SSH2 packet and add it to a deferred data block.
+ * Useful for sending multiple packets in a single sk_write() call,
+ * to prevent a traffic-analysing listener from being able to work
+ * out the length of any particular packet (such as the password
+ * packet).
+ *
+ * Note that because SSH2 sequence-numbers its packets, this can
+ * NOT be used as an m4-style `defer' allowing packets to be
+ * constructed in one order and sent in another.
+ */
+static void ssh2_pkt_defer(void) {
+ int len = ssh2_pkt_construct();
+ if (deferred_len + len > deferred_size) {
+ deferred_size = deferred_len + len + 128;
+ deferred_send_data = srealloc(deferred_send_data, deferred_size);
+ }
+ memcpy(deferred_send_data+deferred_len, pktout.data, len);
+ deferred_len += len;
+}
+
+/*
+ * Send the whole deferred data block constructed by
+ * ssh2_pkt_defer().
+ */
+static void ssh2_pkt_defersend(void) {
+ sk_write(s, deferred_send_data, deferred_len);
+ deferred_len = deferred_size = 0;
+ sfree(deferred_send_data);
+ deferred_send_data = NULL;
}
#if 0
}
static Bignum ssh2_pkt_getmp(void) {
char *p;
- int i, j, length;
+ int length;
Bignum b;
ssh2_pkt_getstring(&p, &length);
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]--;
+ b = bignum_from_bytes(p, length);
return b;
}
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;
+ for (i = 0; i < 32; i++) {
+ buffer[i] = bignum_byte(response, 31-i);
}
MD5Init(&md5c);
/*
* Now we begin the fun. Generate and send e for Diffie-Hellman.
*/
+ dh_setup_group1();
+
e = dh_create_e();
ssh2_pkt_init(SSH2_MSG_KEXDH_INIT);
ssh2_pkt_addmp(e);
sha_mpint(&exhash, K);
SHA_Final(&exhash, exchange_hash);
+ dh_cleanup();
+
#if 0
debug(("Exchange hash is:\r\n"));
for (i = 0; i < 20; i++)
* transport. If we ever see a KEXINIT, we must go back to the
* start.
*/
- do {
+ while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT)) {
crReturn(1);
- } while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT));
+ }
logevent("Server initiated key re-exchange");
goto begin_key_exchange;
c_write("\r\n", 2);
}
+ /*
+ * We send the password packet lumped tightly together with
+ * an SSH_MSG_IGNORE packet. The IGNORE packet contains a
+ * string long enough to make the total length of the two
+ * packets constant. This should ensure that a passive
+ * listener doing traffic analyis can't work out the length
+ * of the password.
+ *
+ * For this to work, we need an assumption about the
+ * maximum length of the password packet. I think 256 is
+ * pretty conservative. Anyone using a password longer than
+ * that probably doesn't have much to worry about from
+ * people who find out how long their password is!
+ */
ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
ssh2_pkt_addstring(username);
ssh2_pkt_addstring("ssh-connection"); /* service requested */
ssh2_pkt_addstring("password");
ssh2_pkt_addbool(FALSE);
ssh2_pkt_addstring(password);
- ssh2_pkt_send();
+ ssh2_pkt_defer();
+ /*
+ * We'll include a string that's an exact multiple of the
+ * cipher block size. If the cipher is NULL for some
+ * reason, we don't do this trick at all because we gain
+ * nothing by it.
+ */
+ if (cscipher) {
+ int i, j;
+ ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_addstring_start();
+ for (i = deferred_len; i <= 256; i += cscipher->blksize) {
+ for (j = 0; j < cscipher->blksize; j++) {
+ char c = (char)random_byte();
+ ssh2_pkt_addstring_data(&c, 1);
+ }
+ }
+ ssh2_pkt_defer();
+ }
+ ssh2_pkt_defersend();
crWaitUntilV(ispkt);
if (pktin.type != SSH2_MSG_USERAUTH_SUCCESS) {
*/
ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
- if (*cfg.remote_cmd) {
+ if (cfg.ssh_subsys) {
+ ssh2_pkt_addstring("subsystem");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cfg.remote_cmd);
+ } else if (*cfg.remote_cmd) {
ssh2_pkt_addstring("exec");
ssh2_pkt_addbool(1); /* want reply */
ssh2_pkt_addstring(cfg.remote_cmd);