#define BUG_SSH2_REKEY 64
#define BUG_SSH2_PK_SESSIONID 128
+/*
+ * Codes for terminal modes.
+ * Most of these are the same in SSH-1 and SSH-2.
+ * This list is derived from draft-ietf-secsh-connect-25 and
+ * SSH-1 RFC-1.2.31.
+ */
+static const struct {
+ const char* const mode;
+ int opcode;
+ enum { TTY_OP_CHAR, TTY_OP_BOOL } type;
+} ssh_ttymodes[] = {
+ /* "V" prefix discarded for special characters relative to SSH specs */
+ { "INTR", 1, TTY_OP_CHAR },
+ { "QUIT", 2, TTY_OP_CHAR },
+ { "ERASE", 3, TTY_OP_CHAR },
+ { "KILL", 4, TTY_OP_CHAR },
+ { "EOF", 5, TTY_OP_CHAR },
+ { "EOL", 6, TTY_OP_CHAR },
+ { "EOL2", 7, TTY_OP_CHAR },
+ { "START", 8, TTY_OP_CHAR },
+ { "STOP", 9, TTY_OP_CHAR },
+ { "SUSP", 10, TTY_OP_CHAR },
+ { "DSUSP", 11, TTY_OP_CHAR },
+ { "REPRINT", 12, TTY_OP_CHAR },
+ { "WERASE", 13, TTY_OP_CHAR },
+ { "LNEXT", 14, TTY_OP_CHAR },
+ { "FLUSH", 15, TTY_OP_CHAR },
+ { "SWTCH", 16, TTY_OP_CHAR },
+ { "STATUS", 17, TTY_OP_CHAR },
+ { "DISCARD", 18, TTY_OP_CHAR },
+ { "IGNPAR", 30, TTY_OP_BOOL },
+ { "PARMRK", 31, TTY_OP_BOOL },
+ { "INPCK", 32, TTY_OP_BOOL },
+ { "ISTRIP", 33, TTY_OP_BOOL },
+ { "INLCR", 34, TTY_OP_BOOL },
+ { "IGNCR", 35, TTY_OP_BOOL },
+ { "ICRNL", 36, TTY_OP_BOOL },
+ { "IUCLC", 37, TTY_OP_BOOL },
+ { "IXON", 38, TTY_OP_BOOL },
+ { "IXANY", 39, TTY_OP_BOOL },
+ { "IXOFF", 40, TTY_OP_BOOL },
+ { "IMAXBEL", 41, TTY_OP_BOOL },
+ { "ISIG", 50, TTY_OP_BOOL },
+ { "ICANON", 51, TTY_OP_BOOL },
+ { "XCASE", 52, TTY_OP_BOOL },
+ { "ECHO", 53, TTY_OP_BOOL },
+ { "ECHOE", 54, TTY_OP_BOOL },
+ { "ECHOK", 55, TTY_OP_BOOL },
+ { "ECHONL", 56, TTY_OP_BOOL },
+ { "NOFLSH", 57, TTY_OP_BOOL },
+ { "TOSTOP", 58, TTY_OP_BOOL },
+ { "IEXTEN", 59, TTY_OP_BOOL },
+ { "ECHOCTL", 60, TTY_OP_BOOL },
+ { "ECHOKE", 61, TTY_OP_BOOL },
+ { "PENDIN", 62, TTY_OP_BOOL }, /* XXX is this a real mode? */
+ { "OPOST", 70, TTY_OP_BOOL },
+ { "OLCUC", 71, TTY_OP_BOOL },
+ { "ONLCR", 72, TTY_OP_BOOL },
+ { "OCRNL", 73, TTY_OP_BOOL },
+ { "ONOCR", 74, TTY_OP_BOOL },
+ { "ONLRET", 75, TTY_OP_BOOL },
+ { "CS7", 90, TTY_OP_BOOL },
+ { "CS8", 91, TTY_OP_BOOL },
+ { "PARENB", 92, TTY_OP_BOOL },
+ { "PARODD", 93, TTY_OP_BOOL }
+};
+
+/* Miscellaneous other tty-related constants. */
+#define SSH_TTY_OP_END 0
+/* The opcodes for ISPEED/OSPEED differ between SSH-1 and SSH-2. */
+#define SSH1_TTY_OP_ISPEED 192
+#define SSH1_TTY_OP_OSPEED 193
+#define SSH2_TTY_OP_ISPEED 128
+#define SSH2_TTY_OP_OSPEED 129
+
+/* Helper functions for parsing tty-related config. */
+static unsigned int ssh_tty_parse_specchar(char *s)
+{
+ unsigned int ret;
+ if (*s) {
+ char *next = NULL;
+ ret = ctrlparse(s, &next);
+ if (!next) ret = s[0];
+ } else {
+ ret = 255; /* special value meaning "don't set" */
+ }
+ return ret;
+}
+static unsigned int ssh_tty_parse_boolean(char *s)
+{
+ if (stricmp(s, "yes") == 0 ||
+ stricmp(s, "on") == 0 ||
+ stricmp(s, "true") == 0 ||
+ stricmp(s, "+") == 0)
+ return 1; /* true */
+ else if (stricmp(s, "no") == 0 ||
+ stricmp(s, "off") == 0 ||
+ stricmp(s, "false") == 0 ||
+ stricmp(s, "-") == 0)
+ return 0; /* false */
+ else
+ return (atoi(s) != 0);
+}
+
#define translate(x) if (type == x) return #x
#define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x
static char *ssh1_pkt_type(int type)
#undef translate
#undef translatec
-#define GET_32BIT(cp) \
- (((unsigned long)(unsigned char)(cp)[0] << 24) | \
- ((unsigned long)(unsigned char)(cp)[1] << 16) | \
- ((unsigned long)(unsigned char)(cp)[2] << 8) | \
- ((unsigned long)(unsigned char)(cp)[3]))
-
-#define PUT_32BIT(cp, value) { \
- (cp)[0] = (unsigned char)((value) >> 24); \
- (cp)[1] = (unsigned char)((value) >> 16); \
- (cp)[2] = (unsigned char)((value) >> 8); \
- (cp)[3] = (unsigned char)(value); }
-
/* Enumeration values for fields in SSH-1 packets */
enum {
PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM,
typedef struct ssh_tag *Ssh;
struct Packet;
+static struct Packet *ssh1_pkt_init(int pkt_type);
static struct Packet *ssh2_pkt_init(int pkt_type);
+static void ssh_pkt_ensure(struct Packet *, int length);
+static void ssh_pkt_adddata(struct Packet *, void *data, int len);
+static void ssh_pkt_addbyte(struct Packet *, unsigned char value);
static void ssh2_pkt_addbool(struct Packet *, unsigned char value);
-static void ssh2_pkt_adduint32(struct Packet *, unsigned long value);
-static void ssh2_pkt_addstring_start(struct Packet *);
-static void ssh2_pkt_addstring_str(struct Packet *, char *data);
-static void ssh2_pkt_addstring_data(struct Packet *, char *data, int len);
-static void ssh2_pkt_addstring(struct Packet *, char *data);
+static void ssh_pkt_adduint32(struct Packet *, unsigned long value);
+static void ssh_pkt_addstring_start(struct Packet *);
+static void ssh_pkt_addstring_str(struct Packet *, char *data);
+static void ssh_pkt_addstring_data(struct Packet *, char *data, int len);
+static void ssh_pkt_addstring(struct Packet *, char *data);
static unsigned char *ssh2_mpint_fmt(Bignum b, int *len);
+static void ssh1_pkt_addmp(struct Packet *, Bignum b);
static void ssh2_pkt_addmp(struct Packet *, Bignum b);
static int ssh2_pkt_construct(Ssh, struct Packet *);
static void ssh2_pkt_send(Ssh, struct Packet *);
*
* - SSH1_BUFFER_LIMIT is the amount of backlog that must build up
* on a local data stream before we throttle the whole SSH
- * connection (in SSH1 only). Throttling the whole connection is
+ * connection (in SSH-1 only). Throttling the whole connection is
* pretty drastic so we set this high in the hope it won't
* happen very often.
*
* - SSH_MAX_BACKLOG is the amount of backlog that must build up
* on the SSH connection itself before we defensively throttle
* _all_ local data streams. This is pretty drastic too (though
- * thankfully unlikely in SSH2 since the window mechanism should
+ * thankfully unlikely in SSH-2 since the window mechanism should
* ensure that the server never has any need to throttle its end
* of the connection), so we set this high as well.
*
- * - OUR_V2_WINSIZE is the maximum window size we present on SSH2
+ * - OUR_V2_WINSIZE is the maximum window size we present on SSH-2
* channels.
*/
const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
-static void *nullmac_make_context(void)
-{
- return NULL;
-}
-static void nullmac_free_context(void *handle)
-{
-}
-static void nullmac_key(void *handle, unsigned char *key)
-{
-}
-static void nullmac_generate(void *handle, unsigned char *blk, int len,
- unsigned long seq)
-{
-}
-static int nullmac_verify(void *handle, unsigned char *blk, int len,
- unsigned long seq)
-{
- return 1;
-}
-const static struct ssh_mac ssh_mac_none = {
- nullmac_make_context, nullmac_free_context, nullmac_key,
- nullmac_generate, nullmac_verify, "none", 0
-};
const static struct ssh_mac *macs[] = {
- &ssh_sha1, &ssh_md5, &ssh_mac_none
+ &ssh_sha1, &ssh_md5
};
const static struct ssh_mac *buggymacs[] = {
- &ssh_sha1_buggy, &ssh_md5, &ssh_mac_none
+ &ssh_sha1_buggy, &ssh_md5
};
static void *ssh_comp_none_init(void)
Ssh ssh; /* pointer back to main context */
unsigned remoteid, localid;
int type;
+ /* True if we opened this channel but server hasn't confirmed. */
+ int halfopen;
/*
- * In SSH1, this value contains four bits:
+ * In SSH-1, this value contains four bits:
*
* 1 We have sent SSH1_MSG_CHANNEL_CLOSE.
* 2 We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
};
/*
- * 2-3-4 tree storing remote->local port forwardings. SSH 1 and SSH
- * 2 use this structure in different ways, reflecting SSH 2's
+ * 2-3-4 tree storing remote->local port forwardings. SSH-1 and SSH-2
+ * use this structure in different ways, reflecting SSH-2's
* altogether saner approach to port forwarding.
*
- * In SSH 1, you arrange a remote forwarding by sending the server
+ * In SSH-1, you arrange a remote forwarding by sending the server
* the remote port number, and the local destination host:port.
* When a connection comes in, the server sends you back that
* host:port pair, and you connect to it. This is a ready-made
* host:port pairs we _are_ trying to forward to, and reject a
* connection request from the server if it's not in the list.
*
- * In SSH 2, each side of the connection minds its own business and
+ * In SSH-2, each side of the connection minds its own business and
* doesn't send unnecessary information to the other. You arrange a
* remote forwarding by sending the server just the remote port
* number. When a connection comes in, the server tells you which
* of its ports was connected to; and _you_ have to remember what
* local host:port pair went with that port number.
*
- * Hence, in SSH 1 this structure is indexed by destination
- * host:port pair, whereas in SSH 2 it is indexed by source port.
+ * Hence, in SSH-1 this structure is indexed by destination
+ * host:port pair, whereas in SSH-2 it is indexed by source port.
*/
struct ssh_portfwd; /* forward declaration */
sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )
struct Packet {
- long length;
- long forcepad; /* Force padding to at least this length */
- int type;
- unsigned long sequence;
- unsigned char *data;
- unsigned char *body;
- long savedpos;
- long maxlen;
- long encrypted_len; /* for SSH2 total-size counting */
+ long length; /* length of `data' actually used */
+ long forcepad; /* SSH-2: force padding to at least this length */
+ int type; /* only used for incoming packets */
+ unsigned long sequence; /* SSH-2 incoming sequence number */
+ unsigned char *data; /* allocated storage */
+ unsigned char *body; /* offset of payload within `data' */
+ long savedpos; /* temporary index into `data' (for strings) */
+ long maxlen; /* amount of storage allocated for `data' */
+ long encrypted_len; /* for SSH-2 total-size counting */
/*
* State associated with packet logging
struct ssh_channel *mainchan; /* primary session channel */
int exitcode;
int close_expected;
+ int clean_exit;
tree234 *rportfwds, *portfwds;
*/
int fallback_cmd;
+ bufchain banner; /* accumulates banners during do_ssh2_authconn */
/*
* Used for username and password input.
*/
struct rdpkt1_state_tag rdpkt1_state;
struct rdpkt2_state_tag rdpkt2_state;
- /* ssh1 and ssh2 use this for different things, but both use it */
+ /* SSH-1 and SSH-2 use this for different things, but both use it */
int protocol_initial_phase_done;
void (*protocol) (Ssh ssh, void *vin, int inlen,
Config cfg;
/*
- * Used to transfer data back from async agent callbacks.
+ * Used to transfer data back from async callbacks.
*/
void *agent_response;
int agent_response_len;
+ int user_response;
+
+ /*
+ * The SSH connection can be set as `frozen', meaning we are
+ * not currently accepting incoming data from the network. This
+ * is slightly more serious than setting the _socket_ as
+ * frozen, because we may already have had data passed to us
+ * from the network which we need to delay processing until
+ * after the freeze is lifted, so we also need a bufchain to
+ * store that data.
+ */
+ int frozen;
+ bufchain queued_incoming_data;
/*
* Dispatch table for packet types that we may have to deal
pkt->logmode = PKTLOG_EMIT;
}
+/* Helper function for common bits of parsing cfg.ttymodes. */
+static void parse_ttymodes(Ssh ssh, char *modes,
+ void (*do_mode)(void *data, char *mode, char *val),
+ void *data)
+{
+ while (*modes) {
+ char *t = strchr(modes, '\t');
+ char *m = snewn(t-modes+1, char);
+ char *val;
+ strncpy(m, modes, t-modes);
+ m[t-modes] = '\0';
+ if (*(t+1) == 'A')
+ val = get_ttymode(ssh->frontend, m);
+ else
+ val = dupstr(t+2);
+ if (val)
+ do_mode(data, m, val);
+ sfree(m);
+ sfree(val);
+ modes += strlen(modes) + 1;
+ }
+}
+
static int ssh_channelcmp(void *av, void *bv)
{
struct ssh_channel *a = (struct ssh_channel *) av;
{
struct Packet *pkt = snew(struct Packet);
- pkt->data = NULL;
+ pkt->body = pkt->data = NULL;
pkt->maxlen = 0;
pkt->logmode = PKTLOG_EMIT;
pkt->nblanks = 0;
* _Completely_ silly lengths should be stomped on before they
* do us any more damage.
*/
- if (st->len < 0 || st->pad < 0 || st->len + st->pad < 0) {
+ if (st->len < 0 || st->len > 35000 || st->pad < 4 ||
+ st->len - st->pad < 1 || (st->len + 4) % st->cipherblk != 0) {
bombout(("Incoming packet was garbled on decryption"));
ssh_free_packet(st->pktin);
crStop(NULL);
crFinish(st->pktin);
}
-static void ssh1_pktout_size(struct Packet *pkt, int len)
+static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p)
{
- int pad, biglen;
-
- len += 5; /* type and CRC */
- pad = 8 - (len % 8);
- biglen = len + pad;
-
- pkt->length = len - 5;
- if (pkt->maxlen < biglen) {
- pkt->maxlen = biglen;
- pkt->data = sresize(pkt->data, biglen + 4 + APIEXTRA, unsigned char);
- }
- pkt->body = pkt->data + 4 + pad + 1;
-}
-
-static struct Packet *s_wrpkt_start(int type, int len)
-{
- struct Packet *pkt = ssh_new_packet();
- ssh1_pktout_size(pkt, len);
- pkt->type = type;
- /* Initialise log omission state */
- pkt->nblanks = 0;
- pkt->blanks = NULL;
- return pkt;
-}
-
-static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt)
-{
- int pad, biglen, i;
+ int pad, biglen, i, pktoffs;
unsigned long crc;
#ifdef __SC__
/*
#endif
int len;
- pkt->body[-1] = pkt->type;
-
if (ssh->logctx)
- log_packet(ssh->logctx, PKT_OUTGOING, pkt->type,
- ssh1_pkt_type(pkt->type),
- pkt->body, pkt->length,
+ log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12],
+ ssh1_pkt_type(pkt->data[12]),
+ pkt->body, pkt->length - (pkt->body - pkt->data),
pkt->nblanks, pkt->blanks);
sfree(pkt->blanks); pkt->blanks = NULL;
pkt->nblanks = 0;
unsigned char *compblk;
int complen;
zlib_compress_block(ssh->cs_comp_ctx,
- pkt->body - 1, pkt->length + 1,
+ pkt->data + 12, pkt->length - 12,
&compblk, &complen);
- ssh1_pktout_size(pkt, complen - 1);
- memcpy(pkt->body - 1, compblk, complen);
+ memcpy(pkt->data + 12, compblk, complen);
sfree(compblk);
+ pkt->length = complen + 12;
}
- len = pkt->length + 5; /* type and CRC */
+ ssh_pkt_ensure(pkt, pkt->length + 4); /* space for CRC */
+ pkt->length += 4;
+ len = pkt->length - 4 - 8; /* len(type+data+CRC) */
pad = 8 - (len % 8);
- biglen = len + pad;
+ pktoffs = 8 - pad;
+ biglen = len + pad; /* len(padding+type+data+CRC) */
- for (i = 0; i < pad; i++)
- pkt->data[i + 4] = random_byte();
- crc = crc32_compute(pkt->data + 4, biglen - 4);
- PUT_32BIT(pkt->data + biglen, crc);
- PUT_32BIT(pkt->data, len);
+ for (i = pktoffs; i < 4+8; i++)
+ pkt->data[i] = random_byte();
+ crc = crc32_compute(pkt->data + pktoffs + 4, biglen - 4); /* all ex len */
+ PUT_32BIT(pkt->data + pktoffs + 4 + biglen - 4, crc);
+ PUT_32BIT(pkt->data + pktoffs, len);
if (ssh->cipher)
- ssh->cipher->encrypt(ssh->v1_cipher_ctx, pkt->data + 4, biglen);
+ ssh->cipher->encrypt(ssh->v1_cipher_ctx,
+ pkt->data + pktoffs + 4, biglen);
- return biglen + 4;
+ if (offset_p) *offset_p = pktoffs;
+ return biglen + 4; /* len(length+padding+type+data+CRC) */
}
static void s_wrpkt(Ssh ssh, struct Packet *pkt)
{
- int len, backlog;
- len = s_wrpkt_prepare(ssh, pkt);
- backlog = sk_write(ssh->s, (char *)pkt->data, len);
+ int len, backlog, offset;
+ len = s_wrpkt_prepare(ssh, pkt, &offset);
+ backlog = sk_write(ssh->s, (char *)pkt->data + offset, len);
if (backlog > SSH_MAX_BACKLOG)
ssh_throttle_all(ssh, 1, backlog);
+ ssh_free_packet(pkt);
}
static void s_wrpkt_defer(Ssh ssh, struct Packet *pkt)
{
- int len;
- len = s_wrpkt_prepare(ssh, pkt);
+ int len, offset;
+ len = s_wrpkt_prepare(ssh, pkt, &offset);
if (ssh->deferred_len + len > ssh->deferred_size) {
ssh->deferred_size = ssh->deferred_len + len + 128;
ssh->deferred_send_data = sresize(ssh->deferred_send_data,
ssh->deferred_size,
unsigned char);
}
- memcpy(ssh->deferred_send_data + ssh->deferred_len, pkt->data, len);
+ memcpy(ssh->deferred_send_data + ssh->deferred_len,
+ pkt->data + offset, len);
ssh->deferred_len += len;
+ ssh_free_packet(pkt);
}
/*
- * Construct a packet with the specified contents.
+ * Construct a SSH-1 packet with the specified contents.
+ * (This all-at-once interface used to be the only one, but now SSH-1
+ * packets can also be constructed incrementally.)
*/
-static struct Packet *construct_packet(Ssh ssh, int pkttype,
- va_list ap1, va_list ap2)
+static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap)
{
- unsigned char *p, *argp, argchar;
- unsigned long argint;
- int pktlen, argtype, arglen;
+ int argtype;
Bignum bn;
struct Packet *pkt;
- pktlen = 0;
- while ((argtype = va_arg(ap1, int)) != PKT_END) {
- switch (argtype) {
- case PKT_INT:
- (void) va_arg(ap1, int);
- pktlen += 4;
- break;
- case PKT_CHAR:
- (void) va_arg(ap1, int);
- pktlen++;
- break;
- case PKT_DATA:
- (void) va_arg(ap1, unsigned char *);
- arglen = va_arg(ap1, int);
- pktlen += arglen;
- break;
- case PKT_STR:
- argp = va_arg(ap1, unsigned char *);
- arglen = strlen((char *)argp);
- pktlen += 4 + arglen;
- break;
- case PKT_BIGNUM:
- bn = va_arg(ap1, Bignum);
- pktlen += ssh1_bignum_length(bn);
- break;
- case PKTT_PASSWORD:
- case PKTT_DATA:
- case PKTT_OTHER:
- /* ignore this pass */
- break;
- default:
- assert(0);
- }
- }
-
- pkt = s_wrpkt_start(pkttype, pktlen);
- p = pkt->body;
+ pkt = ssh1_pkt_init(pkttype);
- while ((argtype = va_arg(ap2, int)) != PKT_END) {
- int offset = p - pkt->body, len = 0;
+ while ((argtype = va_arg(ap, int)) != PKT_END) {
+ unsigned char *argp, argchar;
+ unsigned long argint;
+ int arglen;
switch (argtype) {
/* Actual fields in the packet */
case PKT_INT:
- argint = va_arg(ap2, int);
- PUT_32BIT(p, argint);
- len = 4;
+ argint = va_arg(ap, int);
+ ssh_pkt_adduint32(pkt, argint);
break;
case PKT_CHAR:
- argchar = (unsigned char) va_arg(ap2, int);
- *p = argchar;
- len = 1;
+ argchar = (unsigned char) va_arg(ap, int);
+ ssh_pkt_addbyte(pkt, argchar);
break;
case PKT_DATA:
- argp = va_arg(ap2, unsigned char *);
- arglen = va_arg(ap2, int);
- memcpy(p, argp, arglen);
- len = arglen;
+ argp = va_arg(ap, unsigned char *);
+ arglen = va_arg(ap, int);
+ ssh_pkt_adddata(pkt, argp, arglen);
break;
case PKT_STR:
- argp = va_arg(ap2, unsigned char *);
- arglen = strlen((char *)argp);
- PUT_32BIT(p, arglen);
- memcpy(p + 4, argp, arglen);
- len = arglen + 4;
+ argp = va_arg(ap, unsigned char *);
+ ssh_pkt_addstring(pkt, argp);
break;
case PKT_BIGNUM:
- bn = va_arg(ap2, Bignum);
- len = ssh1_write_bignum(p, bn);
+ bn = va_arg(ap, Bignum);
+ ssh1_pkt_addmp(pkt, bn);
break;
/* Tokens for modifications to packet logging */
case PKTT_PASSWORD:
end_log_omission(ssh, pkt);
break;
}
- p += len;
- /* Deal with logfile omission, if required. */
- if (len && (pkt->logmode != PKTLOG_EMIT)) {
- pkt->nblanks++;
- pkt->blanks = sresize(pkt->blanks, pkt->nblanks,
- struct logblank_t);
- pkt->blanks[pkt->nblanks-1].offset = offset;
- pkt->blanks[pkt->nblanks-1].len = len;
- pkt->blanks[pkt->nblanks-1].type = pkt->logmode;
- }
}
return pkt;
static void send_packet(Ssh ssh, int pkttype, ...)
{
struct Packet *pkt;
- va_list ap1, ap2;
- va_start(ap1, pkttype);
- va_start(ap2, pkttype);
- pkt = construct_packet(ssh, pkttype, ap1, ap2);
- va_end(ap2);
- va_end(ap1);
+ va_list ap;
+ va_start(ap, pkttype);
+ pkt = construct_packet(ssh, pkttype, ap);
+ va_end(ap);
s_wrpkt(ssh, pkt);
- ssh_free_packet(pkt);
}
static void defer_packet(Ssh ssh, int pkttype, ...)
{
struct Packet *pkt;
- va_list ap1, ap2;
- va_start(ap1, pkttype);
- va_start(ap2, pkttype);
- pkt = construct_packet(ssh, pkttype, ap1, ap2);
- va_end(ap2);
- va_end(ap1);
+ va_list ap;
+ va_start(ap, pkttype);
+ pkt = construct_packet(ssh, pkttype, ap);
+ va_end(ap);
s_wrpkt_defer(ssh, pkt);
- ssh_free_packet(pkt);
}
static int ssh_versioncmp(char *a, char *b)
* Utility routines for putting an SSH-protocol `string' and
* `uint32' into a SHA state.
*/
-#include <stdio.h>
static void sha_string(SHA_State * s, void *str, int len)
{
unsigned char lenblk[4];
}
/*
- * SSH2 packet construction functions.
+ * Packet construction functions. Mostly shared between SSH-1 and SSH-2.
*/
-static void ssh2_pkt_ensure(struct Packet *pkt, int length)
+static void ssh_pkt_ensure(struct Packet *pkt, int length)
{
if (pkt->maxlen < length) {
+ unsigned char *body = pkt->body;
+ int offset = body ? pkt->data - body : 0;
pkt->maxlen = length + 256;
pkt->data = sresize(pkt->data, pkt->maxlen + APIEXTRA, unsigned char);
+ if (body) pkt->body = pkt->data + offset;
}
}
-static void ssh2_pkt_adddata(struct Packet *pkt, void *data, int len)
+static void ssh_pkt_adddata(struct Packet *pkt, void *data, int len)
{
if (pkt->logmode != PKTLOG_EMIT) {
pkt->nblanks++;
pkt->blanks = sresize(pkt->blanks, pkt->nblanks, struct logblank_t);
- pkt->blanks[pkt->nblanks-1].offset = pkt->length - 6;
+ assert(pkt->body);
+ pkt->blanks[pkt->nblanks-1].offset = pkt->length -
+ (pkt->body - pkt->data);
pkt->blanks[pkt->nblanks-1].len = len;
pkt->blanks[pkt->nblanks-1].type = pkt->logmode;
}
pkt->length += len;
- ssh2_pkt_ensure(pkt, pkt->length);
+ ssh_pkt_ensure(pkt, pkt->length);
memcpy(pkt->data + pkt->length - len, data, len);
}
-static void ssh2_pkt_addbyte(struct Packet *pkt, unsigned char byte)
+static void ssh_pkt_addbyte(struct Packet *pkt, unsigned char byte)
{
- ssh2_pkt_adddata(pkt, &byte, 1);
-}
-static struct Packet *ssh2_pkt_init(int pkt_type)
-{
- struct Packet *pkt = ssh_new_packet();
- pkt->length = 5;
- pkt->forcepad = 0;
- ssh2_pkt_addbyte(pkt, (unsigned char) pkt_type);
- return pkt;
+ ssh_pkt_adddata(pkt, &byte, 1);
}
static void ssh2_pkt_addbool(struct Packet *pkt, unsigned char value)
{
- ssh2_pkt_adddata(pkt, &value, 1);
+ ssh_pkt_adddata(pkt, &value, 1);
}
-static void ssh2_pkt_adduint32(struct Packet *pkt, unsigned long value)
+static void ssh_pkt_adduint32(struct Packet *pkt, unsigned long value)
{
unsigned char x[4];
PUT_32BIT(x, value);
- ssh2_pkt_adddata(pkt, x, 4);
+ ssh_pkt_adddata(pkt, x, 4);
}
-static void ssh2_pkt_addstring_start(struct Packet *pkt)
+static void ssh_pkt_addstring_start(struct Packet *pkt)
{
- ssh2_pkt_adduint32(pkt, 0);
+ ssh_pkt_adduint32(pkt, 0);
pkt->savedpos = pkt->length;
}
-static void ssh2_pkt_addstring_str(struct Packet *pkt, char *data)
+static void ssh_pkt_addstring_str(struct Packet *pkt, char *data)
{
- ssh2_pkt_adddata(pkt, data, strlen(data));
+ ssh_pkt_adddata(pkt, data, strlen(data));
PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
}
-static void ssh2_pkt_addstring_data(struct Packet *pkt, char *data, int len)
+static void ssh_pkt_addstring_data(struct Packet *pkt, char *data, int len)
{
- ssh2_pkt_adddata(pkt, data, len);
+ ssh_pkt_adddata(pkt, data, len);
PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
}
-static void ssh2_pkt_addstring(struct Packet *pkt, char *data)
+static void ssh_pkt_addstring(struct Packet *pkt, char *data)
{
- ssh2_pkt_addstring_start(pkt);
- ssh2_pkt_addstring_str(pkt, data);
+ ssh_pkt_addstring_start(pkt);
+ ssh_pkt_addstring_str(pkt, data);
+}
+static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b)
+{
+ int len = ssh1_bignum_length(b);
+ unsigned char *data = snewn(len, char);
+ (void) ssh1_write_bignum(data, b);
+ ssh_pkt_adddata(pkt, data, len);
+ sfree(data);
}
static unsigned char *ssh2_mpint_fmt(Bignum b, int *len)
{
unsigned char *p;
int i, n = (bignum_bitcount(b) + 7) / 8;
p = snewn(n + 1, unsigned char);
- if (!p)
- fatalbox("out of memory");
p[0] = 0;
for (i = 1; i <= n; i++)
p[i] = bignum_byte(b, n - i);
unsigned char *p;
int len;
p = ssh2_mpint_fmt(b, &len);
- ssh2_pkt_addstring_start(pkt);
- ssh2_pkt_addstring_data(pkt, (char *)p, len);
+ ssh_pkt_addstring_start(pkt);
+ ssh_pkt_addstring_data(pkt, (char *)p, len);
sfree(p);
}
+static struct Packet *ssh1_pkt_init(int pkt_type)
+{
+ struct Packet *pkt = ssh_new_packet();
+ pkt->length = 4 + 8; /* space for length + max padding */
+ ssh_pkt_addbyte(pkt, pkt_type);
+ pkt->body = pkt->data + pkt->length;
+ return pkt;
+}
+
+/* For legacy code (SSH-1 and -2 packet construction used to be separate) */
+#define ssh2_pkt_ensure(pkt, length) ssh_pkt_ensure(pkt, length)
+#define ssh2_pkt_adddata(pkt, data, len) ssh_pkt_adddata(pkt, data, len)
+#define ssh2_pkt_addbyte(pkt, byte) ssh_pkt_addbyte(pkt, byte)
+#define ssh2_pkt_adduint32(pkt, value) ssh_pkt_adduint32(pkt, value)
+#define ssh2_pkt_addstring_start(pkt) ssh_pkt_addstring_start(pkt)
+#define ssh2_pkt_addstring_str(pkt, data) ssh_pkt_addstring_str(pkt, data)
+#define ssh2_pkt_addstring_data(pkt, data, len) ssh_pkt_addstring_data(pkt, data, len)
+#define ssh2_pkt_addstring(pkt, data) ssh_pkt_addstring(pkt, data)
+
+static struct Packet *ssh2_pkt_init(int pkt_type)
+{
+ struct Packet *pkt = ssh_new_packet();
+ pkt->length = 5;
+ pkt->forcepad = 0;
+ ssh_pkt_addbyte(pkt, (unsigned char) pkt_type);
+ pkt->body = pkt->data + pkt->length;
+ return pkt;
+}
+
/*
- * Construct an SSH2 final-form packet: compress it, encrypt it,
+ * Construct an SSH-2 final-form packet: compress it, encrypt it,
* put the MAC on it. Final packet, ready to be sent, is stored in
* pkt->data. Total length is returned.
*/
if (ssh->logctx)
log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5],
ssh2_pkt_type(ssh->pkt_ctx, pkt->data[5]),
- pkt->data + 6, pkt->length - 6,
+ pkt->body, pkt->length - (pkt->body - pkt->data),
pkt->nblanks, pkt->blanks);
sfree(pkt->blanks); pkt->blanks = NULL;
pkt->nblanks = 0;
* ssh2_pkt_send() or ssh2_pkt_defer() either go straight to one of
* these or get queued, and then when the queue is later emptied
* the packets are all passed to defer_noqueue().
+ *
+ * When using a CBC-mode cipher, it's necessary to ensure that an
+ * attacker can't provide data to be encrypted using an IV that they
+ * know. We ensure this by prefixing each packet that might contain
+ * user data with an SSH_MSG_IGNORE. This is done using the deferral
+ * mechanism, so in this case send_noqueue() ends up redirecting to
+ * defer_noqueue(). If you don't like this inefficiency, don't use
+ * CBC.
*/
+static void ssh2_pkt_defer_noqueue(Ssh, struct Packet *, int);
+static void ssh_pkt_defersend(Ssh);
+
/*
- * Send an SSH2 packet immediately, without queuing or deferring.
+ * Send an SSH-2 packet immediately, without queuing or deferring.
*/
static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt)
{
int len;
int backlog;
+ if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC)) {
+ /* We need to send two packets, so use the deferral mechanism. */
+ ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
+ ssh_pkt_defersend(ssh);
+ return;
+ }
len = ssh2_pkt_construct(ssh, pkt);
backlog = sk_write(ssh->s, (char *)pkt->data, len);
if (backlog > SSH_MAX_BACKLOG)
}
/*
- * Defer an SSH2 packet.
+ * Defer an SSH-2 packet.
*/
-static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt)
+static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
{
- int len = ssh2_pkt_construct(ssh, pkt);
+ int len;
+ if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
+ ssh->deferred_len == 0 && !noignore) {
+ /*
+ * Interpose an SSH_MSG_IGNORE to ensure that user data don't
+ * get encrypted with a known IV.
+ */
+ struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);
+ }
+ len = ssh2_pkt_construct(ssh, pkt);
if (ssh->deferred_len + len > ssh->deferred_size) {
ssh->deferred_size = ssh->deferred_len + len + 128;
ssh->deferred_send_data = sresize(ssh->deferred_send_data,
}
/*
- * Queue an SSH2 packet.
+ * Queue an SSH-2 packet.
*/
static void ssh2_pkt_queue(Ssh ssh, struct Packet *pkt)
{
if (ssh->queueing)
ssh2_pkt_queue(ssh, pkt);
else
- ssh2_pkt_defer_noqueue(ssh, pkt);
+ ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
}
#endif
/*
* Send the whole deferred data block constructed by
- * ssh2_pkt_defer() or SSH1's defer_packet().
+ * ssh2_pkt_defer() or SSH-1's defer_packet().
*
* The expected use of the defer mechanism is that you call
* ssh2_pkt_defer() a few times, then call ssh_pkt_defersend(). If
}
/*
- * Send all queued SSH2 packets. We send them by means of
+ * Send all queued SSH-2 packets. We send them by means of
* ssh2_pkt_defer_noqueue(), in case they included a pair of
* packets that needed to be lumped together.
*/
assert(!ssh->queueing);
for (i = 0; i < ssh->queuelen; i++)
- ssh2_pkt_defer_noqueue(ssh, ssh->queue[i]);
+ ssh2_pkt_defer_noqueue(ssh, ssh->queue[i], FALSE);
ssh->queuelen = 0;
ssh_pkt_defersend(ssh);
}
/*
- * Packet decode functions for both SSH1 and SSH2.
+ * Packet decode functions for both SSH-1 and SSH-2.
*/
static unsigned long ssh_pkt_getuint32(struct Packet *pkt)
{
}
/*
- * Helper function to add an SSH2 signature blob to a packet.
+ * Helper function to add an SSH-2 signature blob to a packet.
* Expects to be shown the public key blob as well as the signature
* blob. Normally works just like ssh2_pkt_addstring, but will
* fiddle with the signature packet if necessary for
* sniffing.
*/
ssh->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
- logevent("We believe remote version has SSH1 ignore bug");
+ logevent("We believe remote version has SSH-1 ignore bug");
}
if (ssh->cfg.sshbug_plainpw1 == FORCE_ON ||
* the password.
*/
ssh->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
- logevent("We believe remote version needs a plain SSH1 password");
+ logevent("We believe remote version needs a plain SSH-1 password");
}
if (ssh->cfg.sshbug_rsa1 == FORCE_ON ||
* an AUTH_RSA message.
*/
ssh->remote_bugs |= BUG_CHOKES_ON_RSA;
- logevent("We believe remote version can't handle RSA authentication");
+ logevent("We believe remote version can't handle SSH-1 RSA authentication");
}
if (ssh->cfg.sshbug_hmac2 == FORCE_ON ||
* These versions have the HMAC bug.
*/
ssh->remote_bugs |= BUG_SSH2_HMAC;
- logevent("We believe remote version has SSH2 HMAC bug");
+ logevent("We believe remote version has SSH-2 HMAC bug");
}
if (ssh->cfg.sshbug_derivekey2 == FORCE_ON ||
* generate the keys).
*/
ssh->remote_bugs |= BUG_SSH2_DERIVEKEY;
- logevent("We believe remote version has SSH2 key-derivation bug");
+ logevent("We believe remote version has SSH-2 key-derivation bug");
}
if (ssh->cfg.sshbug_rsapad2 == FORCE_ON ||
(wc_match("OpenSSH_2.[5-9]*", imp) ||
wc_match("OpenSSH_3.[0-2]*", imp)))) {
/*
- * These versions have the SSH2 RSA padding bug.
+ * These versions have the SSH-2 RSA padding bug.
*/
ssh->remote_bugs |= BUG_SSH2_RSA_PADDING;
- logevent("We believe remote version has SSH2 RSA padding bug");
+ logevent("We believe remote version has SSH-2 RSA padding bug");
}
if (ssh->cfg.sshbug_pksessid2 == FORCE_ON ||
(ssh->cfg.sshbug_pksessid2 == AUTO &&
wc_match("OpenSSH_2.[0-2]*", imp))) {
/*
- * These versions have the SSH2 session-ID bug in
+ * These versions have the SSH-2 session-ID bug in
* public-key authentication.
*/
ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID;
- logevent("We believe remote version has SSH2 public-key-session-ID bug");
+ logevent("We believe remote version has SSH-2 public-key-session-ID bug");
}
if (ssh->cfg.sshbug_rekey2 == FORCE_ON ||
wc_match("Sun_SSH_1.0", imp) ||
wc_match("Sun_SSH_1.0.1", imp)))) {
/*
- * These versions have the SSH2 rekey bug.
+ * These versions have the SSH-2 rekey bug.
*/
ssh->remote_bugs |= BUG_SSH2_REKEY;
- logevent("We believe remote version has SSH2 rekey bug");
+ logevent("We believe remote version has SSH-2 rekey bug");
}
}
s->vstring[s->vslen] = 0;
s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */
- {
- char *vlog;
- vlog = snewn(20 + s->vslen, char);
- sprintf(vlog, "Server version: %s", s->vstring);
- logevent(vlog);
- sfree(vlog);
- }
+ logeventf(ssh, "Server version: %s", s->vstring);
ssh_detect_bugs(ssh, s->vstring);
/*
strcspn(s->vstring, "\015\012"));
/*
- * Initialise SSHv2 protocol.
+ * Initialise SSH-2 protocol.
*/
ssh->protocol = ssh2_protocol;
ssh2_protocol_setup(ssh);
ssh->s_rdpkt = ssh2_rdpkt;
} else {
/*
- * Initialise SSHv1 protocol.
+ * Initialise SSH-1 protocol.
*/
ssh->protocol = ssh1_protocol;
ssh1_protocol_setup(ssh);
strcspn(verstring, "\015\012"), verstring);
sk_write(ssh->s, verstring, strlen(verstring));
sfree(verstring);
+ if (ssh->version == 2)
+ do_ssh2_transport(ssh, NULL, -1, NULL);
}
logeventf(ssh, "Using SSH protocol version %d", ssh->version);
crFinish(0);
}
+static void ssh_process_incoming_data(Ssh ssh,
+ unsigned char **data, int *datalen)
+{
+ struct Packet *pktin = ssh->s_rdpkt(ssh, data, datalen);
+ if (pktin) {
+ ssh->protocol(ssh, NULL, 0, pktin);
+ ssh_free_packet(pktin);
+ }
+}
+
+static void ssh_queue_incoming_data(Ssh ssh,
+ unsigned char **data, int *datalen)
+{
+ bufchain_add(&ssh->queued_incoming_data, *data, *datalen);
+ *data += *datalen;
+ *datalen = 0;
+}
+
+static void ssh_process_queued_incoming_data(Ssh ssh)
+{
+ void *vdata;
+ unsigned char *data;
+ int len, origlen;
+
+ while (!ssh->frozen && bufchain_size(&ssh->queued_incoming_data)) {
+ bufchain_prefix(&ssh->queued_incoming_data, &vdata, &len);
+ data = vdata;
+ origlen = len;
+
+ while (!ssh->frozen && len > 0)
+ ssh_process_incoming_data(ssh, &data, &len);
+
+ if (origlen > len)
+ bufchain_consume(&ssh->queued_incoming_data, origlen - len);
+ }
+}
+
+static void ssh_set_frozen(Ssh ssh, int frozen)
+{
+ if (ssh->s)
+ sk_set_frozen(ssh->s, frozen);
+ ssh->frozen = frozen;
+}
+
static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
{
crBegin(ssh->ssh_gotdata_crstate);
*/
if (datalen == 0)
crReturnV;
+
+ /*
+ * Process queued data if there is any.
+ */
+ ssh_process_queued_incoming_data(ssh);
+
while (1) {
while (datalen > 0) {
- struct Packet *pktin = ssh->s_rdpkt(ssh, &data, &datalen);
- if (pktin) {
- ssh->protocol(ssh, NULL, 0, pktin);
- ssh_free_packet(pktin);
- }
+ if (ssh->frozen)
+ ssh_queue_incoming_data(ssh, &data, &datalen);
+
+ ssh_process_incoming_data(ssh, &data, &datalen);
+
if (ssh->state == SSH_STATE_CLOSED)
return;
}
static int ssh_do_close(Ssh ssh, int notify_exit)
{
- int i, ret = 0;
+ int ret = 0;
struct ssh_channel *c;
ssh->state = SSH_STATE_CLOSED;
+ expire_timer_context(ssh);
if (ssh->s) {
sk_close(ssh->s);
ssh->s = NULL;
ret = 1;
}
/*
- * Now we must shut down any port and X forwardings going
+ * Now we must shut down any port- and X-forwarded channels going
* through this connection.
*/
if (ssh->channels) {
- for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) {
+ while (NULL != (c = index234(ssh->channels, 0))) {
switch (c->type) {
case CHAN_X11:
x11_close(c->u.x11.s);
pfd_close(c->u.pfd.s);
break;
}
- del234(ssh->channels, c);
+ del234(ssh->channels, c); /* moving next one to index 0 */
if (ssh->version == 2)
bufchain_clear(&c->v.v2.outbuffer);
sfree(c);
}
}
+ /*
+ * Go through port-forwardings, and close any associated
+ * listening sockets.
+ */
+ if (ssh->portfwds) {
+ struct ssh_portfwd *pf;
+ while (NULL != (pf = index234(ssh->portfwds, 0))) {
+ /* Dispose of any listening socket. */
+ if (pf->local)
+ pfd_terminate(pf->local);
+ del234(ssh->portfwds, pf); /* moving next one to index 0 */
+ free_portfwd(pf);
+ }
+ }
return ret;
}
msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
logevent(msg);
+ sfree(msg);
}
static int ssh_closing(Plug plug, const char *error_msg, int error_code,
Ssh ssh = (Ssh) plug;
int need_notify = ssh_do_close(ssh, FALSE);
- if (!error_msg && !ssh->close_expected) {
- error_msg = "Server unexpectedly closed network connection";
+ if (!error_msg) {
+ if (!ssh->close_expected)
+ error_msg = "Server unexpectedly closed network connection";
+ else
+ error_msg = "Server closed network connection";
}
- if (error_msg) {
- /* A socket error has occurred. */
- logevent(error_msg);
- connection_fatal(ssh->frontend, "%s", error_msg);
- } else {
- logevent("Server closed network connection");
- }
if (need_notify)
notify_remote_exit(ssh->frontend);
+
+ if (error_msg)
+ logevent(error_msg);
+ if (!ssh->close_expected || !ssh->clean_exit)
+ connection_fatal(ssh->frontend, "%s", error_msg);
return 0;
}
const char *err;
ssh->savedhost = snewn(1 + strlen(host), char);
- if (!ssh->savedhost)
- fatalbox("Out of memory");
strcpy(ssh->savedhost, host);
if (port < 0)
ssh->v1_throttle_count += adjust;
assert(ssh->v1_throttle_count >= 0);
if (ssh->v1_throttle_count && !old_count) {
- sk_set_frozen(ssh->s, 1);
+ ssh_set_frozen(ssh, 1);
} else if (!ssh->v1_throttle_count && old_count) {
- sk_set_frozen(ssh->s, 0);
+ ssh_set_frozen(ssh, 0);
}
}
/*
* Username and password input, abstracted off into routines
- * reusable in several places - even between SSH1 and SSH2.
+ * reusable in several places - even between SSH-1 and SSH-2.
*/
/* Set up a username or password input loop on a given buffer. */
do_ssh2_authconn(ssh, NULL, -1, NULL);
}
+static void ssh_dialog_callback(void *sshv, int ret)
+{
+ Ssh ssh = (Ssh) sshv;
+
+ ssh->user_response = ret;
+
+ if (ssh->version == 1)
+ do_ssh1_login(ssh, NULL, -1, NULL);
+ else
+ do_ssh2_transport(ssh, NULL, -1, NULL);
+
+ /*
+ * This may have unfrozen the SSH connection, so do a
+ * queued-data run.
+ */
+ ssh_process_queued_incoming_data(ssh);
+}
+
static void ssh_agentf_callback(void *cv, void *reply, int replylen)
{
struct ssh_channel *c = (struct ssh_channel *)cv;
sfree(reply);
}
+/*
+ * Client-initiated disconnection. Send a DISCONNECT if `wire_reason'
+ * non-NULL, otherwise just close the connection. `client_reason' == NULL
+ * => log `wire_reason'.
+ */
+static void ssh_disconnect(Ssh ssh, char *client_reason, char *wire_reason,
+ int code, int clean_exit)
+{
+ char *error;
+ if (!client_reason)
+ client_reason = wire_reason;
+ if (client_reason)
+ error = dupprintf("Disconnected: %s", client_reason);
+ else
+ error = dupstr("Disconnected");
+ if (wire_reason) {
+ if (ssh->version == 1) {
+ send_packet(ssh, SSH1_MSG_DISCONNECT, PKT_STR, wire_reason,
+ PKT_END);
+ } else if (ssh->version == 2) {
+ struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+ ssh2_pkt_adduint32(pktout, code);
+ ssh2_pkt_addstring(pktout, wire_reason);
+ ssh2_pkt_addstring(pktout, "en"); /* language tag */
+ ssh2_pkt_send_noqueue(ssh, pktout);
+ }
+ }
+ ssh->close_expected = TRUE;
+ ssh->clean_exit = clean_exit;
+ ssh_closing((Plug)ssh, error, 0, 0);
+ sfree(error);
+}
+
/*
* Handle the key exchange and user authentication phases.
*/
Bignum challenge;
char *commentp;
int commentlen;
+ int dlgret;
};
crState(do_ssh1_login_state);
ptr = ssh_pkt_getdata(pktin, 8);
if (!ptr) {
- bombout(("SSH1 public key packet stopped before random cookie"));
+ bombout(("SSH-1 public key packet stopped before random cookie"));
crStop(0);
}
memcpy(cookie, ptr, 8);
if (!ssh1_pkt_getrsakey(pktin, &servkey, &s->keystr1) ||
!ssh1_pkt_getrsakey(pktin, &hostkey, &s->keystr2)) {
- bombout(("Failed to read SSH1 public keys from public key packet"));
+ bombout(("Failed to read SSH-1 public keys from public key packet"));
crStop(0);
}
*/
if (hostkey.bits > hostkey.bytes * 8 ||
servkey.bits > servkey.bytes * 8) {
- bombout(("SSH1 public keys were badly formatted"));
+ bombout(("SSH-1 public keys were badly formatted"));
crStop(0);
}
s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
s->rsabuf = snewn(s->len, unsigned char);
- if (!s->rsabuf)
- fatalbox("Out of memory");
/*
* Verify the host key.
int len = rsastr_len(&hostkey);
char fingerprint[100];
char *keystr = snewn(len, char);
- if (!keystr)
- fatalbox("Out of memory");
rsastr_fmt(keystr, &hostkey);
rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
- verify_ssh_host_key(ssh->frontend,
- ssh->savedhost, ssh->savedport, "rsa", keystr,
- fingerprint);
+
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = verify_ssh_host_key(ssh->frontend,
+ ssh->savedhost, ssh->savedport,
+ "rsa", keystr, fingerprint,
+ ssh_dialog_callback, ssh);
sfree(keystr);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while waiting"
+ " for user host key response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+
+ if (s->dlgret == 0) {
+ ssh_disconnect(ssh, "User aborted at host key verification",
+ NULL, 0, TRUE);
+ crStop(0);
+ }
}
for (i = 0; i < 32; i++) {
ret = rsaencrypt(s->rsabuf, hostkey.bytes, &servkey);
}
if (!ret) {
- bombout(("SSH1 public key encryptions failed due to bad formatting"));
+ bombout(("SSH-1 public key encryptions failed due to bad formatting"));
crStop(0);
}
warn = 1;
} else if (next_cipher == CIPHER_AES) {
/* XXX Probably don't need to mention this. */
- logevent("AES not supported in SSH1, skipping");
+ logevent("AES not supported in SSH-1, skipping");
} else {
switch (next_cipher) {
case CIPHER_3DES: s->cipher_type = SSH_CIPHER_3DES;
}
if (!cipher_chosen) {
if ((s->supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0)
- bombout(("Server violates SSH 1 protocol by not "
+ bombout(("Server violates SSH-1 protocol by not "
"supporting 3DES encryption"));
else
/* shouldn't happen */
/* Warn about chosen cipher if necessary. */
if (warn) {
- sk_set_frozen(ssh->s, 1);
- askalg(ssh->frontend, "cipher", cipher_string);
- sk_set_frozen(ssh->s, 0);
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = askalg(ssh->frontend, "cipher", cipher_string,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while waiting"
+ " for user response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
+ 0, TRUE);
+ crStop(0);
+ }
}
}
* get_line failed to get a username.
* Terminate.
*/
- logevent("No username provided. Abandoning session.");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
crStop(1);
}
} else {
s->p = s->response + 5;
s->nkeys = GET_32BIT(s->p);
s->p += 4;
- {
- char buf[64];
- sprintf(buf, "Pageant has %d SSH1 keys", s->nkeys);
- logevent(buf);
- }
+ logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys);
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
- {
- char buf[64];
- sprintf(buf, "Trying Pageant key #%d", s->keyi);
- logevent(buf);
- }
+ logeventf(ssh, "Trying Pageant key #%d", s->keyi);
if (s->publickey_blob &&
!memcmp(s->p, s->publickey_blob,
s->publickey_bloblen)) {
bombout(("TIS challenge packet was badly formed"));
crStop(0);
}
+ c_write_str(ssh, "Using TIS authentication.\r\n");
logevent("Received TIS challenge");
if (challengelen > sizeof(s->prompt) - 1)
challengelen = sizeof(s->prompt) - 1;/* prevent overrun */
bombout(("CryptoCard challenge packet was badly formed"));
crStop(0);
}
+ c_write_str(ssh, "Using CryptoCard authentication.\r\n");
logevent("Received CryptoCard challenge");
if (challengelen > sizeof(s->prompt) - 1)
challengelen = sizeof(s->prompt) - 1;/* prevent overrun */
if (s->pwpkt_type == SSH1_CMSG_AUTH_RSA) {
char *comment = NULL;
int type;
- char msgbuf[256];
if (flags & FLAG_VERBOSE)
c_write_str(ssh, "Trying public key authentication.\r\n");
logeventf(ssh, "Trying public key \"%s\"",
filename_to_str(&ssh->cfg.keyfile));
type = key_type(&ssh->cfg.keyfile);
if (type != SSH_KEYTYPE_SSH1) {
- sprintf(msgbuf, "Key is of wrong type (%s)",
- key_type_to_str(type));
- logevent(msgbuf);
- c_write_str(ssh, msgbuf);
+ char *msg = dupprintf("Key is of wrong type (%s)",
+ key_type_to_str(type));
+ logevent(msg);
+ c_write_str(ssh, msg);
c_write_str(ssh, "\r\n");
+ sfree(msg);
s->tried_publickey = 1;
continue;
}
* because one was supplied on the command line
* which has already failed to work). Terminate.
*/
- send_packet(ssh, SSH1_MSG_DISCONNECT,
- PKT_STR, "No more passwords available to try",
- PKT_END);
- logevent("Unable to authenticate");
- connection_fatal(ssh->frontend, "Unable to authenticate");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate", 0, FALSE);
crStop(1);
}
} else {
{
Ssh ssh = c->ssh;
- if (ssh->state != SSH_STATE_SESSION) {
- assert(ssh->state == SSH_STATE_CLOSED);
+ if (ssh->state == SSH_STATE_CLOSED)
return;
- }
if (c && !c->closes) {
/*
- * If the channel's remoteid is -1, we have sent
+ * If halfopen is true, we have sent
* CHANNEL_OPEN for this channel, but it hasn't even been
* acknowledged by the server. So we must set a close flag
* on it now, and then when the server acks the channel
* open, we can close it then.
*/
- if (((int)c->remoteid) != -1) {
+ if (!c->halfopen) {
if (ssh->version == 1) {
send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
PKT_END);
{
Ssh ssh = c->ssh;
- if (ssh->state != SSH_STATE_SESSION) {
- assert(ssh->state == SSH_STATE_CLOSED);
+ if (ssh->state == SSH_STATE_CLOSED)
return 0;
- }
if (ssh->version == 1) {
send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
PKT_INT, len, PKT_DATA, buf, len,
PKTT_OTHER, PKT_END);
/*
- * In SSH1 we can return 0 here - implying that forwarded
+ * In SSH-1 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
{
Ssh ssh = c->ssh;
- if (ssh->state != SSH_STATE_SESSION) {
- assert(ssh->state == SSH_STATE_CLOSED);
+ if (ssh->state == SSH_STATE_CLOSED)
return;
- }
if (ssh->version == 1) {
if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) {
portfwd_strptr++;
sports[n] = '\0';
if (ssh->version == 1 && type == 'R') {
- logeventf(ssh, "SSH1 cannot handle remote source address "
+ logeventf(ssh, "SSH-1 cannot handle remote source address "
"spec \"%s\"; ignoring", sports);
} else
strcpy(saddr, sports);
n = 0;
}
- if (n < 255) sports[n++] = *portfwd_strptr++;
+ if (n < lenof(sports)-1) sports[n++] = *portfwd_strptr++;
}
sports[n] = 0;
if (type != 'D') {
portfwd_strptr++;
n = 0;
while (*portfwd_strptr && *portfwd_strptr != ':') {
- if (n < 255) host[n++] = *portfwd_strptr++;
+ if (n < lenof(host)-1) host[n++] = *portfwd_strptr++;
}
host[n] = 0;
if (*portfwd_strptr == ':')
portfwd_strptr++;
n = 0;
while (*portfwd_strptr) {
- if (n < 255) dports[n++] = *portfwd_strptr++;
+ if (n < lenof(dports)-1) dports[n++] = *portfwd_strptr++;
}
dports[n] = 0;
portfwd_strptr++;
}
} else {
while (*portfwd_strptr) portfwd_strptr++;
+ host[0] = 0;
+ dports[0] = 0;
dport = dserv = -1;
portfwd_strptr++; /* eat the NUL and move to next one */
}
if (ssh->version == 1) {
/*
* We cannot cancel listening ports on the
- * server side in SSH1! There's no message
+ * server side in SSH-1! There's no message
* to support it. Instead, we simply remove
* the rportfwd record from the local end
* so that any connections the server tries
if (epf->saddr) {
ssh2_pkt_addstring(pktout, epf->saddr);
} else if (ssh->cfg.rport_acceptall) {
+ /* XXX: ssh->cfg.rport_acceptall may not represent
+ * what was used to open the original connection,
+ * since it's reconfigurable. */
ssh2_pkt_addstring(pktout, "0.0.0.0");
} else {
ssh2_pkt_addstring(pktout, "127.0.0.1");
if (epf->type == 'L') {
const char *err = pfd_addforward(epf->daddr, epf->dport,
epf->saddr, epf->sport,
- ssh, &ssh->cfg,
+ ssh, cfg,
&epf->local,
epf->addressfamily);
} else if (epf->type == 'D') {
const char *err = pfd_addforward(NULL, -1,
epf->saddr, epf->sport,
- ssh, &ssh->cfg,
+ ssh, cfg,
&epf->local,
epf->addressfamily);
ssh2_pkt_addbool(pktout, 1);/* want reply */
if (epf->saddr) {
ssh2_pkt_addstring(pktout, epf->saddr);
- } else if (ssh->cfg.rport_acceptall) {
+ } else if (cfg->rport_acceptall) {
ssh2_pkt_addstring(pktout, "0.0.0.0");
} else {
ssh2_pkt_addstring(pktout, "127.0.0.1");
logevent
("Opening X11 forward connection succeeded");
c->remoteid = remoteid;
+ c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
c->v.v1.throttling = 0;
c = snew(struct ssh_channel);
c->ssh = ssh;
c->remoteid = remoteid;
+ c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
c->v.v1.throttling = 0;
struct ssh_rportfwd pf, *pfp;
int remoteid;
int hostsize, port;
- char *host, buf[1024];
+ char *host;
const char *e;
c = snew(struct ssh_channel);
c->ssh = ssh;
pfp = find234(ssh->rportfwds, &pf, NULL);
if (pfp == NULL) {
- sprintf(buf, "Rejected remote port open request for %s:%d",
- pf.dhost, port);
- logevent(buf);
+ logeventf(ssh, "Rejected remote port open request for %s:%d",
+ pf.dhost, port);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
PKT_INT, remoteid, PKT_END);
} else {
- sprintf(buf, "Received remote port open request for %s:%d",
- pf.dhost, port);
- logevent(buf);
+ logeventf(ssh, "Received remote port open request for %s:%d",
+ pf.dhost, port);
e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port,
c, &ssh->cfg, pfp->pfrec->addressfamily);
if (e != NULL) {
- char buf[256];
- sprintf(buf, "Port open failed: %s", e);
- logevent(buf);
+ logeventf(ssh, "Port open failed: %s", e);
sfree(c);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
PKT_INT, remoteid, PKT_END);
} else {
c->remoteid = remoteid;
+ c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
c->v.v1.throttling = 0;
c = find234(ssh->channels, &remoteid, ssh_channelfind);
if (c && c->type == CHAN_SOCKDATA_DORMANT) {
c->remoteid = localid;
+ c->halfopen = FALSE;
c->type = CHAN_SOCKDATA;
c->v.v1.throttling = 0;
pfd_confirm(c->u.pfd.s);
unsigned i = ssh_pkt_getuint32(pktin);
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
- if (c && ((int)c->remoteid) != -1) {
+ if (c && !c->halfopen) {
int closetype;
closetype =
(pktin->type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
static void ssh1_smsg_exit_status(Ssh ssh, struct Packet *pktin)
{
- char buf[100];
ssh->exitcode = ssh_pkt_getuint32(pktin);
- sprintf(buf, "Server sent command exit status %d",
- ssh->exitcode);
- logevent(buf);
+ logeventf(ssh, "Server sent command exit status %d", ssh->exitcode);
send_packet(ssh, SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
/*
* In case `helpful' firewalls or proxies tack
* encrypted packet, we close the session once
* we've sent EXIT_CONFIRMATION.
*/
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL, NULL, 0, TRUE);
}
+/* Helper function to deal with sending tty modes for REQUEST_PTY */
+static void ssh1_send_ttymode(void *data, char *mode, char *val)
+{
+ struct Packet *pktout = (struct Packet *)data;
+ int i = 0;
+ unsigned int arg = 0;
+ while (strcmp(mode, ssh_ttymodes[i].mode) != 0) i++;
+ if (i == lenof(ssh_ttymodes)) return;
+ switch (ssh_ttymodes[i].type) {
+ case TTY_OP_CHAR:
+ arg = ssh_tty_parse_specchar(val);
+ break;
+ case TTY_OP_BOOL:
+ arg = ssh_tty_parse_boolean(val);
+ break;
+ }
+ ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode);
+ ssh2_pkt_addbyte(pktout, arg);
+}
+
+
static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
struct Packet *pktin)
{
ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
data, sizeof(data), ssh->cfg.x11_auth);
x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display);
+ /*
+ * Note that while we blank the X authentication data here, we don't
+ * take any special action to blank the start of an X11 channel,
+ * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection
+ * without having session blanking enabled is likely to leak your
+ * cookie into the log.
+ */
if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
- PKT_STR, proto, PKT_STR, data,
+ PKT_STR, proto,
+ PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER,
PKT_INT, x11_get_screen_number(ssh->cfg.x11_display),
PKT_END);
} else {
send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
- PKT_STR, proto, PKT_STR, data, PKT_END);
+ PKT_STR, proto,
+ PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, PKT_END);
}
do {
crReturnV;
ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;
if (!ssh->cfg.nopty) {
+ struct Packet *pkt;
/* Unpick the terminal-speed string. */
/* XXX perhaps we should allow no speeds to be sent. */
ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed);
/* Send the pty request. */
- send_packet(ssh, SSH1_CMSG_REQUEST_PTY,
- PKT_STR, ssh->cfg.termtype,
- PKT_INT, ssh->term_height,
- PKT_INT, ssh->term_width,
- PKT_INT, 0, PKT_INT, 0, /* width,height in pixels */
- PKT_CHAR, 192, PKT_INT, ssh->ispeed, /* TTY_OP_ISPEED */
- PKT_CHAR, 193, PKT_INT, ssh->ospeed, /* TTY_OP_OSPEED */
- PKT_CHAR, 0, PKT_END);
+ pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY);
+ ssh_pkt_addstring(pkt, ssh->cfg.termtype);
+ ssh_pkt_adduint32(pkt, ssh->term_height);
+ ssh_pkt_adduint32(pkt, ssh->term_width);
+ ssh_pkt_adduint32(pkt, 0); /* width in pixels */
+ ssh_pkt_adduint32(pkt, 0); /* height in pixels */
+ parse_ttymodes(ssh, ssh->cfg.ttymodes,
+ ssh1_send_ttymode, (void *)pkt);
+ ssh_pkt_addbyte(pkt, SSH1_TTY_OP_ISPEED);
+ ssh_pkt_adduint32(pkt, ssh->ispeed);
+ ssh_pkt_addbyte(pkt, SSH1_TTY_OP_OSPEED);
+ ssh_pkt_adduint32(pkt, ssh->ospeed);
+ ssh_pkt_addbyte(pkt, SSH_TTY_OP_END);
+ s_wrpkt(ssh, pkt);
ssh->state = SSH_STATE_INTERMED;
do {
crReturnV;
/*
* Start the shell or command.
*
- * Special case: if the first-choice command is an SSH2
+ * Special case: if the first-choice command is an SSH-2
* subsystem (hence not usable here) and the second choice
* exists, we fall straight back to that.
*/
{
char *cmd = ssh->cfg.remote_cmd_ptr;
+
+ if (!cmd) cmd = ssh->cfg.remote_cmd;
if (ssh->cfg.ssh_subsys && ssh->cfg.remote_cmd_ptr2) {
cmd = ssh->cfg.remote_cmd_ptr2;
}
/*
- * Handle the top-level SSH2 protocol.
+ * Handle the top-level SSH-2 protocol.
*/
static void ssh1_msg_debug(Ssh ssh, struct Packet *pktin)
{
- char *buf, *msg;
+ char *msg;
int msglen;
ssh_pkt_getstring(pktin, &msg, &msglen);
- buf = dupprintf("Remote debug message: %.*s", msglen, msg);
- logevent(buf);
- sfree(buf);
+ logeventf(ssh, "Remote debug message: %.*s", msglen, msg);
}
static void ssh1_msg_disconnect(Ssh ssh, struct Packet *pktin)
bombout(("Server sent disconnect message:\n\"%.*s\"", msglen, msg));
}
-void ssh_msg_ignore(Ssh ssh, struct Packet *pktin)
+static void ssh_msg_ignore(Ssh ssh, struct Packet *pktin)
{
/* Do nothing, because we're ignoring it! Duhh. */
}
}
/*
- * SSH2 key creation method.
+ * Similar routine for checking whether we have the first string in a list.
+ */
+static int first_in_commasep_string(char *needle, char *haystack, int haylen)
+{
+ int needlen;
+ if (!needle || !haystack) /* protect against null pointers */
+ return 0;
+ needlen = strlen(needle);
+ /*
+ * 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;
+ return 0;
+}
+
+
+/*
+ * SSH-2 key creation method.
*/
static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H,
unsigned char *sessid, char chr,
}
/*
- * Handle the SSH2 transport layer.
+ * Handle the SSH-2 transport layer.
*/
static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
struct Packet *pktin)
{
unsigned char *in = (unsigned char *)vin;
struct do_ssh2_transport_state {
- int nbits, pbits, warn;
+ int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher;
Bignum p, g, e, f, K;
int kex_init_value, kex_reply_value;
const struct ssh_mac **maclist;
const struct ssh_compress *preferred_comp;
int got_session_id, activated_authconn;
struct Packet *pktout;
+ int dlgret;
+ int guessok;
+ int ignorepkt;
};
crState(do_ssh2_transport_state);
case CIPHER_AES:
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_aes;
break;
+ case CIPHER_ARCFOUR:
+ s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_arcfour;
+ break;
case CIPHER_WARN:
/* Flag for later. Don't bother if it's the last in
* the list. */
* to.
*/
{
- char *str;
+ char *str, *preferred;
int i, j, len;
if (pktin->type != SSH2_MSG_KEXINIT) {
s->scmac_tobe = NULL;
s->cscomp_tobe = NULL;
s->sccomp_tobe = NULL;
+ s->warn_kex = s->warn_cscipher = s->warn_sccipher = FALSE;
+
pktin->savedpos += 16; /* skip garbage cookie */
ssh_pkt_getstring(pktin, &str, &len); /* key exchange algorithms */
- s->warn = 0;
+
+ preferred = NULL;
for (i = 0; i < s->n_preferred_kex; i++) {
const struct ssh_kex *k = s->preferred_kex[i];
if (!k) {
- s->warn = 1;
- } else if (in_commasep_string(k->name, str, len)) {
- ssh->kex = k;
+ s->warn_kex = TRUE;
+ } else {
+ if (!preferred) preferred = k->name;
+ if (in_commasep_string(k->name, str, len))
+ ssh->kex = k;
}
- if (ssh->kex) {
- if (s->warn) {
- sk_set_frozen(ssh->s, 1);
- askalg(ssh->frontend, "key-exchange algorithm",
- ssh->kex->name);
- sk_set_frozen(ssh->s, 0);
- }
+ if (ssh->kex)
break;
- }
}
if (!ssh->kex) {
bombout(("Couldn't agree a key exchange algorithm (available: %s)",
str ? str : "(null)"));
crStop(0);
}
+ /*
+ * Note that the server's guess is considered wrong if it doesn't match
+ * the first algorithm in our list, even if it's still the algorithm
+ * we end up using.
+ */
+ s->guessok = first_in_commasep_string(preferred, str, len);
ssh_pkt_getstring(pktin, &str, &len); /* host key algorithms */
for (i = 0; i < lenof(hostkey_algs); i++) {
if (in_commasep_string(hostkey_algs[i]->name, str, len)) {
break;
}
}
+ s->guessok = s->guessok &&
+ first_in_commasep_string(hostkey_algs[0]->name, str, len);
ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */
- s->warn = 0;
for (i = 0; i < s->n_preferred_ciphers; i++) {
const struct ssh2_ciphers *c = s->preferred_ciphers[i];
if (!c) {
- s->warn = 1;
+ s->warn_cscipher = TRUE;
} else {
for (j = 0; j < c->nciphers; j++) {
if (in_commasep_string(c->list[j]->name, str, len)) {
}
}
}
- if (s->cscipher_tobe) {
- if (s->warn) {
- sk_set_frozen(ssh->s, 1);
- askalg(ssh->frontend, "client-to-server cipher",
- s->cscipher_tobe->name);
- sk_set_frozen(ssh->s, 0);
- }
+ if (s->cscipher_tobe)
break;
- }
}
if (!s->cscipher_tobe) {
bombout(("Couldn't agree a client-to-server cipher (available: %s)",
}
ssh_pkt_getstring(pktin, &str, &len); /* server->client cipher */
- s->warn = 0;
for (i = 0; i < s->n_preferred_ciphers; i++) {
const struct ssh2_ciphers *c = s->preferred_ciphers[i];
if (!c) {
- s->warn = 1;
+ s->warn_sccipher = TRUE;
} else {
for (j = 0; j < c->nciphers; j++) {
if (in_commasep_string(c->list[j]->name, str, len)) {
}
}
}
- if (s->sccipher_tobe) {
- if (s->warn) {
- sk_set_frozen(ssh->s, 1);
- askalg(ssh->frontend, "server-to-client cipher",
- s->sccipher_tobe->name);
- sk_set_frozen(ssh->s, 0);
- }
+ if (s->sccipher_tobe)
break;
- }
}
if (!s->sccipher_tobe) {
bombout(("Couldn't agree a server-to-client cipher (available: %s)",
break;
}
}
+ ssh_pkt_getstring(pktin, &str, &len); /* client->server language */
+ ssh_pkt_getstring(pktin, &str, &len); /* server->client language */
+ s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok;
+
+ if (s->warn_kex) {
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",
+ ssh->kex->name,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while"
+ " waiting for user response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh_disconnect(ssh, "User aborted at kex warning", NULL,
+ 0, TRUE);
+ crStop(0);
+ }
+ }
+
+ if (s->warn_cscipher) {
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = askalg(ssh->frontend,
+ "client-to-server cipher",
+ s->cscipher_tobe->name,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while"
+ " waiting for user response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
+ 0, TRUE);
+ crStop(0);
+ }
+ }
+
+ if (s->warn_sccipher) {
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = askalg(ssh->frontend,
+ "server-to-client cipher",
+ s->sccipher_tobe->name,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while"
+ " waiting for user response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
+ 0, TRUE);
+ crStop(0);
+ }
+ }
+
+ if (s->ignorepkt) /* first_kex_packet_follows */
+ crWaitUntil(pktin); /* Ignore packet */
}
/*
/*
* Now generate and send e for Diffie-Hellman.
*/
+ set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
s->pktout = ssh2_pkt_init(s->kex_init_value);
ssh2_pkt_addmp(s->pktout, s->e);
ssh2_pkt_send_noqueue(ssh, s->pktout);
+ set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
crWaitUntil(pktin);
if (pktin->type != s->kex_reply_value) {
bombout(("expected key exchange reply packet from server"));
crStop(0);
}
+ set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
s->f = ssh2_pkt_getmp(pktin);
if (!s->f) {
s->K = dh_find_K(ssh->kex_ctx, s->f);
+ /* We assume everything from now on will be quick, and it might
+ * involve user interaction. */
+ set_busy_status(ssh->frontend, BUSY_NOT);
+
sha_string(&ssh->exhash, s->hostkeydata, s->hostkeylen);
if (ssh->kex == &ssh_diffiehellman_gex) {
sha_uint32(&ssh->exhash, s->pbits);
*/
s->keystr = ssh->hostkey->fmtkey(s->hkey);
s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
- sk_set_frozen(ssh->s, 1);
- verify_ssh_host_key(ssh->frontend,
- ssh->savedhost, ssh->savedport, ssh->hostkey->keytype,
- s->keystr, s->fingerprint);
- sk_set_frozen(ssh->s, 0);
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = verify_ssh_host_key(ssh->frontend,
+ ssh->savedhost, ssh->savedport,
+ ssh->hostkey->keytype, s->keystr,
+ s->fingerprint,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while waiting"
+ " for user host key response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh_disconnect(ssh, "User aborted at host key verification", NULL,
+ 0, TRUE);
+ crStop(0);
+ }
if (!s->got_session_id) { /* don't bother logging this in rekeys */
logevent("Host key fingerprint is:");
logevent(s->fingerprint);
* it would only confuse the layer above.
*/
if (s->activated_authconn) {
- crReturn(1);
+ crReturn(0);
}
s->activated_authconn = TRUE;
}
/*
- * Add data to an SSH2 channel output buffer.
+ * Add data to an SSH-2 channel output buffer.
*/
static void ssh2_add_channel_data(struct ssh_channel *c, char *buf,
int len)
}
/*
- * Attempt to send data on an SSH2 channel.
+ * Attempt to send data on an SSH-2 channel.
*/
static int ssh2_try_send(struct ssh_channel *c)
{
return bufchain_size(&c->v.v2.outbuffer);
}
+static void ssh2_try_send_and_unthrottle(struct ssh_channel *c)
+{
+ int bufsize;
+ if (c->closes)
+ return; /* don't send on closing channels */
+ bufsize = ssh2_try_send(c);
+ if (bufsize == 0) {
+ switch (c->type) {
+ case CHAN_MAINSESSION:
+ /* stdin need not receive an unthrottle
+ * notification since it will be polled */
+ break;
+ case CHAN_X11:
+ x11_unthrottle(c->u.x11.s);
+ break;
+ case CHAN_AGENT:
+ /* agent sockets are request/response and need no
+ * buffer management */
+ break;
+ case CHAN_SOCKDATA:
+ pfd_unthrottle(c->u.pfd.s);
+ break;
+ }
+ }
+}
+
/*
- * Potentially enlarge the window on an SSH2 channel.
+ * Potentially enlarge the window on an SSH-2 channel.
*/
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
{
unsigned i = ssh_pkt_getuint32(pktin);
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
- if (c && !c->closes)
+ if (c && !c->closes) {
c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
+ ssh2_try_send_and_unthrottle(c);
+ }
}
static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
struct Packet *pktout;
c = find234(ssh->channels, &i, ssh_channelfind);
- if (!c || ((int)c->remoteid) == -1) {
+ if (!c || c->halfopen) {
bombout(("Received CHANNEL_CLOSE for %s channel %d\n",
c ? "half-open" : "nonexistent", i));
return;
* not running in -N mode.)
*/
if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) {
- logevent("All channels closed. Disconnecting");
-#if 0
/*
* We used to send SSH_MSG_DISCONNECT here,
* because I'd believed that _every_ conforming
- * SSH2 connection had to end with a disconnect
+ * SSH-2 connection had to end with a disconnect
* being sent by at least one side; apparently
* I was wrong and it's perfectly OK to
* unceremoniously slam the connection shut
* this is more polite than sending a
* DISCONNECT. So now we don't.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "All open channels closed");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
-#endif
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);
}
}
if (c->type != CHAN_SOCKDATA_DORMANT)
return; /* dunno why they're confirming this */
c->remoteid = ssh_pkt_getuint32(pktin);
+ c->halfopen = FALSE;
c->type = CHAN_SOCKDATA;
c->v.v2.remwindow = ssh_pkt_getuint32(pktin);
c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
unsigned reason_code;
char *reason_string;
int reason_length;
- char *message;
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
if (!c)
if (reason_code >= lenof(reasons))
reason_code = 0; /* ensure reasons[reason_code] in range */
ssh_pkt_getstring(pktin, &reason_string, &reason_length);
- message = dupprintf("Forwarded connection refused by"
- " server: %s [%.*s]", reasons[reason_code],
- reason_length, reason_string);
- logevent(message);
- sfree(message);
+ logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
+ reasons[reason_code], reason_length, reason_string);
pfd_close(c->u.pfd.s);
*/
c = find234(ssh->channels, &localid, ssh_channelfind);
if (!c) {
- char buf[80];
- sprintf(buf, "Received channel request for nonexistent"
- " channel %d", localid);
- logevent(buf);
- pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(pktout, buf);
- ssh2_pkt_addstring(pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, pktout);
- connection_fatal(ssh->frontend, "%s", buf);
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ char *buf = dupprintf("Received channel request for nonexistent"
+ " channel %d", localid);
+ ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+ sfree(buf);
return;
}
}
}
} else if (typelen == 22 &&
- !memcmp(type, "auth-agent@openssh.com", 3)) {
+ !memcmp(type, "auth-agent@openssh.com", 22)) {
if (!ssh->agentfwd_enabled)
error = "Agent forwarding is not enabled";
else {
}
c->remoteid = remid;
+ c->halfopen = FALSE;
if (error) {
pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_FAILURE);
ssh2_pkt_adduint32(pktout, c->remoteid);
}
/*
- * Handle the SSH2 userauth and connection layers.
+ * Buffer banner messages for later display at some convenient point.
+ */
+static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
+{
+ /* Arbitrary limit to prevent unbounded inflation of buffer */
+ if (bufchain_size(&ssh->banner) <= 131072) {
+ char *banner = NULL;
+ int size = 0;
+ ssh_pkt_getstring(pktin, &banner, &size);
+ if (banner)
+ bufchain_add(&ssh->banner, banner, size);
+ }
+}
+
+/* Helper function to deal with sending tty modes for "pty-req" */
+static void ssh2_send_ttymode(void *data, char *mode, char *val)
+{
+ struct Packet *pktout = (struct Packet *)data;
+ int i = 0;
+ unsigned int arg = 0;
+ while (strcmp(mode, ssh_ttymodes[i].mode) != 0) i++;
+ if (i == lenof(ssh_ttymodes)) return;
+ switch (ssh_ttymodes[i].type) {
+ case TTY_OP_CHAR:
+ arg = ssh_tty_parse_specchar(val);
+ break;
+ case TTY_OP_BOOL:
+ arg = ssh_tty_parse_boolean(val);
+ break;
+ }
+ ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode);
+ ssh2_pkt_adduint32(pktout, arg);
+}
+
+/*
+ * Handle the SSH-2 userauth and connection layers.
*/
static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
struct Packet *pktin)
AUTH_TYPE_KEYBOARD_INTERACTIVE,
AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
} type;
+ int done_service_req;
int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter;
int tried_pubkey_config, tried_agent;
int kbd_inter_running, kbd_inter_refused;
crBegin(ssh->do_ssh2_authconn_crstate);
- /*
- * Request userauth protocol, and await a response to it.
- */
- s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
- ssh2_pkt_addstring(s->pktout, "ssh-userauth");
- ssh2_pkt_send(ssh, s->pktout);
- crWaitUntilV(pktin);
- if (pktin->type != SSH2_MSG_SERVICE_ACCEPT) {
- bombout(("Server refused user authentication protocol"));
- crStopV;
+ s->done_service_req = FALSE;
+ s->we_are_in = FALSE;
+ if (!ssh->cfg.ssh_no_userauth) {
+ /*
+ * Request userauth protocol, and await a response to it.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
+ ssh2_pkt_addstring(s->pktout, "ssh-userauth");
+ ssh2_pkt_send(ssh, s->pktout);
+ crWaitUntilV(pktin);
+ if (pktin->type == SSH2_MSG_SERVICE_ACCEPT)
+ s->done_service_req = TRUE;
+ }
+ if (!s->done_service_req) {
+ /*
+ * Request connection protocol directly, without authentication.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
+ ssh2_pkt_addstring(s->pktout, "ssh-connection");
+ ssh2_pkt_send(ssh, s->pktout);
+ crWaitUntilV(pktin);
+ if (pktin->type == SSH2_MSG_SERVICE_ACCEPT) {
+ s->we_are_in = TRUE; /* no auth required */
+ } else {
+ bombout(("Server refused service request"));
+ crStopV;
+ }
}
/*
*/
s->username[0] = '\0';
s->got_username = FALSE;
- do {
+ bufchain_init(&ssh->banner);
+ ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] =
+ ssh2_msg_userauth_banner;
+ while (!s->we_are_in) {
/*
* Get a username.
*/
* get_line failed to get a username.
* Terminate.
*/
- logevent("No username provided. Abandoning session.");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
crStopV;
}
} else {
*/
if (!s->gotit)
crWaitUntilV(pktin);
- while (pktin->type == SSH2_MSG_USERAUTH_BANNER) {
- char *banner;
- int size;
+ /*
+ * Now is a convenient point to spew any banner material
+ * that we've accumulated. (This should ensure that when
+ * we exit the auth loop, we haven't any left to deal
+ * with.)
+ */
+ {
+ int size = bufchain_size(&ssh->banner);
/*
* Don't show the banner if we're operating in
* non-verbose non-interactive mode. (It's probably
* the banner will screw up processing on the
* output of (say) plink.)
*/
- if (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE)) {
- ssh_pkt_getstring(pktin, &banner, &size);
- if (banner)
- c_write_untrusted(ssh, banner, size);
+ if (size && (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) {
+ char *banner = snewn(size, char);
+ bufchain_fetch(&ssh->banner, banner, size);
+ c_write_untrusted(ssh, banner, size);
+ sfree(banner);
}
- crWaitUntilV(pktin);
+ bufchain_clear(&ssh->banner);
}
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
logevent("Access granted");
s->p = s->response + 5;
s->nkeys = GET_32BIT(s->p);
s->p += 4;
- {
- char buf[64];
- sprintf(buf, "Pageant has %d SSH2 keys", s->nkeys);
- logevent(buf);
- }
+ logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
void *vret;
- {
- char buf[64];
- sprintf(buf, "Trying Pageant key #%d", s->keyi);
- logevent(buf);
- }
+ logeventf(ssh, "Trying Pageant key #%d", s->keyi);
s->pklen = GET_32BIT(s->p);
s->p += 4;
if (s->publickey_blob &&
continue;
}
+ c_write_str(ssh, "Using keyboard-interactive authentication.\r\n");
s->kbd_inter_running = TRUE;
s->curr_prompt = 0;
}
* command line which has already failed to
* work). Terminate.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout,SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "No more passwords available"
- " to try");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
- logevent("Unable to authenticate");
- connection_fatal(ssh->frontend,
- "Unable to authenticate");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate",
+ SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ FALSE);
crStopV;
}
} else {
}
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
} else {
- c_write_str(ssh, "No supported authentication methods"
- " left to try!\r\n");
- logevent("No supported authentications offered."
- " Disconnecting");
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "No supported authentication"
- " methods available");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL,
+ "No supported authentication methods available",
+ SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+ FALSE);
crStopV;
}
}
- } while (!s->we_are_in);
+ }
+ ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL;
/*
- * Now we're authenticated for the connection protocol. The
- * connection protocol will automatically have started at this
- * point; there's no need to send SERVICE_REQUEST.
+ * Now the connection protocol has started, one way or another.
*/
ssh->channels = newtree234(ssh_channelcmp);
crStopV;
}
ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
+ ssh->mainchan->halfopen = FALSE;
ssh->mainchan->type = CHAN_MAINSESSION;
ssh->mainchan->closes = 0;
ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
ssh2_pkt_addbool(s->pktout, 1); /* want reply */
ssh2_pkt_addbool(s->pktout, 0); /* many connections */
ssh2_pkt_addstring(s->pktout, proto);
+ /*
+ * Note that while we blank the X authentication data here, we don't
+ * take any special action to blank the start of an X11 channel,
+ * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection
+ * without having session blanking enabled is likely to leak your
+ * cookie into the log.
+ */
+ dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
ssh2_pkt_addstring(s->pktout, data);
+ end_log_omission(ssh, s->pktout);
ssh2_pkt_adduint32(s->pktout, x11_get_screen_number(ssh->cfg.x11_display));
ssh2_pkt_send(ssh, s->pktout);
ssh2_pkt_adduint32(s->pktout, 0); /* pixel width */
ssh2_pkt_adduint32(s->pktout, 0); /* pixel height */
ssh2_pkt_addstring_start(s->pktout);
- ssh2_pkt_addbyte(s->pktout, 128); /* TTY_OP_ISPEED */
+ parse_ttymodes(ssh, ssh->cfg.ttymodes,
+ ssh2_send_ttymode, (void *)s->pktout);
+ ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_ISPEED);
ssh2_pkt_adduint32(s->pktout, ssh->ispeed);
- ssh2_pkt_addbyte(s->pktout, 129); /* TTY_OP_OSPEED */
+ ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_OSPEED);
ssh2_pkt_adduint32(s->pktout, ssh->ospeed);
ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */
ssh2_pkt_send(ssh, s->pktout);
} else {
subsys = ssh->cfg.ssh_subsys;
cmd = ssh->cfg.remote_cmd_ptr;
+ if (!cmd) cmd = ssh->cfg.remote_cmd;
}
s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
/*
* Try to send data on all channels if we can.
*/
- for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) {
- int bufsize;
- if (c->closes)
- continue; /* don't send on closing channels */
- bufsize = ssh2_try_send(c);
- if (bufsize == 0) {
- switch (c->type) {
- case CHAN_MAINSESSION:
- /* stdin need not receive an unthrottle
- * notification since it will be polled */
- break;
- case CHAN_X11:
- x11_unthrottle(c->u.x11.s);
- break;
- case CHAN_AGENT:
- /* agent sockets are request/response and need no
- * buffer management */
- break;
- case CHAN_SOCKDATA:
- pfd_unthrottle(c->u.pfd.s);
- break;
- }
- }
- }
+ for (i = 0; NULL != (c = index234(ssh->channels, i)); i++)
+ ssh2_try_send_and_unthrottle(c);
}
}
}
/*
- * Handlers for SSH2 messages that might arrive at any moment.
+ * Handlers for SSH-2 messages that might arrive at any moment.
*/
-void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin)
{
/* log reason code in disconnect message */
char *buf, *msg;
sfree(buf);
}
-void ssh2_msg_debug(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin)
{
/* log the debug message */
- char *buf, *msg;
+ char *msg;
int msglen;
int always_display;
always_display = ssh2_pkt_getbool(pktin);
ssh_pkt_getstring(pktin, &msg, &msglen);
- buf = dupprintf("Remote debug message: %.*s", msglen, msg);
- logevent(buf);
- sfree(buf);
+ logeventf(ssh, "Remote debug message: %.*s", msglen, msg);
}
-void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin)
+static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin)
{
struct Packet *pktout;
pktout = ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED);
}
/*
- * Handle the top-level SSH2 protocol.
+ * Handle the top-level SSH-2 protocol.
*/
static void ssh2_protocol_setup(Ssh ssh)
{
* These special message types we install handlers for.
*/
ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect;
- ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with ssh1 */
+ ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with SSH-1 */
ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug;
}
{
Ssh ssh = (Ssh)ctx;
+ if (ssh->state == SSH_STATE_CLOSED)
+ return;
+
if (!ssh->kex_in_progress && ssh->cfg.ssh_rekey_time != 0 &&
now - ssh->next_rekey >= 0) {
do_ssh2_transport(ssh, "timeout", -1, NULL);
ssh->hostkey = NULL;
ssh->exitcode = -1;
ssh->close_expected = FALSE;
+ ssh->clean_exit = FALSE;
ssh->state = SSH_STATE_PREPACKET;
ssh->size_needed = FALSE;
ssh->eof_needed = FALSE;
ssh->queueing = FALSE;
ssh->qhead = ssh->qtail = NULL;
ssh->deferred_rekey_reason = NULL;
+ bufchain_init(&ssh->queued_incoming_data);
+ ssh->frozen = FALSE;
*backend_handle = ssh;
expire_timer_context(ssh);
if (ssh->pinger)
pinger_free(ssh->pinger);
+ bufchain_clear(&ssh->queued_incoming_data);
sfree(ssh);
random_unref();
unsigned long old_max_data_size;
pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);
- ssh_setup_portfwd(ssh, cfg);
+ if (ssh->portfwds)
+ ssh_setup_portfwd(ssh, cfg);
if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time &&
cfg->ssh_rekey_time != 0) {
}
/*
- * Called to send data down the Telnet connection.
+ * Called to send data down the SSH connection.
*/
static int ssh_send(void *handle, char *buf, int len)
{
}
/*
- * Send Telnet special codes. TS_EOF is useful for `plink', so you
+ * Send special codes. TS_EOF is useful for `plink', so you
* can send an EOF and collect resulting output (e.g. `plink
* hostname sort').
*/
struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);
ssh2_pkt_send(ssh, pktout);
+ ssh->send_ok = 0; /* now stop trying to read from stdin */
}
logevent("Sent EOF message");
} else if (code == TS_PING || code == TS_NOP) {
if (ssh->state == SSH_STATE_CLOSED
|| ssh->state == SSH_STATE_PREPACKET) return;
if (ssh->version == 1) {
- logevent("Unable to send BREAK signal in SSH1");
+ logevent("Unable to send BREAK signal in SSH-1");
} else if (ssh->mainchan) {
pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);
c->ssh = ssh;
if (c) {
- c->remoteid = -1; /* to be set when open confirmed */
+ c->halfopen = TRUE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */