1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 09:58:01 +00:00

do_ssh1_login: get packets from a PacketQueue.

This introduces the first of several filtered PacketQueues that
receive subsets of pq_full destined for a particular coroutine. The
wrapper function ssh1_coro_wrapper_initial, whose purpose I just
removed in the previous commit, now gains the replacement purpose of
accepting a packet as a function argument and putting it on the new
queue for do_ssh1_login to handle when it's ready. That wrapper in
turn is called from the packet-type dispatch table, meaning that the
new pq_ssh1_login will be filtered down to only the packets destined
for this coroutine.

This is the point where I finally start using the reference counting
system that I added to 'struct Packet' about a dozen commits ago. The
general packet handling calls ssh_unref_packet for everything that
it's just pulled off pq_full and handed to a dispatch-table function -
so _this_ dispatch-table function, which needs the packet not to be
freed because it's going to go on to another queue and wait to be
handled there, can arrange that by incrementing its ref count.

This completes the transformation of do_ssh1_login into a function
with a trivial argument list, whose job is to read from a pair of
input queues (one for user keyboard input and one for SSH packets) and
respond by taking action directly rather than returning a value to its
caller to request action.

It also lets me get rid of a big pile of those really annoying
bombout() calls that I used to work around the old coroutine system's
inability to deal with receiving an SSH packet when the control flow
was in the middle of waiting for some other kind of input. That was
always the weakness of the coroutine structure of this code, which I
accepted as the price for the convenience of coroutines the rest of
the time - but now I think I've got the benefits without that cost :-)

The one remaining argument to do_ssh1_login is the main Ssh structure,
although I've had to turn it into a void * to make the function's type
compatible with the idempotent callback mechanism, since that will be
calling do_ssh1_login directly when its input queue needs looking at.
This commit is contained in:
Simon Tatham 2018-05-18 07:22:58 +01:00
parent 96d9d788f6
commit 6cfe0a212e

136
ssh.c
View File

