From b61f81bc4c102d06f13fd68e4a73ab8fdfdb3cf5 Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 2 Oct 2011 11:50:45 +0000 Subject: [PATCH] While I'm crusading against arbitrary limits, here's a redesign of prompt_t to arrange that the buffer in which each prompt is stored can be reallocated larger during the input process. git-svn-id: svn://svn.tartarus.org/sgt/putty@9317 cda61777-01e9-0310-a592-d414129be87e --- cmdgen.c | 6 +++--- cmdline.c | 5 +---- misc.c | 34 +++++++++++++++++++++++++++++----- putty.h | 19 ++++++++++++++++--- rlogin.c | 3 +-- ssh.c | 30 ++++++++++++------------------ terminal.c | 11 +++++------ unix/uxcons.c | 31 ++++++++++++++++++++++++------- windows/wincons.c | 41 ++++++++++++++++++++++++++++++----------- 9 files changed, 121 insertions(+), 59 deletions(-) diff --git a/cmdgen.c b/cmdgen.c index 9cd5b528..3abc2452 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -720,7 +720,7 @@ int main(int argc, char **argv) int ret; p->to_server = FALSE; p->name = dupstr("SSH key passphrase"); - add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE, 512); + add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE); ret = console_get_userpass_input(p, NULL, 0); assert(ret >= 0); if (!ret) { @@ -845,8 +845,8 @@ int main(int argc, char **argv) p->to_server = FALSE; p->name = dupstr("New SSH key passphrase"); - add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE, 512); - add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE, 512); + add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE); + add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE); ret = console_get_userpass_input(p, NULL, 0); assert(ret >= 0); if (!ret) { diff --git a/cmdline.c b/cmdline.c index 9c873503..2c3b7ce5 100644 --- a/cmdline.c +++ b/cmdline.c @@ -105,15 +105,12 @@ int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen) { if (tried_once) return 0; - strncpy(p->prompts[0]->result, cmdline_password, - p->prompts[0]->result_len); - p->prompts[0]->result[p->prompts[0]->result_len-1] = '\0'; + prompt_set_result(p->prompts[0], cmdline_password); memset(cmdline_password, 0, strlen(cmdline_password)); sfree(cmdline_password); cmdline_password = NULL; tried_once = 1; return 1; - } /* diff --git a/misc.c b/misc.c index cf97c049..8a9c7b11 100644 --- a/misc.c +++ b/misc.c @@ -99,24 +99,48 @@ prompts_t *new_prompts(void *frontend) p->name_reqd = p->instr_reqd = FALSE; return p; } -void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len) +void add_prompt(prompts_t *p, char *promptstr, int echo) { prompt_t *pr = snew(prompt_t); - char *result = snewn(len, char); pr->prompt = promptstr; pr->echo = echo; - pr->result = result; - pr->result_len = len; + pr->result = NULL; + pr->resultsize = 0; p->n_prompts++; p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *); p->prompts[p->n_prompts-1] = pr; } +void prompt_ensure_result_size(prompt_t *pr, int newlen) +{ + if ((int)pr->resultsize < newlen) { + char *newbuf; + newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */ + + /* + * We don't use sresize / realloc here, because we will be + * storing sensitive stuff like passwords in here, and we want + * to make sure that the data doesn't get copied around in + * memory without the old copy being destroyed. + */ + newbuf = snewn(newlen, char); + memcpy(newbuf, pr->result, pr->resultsize); + memset(pr->result, '\0', pr->resultsize); + sfree(pr->result); + pr->result = newbuf; + pr->resultsize = newlen; + } +} +void prompt_set_result(prompt_t *pr, const char *newstr) +{ + prompt_ensure_result_size(pr, strlen(newstr) + 1); + strcpy(pr->result, newstr); +} void free_prompts(prompts_t *p) { size_t i; for (i=0; i < p->n_prompts; i++) { prompt_t *pr = p->prompts[i]; - memset(pr->result, 0, pr->result_len); /* burn the evidence */ + memset(pr->result, 0, pr->resultsize); /* burn the evidence */ sfree(pr->result); sfree(pr->prompt); sfree(pr); diff --git a/putty.h b/putty.h index e4d3e777..4c338b4e 100644 --- a/putty.h +++ b/putty.h @@ -525,8 +525,19 @@ struct RSAKey; /* be a little careful of scope */ typedef struct { char *prompt; int echo; - char *result; /* allocated/freed by caller */ - size_t result_len; + /* + * 'result' must be a dynamically allocated array of exactly + * 'resultsize' chars. The code for actually reading input may + * realloc it bigger (and adjust resultsize accordingly) if it has + * to. The caller should free it again when finished with it. + * + * If resultsize==0, then result may be NULL. When setting up a + * prompt_t, it's therefore easiest to initialise them this way, + * which means all actual allocation is done by the callee. This + * is what add_prompt does. + */ + char *result; + size_t resultsize; } prompt_t; typedef struct { /* @@ -549,7 +560,9 @@ typedef struct { * get_userpass_input(); initially NULL */ } prompts_t; prompts_t *new_prompts(void *frontend); -void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len); +void add_prompt(prompts_t *p, char *promptstr, int echo); +void prompt_set_result(prompt_t *pr, const char *newstr); +void prompt_ensure_result_size(prompt_t *pr, int len); /* Burn the evidence. (Assumes _all_ strings want free()ing.) */ void free_prompts(prompts_t *p); diff --git a/rlogin.c b/rlogin.c index a79576e5..aa6b1d7d 100644 --- a/rlogin.c +++ b/rlogin.c @@ -246,8 +246,7 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle, rlogin->prompt = new_prompts(rlogin->frontend); rlogin->prompt->to_server = TRUE; rlogin->prompt->name = dupstr("Rlogin login name"); - /* 512 is an arbitrary limit :-( */ - add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE, 512); + add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE); ret = get_userpass_input(rlogin->prompt, NULL, 0); if (ret >= 0) { rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); diff --git a/ssh.c b/ssh.c index b17a57d4..aabbcc4a 100644 --- a/ssh.c +++ b/ssh.c @@ -517,9 +517,6 @@ static void ssh_channel_destroy(struct ssh_channel *c); #define OUR_V2_MAXPKT 0x4000UL #define OUR_V2_PACKETLIMIT 0x9000UL -/* Maximum length of passwords/passphrases (arbitrary) */ -#define SSH_MAX_PASSWORD_LEN 100 - const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss }; const static struct ssh_mac *macs[] = { @@ -3526,8 +3523,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); - /* 512 is an arbitrary upper limit on username size */ - add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, 512); + add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -3820,8 +3816,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt->name = dupstr("SSH key passphrase"); add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", - s->publickey_comment), - FALSE, SSH_MAX_PASSWORD_LEN); + s->publickey_comment), FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -3976,7 +3971,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; - add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN); + add_prompt(s->cur_prompt, prompt, FALSE); sfree(instr_suf); } } @@ -4019,7 +4014,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, (*instr_suf) ? "\n" : "", instr_suf); s->cur_prompt->instr_reqd = TRUE; - add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN); + add_prompt(s->cur_prompt, prompt, FALSE); sfree(instr_suf); } } @@ -4032,7 +4027,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt->name = dupstr("SSH password"); add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", ssh->username, ssh->savedhost), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); } /* @@ -7675,8 +7670,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt = new_prompts(ssh->frontend); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); - /* 512 is an arbitrary limit :-( */ - add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, 512); + add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -8082,7 +8076,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", s->publickey_comment), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { ssh->send_ok = 1; @@ -8462,7 +8456,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } add_prompt(s->cur_prompt, dupprintf("%.*s", prompt_len, prompt), - echo, SSH_MAX_PASSWORD_LEN); + echo); } if (name_len) { @@ -8559,7 +8553,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", ssh->username, ssh->savedhost), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); ret = get_userpass_input(s->cur_prompt, NULL, 0); while (ret < 0) { @@ -8661,11 +8655,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ add_prompt(s->cur_prompt, dupstr("Current password (blank for previously entered password): "), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); add_prompt(s->cur_prompt, dupstr("Enter new password: "), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); add_prompt(s->cur_prompt, dupstr("Confirm new password: "), - FALSE, SSH_MAX_PASSWORD_LEN); + FALSE); /* * Loop until the user manages to enter the same diff --git a/terminal.c b/terminal.c index 08ce42e6..abb06e2a 100644 --- a/terminal.c +++ b/terminal.c @@ -6604,7 +6604,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, { int i; for (i = 0; i < (int)p->n_prompts; i++) - memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + prompt_set_result(p->prompts[i], ""); } } @@ -6631,8 +6631,8 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, case 10: case 13: term_data(term, 0, "\r\n", 2); + prompt_ensure_result_size(pr, s->pos + 1); 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; @@ -6667,10 +6667,9 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, * 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) { + if (!pr->echo || (c >= ' ' && c <= '~') || + ((unsigned char) c >= 160)) { + prompt_ensure_result_size(pr, s->pos + 1); pr->result[s->pos++] = c; if (pr->echo) term_data(term, 0, &c, 1); diff --git a/unix/uxcons.c b/unix/uxcons.c index 9ceed0d0..ce1a4923 100644 --- a/unix/uxcons.c +++ b/unix/uxcons.c @@ -374,7 +374,7 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int i; for (i = 0; i < p->n_prompts; i++) - memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + prompt_set_result(p->prompts[i], ""); } if (p->n_prompts && console_batch_mode) @@ -403,7 +403,7 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { struct termios oldmode, newmode; - int i; + int len; prompt_t *pr = p->prompts[curr_prompt]; tcgetattr(infd, &oldmode); @@ -417,17 +417,34 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) console_prompt_text(outfp, pr->prompt, strlen(pr->prompt)); - i = read(infd, pr->result, pr->result_len - 1); + len = 0; + while (1) { + int ret; + + prompt_ensure_result_size(pr, len * 5 / 4 + 512); + ret = read(infd, pr->result + len, pr->resultsize - len - 1); + if (ret <= 0) { + len = -1; + break; + } + len += ret; + if (pr->result[len - 1] == '\n') { + len--; + break; + } + } tcsetattr(infd, TCSANOW, &oldmode); - if (i > 0 && pr->result[i-1] == '\n') - i--; - pr->result[i] = '\0'; - if (!pr->echo) console_prompt_text(outfp, "\n", 1); + if (len < 0) { + console_close(outfp, infd); + return 0; /* failure due to read error */ + } + + pr->result[len] = '\0'; } console_close(outfp, infd); diff --git a/windows/wincons.c b/windows/wincons.c index da5bac51..9824a5a5 100644 --- a/windows/wincons.c +++ b/windows/wincons.c @@ -315,7 +315,7 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) { int i; for (i = 0; i < (int)p->n_prompts; i++) - memset(p->prompts[i]->result, 0, p->prompts[i]->result_len); + prompt_set_result(p->prompts[i], ""); } /* @@ -365,9 +365,9 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { - DWORD savemode, newmode, i = 0; + DWORD savemode, newmode; + int len; prompt_t *pr = p->prompts[curr_prompt]; - BOOL r; GetConsoleMode(hin, &savemode); newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT; @@ -379,25 +379,44 @@ int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen) console_data_untrusted(hout, pr->prompt, strlen(pr->prompt)); - r = ReadFile(hin, pr->result, pr->result_len - 1, &i, NULL); + len = 0; + while (1) { + DWORD ret = 0; + BOOL r; + + prompt_ensure_result_size(pr, len * 5 / 4 + 512); + + r = ReadFile(hin, pr->result + len, pr->resultsize - len - 1, + &ret, NULL); + + if (!r || ret == 0) { + len = -1; + break; + } + len += ret; + if (pr->result[len - 1] == '\n') { + len--; + if (pr->result[len - 1] == '\r') + len--; + break; + } + } SetConsoleMode(hin, savemode); - if ((int) i > pr->result_len) - i = pr->result_len - 1; - else - i = i - 2; - pr->result[i] = '\0'; - if (!pr->echo) { DWORD dummy; WriteFile(hout, "\r\n", 2, &dummy, NULL); } + if (len < 0) { + return 0; /* failure due to read error */ + } + + pr->result[len] = '\0'; } return 1; /* success */ - } void frontend_keypress(void *handle) -- 2.11.0