#define translate(x) if (type == x) return #x
#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
-static char *ssh1_pkt_type(int type)
+static const char *ssh1_pkt_type(int type)
{
translate(SSH1_MSG_DISCONNECT);
translate(SSH1_SMSG_PUBLIC_KEY);
translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
return "unknown";
}
-static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
+static const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx,
+ int type)
{
translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
static int ssh2_pkt_construct(Ssh, struct Packet *);
static void ssh2_pkt_send(Ssh, struct Packet *);
static void ssh2_pkt_send_noqueue(Ssh, struct Packet *);
-static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
+static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin);
-static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
+static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin);
static void ssh2_channel_check_close(struct ssh_channel *c);
static void ssh_channel_destroy(struct ssh_channel *c);
+static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin);
/*
* Buffer management constants. There are several of these for
const char *additional_log_text;
};
-static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh1_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin);
-static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh2_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin);
-static void ssh2_bare_connection_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh2_bare_connection_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin);
static void ssh1_protocol_setup(Ssh ssh);
static void ssh2_protocol_setup(Ssh ssh);
static void ssh_size(void *handle, int width, int height);
static void ssh_special(void *handle, Telnet_Special);
static int ssh2_try_send(struct ssh_channel *c);
-static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
+static void ssh2_add_channel_data(struct ssh_channel *c,
+ const char *buf, int len);
static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, int newwin);
static int ssh_sendbuffer(void *handle);
static int ssh2_pkt_getbool(struct Packet *pkt);
static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
static void ssh2_timer(void *ctx, unsigned long now);
-static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
+static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin);
static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin);
int send_ok;
int echoing, editing;
+ int session_started;
void *frontend;
int ospeed, ispeed; /* temporaries */
/* 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,
+ void (*protocol) (Ssh ssh, const void *vin, int inlen,
struct Packet *pkt);
- struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);
+ struct Packet *(*s_rdpkt) (Ssh ssh, const unsigned char **data,
+ int *datalen);
int (*do_ssh_init)(Ssh ssh, unsigned char c);
/*
unsigned long max_data_size;
int kex_in_progress;
unsigned long next_rekey, last_rekey;
- char *deferred_rekey_reason; /* points to STATIC string; don't free */
+ const char *deferred_rekey_reason;
/*
* Fully qualified host name, which we need if doing GSSAPI.
* Update the *data and *datalen variables.
* Return a Packet structure when a packet is completed.
*/
-static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
+static struct Packet *ssh1_rdpkt(Ssh ssh, const unsigned char **data,
+ int *datalen)
{
struct rdpkt1_state_tag *st = &ssh->rdpkt1_state;
pkt->length += (pkt->body - pkt->data);
}
-static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
+static struct Packet *ssh2_rdpkt(Ssh ssh, const unsigned char **data,
+ int *datalen)
{
struct rdpkt2_state_tag *st = &ssh->rdpkt2_state;
/*
* OpenSSH encrypt-then-MAC mode: the packet length is
- * unencrypted.
+ * unencrypted, unless the cipher supports length encryption.
*/
for (st->i = st->len = 0; st->i < 4; st->i++) {
while ((*datalen) == 0)
st->pktin->data[st->i] = *(*data)++;
(*datalen)--;
}
- st->len = toint(GET_32BIT(st->pktin->data));
+ /* Cipher supports length decryption, so do it */
+ if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+ /* Keep the packet the same though, so the MAC passes */
+ unsigned char len[4];
+ memcpy(len, st->pktin->data, 4);
+ ssh->sccipher->decrypt_length(ssh->sc_cipher_ctx, len, 4, st->incoming_sequence);
+ st->len = toint(GET_32BIT(len));
+ } else {
+ st->len = toint(GET_32BIT(st->pktin->data));
+ }
/*
* _Completely_ silly lengths should be stomped on before they
}
}
+ /*
+ * RFC 4253 doesn't explicitly say that completely empty packets
+ * with no type byte are forbidden, so treat them as deserving
+ * an SSH_MSG_UNIMPLEMENTED.
+ */
+ if (st->pktin->length <= 5) { /* == 5 we hope, but robustness */
+ ssh2_msg_something_unimplemented(ssh, st->pktin);
+ crStop(NULL);
+ }
/*
* pktin->body and pktin->length should identify the semantic
* content of the packet, excluding the initial type byte.
crFinish(st->pktin);
}
-static struct Packet *ssh2_bare_connection_rdpkt(Ssh ssh, unsigned char **data,
+static struct Packet *ssh2_bare_connection_rdpkt(Ssh ssh,
+ const unsigned char **data,
int *datalen)
{
struct rdpkt2_bare_state_tag *st = &ssh->rdpkt2_bare_state;
s_wrpkt_defer(ssh, pkt);
}
-static int ssh_versioncmp(char *a, char *b)
+static int ssh_versioncmp(const char *a, const char *b)
{
char *ae, *be;
unsigned long av, bv;
for (i = 0; i < padding; i++)
pkt->data[pkt->length + i] = random_byte();
PUT_32BIT(pkt->data, pkt->length + padding - 4);
+
+ /* Encrypt length if the scheme requires it */
+ if (ssh->cscipher && (ssh->cscipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+ ssh->cscipher->encrypt_length(ssh->cs_cipher_ctx, pkt->data, 4,
+ ssh->v2_outgoing_sequence);
+ }
+
if (ssh->csmac && ssh->csmac_etm) {
/*
* OpenSSH-defined encrypt-then-MAC protocol.
}
ssh_fix_verstring(verstring + strlen(protoname));
+#ifdef FUZZING
+ /* FUZZING make PuTTY insecure, so make live use difficult. */
+ verstring[0] = 'I';
+#endif
if (ssh->version == 2) {
size_t len;
crReturn(1);
}
+ ssh->session_started = TRUE;
+
s->vstrsize = sizeof(protoname) + 16;
s->vstring = snewn(s->vstrsize, char);
strcpy(s->vstring, protoname);
}
static void ssh_process_incoming_data(Ssh ssh,
- unsigned char **data, int *datalen)
+ const unsigned char **data, int *datalen)
{
struct Packet *pktin;
}
static void ssh_queue_incoming_data(Ssh ssh,
- unsigned char **data, int *datalen)
+ const unsigned char **data, int *datalen)
{
bufchain_add(&ssh->queued_incoming_data, *data, *datalen);
*data += *datalen;
static void ssh_process_queued_incoming_data(Ssh ssh)
{
void *vdata;
- unsigned char *data;
+ const unsigned char *data;
int len, origlen;
while (!ssh->frozen && bufchain_size(&ssh->queued_incoming_data)) {
ssh->frozen = frozen;
}
-static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
+static void ssh_gotdata(Ssh ssh, const unsigned char *data, int datalen)
{
/* Log raw data, if we're in that mode. */
if (ssh->logctx)
const char *error_msg, int error_code)
{
Ssh ssh = (Ssh) plug;
- char addrbuf[256], *msg;
- if (ssh->attempting_connshare) {
- /*
- * While we're attempting connection sharing, don't loudly log
- * everything that happens. Real TCP connections need to be
- * logged when we _start_ trying to connect, because it might
- * be ages before they respond if something goes wrong; but
- * connection sharing is local and quick to respond, and it's
- * sufficient to simply wait and see whether it worked
- * afterwards.
- */
- } else {
- sk_getaddr(addr, addrbuf, lenof(addrbuf));
-
- if (type == 0) {
- if (sk_addr_needs_port(addr)) {
- msg = dupprintf("Connecting to %s port %d", addrbuf, port);
- } else {
- msg = dupprintf("Connecting to %s", addrbuf);
- }
- } else {
- msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
- }
+ /*
+ * While we're attempting connection sharing, don't loudly log
+ * everything that happens. Real TCP connections need to be logged
+ * when we _start_ trying to connect, because it might be ages
+ * before they respond if something goes wrong; but connection
+ * sharing is local and quick to respond, and it's sufficient to
+ * simply wait and see whether it worked afterwards.
+ */
- logevent(msg);
- sfree(msg);
- }
+ if (!ssh->attempting_connshare)
+ backend_socket_log(ssh->frontend, type, addr, port,
+ error_msg, error_code, ssh->conf,
+ ssh->session_started);
}
void ssh_connshare_log(Ssh ssh, int event, const char *logtext,
ssh_throttle_all(ssh, 0, bufsize);
}
-/*
- * Connect to specified host and port.
- * Returns an error message, or NULL on success.
- * Also places the canonical host name into `realhost'. It must be
- * freed by the caller.
- */
-static const char *connect_to_host(Ssh ssh, char *host, int port,
- char **realhost, int nodelay, int keepalive)
+static void ssh_hostport_setup(const char *host, int port, Conf *conf,
+ char **savedhost, int *savedport,
+ char **loghost_ret)
{
- static const struct plug_function_table fn_table = {
- ssh_socket_log,
- ssh_closing,
- ssh_receive,
- ssh_sent,
- NULL
- };
+ char *loghost = conf_get_str(conf, CONF_loghost);
+ if (loghost_ret)
+ *loghost_ret = loghost;
- SockAddr addr;
- const char *err;
- char *loghost;
- int addressfamily, sshprot;
-
- loghost = conf_get_str(ssh->conf, CONF_loghost);
if (*loghost) {
char *tmphost;
char *colon;
tmphost = dupstr(loghost);
- ssh->savedport = 22; /* default ssh port */
+ *savedport = 22; /* default ssh port */
/*
* A colon suffix on the hostname string also lets us affect
if (colon && colon == host_strchr(tmphost, ':')) {
*colon++ = '\0';
if (*colon)
- ssh->savedport = atoi(colon);
+ *savedport = atoi(colon);
}
- ssh->savedhost = host_strduptrim(tmphost);
+ *savedhost = host_strduptrim(tmphost);
sfree(tmphost);
} else {
- ssh->savedhost = host_strduptrim(host);
+ *savedhost = host_strduptrim(host);
if (port < 0)
port = 22; /* default ssh port */
- ssh->savedport = port;
+ *savedport = port;
}
+}
+
+static int ssh_test_for_upstream(const char *host, int port, Conf *conf)
+{
+ char *savedhost;
+ int savedport;
+ int ret;
+
+ random_ref(); /* platform may need this to determine share socket name */
+ ssh_hostport_setup(host, port, conf, &savedhost, &savedport, NULL);
+ ret = ssh_share_test_for_upstream(savedhost, savedport, conf);
+ sfree(savedhost);
+ random_unref();
+
+ return ret;
+}
+
+/*
+ * Connect to specified host and port.
+ * Returns an error message, or NULL on success.
+ * Also places the canonical host name into `realhost'. It must be
+ * freed by the caller.
+ */
+static const char *connect_to_host(Ssh ssh, const char *host, int port,
+ char **realhost, int nodelay, int keepalive)
+{
+ static const struct plug_function_table fn_table = {
+ ssh_socket_log,
+ ssh_closing,
+ ssh_receive,
+ ssh_sent,
+ NULL
+ };
+
+ SockAddr addr;
+ const char *err;
+ char *loghost;
+ int addressfamily, sshprot;
+
+ ssh_hostport_setup(host, port, ssh->conf,
+ &ssh->savedhost, &ssh->savedport, &loghost);
ssh->fn = &fn_table; /* make 'ssh' usable as a Plug */
* Try to find host.
*/
addressfamily = conf_get_int(ssh->conf, CONF_addressfamily);
- logeventf(ssh, "Looking up host \"%s\"%s", host,
- (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
- (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : "")));
- addr = name_lookup(host, port, realhost, ssh->conf, addressfamily);
+ addr = name_lookup(host, port, realhost, ssh->conf, addressfamily,
+ ssh->frontend, "SSH connection");
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
{
struct ssh_channel *c = (struct ssh_channel *)cv;
Ssh ssh = c->ssh;
- void *sentreply = reply;
+ const void *sentreply = reply;
c->u.a.outstanding_requests--;
if (!sentreply) {
* 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,
+static void ssh_disconnect(Ssh ssh, const char *client_reason,
+ const char *wire_reason,
int code, int clean_exit)
{
char *error;
/*
* Handle the key exchange and user authentication phases.
*/
-static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
+static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin)
{
int i, j, ret;
"rsa", keystr, fingerprint,
ssh_dialog_callback, ssh);
sfree(keystr);
+#ifdef FUZZING
+ s->dlgret = 1;
+#endif
if (s->dlgret < 0) {
do {
crReturn(0);
{
int cipher_chosen = 0, warn = 0;
- char *cipher_string = NULL;
+ const char *cipher_string = NULL;
int i;
for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {
int next_cipher = conf_get_int_int(ssh->conf,
return conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists();
}
-static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
+static void do_ssh1_connection(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin)
{
crBegin(ssh->do_ssh1_connection_crstate);
ssh->packet_dispatch[SSH1_MSG_DEBUG] = ssh1_msg_debug;
}
-static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh1_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin)
{
- unsigned char *in=(unsigned char*)vin;
+ const unsigned char *in = (const unsigned char *)vin;
if (ssh->state == SSH_STATE_CLOSED)
return;
}
/*
- * Utility routine for decoding comma-separated strings in KEXINIT.
- */
-static int in_commasep_string(char const *needle, char const *haystack,
- int haylen)
-{
- int needlen;
- if (!needle || !haystack) /* protect against null pointers */
- return 0;
- needlen = strlen(needle);
- while (1) {
- /*
- * Is it at the start of the string?
- */
- if (haylen >= needlen && /* haystack is long enough */
- !memcmp(needle, haystack, needlen) && /* initial match */
- (haylen == needlen || haystack[needlen] == ',')
- /* either , or EOS follows */
- )
- return 1;
- /*
- * If not, search for the next comma and resume after that.
- * If no comma found, terminate.
- */
- while (haylen > 0 && *haystack != ',')
- haylen--, haystack++;
- if (haylen == 0)
- return 0;
- haylen--, haystack++; /* skip over comma itself */
- }
-}
-
-/*
- * Similar routine for checking whether we have the first string in a list.
+ * Utility routines for decoding comma-separated strings in KEXINIT.
*/
static int first_in_commasep_string(char const *needle, char const *haystack,
int haylen)
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] == ',')
return 0;
}
+static int in_commasep_string(char const *needle, char const *haystack,
+ int haylen)
+{
+ char *p;
+
+ if (!needle || !haystack) /* protect against null pointers */
+ return 0;
+ /*
+ * Is it at the start of the string?
+ */
+ if (first_in_commasep_string(needle, haystack, haylen))
+ return 1;
+ /*
+ * If not, search for the next comma and resume after that.
+ * If no comma found, terminate.
+ */
+ p = memchr(haystack, ',', haylen);
+ if (!p) return 0;
+ /* + 1 to skip over comma */
+ return in_commasep_string(needle, p + 1, haylen - (p + 1 - haystack));
+}
+
/*
* Add a value to the comma-separated string at the end of the packet.
- * If the value is already in the string, don't bother adding it again.
*/
static void ssh2_pkt_addstring_commasep(struct Packet *pkt, const char *data)
{
- if (in_commasep_string(data, (char *)pkt->data + pkt->savedpos,
- pkt->length - pkt->savedpos)) return;
if (pkt->length - pkt->savedpos > 0)
ssh_pkt_addstring_str(pkt, ",");
ssh_pkt_addstring_str(pkt, data);
/*
- * SSH-2 key creation method.
- * (Currently assumes 2 lots of any hash are sufficient to generate
- * keys/IVs for any cipher/MAC. SSH2_MKKEY_ITERS documents this assumption.)
+ * SSH-2 key derivation (RFC 4253 section 7.2).
*/
-#define SSH2_MKKEY_ITERS (2)
-static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr,
- unsigned char *keyspace)
+static unsigned char *ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H,
+ char chr, int keylen)
{
const struct ssh_hash *h = ssh->kex->hash;
- void *s;
+ int keylen_padded;
+ unsigned char *key;
+ void *s, *s2;
+
+ if (keylen == 0)
+ return NULL;
+
+ /* Round up to the next multiple of hash length. */
+ keylen_padded = ((keylen + h->hlen - 1) / h->hlen) * h->hlen;
+
+ key = snewn(keylen_padded, unsigned char);
+
/* First hlen bytes. */
s = h->init();
if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
h->bytes(s, H, h->hlen);
h->bytes(s, &chr, 1);
h->bytes(s, ssh->v2_session_id, ssh->v2_session_id_len);
- h->final(s, keyspace);
- /* Next hlen bytes. */
- s = h->init();
- if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
- hash_mpint(h, s, K);
- h->bytes(s, H, h->hlen);
- h->bytes(s, keyspace, h->hlen);
- h->final(s, keyspace + h->hlen);
+ h->final(s, key);
+
+ /* Subsequent blocks of hlen bytes. */
+ if (keylen_padded > h->hlen) {
+ int offset;
+
+ s = h->init();
+ if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
+ hash_mpint(h, s, K);
+ h->bytes(s, H, h->hlen);
+
+ for (offset = h->hlen; offset < keylen_padded; offset += h->hlen) {
+ h->bytes(s, key + offset - h->hlen, h->hlen);
+ s2 = h->copy(s);
+ h->final(s2, key + offset);
+ }
+
+ h->free(s);
+ }
+
+ /* Now clear any extra bytes of key material beyond the length
+ * we're officially returning, because the caller won't know to
+ * smemclr those. */
+ if (keylen_padded > keylen)
+ smemclr(key + keylen, keylen_padded - keylen);
+
+ return key;
+}
+
+/*
+ * Structure for constructing KEXINIT algorithm lists.
+ */
+#define MAXKEXLIST 16
+struct kexinit_algorithm {
+ const char *name;
+ union {
+ struct {
+ const struct ssh_kex *kex;
+ int warn;
+ } kex;
+ const struct ssh_signkey *hostkey;
+ struct {
+ const struct ssh2_cipher *cipher;
+ int warn;
+ } cipher;
+ struct {
+ const struct ssh_mac *mac;
+ int etm;
+ } mac;
+ const struct ssh_compress *comp;
+ } u;
+};
+
+/*
+ * Find a slot in a KEXINIT algorithm list to use for a new algorithm.
+ * If the algorithm is already in the list, return a pointer to its
+ * entry, otherwise return an entry from the end of the list.
+ * This assumes that every time a particular name is passed in, it
+ * comes from the same string constant. If this isn't true, this
+ * function may need to be rewritten to use strcmp() instead.
+ */
+static struct kexinit_algorithm *ssh2_kexinit_addalg(struct kexinit_algorithm
+ *list, const char *name)
+{
+ int i;
+
+ for (i = 0; i < MAXKEXLIST; i++)
+ if (list[i].name == NULL || list[i].name == name) {
+ list[i].name = name;
+ return &list[i];
+ }
+ assert(!"No space in KEXINIT list");
+ return NULL;
}
/*
* Handle the SSH-2 transport layer.
*/
-static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
+static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin)
{
- unsigned char *in = (unsigned char *)vin;
+ const unsigned char *in = (const unsigned char *)vin;
+ enum kexlist {
+ KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER,
+ KEXLIST_CSMAC, KEXLIST_SCMAC, KEXLIST_CSCOMP, KEXLIST_SCCOMP,
+ NKEXLIST
+ };
+ const char * kexlist_descr[NKEXLIST] = {
+ "key exchange algorithm", "host key algorithm",
+ "client-to-server cipher", "server-to-client cipher",
+ "client-to-server MAC", "server-to-client MAC",
+ "client-to-server compression method",
+ "server-to-client compression method" };
struct do_ssh2_transport_state {
int crLine;
int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher;
int dlgret;
int guessok;
int ignorepkt;
+ struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST];
};
crState(do_ssh2_transport_state);
begin_key_exchange:
ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
{
- int i, j, k;
+ int i, j, k, warn;
+ struct kexinit_algorithm *alg;
/*
* Set up the preferred key exchange. (NULL => warn below here)
case CIPHER_ARCFOUR:
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_arcfour;
break;
+ case CIPHER_CHACHA20:
+ s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_ccp;
+ break;
case CIPHER_WARN:
/* Flag for later. Don't bother if it's the last in
* the list. */
*/
ssh->kex_in_progress = TRUE;
- /*
- * Construct and send our key exchange packet.
- */
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEXINIT);
- for (i = 0; i < 16; i++)
- ssh2_pkt_addbyte(s->pktout, (unsigned char) random_byte());
+ for (i = 0; i < NKEXLIST; i++)
+ for (j = 0; j < MAXKEXLIST; j++)
+ s->kexlists[i][j].name = NULL;
/* List key exchange algorithms. */
- ssh2_pkt_addstring_start(s->pktout);
+ warn = FALSE;
for (i = 0; i < s->n_preferred_kex; i++) {
const struct ssh_kexes *k = s->preferred_kex[i];
- if (!k) continue; /* warning flag */
- for (j = 0; j < k->nkexes; j++)
- ssh2_pkt_addstring_commasep(s->pktout, k->list[j]->name);
+ if (!k) warn = TRUE;
+ else for (j = 0; j < k->nkexes; j++) {
+ alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_KEX],
+ k->list[j]->name);
+ alg->u.kex.kex = k->list[j];
+ alg->u.kex.warn = warn;
+ }
}
/* List server host key algorithms. */
if (!s->got_session_id) {
/*
* In the first key exchange, we list all the algorithms
- * we're prepared to cope with.
+ * we're prepared to cope with, but prefer those algorithms
+ * for which we have a host key for this host.
*/
- ssh2_pkt_addstring_start(s->pktout);
- for (i = 0; i < lenof(hostkey_algs); i++)
- ssh2_pkt_addstring_commasep(s->pktout, hostkey_algs[i]->name);
+ for (i = 0; i < lenof(hostkey_algs); i++) {
+ if (have_ssh_host_key(ssh->savedhost, ssh->savedport,
+ hostkey_algs[i]->keytype)) {
+ alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
+ hostkey_algs[i]->name);
+ alg->u.hostkey = hostkey_algs[i];
+ }
+ }
+ for (i = 0; i < lenof(hostkey_algs); i++) {
+ alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
+ hostkey_algs[i]->name);
+ alg->u.hostkey = hostkey_algs[i];
+ }
} else {
/*
* In subsequent key exchanges, we list only the kex
* reverification.
*/
assert(ssh->kex);
- ssh2_pkt_addstring(s->pktout, ssh->hostkey->name);
+ alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
+ ssh->hostkey->name);
+ alg->u.hostkey = ssh->hostkey;
}
/* List encryption algorithms (client->server then server->client). */
- for (k = 0; k < 2; k++) {
- ssh2_pkt_addstring_start(s->pktout);
+ for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) {
+ warn = FALSE;
+#ifdef FUZZING
+ alg = ssh2_kexinit_addalg(s->kexlists[k], "none");
+ alg->u.cipher.cipher = NULL;
+ alg->u.cipher.warn = warn;
+#endif /* FUZZING */
for (i = 0; i < s->n_preferred_ciphers; i++) {
const struct ssh2_ciphers *c = s->preferred_ciphers[i];
- if (!c) continue; /* warning flag */
- for (j = 0; j < c->nciphers; j++)
- ssh2_pkt_addstring_commasep(s->pktout, c->list[j]->name);
+ if (!c) warn = TRUE;
+ else for (j = 0; j < c->nciphers; j++) {
+ alg = ssh2_kexinit_addalg(s->kexlists[k],
+ c->list[j]->name);
+ alg->u.cipher.cipher = c->list[j];
+ alg->u.cipher.warn = warn;
+ }
}
}
/* List MAC algorithms (client->server then server->client). */
- for (j = 0; j < 2; j++) {
- ssh2_pkt_addstring_start(s->pktout);
+ for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) {
+#ifdef FUZZING
+ alg = ssh2_kexinit_addalg(s->kexlists[j], "none");
+ alg->u.mac.mac = NULL;
+ alg->u.mac.etm = FALSE;
+#endif /* FUZZING */
for (i = 0; i < s->nmacs; i++) {
- ssh2_pkt_addstring_commasep(s->pktout, s->maclist[i]->name);
+ alg = ssh2_kexinit_addalg(s->kexlists[j], s->maclist[i]->name);
+ alg->u.mac.mac = s->maclist[i];
+ alg->u.mac.etm = FALSE;
}
- for (i = 0; i < s->nmacs; i++) {
+ for (i = 0; i < s->nmacs; i++)
/* For each MAC, there may also be an ETM version,
* which we list second. */
- if (s->maclist[i]->etm_name)
- ssh2_pkt_addstring_commasep(s->pktout, s->maclist[i]->etm_name);
- }
+ if (s->maclist[i]->etm_name) {
+ alg = ssh2_kexinit_addalg(s->kexlists[j],
+ s->maclist[i]->etm_name);
+ alg->u.mac.mac = s->maclist[i];
+ alg->u.mac.etm = TRUE;
+ }
}
/* List client->server compression algorithms,
* then server->client compression algorithms. (We use the
* same set twice.) */
- for (j = 0; j < 2; j++) {
- ssh2_pkt_addstring_start(s->pktout);
+ for (j = KEXLIST_CSCOMP; j <= KEXLIST_SCCOMP; j++) {
assert(lenof(compressions) > 1);
/* Prefer non-delayed versions */
- ssh2_pkt_addstring_commasep(s->pktout, s->preferred_comp->name);
+ alg = ssh2_kexinit_addalg(s->kexlists[j], s->preferred_comp->name);
+ alg->u.comp = s->preferred_comp;
/* We don't even list delayed versions of algorithms until
* they're allowed to be used, to avoid a race. See the end of
* this function. */
- if (s->userauth_succeeded && s->preferred_comp->delayed_name)
- ssh2_pkt_addstring_commasep(s->pktout,
- s->preferred_comp->delayed_name);
+ if (s->userauth_succeeded && s->preferred_comp->delayed_name) {
+ alg = ssh2_kexinit_addalg(s->kexlists[j],
+ s->preferred_comp->delayed_name);
+ alg->u.comp = s->preferred_comp;
+ }
for (i = 0; i < lenof(compressions); i++) {
const struct ssh_compress *c = compressions[i];
- ssh2_pkt_addstring_commasep(s->pktout, c->name);
- if (s->userauth_succeeded && c->delayed_name)
- ssh2_pkt_addstring_commasep(s->pktout, c->delayed_name);
+ alg = ssh2_kexinit_addalg(s->kexlists[j], c->name);
+ alg->u.comp = c;
+ if (s->userauth_succeeded && c->delayed_name) {
+ alg = ssh2_kexinit_addalg(s->kexlists[j], c->delayed_name);
+ alg->u.comp = c;
+ }
+ }
+ }
+ /*
+ * Construct and send our key exchange packet.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_KEXINIT);
+ for (i = 0; i < 16; i++)
+ ssh2_pkt_addbyte(s->pktout, (unsigned char) random_byte());
+ for (i = 0; i < NKEXLIST; i++) {
+ ssh2_pkt_addstring_start(s->pktout);
+ for (j = 0; j < MAXKEXLIST; j++) {
+ if (s->kexlists[i][j].name == NULL) break;
+ ssh2_pkt_addstring_commasep(s->pktout, s->kexlists[i][j].name);
}
}
/* List client->server languages. Empty list. */
* to.
*/
{
- char *str, *preferred;
+ char *str;
int i, j, len;
if (pktin->type != SSH2_MSG_KEXINIT) {
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 */
- if (!str) {
- bombout(("KEXINIT packet was incomplete"));
- crStopV;
- }
- preferred = NULL;
- for (i = 0; i < s->n_preferred_kex; i++) {
- const struct ssh_kexes *k = s->preferred_kex[i];
- if (!k) {
- s->warn_kex = TRUE;
- } else {
- for (j = 0; j < k->nkexes; j++) {
- if (!preferred) preferred = k->list[j]->name;
- if (in_commasep_string(k->list[j]->name, str, len)) {
- ssh->kex = k->list[j];
- break;
- }
- }
- }
- if (ssh->kex)
- break;
- }
- if (!ssh->kex) {
- bombout(("Couldn't agree a key exchange algorithm"
- " (available: %.*s)", len, str));
- crStopV;
- }
- /*
- * 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 */
- if (!str) {
- bombout(("KEXINIT packet was incomplete"));
- crStopV;
- }
- for (i = 0; i < lenof(hostkey_algs); i++) {
- if (in_commasep_string(hostkey_algs[i]->name, str, len)) {
- ssh->hostkey = hostkey_algs[i];
- break;
+ s->guessok = FALSE;
+ for (i = 0; i < NKEXLIST; i++) {
+ ssh_pkt_getstring(pktin, &str, &len);
+ if (!str) {
+ bombout(("KEXINIT packet was incomplete"));
+ crStopV;
}
- }
- if (!ssh->hostkey) {
- bombout(("Couldn't agree a host key algorithm"
- " (available: %.*s)", len, str));
- crStopV;
- }
- s->guessok = s->guessok &&
- first_in_commasep_string(hostkey_algs[0]->name, str, len);
- ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */
- if (!str) {
- bombout(("KEXINIT packet was incomplete"));
- crStopV;
- }
- for (i = 0; i < s->n_preferred_ciphers; i++) {
- const struct ssh2_ciphers *c = s->preferred_ciphers[i];
- if (!c) {
- s->warn_cscipher = TRUE;
- } else {
- for (j = 0; j < c->nciphers; j++) {
- if (in_commasep_string(c->list[j]->name, str, len)) {
- s->cscipher_tobe = c->list[j];
- break;
- }
- }
- }
- if (s->cscipher_tobe)
- break;
- }
- if (!s->cscipher_tobe) {
- bombout(("Couldn't agree a client-to-server cipher"
- " (available: %.*s)", len, str));
- crStopV;
- }
+ /* If we've already selected a cipher which requires a
+ * particular MAC, then just select that, and don't even
+ * bother looking through the server's KEXINIT string for
+ * MACs. */
+ if (i == KEXLIST_CSMAC && s->cscipher_tobe &&
+ s->cscipher_tobe->required_mac) {
+ s->csmac_tobe = s->cscipher_tobe->required_mac;
+ s->csmac_etm_tobe = !!(s->csmac_tobe->etm_name);
+ goto matched;
+ }
+ if (i == KEXLIST_SCMAC && s->sccipher_tobe &&
+ s->sccipher_tobe->required_mac) {
+ s->scmac_tobe = s->sccipher_tobe->required_mac;
+ s->scmac_etm_tobe = !!(s->scmac_tobe->etm_name);
+ goto matched;
+ }
- ssh_pkt_getstring(pktin, &str, &len); /* server->client cipher */
- if (!str) {
- bombout(("KEXINIT packet was incomplete"));
- crStopV;
- }
- for (i = 0; i < s->n_preferred_ciphers; i++) {
- const struct ssh2_ciphers *c = s->preferred_ciphers[i];
- if (!c) {
- s->warn_sccipher = TRUE;
- } else {
- for (j = 0; j < c->nciphers; j++) {
- if (in_commasep_string(c->list[j]->name, str, len)) {
- s->sccipher_tobe = c->list[j];
- break;
+ for (j = 0; j < MAXKEXLIST; j++) {
+ struct kexinit_algorithm *alg = &s->kexlists[i][j];
+ if (alg->name == NULL) break;
+ if (in_commasep_string(alg->name, str, len)) {
+ /* We've found a matching algorithm. */
+ if (i == KEXLIST_KEX || i == KEXLIST_HOSTKEY) {
+ /* Check if we might need to ignore first kex pkt */
+ if (j != 0 ||
+ !first_in_commasep_string(alg->name, str, len))
+ s->guessok = FALSE;
}
+ if (i == KEXLIST_KEX) {
+ ssh->kex = alg->u.kex.kex;
+ s->warn_kex = alg->u.kex.warn;
+ } else if (i == KEXLIST_HOSTKEY) {
+ ssh->hostkey = alg->u.hostkey;
+ } else if (i == KEXLIST_CSCIPHER) {
+ s->cscipher_tobe = alg->u.cipher.cipher;
+ s->warn_cscipher = alg->u.cipher.warn;
+ } else if (i == KEXLIST_SCCIPHER) {
+ s->sccipher_tobe = alg->u.cipher.cipher;
+ s->warn_sccipher = alg->u.cipher.warn;
+ } else if (i == KEXLIST_CSMAC) {
+ s->csmac_tobe = alg->u.mac.mac;
+ s->csmac_etm_tobe = alg->u.mac.etm;
+ } else if (i == KEXLIST_SCMAC) {
+ s->scmac_tobe = alg->u.mac.mac;
+ s->scmac_etm_tobe = alg->u.mac.etm;
+ } else if (i == KEXLIST_CSCOMP) {
+ s->cscomp_tobe = alg->u.comp;
+ } else if (i == KEXLIST_SCCOMP) {
+ s->sccomp_tobe = alg->u.comp;
+ }
+ goto matched;
}
+ if ((i == KEXLIST_CSCOMP || i == KEXLIST_SCCOMP) &&
+ in_commasep_string(alg->u.comp->delayed_name, str, len))
+ s->pending_compression = TRUE; /* try this later */
}
- if (s->sccipher_tobe)
- break;
- }
- if (!s->sccipher_tobe) {
- bombout(("Couldn't agree a server-to-client cipher"
- " (available: %.*s)", len, str));
+ bombout(("Couldn't agree a %s ((available: %.*s)",
+ kexlist_descr[i], len, str));
crStopV;
+ matched:;
}
- ssh_pkt_getstring(pktin, &str, &len); /* client->server mac */
- if (!str) {
- bombout(("KEXINIT packet was incomplete"));
- crStopV;
- }
- for (i = 0; i < s->nmacs; i++) {
- if (in_commasep_string(s->maclist[i]->name, str, len)) {
- s->csmac_tobe = s->maclist[i];
- s->csmac_etm_tobe = FALSE;
- break;
- }
- }
- if (!s->csmac_tobe) {
- for (i = 0; i < s->nmacs; i++) {
- if (s->maclist[i]->etm_name &&
- in_commasep_string(s->maclist[i]->etm_name, str, len)) {
- s->csmac_tobe = s->maclist[i];
- s->csmac_etm_tobe = TRUE;
- break;
- }
- }
- }
- if (!s->csmac_tobe) {
- bombout(("Couldn't agree a client-to-server MAC"
- " (available: %.*s)", len, str));
- crStopV;
- }
- ssh_pkt_getstring(pktin, &str, &len); /* server->client mac */
- if (!str) {
- bombout(("KEXINIT packet was incomplete"));
- crStopV;
- }
- for (i = 0; i < s->nmacs; i++) {
- if (in_commasep_string(s->maclist[i]->name, str, len)) {
- s->scmac_tobe = s->maclist[i];
- s->scmac_etm_tobe = FALSE;
- break;
- }
- }
- if (!s->scmac_tobe) {
- for (i = 0; i < s->nmacs; i++) {
- if (s->maclist[i]->etm_name &&
- in_commasep_string(s->maclist[i]->etm_name, str, len)) {
- s->scmac_tobe = s->maclist[i];
- s->scmac_etm_tobe = TRUE;
- break;
- }
- }
- }
- if (!s->scmac_tobe) {
- bombout(("Couldn't agree a server-to-client MAC"
- " (available: %.*s)", len, str));
- crStopV;
- }
- ssh_pkt_getstring(pktin, &str, &len); /* client->server compression */
- if (!str) {
- bombout(("KEXINIT packet was incomplete"));
- crStopV;
- }
- for (i = 0; i < lenof(compressions) + 1; i++) {
- const struct ssh_compress *c =
- i == 0 ? s->preferred_comp : compressions[i - 1];
- if (in_commasep_string(c->name, str, len)) {
- s->cscomp_tobe = c;
- break;
- } else if (in_commasep_string(c->delayed_name, str, len)) {
- if (s->userauth_succeeded) {
- s->cscomp_tobe = c;
- break;
- } else {
- s->pending_compression = TRUE; /* try this later */
- }
- }
- }
- ssh_pkt_getstring(pktin, &str, &len); /* server->client compression */
- if (!str) {
- bombout(("KEXINIT packet was incomplete"));
- crStopV;
- }
- for (i = 0; i < lenof(compressions) + 1; i++) {
- const struct ssh_compress *c =
- i == 0 ? s->preferred_comp : compressions[i - 1];
- if (in_commasep_string(c->name, str, len)) {
- s->sccomp_tobe = c;
- break;
- } else if (in_commasep_string(c->delayed_name, str, len)) {
- if (s->userauth_succeeded) {
- s->sccomp_tobe = c;
- break;
- } else {
- s->pending_compression = TRUE; /* try this later */
- }
- }
- }
if (s->pending_compression) {
logevent("Server supports delayed compression; "
"will try this later");
{
int csbits, scbits;
- csbits = s->cscipher_tobe->keylen;
- scbits = s->sccipher_tobe->keylen;
+ csbits = s->cscipher_tobe ? s->cscipher_tobe->real_keybits : 0;
+ scbits = s->sccipher_tobe ? s->sccipher_tobe->real_keybits : 0;
s->nbits = (csbits > scbits ? csbits : scbits);
}
/* The keys only have hlen-bit entropy, since they're based on
}
set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+ if (!s->hostkeydata) {
+ bombout(("unable to parse key exchange reply packet"));
+ crStopV;
+ }
s->hkey = ssh->hostkey->newkey(ssh->hostkey,
s->hostkeydata, s->hostkeylen);
s->f = ssh2_pkt_getmp(pktin);
crStopV;
}
ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+ if (!s->sigdata) {
+ bombout(("unable to parse key exchange reply packet"));
+ crStopV;
+ }
{
const char *err = dh_validate_f(ssh->kex_ctx, s->f);
}
} else if (ssh->kex->main_type == KEXTYPE_ECDH) {
- logeventf(ssh, "Doing ECDH key exchange with hash %s",
+ logeventf(ssh, "Doing ECDH key exchange with curve %s and hash %s",
+ ssh_ecdhkex_curve_textname(ssh->kex),
ssh->kex->hash->text_name);
ssh->pkt_kctx = SSH2_PKTCTX_ECDHKEX;
- s->eckey = ssh_ecdhkex_newkey(ssh->kex->name);
+ s->eckey = ssh_ecdhkex_newkey(ssh->kex);
if (!s->eckey) {
bombout(("Unable to generate key for ECDH"));
crStopV;
}
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+ if (!s->hostkeydata) {
+ bombout(("unable to parse ECDH reply packet"));
+ crStopV;
+ }
hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
s->hkey = ssh->hostkey->newkey(ssh->hostkey,
s->hostkeydata, s->hostkeylen);
char *keydata;
int keylen;
ssh_pkt_getstring(pktin, &keydata, &keylen);
+ if (!keydata) {
+ bombout(("unable to parse ECDH reply packet"));
+ crStopV;
+ }
hash_string(ssh->kex->hash, ssh->exhash, keydata, keylen);
s->K = ssh_ecdhkex_getkey(s->eckey, keydata, keylen);
if (!s->K) {
}
ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+ if (!s->sigdata) {
+ bombout(("unable to parse key exchange reply packet"));
+ crStopV;
+ }
ssh_ecdhkex_freekey(s->eckey);
} else {
}
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+ if (!s->hostkeydata) {
+ bombout(("unable to parse RSA public key packet"));
+ crStopV;
+ }
hash_string(ssh->kex->hash, ssh->exhash,
s->hostkeydata, s->hostkeylen);
s->hkey = ssh->hostkey->newkey(ssh->hostkey,
{
char *keydata;
ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);
+ if (!keydata) {
+ bombout(("unable to parse RSA public key packet"));
+ crStopV;
+ }
s->rsakeydata = snewn(s->rsakeylen, char);
memcpy(s->rsakeydata, keydata, s->rsakeylen);
}
}
ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+ if (!s->sigdata) {
+ bombout(("unable to parse signature packet"));
+ crStopV;
+ }
sfree(s->rsakeydata);
}
dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
#endif
- if (!s->hkey ||
- !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
+ if (!s->hkey) {
+ bombout(("Server's host key is invalid"));
+ crStopV;
+ }
+
+ if (!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
(char *)s->exchange_hash,
ssh->kex->hash->hlen)) {
+#ifndef FUZZING
bombout(("Server's host key did not match the signature supplied"));
crStopV;
+#endif
}
s->keystr = ssh->hostkey->fmtkey(s->hkey);
ssh->hostkey->keytype, s->keystr,
s->fingerprint,
ssh_dialog_callback, ssh);
+#ifdef FUZZING
+ s->dlgret = 1;
+#endif
if (s->dlgret < 0) {
do {
crReturnV;
* the one we saw before.
*/
if (strcmp(ssh->hostkey_str, s->keystr)) {
+#ifndef FUZZING
bombout(("Host key was different in repeat key exchange"));
crStopV;
+#endif
}
sfree(s->keystr);
}
if (ssh->cs_cipher_ctx)
ssh->cscipher->free_context(ssh->cs_cipher_ctx);
ssh->cscipher = s->cscipher_tobe;
- ssh->cs_cipher_ctx = ssh->cscipher->make_context();
+ if (ssh->cscipher) ssh->cs_cipher_ctx = ssh->cscipher->make_context();
if (ssh->cs_mac_ctx)
ssh->csmac->free_context(ssh->cs_mac_ctx);
ssh->csmac = s->csmac_tobe;
ssh->csmac_etm = s->csmac_etm_tobe;
- ssh->cs_mac_ctx = ssh->csmac->make_context();
+ if (ssh->csmac)
+ ssh->cs_mac_ctx = ssh->csmac->make_context(ssh->cs_cipher_ctx);
if (ssh->cs_comp_ctx)
ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx);
* Set IVs on client-to-server keys. Here we use the exchange
* hash from the _first_ key exchange.
*/
- {
- unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS];
- assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace);
- assert((ssh->cscipher->keylen+7) / 8 <=
- ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
- ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace);
- assert(ssh->cscipher->blksize <=
- ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
- ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace);
- assert(ssh->csmac->len <=
- ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
- ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace);
- smemclr(keyspace, sizeof(keyspace));
- }
-
- logeventf(ssh, "Initialised %.200s client->server encryption",
- ssh->cscipher->text_name);
- logeventf(ssh, "Initialised %.200s client->server MAC algorithm%s",
- ssh->csmac->text_name,
- ssh->csmac_etm ? " (in ETM mode)" : "");
+ if (ssh->cscipher) {
+ unsigned char *key;
+
+ key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'C',
+ ssh->cscipher->padded_keybytes);
+ ssh->cscipher->setkey(ssh->cs_cipher_ctx, key);
+ smemclr(key, ssh->cscipher->padded_keybytes);
+ sfree(key);
+
+ key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'A',
+ ssh->cscipher->blksize);
+ ssh->cscipher->setiv(ssh->cs_cipher_ctx, key);
+ smemclr(key, ssh->cscipher->blksize);
+ sfree(key);
+ }
+ if (ssh->csmac) {
+ unsigned char *key;
+
+ key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'E',
+ ssh->csmac->keylen);
+ ssh->csmac->setkey(ssh->cs_mac_ctx, key);
+ smemclr(key, ssh->csmac->keylen);
+ sfree(key);
+ }
+
+ if (ssh->cscipher)
+ logeventf(ssh, "Initialised %.200s client->server encryption",
+ ssh->cscipher->text_name);
+ if (ssh->csmac)
+ logeventf(ssh, "Initialised %.200s client->server MAC algorithm%s%s",
+ ssh->csmac->text_name,
+ ssh->csmac_etm ? " (in ETM mode)" : "",
+ ssh->cscipher->required_mac ? " (required by cipher)" : "");
if (ssh->cscomp->text_name)
logeventf(ssh, "Initialised %s compression",
ssh->cscomp->text_name);
*/
if (ssh->sc_cipher_ctx)
ssh->sccipher->free_context(ssh->sc_cipher_ctx);
- ssh->sccipher = s->sccipher_tobe;
- ssh->sc_cipher_ctx = ssh->sccipher->make_context();
+ if (s->sccipher_tobe) {
+ ssh->sccipher = s->sccipher_tobe;
+ ssh->sc_cipher_ctx = ssh->sccipher->make_context();
+ }
if (ssh->sc_mac_ctx)
ssh->scmac->free_context(ssh->sc_mac_ctx);
- ssh->scmac = s->scmac_tobe;
- ssh->scmac_etm = s->scmac_etm_tobe;
- ssh->sc_mac_ctx = ssh->scmac->make_context();
+ if (s->scmac_tobe) {
+ ssh->scmac = s->scmac_tobe;
+ ssh->scmac_etm = s->scmac_etm_tobe;
+ ssh->sc_mac_ctx = ssh->scmac->make_context(ssh->sc_cipher_ctx);
+ }
if (ssh->sc_comp_ctx)
ssh->sccomp->decompress_cleanup(ssh->sc_comp_ctx);
* Set IVs on server-to-client keys. Here we use the exchange
* hash from the _first_ key exchange.
*/
- {
- unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS];
- assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace);
- assert((ssh->sccipher->keylen+7) / 8 <=
- ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
- ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace);
- assert(ssh->sccipher->blksize <=
- ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
- ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace);
- ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace);
- assert(ssh->scmac->len <=
- ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
- ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace);
- smemclr(keyspace, sizeof(keyspace));
- }
- logeventf(ssh, "Initialised %.200s server->client encryption",
- ssh->sccipher->text_name);
- logeventf(ssh, "Initialised %.200s server->client MAC algorithm%s",
- ssh->scmac->text_name,
- ssh->scmac_etm ? " (in ETM mode)" : "");
+ if (ssh->sccipher) {
+ unsigned char *key;
+
+ key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'D',
+ ssh->sccipher->padded_keybytes);
+ ssh->sccipher->setkey(ssh->sc_cipher_ctx, key);
+ smemclr(key, ssh->sccipher->padded_keybytes);
+ sfree(key);
+
+ key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'B',
+ ssh->sccipher->blksize);
+ ssh->sccipher->setiv(ssh->sc_cipher_ctx, key);
+ smemclr(key, ssh->sccipher->blksize);
+ sfree(key);
+ }
+ if (ssh->scmac) {
+ unsigned char *key;
+
+ key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'F',
+ ssh->scmac->keylen);
+ ssh->scmac->setkey(ssh->sc_mac_ctx, key);
+ smemclr(key, ssh->scmac->keylen);
+ sfree(key);
+ }
+ if (ssh->sccipher)
+ logeventf(ssh, "Initialised %.200s server->client encryption",
+ ssh->sccipher->text_name);
+ if (ssh->scmac)
+ logeventf(ssh, "Initialised %.200s server->client MAC algorithm%s%s",
+ ssh->scmac->text_name,
+ ssh->scmac_etm ? " (in ETM mode)" : "",
+ ssh->sccipher->required_mac ? " (required by cipher)" : "");
if (ssh->sccomp->text_name)
logeventf(ssh, "Initialised %s decompression",
ssh->sccomp->text_name);
/*
* Add data to an SSH-2 channel output buffer.
*/
-static void ssh2_add_channel_data(struct ssh_channel *c, char *buf,
+static void ssh2_add_channel_data(struct ssh_channel *c, const char *buf,
int len)
{
bufchain_add(&c->v.v2.outbuffer, buf, len);
/*
* Construct the common parts of a CHANNEL_OPEN.
*/
-static struct Packet *ssh2_chanopen_init(struct ssh_channel *c, char *type)
+static struct Packet *ssh2_chanopen_init(struct ssh_channel *c,
+ const char *type)
{
struct Packet *pktout;
* the server initiated channel closure before we saw the response)
* and the handler should free any storage it's holding.
*/
-static struct Packet *ssh2_chanreq_init(struct ssh_channel *c, char *type,
+static struct Packet *ssh2_chanreq_init(struct ssh_channel *c,
+ const char *type,
cchandler_fn_t handler, void *ctx)
{
struct Packet *pktout;
{
if (ssh->version == 2 &&
!conf_get_int(ssh->conf, CONF_ssh_no_shell) &&
- count234(ssh->channels) == 0 &&
+ (ssh->channels && count234(ssh->channels) == 0) &&
!(ssh->connshare && share_ndownstreams(ssh->connshare) > 0)) {
/*
* We used to send SSH_MSG_DISCONNECT here, because I'd
}
}
-void ssh_sharing_downstream_connected(Ssh ssh, unsigned id)
+void ssh_sharing_downstream_connected(Ssh ssh, unsigned id,
+ const char *peerinfo)
{
- logeventf(ssh, "Connection sharing downstream #%u connected", id);
+ if (peerinfo)
+ logeventf(ssh, "Connection sharing downstream #%u connected from %s",
+ id, peerinfo);
+ else
+ logeventf(ssh, "Connection sharing downstream #%u connected", id);
}
void ssh_sharing_downstream_disconnected(Ssh ssh, unsigned id)
!memcmp(type, "exit-signal", 11)) {
int is_plausible = TRUE, is_int = FALSE;
- char *fmt_sig = "", *fmt_msg = "";
+ char *fmt_sig = NULL, *fmt_msg = NULL;
char *msg;
int msglen = 0, core = FALSE;
/* ICK: older versions of OpenSSH (e.g. 3.4p1)
/* ignore lang tag */
} /* else don't attempt to parse */
logeventf(ssh, "Server exited on signal%s%s%s",
- fmt_sig, core ? " (core dumped)" : "",
- fmt_msg);
- if (*fmt_sig) sfree(fmt_sig);
- if (*fmt_msg) sfree(fmt_msg);
+ fmt_sig ? fmt_sig : "",
+ core ? " (core dumped)" : "",
+ fmt_msg ? fmt_msg : "");
+ sfree(fmt_sig);
+ sfree(fmt_msg);
reply = SSH2_MSG_CHANNEL_SUCCESS;
}
char *peeraddr;
int peeraddrlen;
int peerport;
- char *error = NULL;
+ const char *error = NULL;
struct ssh_channel *c;
unsigned remid, winsize, pktsize;
unsigned our_winsize_override = 0;
do_ssh2_authconn(c->ssh, NULL, 0, pktin);
}
-static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
+static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin)
{
struct do_ssh2_authconn_state {
s->can_keyb_inter = conf_get_int(ssh->conf, CONF_try_ki_auth) &&
in_commasep_string("keyboard-interactive", methods, methlen);
#ifndef NO_GSSAPI
- if (!ssh->gsslibs)
- ssh->gsslibs = ssh_gss_setup(ssh->conf);
- s->can_gssapi = conf_get_int(ssh->conf, CONF_try_gssapi_auth) &&
- in_commasep_string("gssapi-with-mic", methods, methlen) &&
- ssh->gsslibs->nlibraries > 0;
+ if (conf_get_int(ssh->conf, CONF_try_gssapi_auth) &&
+ in_commasep_string("gssapi-with-mic", methods, methlen)) {
+ /* Try loading the GSS libraries and see if we
+ * have any. */
+ if (!ssh->gsslibs)
+ ssh->gsslibs = ssh_gss_setup(ssh->conf);
+ s->can_gssapi = (ssh->gsslibs->nlibraries > 0);
+ } else {
+ /* No point in even bothering to try to load the
+ * GSS libraries, if the user configuration and
+ * server aren't both prepared to attempt GSSAPI
+ * auth in the first place. */
+ s->can_gssapi = FALSE;
+ }
#endif
}
int prompt_len; /* not live over crReturn */
{
- char *msg;
+ const char *msg;
if (changereq_first_time)
msg = "Server requested password change";
else
* Try to send data on all channels if we can.
*/
for (i = 0; NULL != (c = index234(ssh->channels, i)); i++)
- ssh2_try_send_and_unthrottle(ssh, c);
+ if (c->type != CHAN_SHARING)
+ ssh2_try_send_and_unthrottle(ssh, c);
}
}
}
}
-static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh2_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin)
{
- unsigned char *in = (unsigned char *)vin;
+ const unsigned char *in = (const unsigned char *)vin;
if (ssh->state == SSH_STATE_CLOSED)
return;
do_ssh2_authconn(ssh, in, inlen, pktin);
}
-static void ssh2_bare_connection_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh2_bare_connection_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin)
{
- unsigned char *in = (unsigned char *)vin;
+ const unsigned char *in = (const unsigned char *)vin;
if (ssh->state == SSH_STATE_CLOSED)
return;
* Returns an error message, or NULL on success.
*/
static const char *ssh_init(void *frontend_handle, void **backend_handle,
- Conf *conf, char *host, int port, char **realhost,
+ Conf *conf,
+ const char *host, int port, char **realhost,
int nodelay, int keepalive)
{
const char *p;
ssh->X11_fwd_enabled = FALSE;
ssh->connshare = NULL;
ssh->attempting_connshare = FALSE;
+ ssh->session_started = FALSE;
*backend_handle = ssh;
static void ssh_reconfig(void *handle, Conf *conf)
{
Ssh ssh = (Ssh) handle;
- char *rekeying = NULL, rekey_mandatory = FALSE;
+ const char *rekeying = NULL;
+ int rekey_mandatory = FALSE;
unsigned long old_max_data_size;
int i, rekey_time;
/*
* Called to send data down the SSH connection.
*/
-static int ssh_send(void *handle, char *buf, int len)
+static int ssh_send(void *handle, const char *buf, int len)
{
Ssh ssh = (Ssh) handle;
if (ssh == NULL || ssh->s == NULL || ssh->protocol == NULL)
return 0;
- ssh->protocol(ssh, (unsigned char *)buf, len, 0);
+ ssh->protocol(ssh, (const unsigned char *)buf, len, 0);
return ssh_sendbuffer(ssh);
}
}
} else {
/* Is is a POSIX signal? */
- char *signame = NULL;
+ const char *signame = NULL;
if (code == TS_SIGABRT) signame = "ABRT";
if (code == TS_SIGALRM) signame = "ALRM";
if (code == TS_SIGFPE) signame = "FPE";
ssh_process_queued_incoming_data(ssh);
}
-void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
+void ssh_send_port_open(void *channel, const char *hostname, int port,
+ const char *org)
{
struct ssh_channel *c = (struct ssh_channel *)channel;
Ssh ssh = c->ssh;
ssh_provide_logctx,
ssh_unthrottle,
ssh_cfg_info,
+ ssh_test_for_upstream,
"ssh",
PROT_SSH,
22