#define BUG_CHOKES_ON_RSA 8
#define BUG_SSH2_RSA_PADDING 16
#define BUG_SSH2_DERIVEKEY 32
-#define BUG_SSH2_DH_GEX 64
+/* 64 was BUG_SSH2_DH_GEX, now spare */
#define BUG_SSH2_PK_SESSIONID 128
#define translate(x) if (type == x) return #x
#define SSH_MAX_BACKLOG 32768
#define OUR_V2_WINSIZE 16384
-const static struct ssh_kex *kex_algs[] = {
- &ssh_diffiehellman_gex,
- &ssh_diffiehellman_group14,
- &ssh_diffiehellman_group1,
-};
-
const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
static void *nullmac_make_context(void)
* size-based rekeys.
*/
unsigned long incoming_data_size, outgoing_data_size, deferred_data_size;
+ unsigned long max_data_size;
int kex_in_progress;
long next_rekey;
};
-#define MAX_DATA_BEFORE_REKEY (0x40000000UL)
-#define REKEY_TIMEOUT (3600 * TICKSPERSEC)
-
#define logevent(s) logevent(ssh->frontend, s)
/* logevent, only printf-formatted. */
ssh->outgoing_data_size += pkt->encrypted_len;
if (!ssh->kex_in_progress &&
- ssh->outgoing_data_size > MAX_DATA_BEFORE_REKEY)
+ ssh->max_data_size != 0 &&
+ ssh->outgoing_data_size > ssh->max_data_size)
do_ssh2_transport(ssh, "Initiating key re-exchange "
"(too much data sent)", -1, NULL);
ssh->outgoing_data_size += ssh->deferred_data_size;
if (!ssh->kex_in_progress &&
- ssh->outgoing_data_size > MAX_DATA_BEFORE_REKEY)
+ ssh->max_data_size != 0 &&
+ ssh->outgoing_data_size > ssh->max_data_size)
do_ssh2_transport(ssh, "Initiating key re-exchange "
"(too much data sent)", -1, NULL);
ssh->deferred_data_size = 0;
ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID;
logevent("We believe remote version has SSH2 public-key-session-ID bug");
}
-
- if (ssh->cfg.sshbug_dhgex2 == FORCE_ON) {
- /*
- * User specified the SSH2 DH GEX bug.
- */
- ssh->remote_bugs |= BUG_SSH2_DH_GEX;
- logevent("We believe remote version has SSH2 DH group exchange bug");
- }
}
/*
/* Warn about chosen cipher if necessary. */
if (warn)
- askcipher(ssh->frontend, cipher_string, 0);
+ askalg(ssh->frontend, "cipher", cipher_string);
}
switch (s->cipher_type) {
int hostkeylen, siglen;
void *hkey; /* actual host key */
unsigned char exchange_hash[20];
+ int n_preferred_kex;
+ const struct ssh_kex *preferred_kex[KEX_MAX];
int n_preferred_ciphers;
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
const struct ssh_compress *preferred_comp;
{
int i;
/*
+ * Set up the preferred key exchange. (NULL => warn below here)
+ */
+ s->n_preferred_kex = 0;
+ for (i = 0; i < KEX_MAX; i++) {
+ switch (ssh->cfg.ssh_kexlist[i]) {
+ case KEX_DHGEX:
+ s->preferred_kex[s->n_preferred_kex++] =
+ &ssh_diffiehellman_gex;
+ break;
+ case KEX_DHGROUP14:
+ s->preferred_kex[s->n_preferred_kex++] =
+ &ssh_diffiehellman_group14;
+ break;
+ case KEX_DHGROUP1:
+ s->preferred_kex[s->n_preferred_kex++] =
+ &ssh_diffiehellman_group1;
+ break;
+ case CIPHER_WARN:
+ /* Flag for later. Don't bother if it's the last in
+ * the list. */
+ if (i < KEX_MAX - 1) {
+ s->preferred_kex[s->n_preferred_kex++] = NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ {
+ int i;
+ /*
* Set up the preferred ciphers. (NULL => warn below here)
*/
s->n_preferred_ciphers = 0;
begin_key_exchange:
{
- int i, j, cipherstr_started;
+ int i, j, commalist_started;
/*
* Enable queueing of outgoing auth- or connection-layer
ssh2_pkt_addbyte(s->pktout, (unsigned char) random_byte());
/* List key exchange algorithms. */
ssh2_pkt_addstring_start(s->pktout);
- for (i = 0; i < lenof(kex_algs); i++) {
- if (kex_algs[i] == &ssh_diffiehellman_gex &&
- (ssh->remote_bugs & BUG_SSH2_DH_GEX))
- continue;
- ssh2_pkt_addstring_str(s->pktout, kex_algs[i]->name);
- if (i < lenof(kex_algs) - 1)
+ commalist_started = 0;
+ for (i = 0; i < s->n_preferred_kex; i++) {
+ const struct ssh_kex *k = s->preferred_kex[i];
+ if (!k) continue; /* warning flag */
+ if (commalist_started)
ssh2_pkt_addstring_str(s->pktout, ",");
+ ssh2_pkt_addstring_str(s->pktout, s->preferred_kex[i]->name);
+ commalist_started = 1;
}
/* List server host key algorithms. */
ssh2_pkt_addstring_start(s->pktout);
}
/* List client->server encryption algorithms. */
ssh2_pkt_addstring_start(s->pktout);
- cipherstr_started = 0;
+ commalist_started = 0;
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++) {
- if (cipherstr_started)
+ if (commalist_started)
ssh2_pkt_addstring_str(s->pktout, ",");
ssh2_pkt_addstring_str(s->pktout, c->list[j]->name);
- cipherstr_started = 1;
+ commalist_started = 1;
}
}
/* List server->client encryption algorithms. */
ssh2_pkt_addstring_start(s->pktout);
- cipherstr_started = 0;
+ commalist_started = 0;
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++) {
- if (cipherstr_started)
+ if (commalist_started)
ssh2_pkt_addstring_str(s->pktout, ",");
ssh2_pkt_addstring_str(s->pktout, c->list[j]->name);
- cipherstr_started = 1;
+ commalist_started = 1;
}
}
/* List client->server MAC algorithms. */
s->sccomp_tobe = NULL;
pktin->savedpos += 16; /* skip garbage cookie */
ssh_pkt_getstring(pktin, &str, &len); /* key exchange algorithms */
- for (i = 0; i < lenof(kex_algs); i++) {
- if (kex_algs[i] == &ssh_diffiehellman_gex &&
- (ssh->remote_bugs & BUG_SSH2_DH_GEX))
- continue;
- if (in_commasep_string(kex_algs[i]->name, str, len)) {
- ssh->kex = kex_algs[i];
+ s->warn = 0;
+ 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;
+ }
+ if (ssh->kex) {
+ if (s->warn)
+ askalg(ssh->frontend, "key-exchange algorithm",
+ ssh->kex->name);
break;
}
}
+ if (!ssh->kex) {
+ bombout(("Couldn't agree a key exchange algorithm (available: %s)",
+ str ? str : "(null)"));
+ crStop(0);
+ }
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)) {
}
if (s->cscipher_tobe) {
if (s->warn)
- askcipher(ssh->frontend, s->cscipher_tobe->name, 1);
+ askalg(ssh->frontend, "client-to-server cipher",
+ s->cscipher_tobe->name);
break;
}
}
}
if (s->sccipher_tobe) {
if (s->warn)
- askcipher(ssh->frontend, s->sccipher_tobe->name, 2);
+ askalg(ssh->frontend, "server-to-client cipher",
+ s->sccipher_tobe->name);
break;
}
}
* Key exchange is over. Schedule a timer for our next rekey.
*/
ssh->kex_in_progress = FALSE;
- ssh->next_rekey = schedule_timer(REKEY_TIMEOUT, ssh2_timer, ssh);
-
+ if (ssh->cfg.ssh_rekey_time != 0)
+ ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
+ ssh2_timer, ssh);
+
/*
* If this is the first key exchange phase, we must pass the
* SSH2_MSG_NEWKEYS packet to the next layer, not because it
if (pktin) {
ssh->incoming_data_size += pktin->encrypted_len;
if (!ssh->kex_in_progress &&
- ssh->incoming_data_size > MAX_DATA_BEFORE_REKEY)
+ ssh->max_data_size != 0 &&
+ ssh->incoming_data_size > ssh->max_data_size)
do_ssh2_transport(ssh, "Initiating key re-exchange "
"(too much data received)", -1, NULL);
}
ssh->incoming_data_size = ssh->outgoing_data_size =
ssh->deferred_data_size = 0L;
+ ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);
ssh->kex_in_progress = FALSE;
p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
*/
static const struct telnet_special *ssh_get_specials(void *handle)
{
- static const struct telnet_special ignore_special[] = {
+ static const struct telnet_special ssh1_ignore_special[] = {
+ {"IGNORE message", TS_NOP}
+ };
+ static const struct telnet_special ssh2_transport_specials[] = {
{"IGNORE message", TS_NOP},
{"Repeat key exchange", TS_REKEY},
};
static const struct telnet_special ssh2_session_specials[] = {
{NULL, TS_SEP},
{"Break", TS_BRK},
- /* These are the signal names defined by draft-ietf-secsh-connect-19.
+ /* These are the signal names defined by draft-ietf-secsh-connect-23.
* They include all the ISO C signals, but are a subset of the POSIX
* required signals. */
{"SIGINT (Interrupt)", TS_SIGINT},
static const struct telnet_special specials_end[] = {
{NULL, TS_EXITMENU}
};
- static struct telnet_special ssh_specials[lenof(ignore_special) +
+ /* XXX review this length for any changes: */
+ static struct telnet_special ssh_specials[lenof(ssh2_transport_specials) +
lenof(ssh2_session_specials) +
lenof(specials_end)];
Ssh ssh = (Ssh) handle;
* won't cope with it, since we wouldn't bother sending it if
* asked anyway. */
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
- ADD_SPECIALS(ignore_special);
+ ADD_SPECIALS(ssh1_ignore_special);
} else if (ssh->version == 2) {
- ADD_SPECIALS(ignore_special);
+ ADD_SPECIALS(ssh2_transport_specials);
if (ssh->mainchan)
ADD_SPECIALS(ssh2_session_specials);
} /* else we're not ready yet */