1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-07-02 03:52:49 -05:00

Revamp interface to verify_ssh_host_key() and askalg(). Each of them

now returns an integer: 0 means cancel the SSH connection and 1
means continue with it. Additionally, they can return -1, which
means `front end has set an asynchronous alert box in motion, please
wait to be called back with the result', and each one is passed a
callback function pointer and context for this purpose.

I have not yet done the same to askappend() yet, because it will
take a certain amount of reorganisation of logging.c.

Importantly, this checkin means the host key dialog box now works on
OS X.

[originally from svn r5330]
This commit is contained in:
Simon Tatham
2005-02-17 18:34:24 +00:00
parent 92ccb964a2
commit 8574822b9b
11 changed files with 456 additions and 140 deletions

260
ssh.c
View File

@ -726,10 +726,23 @@ struct ssh_tag {
Config cfg;
/*
* Used to transfer data back from async agent callbacks.
* Used to transfer data back from async callbacks.
*/
void *agent_response;
int agent_response_len;
int user_response;
/*
* The SSH connection can be set as `frozen', meaning we are
* not currently accepting incoming data from the network. This
* is slightly more serious than setting the _socket_ as
* frozen, because we may already have had data passed to us
* from the network which we need to delay processing until
* after the freeze is lifted, so we also need a bufchain to
* store that data.
*/
int frozen;
bufchain queued_incoming_data;
/*
* Dispatch table for packet types that we may have to deal
@ -2331,6 +2344,49 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
crFinish(0);
}
static void ssh_process_incoming_data(Ssh ssh,
unsigned char **data, int *datalen)
{
struct Packet *pktin = ssh->s_rdpkt(ssh, data, datalen);
if (pktin) {
ssh->protocol(ssh, NULL, 0, pktin);
ssh_free_packet(pktin);
}
}
static void ssh_queue_incoming_data(Ssh ssh,
unsigned char **data, int *datalen)
{
bufchain_add(&ssh->queued_incoming_data, *data, *datalen);
*data += *datalen;
*datalen = 0;
}
static void ssh_process_queued_incoming_data(Ssh ssh)
{
void *vdata;
unsigned char *data;
int len, origlen;
while (!ssh->frozen && bufchain_size(&ssh->queued_incoming_data)) {
bufchain_prefix(&ssh->queued_incoming_data, &vdata, &len);
data = vdata;
origlen = len;
while (!ssh->frozen && len > 0)
ssh_process_incoming_data(ssh, &data, &len);
if (origlen > len)
bufchain_consume(&ssh->queued_incoming_data, origlen - len);
}
}
static void ssh_set_frozen(Ssh ssh, int frozen)
{
sk_set_frozen(ssh->s, frozen);
ssh->frozen = frozen;
}
static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
{
crBegin(ssh->ssh_gotdata_crstate);
@ -2360,13 +2416,19 @@ static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
*/
if (datalen == 0)
crReturnV;
/*
* Process queued data if there is any.
*/
ssh_process_queued_incoming_data(ssh);
while (1) {
while (datalen > 0) {
struct Packet *pktin = ssh->s_rdpkt(ssh, &data, &datalen);
if (pktin) {
ssh->protocol(ssh, NULL, 0, pktin);
ssh_free_packet(pktin);
}
if (ssh->frozen)
ssh_queue_incoming_data(ssh, &data, &datalen);
ssh_process_incoming_data(ssh, &data, &datalen);
if (ssh->state == SSH_STATE_CLOSED)
return;
}
@ -2554,9 +2616,9 @@ static void ssh1_throttle(Ssh ssh, int adjust)
ssh->v1_throttle_count += adjust;
assert(ssh->v1_throttle_count >= 0);
if (ssh->v1_throttle_count && !old_count) {
sk_set_frozen(ssh->s, 1);
ssh_set_frozen(ssh, 1);
} else if (!ssh->v1_throttle_count && old_count) {
sk_set_frozen(ssh->s, 0);
ssh_set_frozen(ssh, 0);
}
}
@ -2680,6 +2742,24 @@ static void ssh_agent_callback(void *sshv, void *reply, int replylen)
do_ssh2_authconn(ssh, NULL, -1, NULL);
}
static void ssh_dialog_callback(void *sshv, int ret)
{
Ssh ssh = (Ssh) sshv;
ssh->user_response = ret;
if (ssh->version == 1)
do_ssh1_login(ssh, NULL, -1, NULL);
else
do_ssh2_transport(ssh, NULL, -1, NULL);
/*
* This may have unfrozen the SSH connection, so do a
* queued-data run.
*/
ssh_process_queued_incoming_data(ssh);
}
static void ssh_agentf_callback(void *cv, void *reply, int replylen)
{
struct ssh_channel *c = (struct ssh_channel *)cv;
@ -2741,6 +2821,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
Bignum challenge;
char *commentp;
int commentlen;
int dlgret;
};
crState(do_ssh1_login_state);
@ -2828,10 +2909,30 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
fatalbox("Out of memory");
rsastr_fmt(keystr, &hostkey);
rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport, "rsa", keystr,
fingerprint);
ssh_set_frozen(ssh, 1);
s->dlgret = verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport,
"rsa", keystr, fingerprint,
ssh_dialog_callback, ssh);
sfree(keystr);
if (s->dlgret < 0) {
do {
crReturn(0);
if (pktin) {
bombout(("Unexpected data from server while waiting"
" for user host key response"));
crStop(0);
}
} while (pktin || inlen > 0);
s->dlgret = ssh->user_response;
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
}
}
for (i = 0; i < 32; i++) {
@ -2893,9 +2994,25 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
/* Warn about chosen cipher if necessary. */
if (warn) {
sk_set_frozen(ssh->s, 1);
askalg(ssh->frontend, "cipher", cipher_string);
sk_set_frozen(ssh->s, 0);
ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend, "cipher", cipher_string,
ssh_dialog_callback, ssh);
if (s->dlgret < 0) {
do {
crReturn(0);
if (pktin) {
bombout(("Unexpected data from server while waiting"
" for user response"));
crStop(0);
}
} while (pktin || inlen > 0);
s->dlgret = ssh->user_response;
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
}
}
}
@ -4732,6 +4849,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
const struct ssh_compress *preferred_comp;
int got_session_id, activated_authconn;
struct Packet *pktout;
int dlgret;
int guessok;
};
crState(do_ssh2_transport_state);
@ -4945,7 +5064,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
*/
{
char *str;
int i, j, len, guessok;
int i, j, len;
if (pktin->type != SSH2_MSG_KEXINIT) {
bombout(("expected key exchange packet from server"));
@ -4971,10 +5090,26 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
}
if (ssh->kex) {
if (s->warn) {
sk_set_frozen(ssh->s, 1);
askalg(ssh->frontend, "key-exchange algorithm",
ssh->kex->name);
sk_set_frozen(ssh->s, 0);
ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",
ssh->kex->name,
ssh_dialog_callback, ssh);
if (s->dlgret < 0) {
do {
crReturn(0);
if (pktin) {
bombout(("Unexpected data from server while"
" waiting for user response"));
crStop(0);
}
} while (pktin || inlen > 0);
s->dlgret = ssh->user_response;
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
}
}
break;
}
@ -4989,7 +5124,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
* the first algorithm in our list, even if it's still the algorithm
* we end up using.
*/
guessok =
s->guessok =
first_in_commasep_string(s->preferred_kex[0]->name, str, len);
ssh_pkt_getstring(pktin, &str, &len); /* host key algorithms */
for (i = 0; i < lenof(hostkey_algs); i++) {
@ -4998,7 +5133,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
break;
}
}
guessok = guessok &&
s->guessok = s->guessok &&
first_in_commasep_string(hostkey_algs[0]->name, str, len);
ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */
s->warn = 0;
@ -5016,10 +5151,27 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
}
if (s->cscipher_tobe) {
if (s->warn) {
sk_set_frozen(ssh->s, 1);
askalg(ssh->frontend, "client-to-server cipher",
s->cscipher_tobe->name);
sk_set_frozen(ssh->s, 0);
ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend,
"client-to-server cipher",
s->cscipher_tobe->name,
ssh_dialog_callback, ssh);
if (s->dlgret < 0) {
do {
crReturn(0);
if (pktin) {
bombout(("Unexpected data from server while"
" waiting for user response"));
crStop(0);
}
} while (pktin || inlen > 0);
s->dlgret = ssh->user_response;
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
}
}
break;
}
@ -5046,10 +5198,27 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
}
if (s->sccipher_tobe) {
if (s->warn) {
sk_set_frozen(ssh->s, 1);
askalg(ssh->frontend, "server-to-client cipher",
s->sccipher_tobe->name);
sk_set_frozen(ssh->s, 0);
ssh_set_frozen(ssh, 1);
s->dlgret = askalg(ssh->frontend,
"server-to-client cipher",
s->sccipher_tobe->name,
ssh_dialog_callback, ssh);
if (s->dlgret < 0) {
do {
crReturn(0);
if (pktin) {
bombout(("Unexpected data from server while"
" waiting for user response"));
crStop(0);
}
} while (pktin || inlen > 0);
s->dlgret = ssh->user_response;
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
}
}
break;
}
@ -5094,7 +5263,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
}
ssh_pkt_getstring(pktin, &str, &len); /* client->server language */
ssh_pkt_getstring(pktin, &str, &len); /* server->client language */
if (ssh2_pkt_getbool(pktin) && !guessok) /* first_kex_packet_follows */
if (ssh2_pkt_getbool(pktin) && !s->guessok) /* first_kex_packet_follows */
crWaitUntil(pktin); /* Ignore packet */
}
@ -5218,11 +5387,29 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
*/
s->keystr = ssh->hostkey->fmtkey(s->hkey);
s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
sk_set_frozen(ssh->s, 1);
verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport, ssh->hostkey->keytype,
s->keystr, s->fingerprint);
sk_set_frozen(ssh->s, 0);
ssh_set_frozen(ssh, 1);
s->dlgret = verify_ssh_host_key(ssh->frontend,
ssh->savedhost, ssh->savedport,
ssh->hostkey->keytype, s->keystr,
s->fingerprint,
ssh_dialog_callback, ssh);
if (s->dlgret < 0) {
do {
crReturn(0);
if (pktin) {
bombout(("Unexpected data from server while waiting"
" for user host key response"));
crStop(0);
}
} while (pktin || inlen > 0);
s->dlgret = ssh->user_response;
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0);
crStop(0);
}
if (!s->got_session_id) { /* don't bother logging this in rekeys */
logevent("Host key fingerprint is:");
logevent(s->fingerprint);
@ -7477,6 +7664,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->queueing = FALSE;
ssh->qhead = ssh->qtail = NULL;
ssh->deferred_rekey_reason = NULL;
bufchain_init(&ssh->queued_incoming_data);
ssh->frozen = FALSE;
*backend_handle = ssh;
@ -7603,6 +7792,7 @@ static void ssh_free(void *handle)
expire_timer_context(ssh);
if (ssh->pinger)
pinger_free(ssh->pinger);
bufchain_clear(&ssh->queued_incoming_data);
sfree(ssh);
random_unref();