@ -384,7 +384,7 @@ static void ssh2_pkt_addmp(struct Packet *, Bignum b);
static int ssh2_pkt_construct(Ssh, struct Packet *); static int ssh2_pkt_construct(Ssh, struct Packet *);
static void ssh2_pkt_send(Ssh, struct Packet *); static void ssh2_pkt_send(Ssh, struct Packet *);
static void ssh2_pkt_send_noqueue(Ssh, struct Packet *); static void ssh2_pkt_send_noqueue(Ssh, struct Packet *);
static void do_ssh1_login(Ssh ssh, struct Packet *pktin); static void do_ssh1_login(void *vctx);
static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen, static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin); struct Packet *pktin);
static void ssh_channel_init(struct ssh_channel *c); static void ssh_channel_init(struct ssh_channel *c);
@ -967,6 +967,9 @@ struct ssh_tag {
struct PacketQueue pq_full; struct PacketQueue pq_full;
struct IdempotentCallback pq_full_consumer; struct IdempotentCallback pq_full_consumer;
struct PacketQueue pq_ssh1_login;
struct IdempotentCallback ssh1_login_icb;
bufchain user_input; bufchain user_input;
struct IdempotentCallback user_input_consumer; struct IdempotentCallback user_input_consumer;
@ -3952,7 +3955,7 @@ static void ssh_agent_callback(void *sshv, void *reply, int replylen)
ssh->agent_response_len = replylen; ssh->agent_response_len = replylen;
if (ssh->version == 1) if (ssh->version == 1)
do_ssh1_login(ssh, NULL); do_ssh1_login(ssh);
else else
do_ssh2_authconn(ssh, NULL, -1, NULL); do_ssh2_authconn(ssh, NULL, -1, NULL);
} }
@ -3964,7 +3967,7 @@ static void ssh_dialog_callback(void *sshv, int ret)
ssh->user_response = ret; ssh->user_response = ret;
if (ssh->version == 1) if (ssh->version == 1)
do_ssh1_login(ssh, NULL); do_ssh1_login(ssh);
else else
do_ssh2_transport(ssh, NULL, -1, NULL); do_ssh2_transport(ssh, NULL, -1, NULL);
@ -4202,8 +4205,11 @@ static void ssh1_connection_input(Ssh ssh);
/* /*
* Handle the key exchange and user authentication phases. * Handle the key exchange and user authentication phases.
*/ */
static void do_ssh1_login(Ssh ssh, struct Packet *pktin) static void do_ssh1_login(void *vctx)
{ {
Ssh ssh = (Ssh)vctx;
struct Packet *pktin;
int i, j, ret; int i, j, ret;
unsigned char *ptr; unsigned char *ptr;
struct MD5Context md5c; struct MD5Context md5c;
@ -4223,6 +4229,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
char *publickey_comment; char *publickey_comment;
int privatekey_available, privatekey_encrypted; int privatekey_available, privatekey_encrypted;
prompts_t *cur_prompt; prompts_t *cur_prompt;
int userpass_ret;
char c; char c;
int pwpkt_type; int pwpkt_type;
unsigned char request[5], *response, *p; unsigned char request[5], *response, *p;
@ -4241,8 +4248,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
crBeginState; crBeginState;
if (!pktin) crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
crWaitUntilV(pktin);
if (pktin->type != SSH1_SMSG_PUBLIC_KEY) { if (pktin->type != SSH1_SMSG_PUBLIC_KEY) {
bombout(("Public key packet not received")); bombout(("Public key packet not received"));
@ -4341,14 +4347,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
#endif #endif
if (s->dlgret < 0) { if (s->dlgret < 0) {
ssh->user_response = -1; ssh->user_response = -1;
do { crWaitUntilV(ssh->user_response >= 0);
crReturnV;
if (pktin) {
bombout(("Unexpected data from server while waiting"
" for user host key response"));
crStop(0);
}
} while (ssh->user_response < 0);
s->dlgret = ssh->user_response; s->dlgret = ssh->user_response;
} }
ssh_set_frozen(ssh, 0); ssh_set_frozen(ssh, 0);
@ -4428,14 +4427,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
ssh_dialog_callback, ssh); ssh_dialog_callback, ssh);
if (s->dlgret < 0) { if (s->dlgret < 0) {
ssh->user_response = -1; ssh->user_response = -1;
do { crWaitUntilV(ssh->user_response >= 0);
crReturnV;
if (pktin) {
bombout(("Unexpected data from server while waiting"
" for user response"));
crStop(0);
}
} while (ssh->user_response < 0);
s->dlgret = ssh->user_response; s->dlgret = ssh->user_response;
} }
ssh_set_frozen(ssh, 0); ssh_set_frozen(ssh, 0);
@ -4496,7 +4488,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
sfree(s->hostkey.exponent); sfree(s->hostkey.exponent);
s->hostkey.exponent = NULL; s->hostkey.exponent = NULL;
} }
crWaitUntilV(pktin); crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
if (pktin->type != SSH1_SMSG_SUCCESS) { if (pktin->type != SSH1_SMSG_SUCCESS) {
bombout(("Encryption not successfully enabled")); bombout(("Encryption not successfully enabled"));
@ -4506,21 +4498,22 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
logevent("Successfully started encryption"); logevent("Successfully started encryption");
fflush(stdout); /* FIXME eh? */ fflush(stdout); /* FIXME eh? */
{
if ((ssh->username = get_remote_username(ssh->conf)) == NULL) { if ((ssh->username = get_remote_username(ssh->conf)) == NULL) {
int ret; /* need not be kept over crReturn */
s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE; s->cur_prompt->to_server = TRUE;
s->cur_prompt->name = dupstr("SSH login name"); s->cur_prompt->name = dupstr("SSH login name");
add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); add_prompt(s->cur_prompt, dupstr("login as: "), TRUE);
ret = get_userpass_input(s->cur_prompt, NULL); s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
while (ret < 0) { while (s->userpass_ret < 0) {
ssh->send_ok = 1; ssh->send_ok = 1;
crWaitUntilV(!pktin); crReturnV;
ret = get_userpass_input(s->cur_prompt, &ssh->user_input); while (s->userpass_ret < 0 &&
bufchain_size(&ssh->user_input) > 0)
s->userpass_ret = get_userpass_input(
s->cur_prompt, &ssh->user_input);
ssh->send_ok = 0; ssh->send_ok = 0;
} }
if (!ret) { if (!s->userpass_ret) {
/* /*
* Failed to get a username. Terminate. * Failed to get a username. Terminate.
*/ */
@ -4543,9 +4536,8 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
} }
sfree(userlog); sfree(userlog);
} }
}
crWaitUntilV(pktin); crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) { if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) {
/* We must not attempt PK auth. Pretend we've already tried it. */ /* We must not attempt PK auth. Pretend we've already tried it. */
@ -4620,14 +4612,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
s->request, 5, &r, &s->responselen, ssh_agent_callback, ssh); s->request, 5, &r, &s->responselen, ssh_agent_callback, ssh);
if (ssh->auth_agent_query) { if (ssh->auth_agent_query) {
ssh->agent_response = NULL; ssh->agent_response = NULL;
do { crWaitUntilV(ssh->agent_response);
crReturnV;
if (pktin) {
bombout(("Unexpected data from server while waiting"
" for agent response"));
crStop(0);
}
} while (!ssh->agent_response);
r = ssh->agent_response; r = ssh->agent_response;
s->responselen = ssh->agent_response_len; s->responselen = ssh->agent_response_len;
} }
@ -4691,7 +4676,8 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
logeventf(ssh, "Trying Pageant key #%d", s->keyi); logeventf(ssh, "Trying Pageant key #%d", s->keyi);
send_packet(ssh, SSH1_CMSG_AUTH_RSA, send_packet(ssh, SSH1_CMSG_AUTH_RSA,
PKT_BIGNUM, s->key.modulus, PKT_END); PKT_BIGNUM, s->key.modulus, PKT_END);
crWaitUntilV(pktin); crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login))
!= NULL);
if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) { if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
logevent("Key refused"); logevent("Key refused");
continue; continue;
@ -4730,15 +4716,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
if (ssh->auth_agent_query) { if (ssh->auth_agent_query) {
sfree(agentreq); sfree(agentreq);
ssh->agent_response = NULL; ssh->agent_response = NULL;
do { crWaitUntilV(ssh->agent_response);
crReturnV;
if (pktin) {
bombout(("Unexpected data from server"
" while waiting for agent"
" response"));
crStop(0);
}
} while (!ssh->agent_response);
vret = ssh->agent_response; vret = ssh->agent_response;
retlen = ssh->agent_response_len; retlen = ssh->agent_response_len;
} else } else
@ -4751,7 +4729,9 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
PKT_DATA, ret + 5, 16, PKT_DATA, ret + 5, 16,
PKT_END); PKT_END);
sfree(ret); sfree(ret);
crWaitUntilV(pktin); crMaybeWaitUntilV(
(pktin = pq_pop(&ssh->pq_ssh1_login))
!= NULL);
if (pktin->type == SSH1_SMSG_SUCCESS) { if (pktin->type == SSH1_SMSG_SUCCESS) {
logevent logevent
("Pageant's response accepted"); ("Pageant's response accepted");
@ -4815,22 +4795,23 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
c_write_str(ssh, "No passphrase required.\r\n"); c_write_str(ssh, "No passphrase required.\r\n");
passphrase = NULL; passphrase = NULL;
} else { } else {
int ret; /* need not be kept over crReturn */
s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = FALSE; s->cur_prompt->to_server = FALSE;
s->cur_prompt->name = dupstr("SSH key passphrase"); s->cur_prompt->name = dupstr("SSH key passphrase");
add_prompt(s->cur_prompt, add_prompt(s->cur_prompt,
dupprintf("Passphrase for key \"%.100s\": ", dupprintf("Passphrase for key \"%.100s\": ",
s->publickey_comment), FALSE); s->publickey_comment), FALSE);
ret = get_userpass_input(s->cur_prompt, NULL); s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
while (ret < 0) { while (s->userpass_ret < 0) {
ssh->send_ok = 1; ssh->send_ok = 1;
crWaitUntilV(!pktin); crReturnV;
ret = get_userpass_input(s->cur_prompt, while (s->userpass_ret < 0 &&
&ssh->user_input); bufchain_size(&ssh->user_input) > 0)
s->userpass_ret = get_userpass_input(
s->cur_prompt, &ssh->user_input);
ssh->send_ok = 0; ssh->send_ok = 0;
} }
if (!ret) { if (!s->userpass_ret) {
/* Failed to get a passphrase. Terminate. */ /* Failed to get a passphrase. Terminate. */
free_prompts(s->cur_prompt); free_prompts(s->cur_prompt);
ssh_disconnect(ssh, NULL, "Unable to authenticate", ssh_disconnect(ssh, NULL, "Unable to authenticate",
@ -4879,7 +4860,8 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
send_packet(ssh, SSH1_CMSG_AUTH_RSA, send_packet(ssh, SSH1_CMSG_AUTH_RSA,
PKT_BIGNUM, s->key.modulus, PKT_END); PKT_BIGNUM, s->key.modulus, PKT_END);
crWaitUntilV(pktin); crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login))
!= NULL);
if (pktin->type == SSH1_SMSG_FAILURE) { if (pktin->type == SSH1_SMSG_FAILURE) {
c_write_str(ssh, "Server refused our public key.\r\n"); c_write_str(ssh, "Server refused our public key.\r\n");
continue; /* go and try something else */ continue; /* go and try something else */
@ -4917,7 +4899,8 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
freebn(response); freebn(response);
} }
crWaitUntilV(pktin); crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login))
!= NULL);
if (pktin->type == SSH1_SMSG_FAILURE) { if (pktin->type == SSH1_SMSG_FAILURE) {
if (flags & FLAG_VERBOSE) if (flags & FLAG_VERBOSE)
c_write_str(ssh, "Failed to authenticate with" c_write_str(ssh, "Failed to authenticate with"
@ -4944,7 +4927,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE; s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
logevent("Requested TIS authentication"); logevent("Requested TIS authentication");
send_packet(ssh, SSH1_CMSG_AUTH_TIS, PKT_END); send_packet(ssh, SSH1_CMSG_AUTH_TIS, PKT_END);
crWaitUntilV(pktin); crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
if (pktin->type != SSH1_SMSG_AUTH_TIS_CHALLENGE) { if (pktin->type != SSH1_SMSG_AUTH_TIS_CHALLENGE) {
logevent("TIS authentication declined"); logevent("TIS authentication declined");
if (flags & FLAG_INTERACTIVE) if (flags & FLAG_INTERACTIVE)
@ -4987,7 +4970,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE; s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
logevent("Requested CryptoCard authentication"); logevent("Requested CryptoCard authentication");
send_packet(ssh, SSH1_CMSG_AUTH_CCARD, PKT_END); send_packet(ssh, SSH1_CMSG_AUTH_CCARD, PKT_END);
crWaitUntilV(pktin); crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
if (pktin->type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) { if (pktin->type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
logevent("CryptoCard authentication declined"); logevent("CryptoCard authentication declined");
c_write_str(ssh, "CryptoCard authentication refused.\r\n"); c_write_str(ssh, "CryptoCard authentication refused.\r\n");
@ -5041,16 +5024,17 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
* or CryptoCard exchange if we're doing TIS or CryptoCard * or CryptoCard exchange if we're doing TIS or CryptoCard
* authentication. * authentication.
*/ */
{ s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
int ret; /* need not be kept over crReturn */ while (s->userpass_ret < 0) {
ret = get_userpass_input(s->cur_prompt, NULL);
while (ret < 0) {
ssh->send_ok = 1; ssh->send_ok = 1;
crWaitUntilV(!pktin); crReturnV;
ret = get_userpass_input(s->cur_prompt, &ssh->user_input); while (s->userpass_ret < 0 &&
bufchain_size(&ssh->user_input) > 0)
s->userpass_ret = get_userpass_input(
s->cur_prompt, &ssh->user_input);
ssh->send_ok = 0; ssh->send_ok = 0;
} }
if (!ret) { if (!s->userpass_ret) {
/* /*
* Failed to get a password (for example * Failed to get a password (for example
* because one was supplied on the command line * because one was supplied on the command line
@ -5060,7 +5044,6 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
ssh_disconnect(ssh, NULL, "Unable to authenticate", 0, TRUE); ssh_disconnect(ssh, NULL, "Unable to authenticate", 0, TRUE);
crStopV; crStopV;
} }
}
if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) { if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
/* /*
@ -5187,7 +5170,7 @@ static void do_ssh1_login(Ssh ssh, struct Packet *pktin)
} }
logevent("Sent password"); logevent("Sent password");
free_prompts(s->cur_prompt); free_prompts(s->cur_prompt);
crWaitUntilV(pktin); crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
if (pktin->type == SSH1_SMSG_FAILURE) { if (pktin->type == SSH1_SMSG_FAILURE) {
if (flags & FLAG_VERBOSE) if (flags & FLAG_VERBOSE)
c_write_str(ssh, "Access denied\r\n"); c_write_str(ssh, "Access denied\r\n");
@ -6278,7 +6261,7 @@ static void ssh_msg_ignore(Ssh ssh, struct Packet *pktin)
static void ssh1_login_input(Ssh ssh) static void ssh1_login_input(Ssh ssh)
{ {
do_ssh1_login(ssh, NULL); do_ssh1_login(ssh);
} }
static void ssh1_connection_input(Ssh ssh) static void ssh1_connection_input(Ssh ssh)
@ -6294,7 +6277,9 @@ static void ssh1_connection_input(Ssh ssh)
static void ssh1_coro_wrapper_initial(Ssh ssh, struct Packet *pktin) static void ssh1_coro_wrapper_initial(Ssh ssh, struct Packet *pktin)
{ {
do_ssh1_login(ssh, pktin); pktin->refcount++; /* avoid packet being freed when we return */
pq_push(&ssh->pq_ssh1_login, pktin);
queue_idempotent_callback(&ssh->ssh1_login_icb);
} }
static void ssh1_coro_wrapper_session(Ssh ssh, struct Packet *pktin) static void ssh1_coro_wrapper_session(Ssh ssh, struct Packet *pktin)
@ -12334,6 +12319,10 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->pq_full_consumer.fn = ssh_process_pq_full; ssh->pq_full_consumer.fn = ssh_process_pq_full;
ssh->pq_full_consumer.ctx = ssh; ssh->pq_full_consumer.ctx = ssh;
ssh->pq_full_consumer.queued = FALSE; ssh->pq_full_consumer.queued = FALSE;
pq_init(&ssh->pq_ssh1_login);
ssh->ssh1_login_icb.fn = do_ssh1_login;
ssh->ssh1_login_icb.ctx = ssh;
ssh->ssh1_login_icb.queued = FALSE;
bufchain_init(&ssh->user_input); bufchain_init(&ssh->user_input);
ssh->user_input_consumer.fn = ssh_process_user_input; ssh->user_input_consumer.fn = ssh_process_user_input;
ssh->user_input_consumer.ctx = ssh; ssh->user_input_consumer.ctx = ssh;
@ -12512,6 +12501,7 @@ static void ssh_free(void *handle)
bufchain_clear(&ssh->incoming_data); bufchain_clear(&ssh->incoming_data);
sfree(ssh->incoming_data_eof_message); sfree(ssh->incoming_data_eof_message);
pq_clear(&ssh->pq_full); pq_clear(&ssh->pq_full);
pq_clear(&ssh->pq_ssh1_login);
bufchain_clear(&ssh->user_input); bufchain_clear(&ssh->user_input);
sfree(ssh->v_c); sfree(ssh->v_c);
sfree(ssh->v_s); sfree(ssh->v_s);