mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Use strbuf to store results in prompts_t.
UBsan pointed out another memcpy from NULL (again with length 0) in the prompts_t system. When I looked at it, I realised that firstly prompt_ensure_result_size was an early not-so-good implementation of sgrowarray_nm that would benefit from being replaced with a call to the real one, and secondly, the whole system for storing prompt results should really have been replaced with strbufs with the no-move option, because that's doing all the same jobs better. So, now each prompt_t holds a strbuf in place of its previous manually managed string. prompt_ensure_result_size is gone (the console prompt-reading functions use strbuf_append, and everything else just adds to the strbuf in the usual marshal.c way). New functions exist to retrieve a prompt_t's result, either by reference or copied.
This commit is contained in:
parent
5891142aee
commit
cd6bc14f04
11
cmdgen.c
11
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;
|
||||
@ -777,7 +777,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);
|
||||
}
|
||||
}
|
||||
@ -921,12 +921,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);
|
||||
}
|
||||
}
|
||||
|
38
misc.c
38
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);
|
||||
}
|
||||
|
18
putty.h
18
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);
|
||||
|
||||
/*
|
||||
|
6
rlogin.c
6
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 */
|
||||
}
|
||||
|
18
ssh1login.c
18
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;
|
||||
}
|
||||
@ -987,8 +987,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;
|
||||
@ -1002,7 +1004,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();
|
||||
@ -1025,7 +1027,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);
|
||||
@ -1037,12 +1040,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");
|
||||
|
@ -430,7 +430,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))
|
||||
@ -882,7 +882,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 */
|
||||
@ -1372,8 +1372,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);
|
||||
@ -1455,7 +1455,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);
|
||||
|
||||
/*
|
||||
@ -1581,20 +1581,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");
|
||||
@ -1612,8 +1612,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);
|
||||
|
15
terminal.c
15
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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user