#define TRUE 1
#endif
-#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_SMSG_X11_OPEN 27 /* 0x1b */
-#define SSH1_CMSG_PORT_FORWARD_REQUEST 28 /* 0x1c */
-#define SSH1_MSG_PORT_OPEN 29 /* 0x1d */
-#define SSH1_CMSG_AGENT_REQUEST_FORWARDING 30 /* 0x1e */
-#define SSH1_SMSG_AGENT_OPEN 31 /* 0x1f */
-#define SSH1_MSG_IGNORE 32 /* 0x20 */
-#define SSH1_CMSG_EXIT_CONFIRMATION 33 /* 0x21 */
-#define SSH1_CMSG_X11_REQUEST_FORWARDING 34 /* 0x22 */
-#define SSH1_CMSG_AUTH_RHOSTS_RSA 35 /* 0x23 */
-#define SSH1_MSG_DEBUG 36 /* 0x24 */
-#define SSH1_CMSG_REQUEST_COMPRESSION 37 /* 0x25 */
-#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_RHOSTS 1 /* 0x1 */
-#define SSH1_AUTH_RSA 2 /* 0x2 */
-#define SSH1_AUTH_PASSWORD 3 /* 0x3 */
-#define SSH1_AUTH_RHOSTS_RSA 4 /* 0x4 */
-#define SSH1_AUTH_TIS 5 /* 0x5 */
-#define SSH1_AUTH_CCARD 16 /* 0x10 */
-
-#define SSH1_PROTOFLAG_SCREEN_NUMBER 1 /* 0x1 */
-/* Mask for protoflags we will echo back to server if seen */
-#define SSH1_PROTOFLAGS_SUPPORTED 0 /* 0x1 */
-
-#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_KEX_DH_GEX_REQUEST 30 /* 0x1e */
-#define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */
-#define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */
-#define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */
-#define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */
-#define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */
-#define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */
-#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_USERAUTH_INFO_REQUEST 60 /* 0x3c */
-#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 /* 0x3d */
-#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_MSG_USERAUTH_GSSAPI_RESPONSE 60
-#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61
-#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63
-#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64
-#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65
-#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66
-
/*
* Packet type contexts, so that ssh2_pkt_type can correctly decode
* the ambiguous type numbers back into the correct type strings.
SSH2_PKTCTX_KBDINTER
} Pkt_ACtx;
-#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 /* 0x1 */
-#define SSH2_DISCONNECT_PROTOCOL_ERROR 2 /* 0x2 */
-#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 /* 0x3 */
-#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 /* 0x4 */
-#define SSH2_DISCONNECT_MAC_ERROR 5 /* 0x5 */
-#define SSH2_DISCONNECT_COMPRESSION_ERROR 6 /* 0x6 */
-#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 /* 0x7 */
-#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 /* 0x8 */
-#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 /* 0x9 */
-#define SSH2_DISCONNECT_CONNECTION_LOST 10 /* 0xa */
-#define SSH2_DISCONNECT_BY_APPLICATION 11 /* 0xb */
-#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 /* 0xc */
-#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 /* 0xd */
-#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 /* 0xe */
-#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 /* 0xf */
-
static const char *const ssh2_disconnect_reasons[] = {
NULL,
"host not allowed to connect",
"illegal user name",
};
-#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 */
-
/*
* Various remote-bug flags.
*/
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_adddata(struct Packet *, const 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 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 void ssh_pkt_addstring_str(struct Packet *, const char *data);
+static void ssh_pkt_addstring_data(struct Packet *, const char *data, int len);
+static void ssh_pkt_addstring(struct Packet *, const 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);
const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
const static struct ssh_mac *macs[] = {
- &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
+ &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
};
const static struct ssh_mac *buggymacs[] = {
&ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
struct ssh_rportfwd {
unsigned sport, dport;
- char dhost[256];
+ char *shost, *dhost;
char *sportdesc;
struct ssh_portfwd *pfrec;
};
-#define free_rportfwd(pf) ( \
- ((pf) ? (sfree((pf)->sportdesc)) : (void)0 ), sfree(pf) )
+
+static void free_rportfwd(struct ssh_rportfwd *pf)
+{
+ if (pf) {
+ sfree(pf->sportdesc);
+ sfree(pf->shost);
+ sfree(pf->dhost);
+ sfree(pf);
+ }
+}
/*
* Separately to the rportfwd tree (which is for looking up port
void *cs_comp_ctx, *sc_comp_ctx;
const struct ssh_kex *kex;
const struct ssh_signkey *hostkey;
+ char *hostkey_str; /* string representation, for easy checking in rekeys */
unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN];
int v2_session_id_len;
void *kex_ctx;
{
struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
-
+ int i;
+ if ( (i = strcmp(a->shost, b->shost)) != 0)
+ return i < 0 ? -1 : +1;
if (a->sport > b->sport)
return +1;
if (a->sport < b->sport)
/* See if that gives us a valid packet. */
if (ssh->scmac->verresult(ssh->sc_mac_ctx,
st->pktin->data + st->packetlen) &&
- (st->len = GET_32BIT(st->pktin->data)) + 4 == st->packetlen)
+ ((st->len = toint(GET_32BIT(st->pktin->data))) ==
+ st->packetlen-4))
break;
if (st->packetlen >= OUR_V2_PACKETLIMIT) {
bombout(("No valid incoming packet found"));
/*
* Now get the length figure.
*/
- st->len = GET_32BIT(st->pktin->data);
+ st->len = toint(GET_32BIT(st->pktin->data));
/*
* _Completely_ silly lengths should be stomped on before they
if (body) pkt->body = pkt->data + offset;
}
}
-static void ssh_pkt_adddata(struct Packet *pkt, void *data, int len)
+static void ssh_pkt_adddata(struct Packet *pkt, const void *data, int len)
{
if (pkt->logmode != PKTLOG_EMIT) {
pkt->nblanks++;
ssh_pkt_adduint32(pkt, 0);
pkt->savedpos = pkt->length;
}
-static void ssh_pkt_addstring_str(struct Packet *pkt, char *data)
+static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data)
{
ssh_pkt_adddata(pkt, data, strlen(data));
PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
}
-static void ssh_pkt_addstring_data(struct Packet *pkt, char *data, int len)
+static void ssh_pkt_addstring_data(struct Packet *pkt, const char *data,
+ int len)
{
ssh_pkt_adddata(pkt, data, len);
PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
}
-static void ssh_pkt_addstring(struct Packet *pkt, char *data)
+static void ssh_pkt_addstring(struct Packet *pkt, const char *data)
{
ssh_pkt_addstring_start(pkt);
ssh_pkt_addstring_str(pkt, data);
*length = 0;
if (pkt->length - pkt->savedpos < 4)
return;
- len = GET_32BIT(pkt->body + pkt->savedpos);
+ len = toint(GET_32BIT(pkt->body + pkt->savedpos));
if (len < 0)
return;
*length = len;
* See if this is in fact an ssh-rsa signature and a buggy
* server; otherwise we can just do this the easy way.
*/
- if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) &&
+ if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) && pkblob_len > 4+7+4 &&
(GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) {
int pos, len, siglen;
*/
pos = 4+7; /* skip over "ssh-rsa" */
- pos += 4 + GET_32BIT(pkblob+pos); /* skip over exponent */
- len = GET_32BIT(pkblob+pos); /* find length of modulus */
+ len = toint(GET_32BIT(pkblob+pos)); /* get length of exponent */
+ if (len < 0 || len > pkblob_len - pos - 4)
+ goto give_up;
+ pos += 4 + len; /* skip over exponent */
+ if (pkblob_len - pos < 4)
+ goto give_up;
+ len = toint(GET_32BIT(pkblob+pos)); /* find length of modulus */
+ if (len < 0 || len > pkblob_len - pos - 4)
+ goto give_up;
pos += 4; /* find modulus itself */
while (len > 0 && pkblob[pos] == 0)
len--, pos++;
* Now find the signature integer.
*/
pos = 4+7; /* skip over "ssh-rsa" */
- siglen = GET_32BIT(sigblob+pos);
+ if (sigblob_len < pos+4)
+ goto give_up;
+ siglen = toint(GET_32BIT(sigblob+pos));
+ if (siglen != sigblob_len - pos - 4)
+ goto give_up;
/* debug(("signature length is %d\n", siglen)); */
if (len != siglen) {
return;
}
- /* Otherwise fall through and do it the easy way. */
+ /* Otherwise fall through and do it the easy way. We also come
+ * here as a fallback if we discover above that the key blob
+ * is misformatted in some way. */
+ give_up:;
}
ssh2_pkt_addstring_start(pkt);
{
int i, j, ret;
unsigned char cookie[8], *ptr;
- struct RSAKey servkey, hostkey;
struct MD5Context md5c;
struct do_ssh1_login_state {
int crLine;
int commentlen;
int dlgret;
Filename *keyfile;
+ struct RSAKey servkey, hostkey;
};
crState(do_ssh1_login_state);
}
memcpy(cookie, ptr, 8);
- if (!ssh1_pkt_getrsakey(pktin, &servkey, &s->keystr1) ||
- !ssh1_pkt_getrsakey(pktin, &hostkey, &s->keystr2)) {
+ if (!ssh1_pkt_getrsakey(pktin, &s->servkey, &s->keystr1) ||
+ !ssh1_pkt_getrsakey(pktin, &s->hostkey, &s->keystr2)) {
bombout(("Failed to read SSH-1 public keys from public key packet"));
crStop(0);
}
char logmsg[80];
logevent("Host key fingerprint is:");
strcpy(logmsg, " ");
- hostkey.comment = NULL;
+ s->hostkey.comment = NULL;
rsa_fingerprint(logmsg + strlen(logmsg),
- sizeof(logmsg) - strlen(logmsg), &hostkey);
+ sizeof(logmsg) - strlen(logmsg), &s->hostkey);
logevent(logmsg);
}
ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER;
MD5Init(&md5c);
- MD5Update(&md5c, s->keystr2, hostkey.bytes);
- MD5Update(&md5c, s->keystr1, servkey.bytes);
+ MD5Update(&md5c, s->keystr2, s->hostkey.bytes);
+ MD5Update(&md5c, s->keystr1, s->servkey.bytes);
MD5Update(&md5c, cookie, 8);
MD5Final(s->session_id, &md5c);
/*
* Verify that the `bits' and `bytes' parameters match.
*/
- if (hostkey.bits > hostkey.bytes * 8 ||
- servkey.bits > servkey.bytes * 8) {
+ if (s->hostkey.bits > s->hostkey.bytes * 8 ||
+ s->servkey.bits > s->servkey.bytes * 8) {
bombout(("SSH-1 public keys were badly formatted"));
crStop(0);
}
- s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
+ s->len = (s->hostkey.bytes > s->servkey.bytes ?
+ s->hostkey.bytes : s->servkey.bytes);
s->rsabuf = snewn(s->len, unsigned char);
/*
* First format the key into a string.
*/
- int len = rsastr_len(&hostkey);
+ int len = rsastr_len(&s->hostkey);
char fingerprint[100];
char *keystr = snewn(len, char);
- rsastr_fmt(keystr, &hostkey);
- rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
+ rsastr_fmt(keystr, &s->hostkey);
+ rsa_fingerprint(fingerprint, sizeof(fingerprint), &s->hostkey);
ssh_set_frozen(ssh, 1);
s->dlgret = verify_ssh_host_key(ssh->frontend,
s->rsabuf[i] ^= s->session_id[i];
}
- if (hostkey.bytes > servkey.bytes) {
- ret = rsaencrypt(s->rsabuf, 32, &servkey);
+ if (s->hostkey.bytes > s->servkey.bytes) {
+ ret = rsaencrypt(s->rsabuf, 32, &s->servkey);
if (ret)
- ret = rsaencrypt(s->rsabuf, servkey.bytes, &hostkey);
+ ret = rsaencrypt(s->rsabuf, s->servkey.bytes, &s->hostkey);
} else {
- ret = rsaencrypt(s->rsabuf, 32, &hostkey);
+ ret = rsaencrypt(s->rsabuf, 32, &s->hostkey);
if (ret)
- ret = rsaencrypt(s->rsabuf, hostkey.bytes, &servkey);
+ ret = rsaencrypt(s->rsabuf, s->hostkey.bytes, &s->servkey);
}
if (!ret) {
bombout(("SSH-1 public key encryptions failed due to bad formatting"));
ssh->crcda_ctx = crcda_make_context();
logevent("Installing CRC compensation attack detector");
- if (servkey.modulus) {
- sfree(servkey.modulus);
- servkey.modulus = NULL;
+ if (s->servkey.modulus) {
+ sfree(s->servkey.modulus);
+ s->servkey.modulus = NULL;
}
- if (servkey.exponent) {
- sfree(servkey.exponent);
- servkey.exponent = NULL;
+ if (s->servkey.exponent) {
+ sfree(s->servkey.exponent);
+ s->servkey.exponent = NULL;
}
- if (hostkey.modulus) {
- sfree(hostkey.modulus);
- hostkey.modulus = NULL;
+ if (s->hostkey.modulus) {
+ sfree(s->hostkey.modulus);
+ s->hostkey.modulus = NULL;
}
- if (hostkey.exponent) {
- sfree(hostkey.exponent);
- hostkey.exponent = NULL;
+ if (s->hostkey.exponent) {
+ sfree(s->hostkey.exponent);
+ s->hostkey.exponent = NULL;
}
crWaitUntil(pktin);
if (s->response && s->responselen >= 5 &&
s->response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
s->p = s->response + 5;
- s->nkeys = GET_32BIT(s->p);
+ s->nkeys = toint(GET_32BIT(s->p));
+ if (s->nkeys < 0) {
+ logeventf(ssh, "Pageant reported negative key count %d",
+ s->nkeys);
+ s->nkeys = 0;
+ }
s->p += 4;
logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys);
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
int n, ok = FALSE;
do { /* do while (0) to make breaking easy */
n = ssh1_read_bignum
- (s->p, s->responselen-(s->p-s->response),
+ (s->p, toint(s->responselen-(s->p-s->response)),
&s->key.exponent);
if (n < 0)
break;
s->p += n;
n = ssh1_read_bignum
- (s->p, s->responselen-(s->p-s->response),
+ (s->p, toint(s->responselen-(s->p-s->response)),
&s->key.modulus);
if (n < 0)
- break;
+ break;
s->p += n;
if (s->responselen - (s->p-s->response) < 4)
break;
- s->commentlen = GET_32BIT(s->p);
+ s->commentlen = toint(GET_32BIT(s->p));
s->p += 4;
- if (s->responselen - (s->p-s->response) <
+ if (s->commentlen < 0 ||
+ toint(s->responselen - (s->p-s->response)) <
s->commentlen)
break;
s->commentp = (char *)s->p;
ssh_channel_try_eof(c);
}
-void sshfwd_unclean_close(struct ssh_channel *c)
+void sshfwd_unclean_close(struct ssh_channel *c, const char *err)
{
Ssh ssh = c->ssh;
switch (c->type) {
case CHAN_X11:
x11_close(c->u.x11.s);
- logevent("Forwarded X11 connection terminated due to local error");
+ logeventf(ssh, "Forwarded X11 connection terminated due to local "
+ "error: %s", err);
break;
case CHAN_SOCKDATA:
case CHAN_SOCKDATA_DORMANT:
pfd_close(c->u.pfd.s);
- logevent("Forwarded port closed due to local error");
+ logeventf(ssh, "Forwarded port closed due to local error: %s", err);
break;
}
c->type = CHAN_ZOMBIE;
+ c->pending_eof = FALSE; /* this will confuse a zombie channel */
ssh2_channel_check_close(c);
}
}
pf = snew(struct ssh_rportfwd);
- strncpy(pf->dhost, epf->daddr, lenof(pf->dhost)-1);
- pf->dhost[lenof(pf->dhost)-1] = '\0';
+ pf->dhost = dupstr(epf->daddr);
pf->dport = epf->dport;
+ if (epf->saddr) {
+ pf->shost = dupstr(epf->saddr);
+ } else if (conf_get_int(conf, CONF_rport_acceptall)) {
+ pf->shost = dupstr("");
+ } else {
+ pf->shost = dupstr("localhost");
+ }
pf->sport = epf->sport;
if (add234(ssh->rportfwds, pf) != pf) {
logeventf(ssh, "Duplicate remote port forwarding to %s:%d",
pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
ssh2_pkt_addstring(pktout, "tcpip-forward");
ssh2_pkt_addbool(pktout, 1);/* want reply */
- if (epf->saddr) {
- ssh2_pkt_addstring(pktout, epf->saddr);
- } else if (conf_get_int(conf, CONF_rport_acceptall)) {
- ssh2_pkt_addstring(pktout, "");
- } else {
- ssh2_pkt_addstring(pktout, "localhost");
- }
- ssh2_pkt_adduint32(pktout, epf->sport);
+ ssh2_pkt_addstring(pktout, pf->shost);
+ ssh2_pkt_adduint32(pktout, pf->sport);
ssh2_pkt_send(ssh, pktout);
ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS,
{
/* Remote side is trying to open a channel to talk to a
* forwarded port. Give them back a local channel number. */
- struct ssh_channel *c;
struct ssh_rportfwd pf, *pfp;
int remoteid;
int hostsize, port;
char *host;
const char *e;
- c = snew(struct ssh_channel);
- c->ssh = ssh;
remoteid = ssh_pkt_getuint32(pktin);
ssh_pkt_getstring(pktin, &host, &hostsize);
port = ssh_pkt_getuint32(pktin);
- if (hostsize >= lenof(pf.dhost))
- hostsize = lenof(pf.dhost)-1;
- memcpy(pf.dhost, host, hostsize);
- pf.dhost[hostsize] = '\0';
+ pf.dhost = dupprintf(".*s", hostsize, host);
pf.dport = port;
pfp = find234(ssh->rportfwds, &pf, NULL);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
PKT_INT, remoteid, PKT_END);
} else {
+ struct ssh_channel *c = snew(struct ssh_channel);
+ c->ssh = ssh;
+
logeventf(ssh, "Received remote port open request for %s:%d",
pf.dhost, port);
e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port,
logevent("Forwarded port opened successfully");
}
}
+
+ sfree(pf.dhost);
}
static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
}
}
/* List server host key algorithms. */
- ssh2_pkt_addstring_start(s->pktout);
- for (i = 0; i < lenof(hostkey_algs); i++) {
- ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name);
- if (i < lenof(hostkey_algs) - 1)
- ssh2_pkt_addstring_str(s->pktout, ",");
- }
+ if (!s->got_session_id) {
+ /*
+ * In the first key exchange, we list all the algorithms
+ * we're prepared to cope with.
+ */
+ ssh2_pkt_addstring_start(s->pktout);
+ for (i = 0; i < lenof(hostkey_algs); i++) {
+ ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name);
+ if (i < lenof(hostkey_algs) - 1)
+ ssh2_pkt_addstring_str(s->pktout, ",");
+ }
+ } else {
+ /*
+ * In subsequent key exchanges, we list only the kex
+ * algorithm that was selected in the first key exchange,
+ * so that we keep getting the same host key and hence
+ * don't have to interrupt the user's session to ask for
+ * reverification.
+ */
+ assert(ssh->kex);
+ ssh2_pkt_addstring(s->pktout, ssh->hostkey->name);
+ }
/* List encryption algorithms (client->server then server->client). */
for (k = 0; k < 2; k++) {
ssh2_pkt_addstring_start(s->pktout);
ssh_pkt_getstring(pktin, &str, &len); /* server->client language */
s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok;
+ ssh->exhash = ssh->kex->hash->init();
+ hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c));
+ hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s));
+ hash_string(ssh->kex->hash, ssh->exhash,
+ s->our_kexinit, s->our_kexinitlen);
+ sfree(s->our_kexinit);
+ if (pktin->length > 5)
+ hash_string(ssh->kex->hash, ssh->exhash,
+ pktin->data + 5, pktin->length - 5);
+
if (s->warn_kex) {
ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",
}
}
- ssh->exhash = ssh->kex->hash->init();
- hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c));
- hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s));
- hash_string(ssh->kex->hash, ssh->exhash,
- s->our_kexinit, s->our_kexinitlen);
- sfree(s->our_kexinit);
- if (pktin->length > 5)
- hash_string(ssh->kex->hash, ssh->exhash,
- pktin->data + 5, pktin->length - 5);
-
if (s->ignorepkt) /* first_kex_packet_follows */
crWaitUntilV(pktin); /* Ignore packet */
}
crStopV;
}
- /*
- * Authenticate remote host: verify host key. (We've already
- * checked the signature of the exchange hash.)
- */
s->keystr = ssh->hostkey->fmtkey(s->hkey);
- s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
- 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 {
- crReturnV;
- if (pktin) {
- bombout(("Unexpected data from server while waiting"
- " for user host key response"));
+ if (!s->got_session_id) {
+ /*
+ * Authenticate remote host: verify host key. (We've already
+ * checked the signature of the exchange hash.)
+ */
+ s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
+ 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 {
+ crReturnV;
+ if (pktin) {
+ bombout(("Unexpected data from server while waiting"
+ " for user host key response"));
crStopV;
- }
- } 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);
- crStopV;
- }
- if (!s->got_session_id) { /* don't bother logging this in rekeys */
- logevent("Host key fingerprint is:");
- logevent(s->fingerprint);
+ }
+ } 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);
+ crStopV;
+ }
+ logevent("Host key fingerprint is:");
+ logevent(s->fingerprint);
+ sfree(s->fingerprint);
+ /*
+ * Save this host key, to check against the one presented in
+ * subsequent rekeys.
+ */
+ ssh->hostkey_str = s->keystr;
+ } else {
+ /*
+ * In a rekey, we never present an interactive host key
+ * verification request to the user. Instead, we simply
+ * enforce that the key we're seeing this time is identical to
+ * the one we saw before.
+ */
+ if (strcmp(ssh->hostkey_str, s->keystr)) {
+ bombout(("Host key was different in repeat key exchange"));
+ crStopV;
+ }
+ sfree(s->keystr);
}
- sfree(s->fingerprint);
- sfree(s->keystr);
ssh->hostkey->freekey(s->hkey);
/*
Ssh ssh = c->ssh;
struct Packet *pktout;
+ if (c->halfopen) {
+ /*
+ * If we've sent out our own CHANNEL_OPEN but not yet seen
+ * either OPEN_CONFIRMATION or OPEN_FAILURE in response, then
+ * it's too early to be sending close messages of any kind.
+ */
+ return;
+ }
+
if ((!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) ||
c->type == CHAN_ZOMBIE) &&
!c->v.v2.chanreq_head &&
break;
}
+ /*
+ * Abandon any buffered data we still wanted to send to this
+ * channel. Receiving a CHANNEL_CLOSE is an indication that
+ * the server really wants to get on and _destroy_ this
+ * channel, and it isn't going to send us any further
+ * WINDOW_ADJUSTs to permit us to send pending stuff.
+ */
+ bufchain_clear(&c->v.v2.outbuffer);
+
/*
* Send outgoing EOF.
*/
c = ssh2_channel_msg(ssh, pktin);
if (!c)
return;
- if (c->type != CHAN_SOCKDATA_DORMANT)
- return; /* dunno why they're confirming this */
+ assert(c->halfopen); /* ssh2_channel_msg will have enforced 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);
- if (c->u.pfd.s)
- pfd_confirm(c->u.pfd.s);
+
+ if (c->type == CHAN_SOCKDATA_DORMANT) {
+ c->type = CHAN_SOCKDATA;
+ if (c->u.pfd.s)
+ pfd_confirm(c->u.pfd.s);
+ } else if (c->type == CHAN_ZOMBIE) {
+ /*
+ * This case can occur if a local socket error occurred
+ * between us sending out CHANNEL_OPEN and receiving
+ * OPEN_CONFIRMATION. In this case, all we can do is
+ * immediately initiate close proceedings now that we know the
+ * server's id to put in the close message.
+ */
+ ssh2_channel_check_close(c);
+ } else {
+ /*
+ * We never expect to receive OPEN_CONFIRMATION for any
+ * *other* channel type (since only local-to-remote port
+ * forwardings cause us to send CHANNEL_OPEN after the main
+ * channel is live - all other auxiliary channel types are
+ * initiated from the server end). It's safe to enforce this
+ * by assertion rather than by ssh_disconnect, because the
+ * real point is that we never constructed a half-open channel
+ * structure in the first place with any type other than the
+ * above.
+ */
+ assert(!"Funny channel type in ssh2_msg_channel_open_confirmation");
+ }
+
if (c->pending_eof)
- ssh_channel_try_eof(c);
+ ssh_channel_try_eof(c); /* in case we had a pending EOF */
}
static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
char *reason_string;
int reason_length;
struct ssh_channel *c;
+
c = ssh2_channel_msg(ssh, pktin);
if (!c)
return;
- if (c->type != CHAN_SOCKDATA_DORMANT)
- return; /* dunno why they're failing this */
+ assert(c->halfopen); /* ssh2_channel_msg will have enforced this */
- reason_code = ssh_pkt_getuint32(pktin);
- if (reason_code >= lenof(reasons))
- reason_code = 0; /* ensure reasons[reason_code] in range */
- ssh_pkt_getstring(pktin, &reason_string, &reason_length);
- logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
- reasons[reason_code], reason_length, reason_string);
+ if (c->type == CHAN_SOCKDATA_DORMANT) {
+ reason_code = ssh_pkt_getuint32(pktin);
+ if (reason_code >= lenof(reasons))
+ reason_code = 0; /* ensure reasons[reason_code] in range */
+ ssh_pkt_getstring(pktin, &reason_string, &reason_length);
+ logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
+ reasons[reason_code], reason_length, reason_string);
- pfd_close(c->u.pfd.s);
+ pfd_close(c->u.pfd.s);
+ } else if (c->type == CHAN_ZOMBIE) {
+ /*
+ * This case can occur if a local socket error occurred
+ * between us sending out CHANNEL_OPEN and receiving
+ * OPEN_FAILURE. In this case, we need do nothing except allow
+ * the code below to throw the half-open channel away.
+ */
+ } else {
+ /*
+ * We never expect to receive OPEN_FAILURE for any *other*
+ * channel type (since only local-to-remote port forwardings
+ * cause us to send CHANNEL_OPEN after the main channel is
+ * live - all other auxiliary channel types are initiated from
+ * the server end). It's safe to enforce this by assertion
+ * rather than by ssh_disconnect, because the real point is
+ * that we never constructed a half-open channel structure in
+ * the first place with any type other than the above.
+ */
+ assert(!"Funny channel type in ssh2_msg_channel_open_failure");
+ }
del234(ssh->channels, c);
sfree(c);
is_int = FALSE;
} else {
int maybe_int = FALSE, maybe_str = FALSE;
-#define CHECK_HYPOTHESIS(offset, result) \
- do { \
- long q = offset; \
- if (q >= 0 && q+4 <= len) { \
- q = q + 4 + GET_32BIT(p+q); \
- if (q >= 0 && q+4 <= len && \
- ((q = q + 4 + GET_32BIT(p+q))!= 0) && q == len) \
- result = TRUE; \
- } \
- } while(0)
+#define CHECK_HYPOTHESIS(offset, result) \
+ do \
+ { \
+ int q = toint(offset); \
+ if (q >= 0 && q+4 <= len) { \
+ q = toint(q + 4 + GET_32BIT(p+q)); \
+ if (q >= 0 && q+4 <= len && \
+ ((q = toint(q + 4 + GET_32BIT(p+q))) != 0) && \
+ q == len) \
+ result = TRUE; \
+ } \
+ } while(0)
CHECK_HYPOTHESIS(4+1, maybe_int);
CHECK_HYPOTHESIS(4+num+1, maybe_str);
#undef CHECK_HYPOTHESIS
} else if (typelen == 15 &&
!memcmp(type, "forwarded-tcpip", 15)) {
struct ssh_rportfwd pf, *realpf;
- char *dummy;
- int dummylen;
- ssh_pkt_getstring(pktin, &dummy, &dummylen);/* skip address */
+ char *shost;
+ int shostlen;
+ ssh_pkt_getstring(pktin, &shost, &shostlen);/* skip address */
+ pf.shost = dupprintf("%.*s", shostlen, shost);
pf.sport = ssh_pkt_getuint32(pktin);
ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
peerport = ssh_pkt_getuint32(pktin);
realpf = find234(ssh->rportfwds, &pf, NULL);
- logeventf(ssh, "Received remote port %d open request "
- "from %s:%d", pf.sport, peeraddr, peerport);
+ logeventf(ssh, "Received remote port %s:%d open request "
+ "from %s:%d", pf.shost, pf.sport, peeraddr, peerport);
+ sfree(pf.shost);
+
if (realpf == NULL) {
error = "Remote port is not recognised";
} else {
else {
c->type = CHAN_AGENT; /* identify channel type */
c->u.a.lensofar = 0;
+ c->u.a.message = NULL;
c->u.a.outstanding_requests = 0;
}
} else {
ssh2_pkt_adduint32(pktout, ssh->x11disp->screennum);
ssh2_pkt_send(ssh, pktout);
- crWaitUntilV(pktin);
+ /* Wait to be called back with either a response packet, or NULL
+ * meaning clean up and free our data */
+ crReturnV;
if (pktin) {
if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
ssh2_setup_agent, s);
ssh2_pkt_send(ssh, pktout);
- crWaitUntilV(pktin);
+ /* Wait to be called back with either a response packet, or NULL
+ * meaning clean up and free our data */
+ crReturnV;
if (pktin) {
if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
ssh2_pkt_send(ssh, pktout);
ssh->state = SSH_STATE_INTERMED;
- crWaitUntilV(pktin);
+ /* Wait to be called back with either a response packet, or NULL
+ * meaning clean up and free our data */
+ crReturnV;
if (pktin) {
if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
s->env_left = s->num_env;
while (s->env_left > 0) {
- crWaitUntilV(pktin);
+ /* Wait to be called back with either a response packet,
+ * or NULL meaning clean up and free our data */
+ crReturnV;
if (!pktin) goto out;
if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS)
s->env_ok++;
int keyi;
unsigned char *p;
p = s->agent_response + 5;
- s->nkeys = GET_32BIT(p);
+ s->nkeys = toint(GET_32BIT(p));
+
+ /*
+ * Vet the Pageant response to ensure that the key
+ * count and blob lengths make sense.
+ */
+ if (s->nkeys < 0) {
+ logeventf(ssh, "Pageant response contained a negative"
+ " key count %d", s->nkeys);
+ s->nkeys = 0;
+ goto done_agent_query;
+ } else {
+ unsigned char *q = p + 4;
+ int lenleft = s->agent_responselen - 5 - 4;
+
+ for (keyi = 0; keyi < s->nkeys; keyi++) {
+ int bloblen, commentlen;
+ if (lenleft < 4) {
+ logeventf(ssh, "Pageant response was truncated");
+ s->nkeys = 0;
+ goto done_agent_query;
+ }
+ bloblen = toint(GET_32BIT(q));
+ if (bloblen < 0 || bloblen > lenleft) {
+ logeventf(ssh, "Pageant response was truncated");
+ s->nkeys = 0;
+ goto done_agent_query;
+ }
+ lenleft -= 4 + bloblen;
+ q += 4 + bloblen;
+ commentlen = toint(GET_32BIT(q));
+ if (commentlen < 0 || commentlen > lenleft) {
+ logeventf(ssh, "Pageant response was truncated");
+ s->nkeys = 0;
+ goto done_agent_query;
+ }
+ lenleft -= 4 + commentlen;
+ q += 4 + commentlen;
+ }
+ }
+
p += 4;
logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);
if (s->publickey_blob) {
/* See if configured key is in agent. */
for (keyi = 0; keyi < s->nkeys; keyi++) {
- s->pklen = GET_32BIT(p);
+ s->pklen = toint(GET_32BIT(p));
if (s->pklen == s->publickey_bloblen &&
!memcmp(p+4, s->publickey_blob,
s->publickey_bloblen)) {
break;
}
p += 4 + s->pklen;
- p += GET_32BIT(p) + 4; /* comment */
+ p += toint(GET_32BIT(p)) + 4; /* comment */
}
if (!s->pkblob_in_agent) {
logevent("Configured key file not in Pageant");
} else {
logevent("Failed to get reply from Pageant");
}
+ done_agent_query:;
}
}
logeventf(ssh, "Trying Pageant key #%d", s->keyi);
/* Unpack key from agent response */
- s->pklen = GET_32BIT(s->agentp);
+ s->pklen = toint(GET_32BIT(s->agentp));
s->agentp += 4;
s->pkblob = (char *)s->agentp;
s->agentp += s->pklen;
- s->alglen = GET_32BIT(s->pkblob);
+ s->alglen = toint(GET_32BIT(s->pkblob));
s->alg = s->pkblob + 4;
- s->commentlen = GET_32BIT(s->agentp);
+ s->commentlen = toint(GET_32BIT(s->agentp));
s->agentp += 4;
s->commentp = (char *)s->agentp;
s->agentp += s->commentlen;
s->ret = vret;
sfree(s->agentreq);
if (s->ret) {
- if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) {
+ if (s->retlen >= 9 &&
+ s->ret[4] == SSH2_AGENT_SIGN_RESPONSE &&
+ GET_32BIT(s->ret + 5) <= (unsigned)(s->retlen-9)) {
logevent("Sending Pageant's response");
ssh2_add_sigblob(ssh, s->pktout,
s->pkblob, s->pklen,
ssh->kex = NULL;
ssh->kex_ctx = NULL;
ssh->hostkey = NULL;
+ ssh->hostkey_str = NULL;
ssh->exitcode = -1;
ssh->close_expected = FALSE;
ssh->clean_exit = FALSE;
while (ssh->qhead) {
struct queued_handler *qh = ssh->qhead;
ssh->qhead = qh->next;
- sfree(ssh->qhead);
+ sfree(qh);
}
ssh->qhead = ssh->qtail = NULL;
sfree(ssh->v_c);
sfree(ssh->v_s);
sfree(ssh->fullhostname);
+ sfree(ssh->hostkey_str);
if (ssh->crcda_ctx) {
crcda_free_context(ssh->crcda_ctx);
ssh->crcda_ctx = NULL;