diff --git a/cmdgen.c b/cmdgen.c index 7ec6dbae..03a1eab2 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -53,10 +53,10 @@ int console_get_userpass_input(prompts_t *p) int ret = 1; for (i = 0; i < p->n_prompts; i++) { if (promptsgot < nprompts) { - p->prompts[i]->result = dupstr(prompts[promptsgot++]); + prompt_set_result(p->prompts[i], prompts[promptsgot++]); if (cgtest_verbose) printf(" prompt \"%s\": response \"%s\"\n", - p->prompts[i]->prompt, p->prompts[i]->result); + p->prompts[i]->prompt, p->prompts[i]->result->s); } else { promptsgot++; /* track number of requests anyway */ ret = 0; @@ -774,7 +774,7 @@ int main(int argc, char **argv) perror("puttygen: unable to read passphrase"); return 1; } else { - old_passphrase = dupstr(p->prompts[0]->result); + old_passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); } } @@ -918,12 +918,13 @@ int main(int argc, char **argv) perror("puttygen: unable to read new passphrase"); return 1; } else { - if (strcmp(p->prompts[0]->result, p->prompts[1]->result)) { + if (strcmp(prompt_get_result_ref(p->prompts[0]), + prompt_get_result_ref(p->prompts[1]))) { free_prompts(p); fprintf(stderr, "puttygen: passphrases do not match\n"); return 1; } - new_passphrase = dupstr(p->prompts[0]->result); + new_passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); } } diff --git a/misc.c b/misc.c index 0e52f7ae..b7da8560 100644 --- a/misc.c +++ b/misc.c @@ -51,43 +51,29 @@ void add_prompt(prompts_t *p, char *promptstr, bool echo) prompt_t *pr = snew(prompt_t); pr->prompt = promptstr; pr->echo = echo; - pr->result = NULL; - pr->resultsize = 0; + pr->result = strbuf_new_nm(); sgrowarray(p->prompts, p->prompts_size, p->n_prompts); p->prompts[p->n_prompts++] = 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); - smemclr(pr->result, 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); + strbuf_clear(pr->result); + put_datapl(pr->result, ptrlen_from_asciz(newstr)); +} +const char *prompt_get_result_ref(prompt_t *pr) +{ + return pr->result->s; +} +char *prompt_get_result(prompt_t *pr) +{ + return dupstr(pr->result->s); } void free_prompts(prompts_t *p) { size_t i; for (i=0; i < p->n_prompts; i++) { prompt_t *pr = p->prompts[i]; - smemclr(pr->result, pr->resultsize); /* burn the evidence */ - sfree(pr->result); + strbuf_free(pr->result); sfree(pr->prompt); sfree(pr); } diff --git a/putty.h b/putty.h index 8f9643e5..5fb90366 100644 --- a/putty.h +++ b/putty.h @@ -636,19 +636,7 @@ GLOBAL char *cmdline_session_name; typedef struct { char *prompt; bool echo; - /* - * '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; + strbuf *result; } prompt_t; typedef struct { /* @@ -682,8 +670,8 @@ typedef struct { prompts_t *new_prompts(); void add_prompt(prompts_t *p, char *promptstr, bool 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.) */ +char *prompt_get_result(prompt_t *pr); +const char *prompt_get_result_ref(prompt_t *pr); void free_prompts(prompts_t *p); /* diff --git a/rlogin.c b/rlogin.c index e03210e5..c4f582b1 100644 --- a/rlogin.c +++ b/rlogin.c @@ -237,7 +237,8 @@ static const char *rlogin_init(Seat *seat, Backend **backend_handle, if (ret >= 0) { /* Next terminal output will come from server */ seat_set_trust_status(rlogin->seat, false); - rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); + rlogin_startup(rlogin, prompt_get_result_ref( + rlogin->prompt->prompts[0])); } } @@ -286,7 +287,8 @@ static size_t rlogin_send(Backend *be, const char *buf, size_t len) if (ret >= 0) { /* Next terminal output will come from server */ seat_set_trust_status(rlogin->seat, false); - rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); + rlogin_startup(rlogin, prompt_get_result_ref( + rlogin->prompt->prompts[0])); /* that nulls out rlogin->prompt, so then we'll start sending * data down the wire in the obvious way */ } diff --git a/ssh1login.c b/ssh1login.c index 7f431ce7..f14068e4 100644 --- a/ssh1login.c +++ b/ssh1login.c @@ -416,7 +416,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) ssh_user_close(s->ppl.ssh, "No username provided"); return; } - s->username = dupstr(s->cur_prompt->prompts[0]->result); + s->username = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); s->cur_prompt = NULL; } @@ -676,7 +676,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) "User aborted at passphrase prompt"); return; } - passphrase = dupstr(s->cur_prompt->prompts[0]->result); + passphrase = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); s->cur_prompt = NULL; } @@ -988,8 +988,10 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) * we can use the primary defence. */ int bottom, top, pwlen, i; + const char *pw = prompt_get_result_ref( + s->cur_prompt->prompts[0]); - pwlen = strlen(s->cur_prompt->prompts[0]->result); + pwlen = strlen(pw); if (pwlen < 16) { bottom = 0; /* zero length passwords are OK! :-) */ top = 15; @@ -1003,7 +1005,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) for (i = bottom; i <= top; i++) { if (i == pwlen) { pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); - put_stringz(pkt, s->cur_prompt->prompts[0]->result); + put_stringz(pkt, pw); pq_push(s->ppl.out_pq, pkt); } else { strbuf *random_data = strbuf_new_nm(); @@ -1026,7 +1028,8 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) ppl_logevent("Sending length-padded password"); pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); - put_asciz(padded_pw, s->cur_prompt->prompts[0]->result); + put_asciz(padded_pw, prompt_get_result_ref( + s->cur_prompt->prompts[0])); size_t pad = 63 & -padded_pw->len; random_read(strbuf_append(padded_pw, pad), pad); put_stringsb(pkt, padded_pw); @@ -1038,12 +1041,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) */ ppl_logevent("Sending unpadded password"); pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); - put_stringz(pkt, s->cur_prompt->prompts[0]->result); + put_stringz(pkt, prompt_get_result_ref( + s->cur_prompt->prompts[0])); pq_push(s->ppl.out_pq, pkt); } } else { pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); - put_stringz(pkt, s->cur_prompt->prompts[0]->result); + put_stringz(pkt, prompt_get_result_ref(s->cur_prompt->prompts[0])); pq_push(s->ppl.out_pq, pkt); } ppl_logevent("Sent password"); diff --git a/ssh2userauth.c b/ssh2userauth.c index d3bbe7a9..a913aff2 100644 --- a/ssh2userauth.c +++ b/ssh2userauth.c @@ -432,7 +432,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) } sfree(s->locally_allocated_username); /* for change_username */ s->username = s->locally_allocated_username = - dupstr(s->cur_prompt->prompts[0]->result); + prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); } else { if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) @@ -884,7 +884,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) return; } passphrase = - dupstr(s->cur_prompt->prompts[0]->result); + prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); } else { passphrase = NULL; /* no passphrase needed */ @@ -1374,8 +1374,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE); put_uint32(s->pktout, s->num_prompts); for (uint32_t i = 0; i < s->num_prompts; i++) { - put_stringz(s->pktout, - s->cur_prompt->prompts[i]->result); + put_stringz(s->pktout, prompt_get_result_ref( + s->cur_prompt->prompts[i])); } s->pktout->minlen = 256; pq_push(s->ppl.out_pq, s->pktout); @@ -1457,7 +1457,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * Squirrel away the password. (We may need it later if * asked to change it.) */ - s->password = dupstr(s->cur_prompt->prompts[0]->result); + s->password = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); /* @@ -1583,20 +1583,20 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * (A side effect is that the user doesn't have to * re-enter it if they louse up the new password.) */ - if (s->cur_prompt->prompts[0]->result[0]) { + if (s->cur_prompt->prompts[0]->result->s[0]) { smemclr(s->password, strlen(s->password)); /* burn the evidence */ sfree(s->password); - s->password = - dupstr(s->cur_prompt->prompts[0]->result); + s->password = prompt_get_result( + s->cur_prompt->prompts[0]); } /* * Check the two new passwords match. */ - got_new = (strcmp(s->cur_prompt->prompts[1]->result, - s->cur_prompt->prompts[2]->result) - == 0); + got_new = !strcmp( + prompt_get_result_ref(s->cur_prompt->prompts[1]), + prompt_get_result_ref(s->cur_prompt->prompts[2])); if (!got_new) /* They don't. Silly user. */ ppl_printf("Passwords do not match\r\n"); @@ -1614,8 +1614,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) put_stringz(s->pktout, "password"); put_bool(s->pktout, true); put_stringz(s->pktout, s->password); - put_stringz(s->pktout, - s->cur_prompt->prompts[1]->result); + put_stringz(s->pktout, prompt_get_result_ref( + s->cur_prompt->prompts[1])); free_prompts(s->cur_prompt); s->pktout->minlen = 256; pq_push(s->ppl.out_pq, s->pktout); diff --git a/terminal.c b/terminal.c index a3885303..08592568 100644 --- a/terminal.c +++ b/terminal.c @@ -7156,7 +7156,6 @@ char *term_get_ttymode(Terminal *term, const char *mode) struct term_userpass_state { size_t curr_prompt; bool done_prompt; /* printed out prompt yet? */ - size_t pos; /* cursor position */ }; /* Tiny wrapper to make it easier to write lots of little strings */ @@ -7211,7 +7210,6 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) if (!s->done_prompt) { term_write(term, ptrlen_from_asciz(pr->prompt)); s->done_prompt = true; - s->pos = 0; } /* Breaking out here ensures that the prompt is printed even @@ -7226,8 +7224,6 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) case 10: case 13: term_write(term, PTRLEN_LITERAL("\r\n")); - prompt_ensure_result_size(pr, s->pos + 1); - pr->result[s->pos] = '\0'; /* go to next prompt, if any */ s->curr_prompt++; s->done_prompt = false; @@ -7235,18 +7231,18 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) break; case 8: case 127: - if (s->pos > 0) { + if (pr->result->len > 0) { if (pr->echo) term_write(term, PTRLEN_LITERAL("\b \b")); - s->pos--; + strbuf_shrink_by(pr->result, 1); } break; case 21: case 27: - while (s->pos > 0) { + while (pr->result->len > 0) { if (pr->echo) term_write(term, PTRLEN_LITERAL("\b \b")); - s->pos--; + strbuf_shrink_by(pr->result, 1); } break; case 3: @@ -7264,8 +7260,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) */ if (!pr->echo || (c >= ' ' && c <= '~') || ((unsigned char) c >= 160)) { - prompt_ensure_result_size(pr, s->pos + 1); - pr->result[s->pos++] = c; + put_byte(pr->result, c); if (pr->echo) term_write(term, make_ptrlen(&c, 1)); } diff --git a/unix/uxcons.c b/unix/uxcons.c index 4811c177..3e8a7ea5 100644 --- a/unix/uxcons.c +++ b/unix/uxcons.c @@ -566,7 +566,6 @@ int console_get_userpass_input(prompts_t *p) for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { struct termios oldmode, newmode; - int len; prompt_t *pr = p->prompts[curr_prompt]; tcgetattr(infd, &oldmode); @@ -580,19 +579,21 @@ int console_get_userpass_input(prompts_t *p) console_write(outfp, ptrlen_from_asciz(pr->prompt)); - len = 0; + bool failed = false; while (1) { - int ret; + size_t toread = 65536; + size_t prev_result_len = pr->result->len; + void *ptr = strbuf_append(pr->result, toread); + int ret = read(infd, ptr, toread); - prompt_ensure_result_size(pr, len * 5 / 4 + 512); - ret = read(infd, pr->result + len, pr->resultsize - len - 1); if (ret <= 0) { - len = -1; + failed = true; break; } - len += ret; - if (pr->result[len - 1] == '\n') { - len--; + + strbuf_shrink_to(pr->result, prev_result_len + ret); + if (pr->result->s[pr->result->len - 1] == '\n') { + strbuf_shrink_by(pr->result, 1); break; } } @@ -602,12 +603,10 @@ int console_get_userpass_input(prompts_t *p) if (!pr->echo) console_write(outfp, PTRLEN_LITERAL("\n")); - if (len < 0) { + if (failed) { console_close(outfp, infd); return 0; /* failure due to read error */ } - - pr->result[len] = '\0'; } console_close(outfp, infd); diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c index 61a94334..3d83fcf0 100644 --- a/unix/uxpgnt.c +++ b/unix/uxpgnt.c @@ -338,7 +338,7 @@ static char *askpass_tty(const char *prompt) free_prompts(p); return NULL; } else { - char *passphrase = dupstr(p->prompts[0]->result); + char *passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); return passphrase; } diff --git a/windows/wincons.c b/windows/wincons.c index 81df8cee..d91d9cef 100644 --- a/windows/wincons.c +++ b/windows/wincons.c @@ -490,7 +490,6 @@ int console_get_userpass_input(prompts_t *p) for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { DWORD savemode, newmode; - size_t len; prompt_t *pr = p->prompts[curr_prompt]; GetConsoleMode(hin, &savemode); @@ -503,22 +502,23 @@ int console_get_userpass_input(prompts_t *p) console_write(hout, ptrlen_from_asciz(pr->prompt)); - len = 0; + bool failed = false; while (1) { + DWORD toread = 65536; + size_t prev_result_len = pr->result->len; + void *ptr = strbuf_append(pr->result, toread); + DWORD ret = 0; - - prompt_ensure_result_size(pr, len * 5 / 4 + 512); - - if (!ReadFile(hin, pr->result + len, pr->resultsize - len - 1, - &ret, NULL) || ret == 0) { - len = (size_t)-1; + if (!ReadFile(hin, ptr, toread, &ret, NULL) || ret == 0) { + failed = true; break; } - len += ret; - if (pr->result[len - 1] == '\n') { - len--; - if (pr->result[len - 1] == '\r') - len--; + + strbuf_shrink_to(pr->result, prev_result_len + ret); + if (pr->result->s[pr->result->len - 1] == '\n') { + strbuf_shrink_by(pr->result, 1); + if (pr->result->s[pr->result->len - 1] == '\r') + strbuf_shrink_by(pr->result, 1); break; } } @@ -528,11 +528,9 @@ int console_get_userpass_input(prompts_t *p) if (!pr->echo) console_write(hout, PTRLEN_LITERAL("\r\n")); - if (len == (size_t)-1) { + if (failed) { return 0; /* failure due to read error */ } - - pr->result[len] = '\0'; } return 1; /* success */