mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-07-02 03:52:49 -05:00
Revamp SSH authentication code so that user interaction is more
abstracted out; replace loops structured around a single interaction per loop with less tortuous code (fixes: `ki-multiprompt-crash', `ssh1-bad-passphrase-crash'; makes `ssh2-password-expiry' and `proxy-password-prompt' easier). The new interaction abstraction has a lot of fields that are unused in the current code (things like window captions); this is groundwork for `gui-auth'. However, ssh.c still writes directly to stderr; that may want to be fixed. In the GUI apps, user interaction is moved to terminal.c. This should make it easier to fix things like UTF-8 username entry, although I haven't attempted to do so. Also, control character filtering can be tailored to be appropriate for individual front-ends; so far I don't promise anything other than not having made it any worse. I've tried to test this fairly exhaustively (although Mac stuff is untested, as usual). It all seems to basically work, but I bet there are new bugs. (One I know about is that you can no longer make the PuTTY window go away with a ^D at the password prompt; this should be fixed.) [originally from svn r6437] [this svn revision also touched putty-wishlist]
This commit is contained in:
143
terminal.c
143
terminal.c
@ -6257,6 +6257,24 @@ int term_data(Terminal *term, int is_stderr, const char *data, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write untrusted data to the terminal.
|
||||
* The only control character that should be honoured is \n (which
|
||||
* will behave as a CRLF).
|
||||
*/
|
||||
int term_data_untrusted(Terminal *term, const char *data, int len)
|
||||
{
|
||||
int i;
|
||||
/* FIXME: more sophisticated checking? */
|
||||
for (i = 0; i < len; i++) {
|
||||
if (data[i] == '\n')
|
||||
term_data(term, 1, "\r\n", 2);
|
||||
else if (data[i] & 0x60)
|
||||
term_data(term, 1, data + i, 1);
|
||||
}
|
||||
return 0; /* assumes that term_data() always returns 0 */
|
||||
}
|
||||
|
||||
void term_provide_logctx(Terminal *term, void *logctx)
|
||||
{
|
||||
term->logctx = logctx;
|
||||
@ -6281,3 +6299,128 @@ char *term_get_ttymode(Terminal *term, const char *mode)
|
||||
/* FIXME: perhaps we should set ONLCR based on cfg.lfhascr as well? */
|
||||
return dupstr(val);
|
||||
}
|
||||
|
||||
struct term_userpass_state {
|
||||
size_t curr_prompt;
|
||||
int done_prompt; /* printed out prompt yet? */
|
||||
size_t pos; /* cursor position */
|
||||
};
|
||||
|
||||
/*
|
||||
* Process some terminal data in the course of username/password
|
||||
* input.
|
||||
*/
|
||||
int term_get_userpass_input(Terminal *term, prompts_t *p,
|
||||
unsigned char *in, int inlen)
|
||||
{
|
||||
struct term_userpass_state *s = (struct term_userpass_state *)p->data;
|
||||
if (!s) {
|
||||
/*
|
||||
* First call. Set some stuff up.
|
||||
*/
|
||||
p->data = s = snew(struct term_userpass_state);
|
||||
s->curr_prompt = 0;
|
||||
s->done_prompt = 0;
|
||||
/* We only print the `name' caption if we have to... */
|
||||
if (p->name_reqd && p->name) {
|
||||
size_t l = strlen(p->name);
|
||||
term_data_untrusted(term, p->name, l);
|
||||
if (p->name[l-1] != '\n')
|
||||
term_data_untrusted(term, "\n", 1);
|
||||
}
|
||||
/* ...but we always print any `instruction'. */
|
||||
if (p->instruction) {
|
||||
size_t l = strlen(p->instruction);
|
||||
term_data_untrusted(term, p->instruction, l);
|
||||
if (p->instruction[l-1] != '\n')
|
||||
term_data_untrusted(term, "\n", 1);
|
||||
}
|
||||
/*
|
||||
* Zero all the results, in case we abort half-way through.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < p->n_prompts; i++)
|
||||
memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);
|
||||
}
|
||||
}
|
||||
|
||||
while (s->curr_prompt < p->n_prompts) {
|
||||
|
||||
prompt_t *pr = p->prompts[s->curr_prompt];
|
||||
int finished_prompt = 0;
|
||||
|
||||
if (!s->done_prompt) {
|
||||
term_data_untrusted(term, pr->prompt, strlen(pr->prompt));
|
||||
s->done_prompt = 1;
|
||||
s->pos = 0;
|
||||
}
|
||||
|
||||
/* Breaking out here ensures that the prompt is printed even
|
||||
* if we're now waiting for user data. */
|
||||
if (!in || !inlen) break;
|
||||
|
||||
/* FIXME: should we be using local-line-editing code instead? */
|
||||
while (!finished_prompt && inlen) {
|
||||
char c = *in++;
|
||||
inlen--;
|
||||
switch (c) {
|
||||
case 10:
|
||||
case 13:
|
||||
term_data(term, 0, "\r\n", 2);
|
||||
pr->result[s->pos] = '\0';
|
||||
pr->result[pr->result_len - 1] = '\0';
|
||||
/* go to next prompt, if any */
|
||||
s->curr_prompt++;
|
||||
s->done_prompt = 0;
|
||||
finished_prompt = 1; /* break out */
|
||||
break;
|
||||
case 8:
|
||||
case 127:
|
||||
if (s->pos > 0) {
|
||||
if (pr->echo)
|
||||
term_data(term, 0, "\b \b", 3);
|
||||
s->pos--;
|
||||
}
|
||||
break;
|
||||
case 21:
|
||||
case 27:
|
||||
while (s->pos > 0) {
|
||||
if (pr->echo)
|
||||
term_data(term, 0, "\b \b", 3);
|
||||
s->pos--;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
/* Immediate abort. */
|
||||
term_data(term, 0, "\r\n", 2);
|
||||
sfree(s);
|
||||
return 0; /* user abort */
|
||||
default:
|
||||
/*
|
||||
* This simplistic check for printability is disabled
|
||||
* when we're doing password input, because some people
|
||||
* have control characters in their passwords.
|
||||
*/
|
||||
if ((!pr->echo ||
|
||||
(c >= ' ' && c <= '~') ||
|
||||
((unsigned char) c >= 160))
|
||||
&& s->pos < pr->result_len - 1) {
|
||||
pr->result[s->pos++] = c;
|
||||
if (pr->echo)
|
||||
term_data(term, 0, &c, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (s->curr_prompt < p->n_prompts) {
|
||||
return -1; /* more data required */
|
||||
} else {
|
||||
sfree(s);
|
||||
return +1; /* all done */
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user