*/
#define SSH2_PKTCTX_DHGROUP 0x0001
#define SSH2_PKTCTX_DHGEX 0x0002
+#define SSH2_PKTCTX_KEX_MASK 0x000F
#define SSH2_PKTCTX_PUBLICKEY 0x0010
#define SSH2_PKTCTX_PASSWORD 0x0020
#define SSH2_PKTCTX_KBDINTER 0x0040
int type;
unsigned sport, dport;
char *saddr, *daddr;
+ char *sserv, *dserv;
struct ssh_rportfwd *remote;
int addressfamily;
void *local;
};
#define free_portfwd(pf) ( \
- ((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr)) : (void)0 ), sfree(pf) )
+ ((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
+ sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )
struct Packet {
long length;
unsigned long max_data_size;
int kex_in_progress;
long next_rekey, last_rekey;
+ char *deferred_rekey_reason; /* points to STATIC string; don't free */
};
#define logevent(s) logevent(ssh->frontend, s)
}
/* Warn about chosen cipher if necessary. */
- if (warn)
+ if (warn) {
+ sk_set_frozen(ssh->s, 1);
askalg(ssh->frontend, "cipher", cipher_string);
+ sk_set_frozen(ssh->s, 0);
+ }
}
switch (s->cipher_type) {
pfrec = snew(struct ssh_portfwd);
pfrec->type = type;
pfrec->saddr = *saddr ? dupstr(saddr) : NULL;
+ pfrec->sserv = sserv ? dupstr(sports) : NULL;
pfrec->sport = sport;
pfrec->daddr = *host ? dupstr(host) : NULL;
+ pfrec->dserv = dserv ? dupstr(dports) : NULL;
pfrec->dport = dport;
pfrec->local = NULL;
pfrec->remote = NULL;
for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
if (epf->status == CREATE) {
char *sportdesc, *dportdesc;
- sportdesc = dupprintf("%s%s%d",
+ sportdesc = dupprintf("%s%s%s%s%d%s",
epf->saddr ? epf->saddr : "",
epf->saddr ? ":" : "",
- epf->sport);
+ epf->sserv ? epf->sserv : "",
+ epf->sserv ? "(" : "",
+ epf->sport,
+ epf->sserv ? ")" : "");
if (epf->type == 'D') {
dportdesc = NULL;
} else {
- dportdesc = dupprintf("%s:%d", epf->daddr, epf->dport);
+ dportdesc = dupprintf("%s:%s%s%d%s",
+ epf->daddr,
+ epf->dserv ? epf->dserv : "",
+ epf->dserv ? "(" : "",
+ epf->dport,
+ epf->dserv ? ")" : "");
}
if (epf->type == 'L') {
int n_preferred_ciphers;
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
const struct ssh_compress *preferred_comp;
- int first_kex;
+ int got_session_id, activated_authconn;
struct Packet *pktout;
};
crState(do_ssh2_transport_state);
s->csmac_tobe = s->scmac_tobe = NULL;
s->cscomp_tobe = s->sccomp_tobe = NULL;
- s->first_kex = 1;
+ s->got_session_id = s->activated_authconn = FALSE;
+
+ /*
+ * Be prepared to work around the buggy MAC problem.
+ */
+ if (ssh->remote_bugs & BUG_SSH2_HMAC)
+ s->maclist = buggymacs, s->nmacs = lenof(buggymacs);
+ else
+ s->maclist = macs, s->nmacs = lenof(macs);
+ begin_key_exchange:
+ ssh->pkt_ctx &= ~SSH2_PKTCTX_KEX_MASK;
{
- int i;
+ int i, j, commalist_started;
+
/*
* Set up the preferred key exchange. (NULL => warn below here)
*/
break;
}
}
- }
- {
- int i;
/*
* Set up the preferred ciphers. (NULL => warn below here)
*/
break;
}
}
- }
-
- /*
- * Set up preferred compression.
- */
- if (ssh->cfg.compression)
- s->preferred_comp = &ssh_zlib;
- else
- s->preferred_comp = &ssh_comp_none;
-
- /*
- * Be prepared to work around the buggy MAC problem.
- */
- if (ssh->remote_bugs & BUG_SSH2_HMAC)
- s->maclist = buggymacs, s->nmacs = lenof(buggymacs);
- else
- s->maclist = macs, s->nmacs = lenof(macs);
- begin_key_exchange:
- {
- int i, j, commalist_started;
+ /*
+ * Set up preferred compression.
+ */
+ if (ssh->cfg.compression)
+ s->preferred_comp = &ssh_zlib;
+ else
+ s->preferred_comp = &ssh_comp_none;
/*
* Enable queueing of outgoing auth- or connection-layer
ssh->kex = k;
}
if (ssh->kex) {
- if (s->warn)
+ if (s->warn) {
+ sk_set_frozen(ssh->s, 1);
askalg(ssh->frontend, "key-exchange algorithm",
ssh->kex->name);
+ sk_set_frozen(ssh->s, 0);
+ }
break;
}
}
}
}
if (s->cscipher_tobe) {
- if (s->warn)
+ 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);
+ }
break;
}
}
}
}
if (s->sccipher_tobe) {
- if (s->warn)
+ 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);
+ }
break;
}
}
*/
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);
- if (s->first_kex) { /* don't bother logging this in rekeys */
+ sk_set_frozen(ssh->s, 0);
+ if (!s->got_session_id) { /* don't bother logging this in rekeys */
logevent("Host key fingerprint is:");
logevent(s->fingerprint);
}
* the session id, used in session key construction and
* authentication.
*/
- if (s->first_kex)
+ if (!s->got_session_id) {
memcpy(ssh->v2_session_id, s->exchange_hash,
sizeof(s->exchange_hash));
+ s->got_session_id = TRUE;
+ }
/*
* Send SSH2_MSG_NEWKEYS.
}
/*
- * Key exchange is over. Schedule a timer for our next rekey.
+ * Key exchange is over. Loop straight back round if we have a
+ * deferred rekey reason.
+ */
+ if (ssh->deferred_rekey_reason) {
+ logevent(ssh->deferred_rekey_reason);
+ pktin = NULL;
+ ssh->deferred_rekey_reason = NULL;
+ goto begin_key_exchange;
+ }
+
+ /*
+ * Otherwise, schedule a timer for our next rekey.
*/
ssh->kex_in_progress = FALSE;
ssh->last_rekey = GETTICKCOUNT();
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
* exchange phases, we don't pass SSH2_MSG_NEWKEYS on, because
* it would only confuse the layer above.
*/
- if (!s->first_kex) {
+ if (s->activated_authconn) {
crReturn(1);
}
- s->first_kex = 0;
+ s->activated_authconn = TRUE;
/*
* Now we're encrypting. Begin returning 1 to the protocol main
AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
} type;
int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter;
- int tried_pubkey_config, tried_agent, tried_keyb_inter;
- int kbd_inter_running;
+ int tried_pubkey_config, tried_agent;
+ int kbd_inter_running, kbd_inter_refused;
int we_are_in;
int num_prompts, curr_prompt, echo;
char username[100];
s->tried_pubkey_config = FALSE;
s->tried_agent = FALSE;
- s->tried_keyb_inter = FALSE;
s->kbd_inter_running = FALSE;
+ s->kbd_inter_refused = FALSE;
/* Load the pub half of ssh->cfg.keyfile so we notice if it's in Pageant */
if (!filename_is_null(ssh->cfg.keyfile)) {
int keytype;
*/
if (!s->gotit)
s->curr_prompt = 0;
+ } else if (pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) {
+ /* FIXME: perhaps we should support this? */
+ bombout(("PASSWD_CHANGEREQ not yet supported"));
+ crStopV;
} else if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) {
bombout(("Strange packet received during authentication: type %d",
pktin->type));
}
}
- if (!s->method && s->can_keyb_inter && !s->tried_keyb_inter) {
+ if (!s->method && s->can_keyb_inter && !s->kbd_inter_refused &&
+ !s->kbd_inter_running) {
s->method = AUTH_KEYBOARD_INTERACTIVE;
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
- s->tried_keyb_inter = TRUE;
ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
ssh->pkt_ctx |= SSH2_PKTCTX_KBDINTER;
s->gotit = TRUE;
logevent("Keyboard-interactive authentication refused");
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
+ s->kbd_inter_refused = TRUE; /* don't try it again */
continue;
}
if (s->kbd_inter_running) {
s->method = AUTH_KEYBOARD_INTERACTIVE;
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
- s->tried_keyb_inter = TRUE;
ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
ssh->pkt_ctx |= SSH2_PKTCTX_KBDINTER;
ssh->queuelen = ssh->queuesize = 0;
ssh->queueing = FALSE;
ssh->qhead = ssh->qtail = NULL;
+ ssh->deferred_rekey_reason = NULL;
*backend_handle = ssh;
static void ssh_reconfig(void *handle, Config *cfg)
{
Ssh ssh = (Ssh) handle;
- char *rekeying = NULL;
+ char *rekeying = NULL, rekey_mandatory = FALSE;
unsigned long old_max_data_size;
pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);
rekeying = "Initiating key re-exchange (data limit lowered)";
}
- if (rekeying && !ssh->kex_in_progress) {
- do_ssh2_transport(ssh, rekeying, -1, NULL);
+ if (ssh->cfg.compression != cfg->compression) {
+ rekeying = "Initiating key re-exchange (compression setting changed)";
+ rekey_mandatory = TRUE;
+ }
+
+ if (ssh->cfg.ssh2_des_cbc != cfg->ssh2_des_cbc ||
+ memcmp(ssh->cfg.ssh_cipherlist, cfg->ssh_cipherlist,
+ sizeof(ssh->cfg.ssh_cipherlist))) {
+ rekeying = "Initiating key re-exchange (cipher settings changed)";
+ rekey_mandatory = TRUE;
}
ssh->cfg = *cfg; /* STRUCTURE COPY */
+
+ if (rekeying) {
+ if (!ssh->kex_in_progress) {
+ do_ssh2_transport(ssh, rekeying, -1, NULL);
+ } else if (rekey_mandatory) {
+ ssh->deferred_rekey_reason = rekeying;
+ }
+ }
}
/*