1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-03-22 14:39:24 -05:00

Fix culpable lack of generality in keyboard-interactive

authentication: a k-i request packet can contain any number of auth
prompts (including zero!) and we must ask the user all of them and
send back a packet containing the same number of responses. FreeBSD
systems were sending a zero-prompts packet which was crashing us;
this now appears fixed (we correctly return a zero-responses packet)
but I haven't tested a multiple-prompts packet because I can't
immediately think of a server that generates them.

[originally from svn r1797]
This commit is contained in:
Simon Tatham 2002-08-03 16:22:55 +00:00
parent 73e32fb7c0
commit 949cecd569

88
ssh.c
View File

@ -1541,6 +1541,7 @@ static int ssh2_pkt_getbool(void)
static void ssh2_pkt_getstring(char **p, int *length) static void ssh2_pkt_getstring(char **p, int *length)
{ {
*p = NULL; *p = NULL;
*length = 0;
if (pktin.length - pktin.savedpos < 4) if (pktin.length - pktin.savedpos < 4)
return; return;
*length = GET_32BIT(pktin.data + pktin.savedpos); *length = GET_32BIT(pktin.data + pktin.savedpos);
@ -4034,7 +4035,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
static int tried_pubkey_config, tried_agent, tried_keyb_inter; static int tried_pubkey_config, tried_agent, tried_keyb_inter;
static int kbd_inter_running; static int kbd_inter_running;
static int we_are_in; static int we_are_in;
static int num_prompts, echo; static int num_prompts, curr_prompt, echo;
static char username[100]; static char username[100];
static int got_username; static int got_username;
static char pwprompt[200]; static char pwprompt[200];
@ -4235,9 +4236,14 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
if (kbd_inter_running && if (kbd_inter_running &&
pktin.type == SSH2_MSG_USERAUTH_INFO_REQUEST) { pktin.type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
/* /*
* This is a further prompt in keyboard-interactive * This is either a further set-of-prompts packet
* authentication. Do nothing. * in keyboard-interactive authentication, or it's
* the same one and we came back here with `gotit'
* set. In the former case, we must reset the
* curr_prompt variable.
*/ */
if (!gotit)
curr_prompt = 0;
} else if (pktin.type != SSH2_MSG_USERAUTH_FAILURE) { } else if (pktin.type != SSH2_MSG_USERAUTH_FAILURE) {
bombout(("Strange packet received during authentication: type %d", bombout(("Strange packet received during authentication: type %d",
pktin.type)); pktin.type));
@ -4537,6 +4543,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
} }
kbd_inter_running = TRUE; kbd_inter_running = TRUE;
curr_prompt = 0;
} }
if (kbd_inter_running) { if (kbd_inter_running) {
@ -4546,28 +4553,50 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER; ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER;
/* We've got packet with that "interactive" info if (curr_prompt == 0) {
dump banners, and set its prompt as ours */ /*
{ * We've got a fresh USERAUTH_INFO_REQUEST.
char *name, *inst, *lang, *prompt; * Display header data, and start going through
int name_len, inst_len, lang_len, prompt_len; * the prompts.
*/
char *name, *inst, *lang;
int name_len, inst_len, lang_len;
ssh2_pkt_getstring(&name, &name_len); ssh2_pkt_getstring(&name, &name_len);
ssh2_pkt_getstring(&inst, &inst_len); ssh2_pkt_getstring(&inst, &inst_len);
ssh2_pkt_getstring(&lang, &lang_len); ssh2_pkt_getstring(&lang, &lang_len);
if (name_len > 0) if (name_len > 0) {
c_write_untrusted(name, name_len); c_write_untrusted(name, name_len);
if (inst_len > 0) c_write_str("\n");
}
if (inst_len > 0) {
c_write_untrusted(inst, inst_len); c_write_untrusted(inst, inst_len);
c_write_str("\n");
}
num_prompts = ssh2_pkt_getuint32(); num_prompts = ssh2_pkt_getuint32();
}
/*
* If there are prompts remaining in the packet,
* display one and get a response.
*/
if (curr_prompt < num_prompts) {
char *prompt;
int prompt_len;
ssh2_pkt_getstring(&prompt, &prompt_len); ssh2_pkt_getstring(&prompt, &prompt_len);
strncpy(pwprompt, prompt, sizeof(pwprompt)); if (prompt_len > 0) {
pwprompt[prompt_len < sizeof(pwprompt) ? strncpy(pwprompt, prompt, sizeof(pwprompt));
prompt_len : sizeof(pwprompt)-1] = '\0'; pwprompt[prompt_len < sizeof(pwprompt) ?
need_pw = TRUE; prompt_len : sizeof(pwprompt)-1] = '\0';
} else {
strcpy(pwprompt,
"<server failed to send prompt>: ");
}
echo = ssh2_pkt_getbool(); echo = ssh2_pkt_getbool();
} need_pw = TRUE;
} else
need_pw = FALSE;
} }
if (!method && can_passwd) { if (!method && can_passwd) {
@ -4763,11 +4792,28 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
logevent("Sent password"); logevent("Sent password");
type = AUTH_TYPE_PASSWORD; type = AUTH_TYPE_PASSWORD;
} else if (method == AUTH_KEYBOARD_INTERACTIVE) { } else if (method == AUTH_KEYBOARD_INTERACTIVE) {
ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE); if (curr_prompt == 0) {
ssh2_pkt_adduint32(num_prompts); ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
ssh2_pkt_addstring(password); ssh2_pkt_adduint32(num_prompts);
memset(password, 0, sizeof(password)); }
ssh2_pkt_send(); if (need_pw) { /* only add pw if we just got one! */
ssh2_pkt_addstring(password);
memset(password, 0, sizeof(password));
curr_prompt++;
}
if (curr_prompt >= num_prompts) {
ssh2_pkt_send();
} else {
/*
* If there are prompts remaining, we set
* `gotit' so that we won't attempt to get
* another packet. Then we go back round the
* loop and will end up retrieving another
* prompt out of the existing packet. Funky or
* what?
*/
gotit = TRUE;
}
type = AUTH_TYPE_KEYBOARD_INTERACTIVE; type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
} else { } else {
c_write_str c_write_str