mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
Move sanitisation of k-i prompts into the SSH code.
Now, instead of each seat's prompt-handling function doing the control-char sanitisation of prompt text, the SSH code does it. This means we can do it differently depending on the prompt. In particular, prompts _we_ generate (e.g. a genuine request for your private key's passphrase) are not sanitised; but prompts coming from the server (in keyboard-interactive mode, or its more restricted SSH-1 analogues, TIS and CryptoCard) are not only sanitised but also line-length limited and surrounded by uncounterfeitable headers, like I've just done to the authentication banners. This should mean that if a malicious server tries to fake the local passphrase prompt (perhaps because it's somehow already got a copy of your _encrypted_ private key), you can tell the difference.
This commit is contained in:
parent
767a9c6e45
commit
e21afff605
90
ssh1login.c
90
ssh1login.c
@ -57,6 +57,9 @@ struct ssh1_login_state {
|
|||||||
RSAKey servkey, hostkey;
|
RSAKey servkey, hostkey;
|
||||||
bool want_user_input;
|
bool want_user_input;
|
||||||
|
|
||||||
|
StripCtrlChars *tis_scc;
|
||||||
|
bool tis_scc_initialised;
|
||||||
|
|
||||||
PacketProtocolLayer ppl;
|
PacketProtocolLayer ppl;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -135,6 +138,8 @@ static PktIn *ssh1_login_pop(struct ssh1_login_state *s)
|
|||||||
return pq_pop(s->ppl.in_pq);
|
return pq_pop(s->ppl.in_pq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ssh1_login_setup_tis_scc(struct ssh1_login_state *s);
|
||||||
|
|
||||||
static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
||||||
{
|
{
|
||||||
struct ssh1_login_state *s =
|
struct ssh1_login_state *s =
|
||||||
@ -784,6 +789,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
|||||||
if (conf_get_bool(s->conf, CONF_try_tis_auth) &&
|
if (conf_get_bool(s->conf, CONF_try_tis_auth) &&
|
||||||
(s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
|
(s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
|
||||||
!s->tis_auth_refused) {
|
!s->tis_auth_refused) {
|
||||||
|
ssh1_login_setup_tis_scc(s);
|
||||||
s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
|
s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
|
||||||
ppl_logevent("Requested TIS authentication");
|
ppl_logevent("Requested TIS authentication");
|
||||||
pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_TIS);
|
pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_TIS);
|
||||||
@ -796,10 +802,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
|||||||
s->tis_auth_refused = true;
|
s->tis_auth_refused = true;
|
||||||
continue;
|
continue;
|
||||||
} else if (pktin->type == SSH1_SMSG_AUTH_TIS_CHALLENGE) {
|
} else if (pktin->type == SSH1_SMSG_AUTH_TIS_CHALLENGE) {
|
||||||
ptrlen challenge;
|
ptrlen challenge = get_string(pktin);
|
||||||
char *instr_suf, *prompt;
|
|
||||||
|
|
||||||
challenge = get_string(pktin);
|
|
||||||
if (get_err(pktin)) {
|
if (get_err(pktin)) {
|
||||||
ssh_proto_error(s->ppl.ssh, "TIS challenge packet was "
|
ssh_proto_error(s->ppl.ssh, "TIS challenge packet was "
|
||||||
"badly formed");
|
"badly formed");
|
||||||
@ -809,21 +812,28 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
|||||||
s->cur_prompt->to_server = true;
|
s->cur_prompt->to_server = true;
|
||||||
s->cur_prompt->from_server = true;
|
s->cur_prompt->from_server = true;
|
||||||
s->cur_prompt->name = dupstr("SSH TIS authentication");
|
s->cur_prompt->name = dupstr("SSH TIS authentication");
|
||||||
/* Prompt heuristic comes from OpenSSH */
|
|
||||||
if (!memchr(challenge.ptr, '\n', challenge.len)) {
|
strbuf *sb = strbuf_new();
|
||||||
instr_suf = dupstr("");
|
put_datapl(sb, PTRLEN_LITERAL("\
|
||||||
prompt = mkstr(challenge);
|
-- TIS authentication challenge from server: ---------------------------------\
|
||||||
|
\r\n"));
|
||||||
|
if (s->tis_scc) {
|
||||||
|
stripctrl_retarget(s->tis_scc, BinarySink_UPCAST(sb));
|
||||||
|
put_datapl(s->tis_scc, challenge);
|
||||||
|
stripctrl_retarget(s->tis_scc, NULL);
|
||||||
} else {
|
} else {
|
||||||
instr_suf = mkstr(challenge);
|
put_datapl(sb, challenge);
|
||||||
prompt = dupstr("Response: ");
|
|
||||||
}
|
}
|
||||||
s->cur_prompt->instruction =
|
if (!ptrlen_endswith(challenge, PTRLEN_LITERAL("\n"), NULL))
|
||||||
dupprintf("Using TIS authentication.%s%s",
|
put_datapl(sb, PTRLEN_LITERAL("\r\n"));
|
||||||
(*instr_suf) ? "\n" : "",
|
put_datapl(sb, PTRLEN_LITERAL("\
|
||||||
instr_suf);
|
-- End of TIS authentication challenge from server: --------------------------\
|
||||||
|
\r\n"));
|
||||||
|
|
||||||
|
s->cur_prompt->instruction = strbuf_to_str(sb);
|
||||||
s->cur_prompt->instr_reqd = true;
|
s->cur_prompt->instr_reqd = true;
|
||||||
add_prompt(s->cur_prompt, prompt, false);
|
add_prompt(s->cur_prompt, dupstr(
|
||||||
sfree(instr_suf);
|
"TIS authentication response: "), false);
|
||||||
} else {
|
} else {
|
||||||
ssh_proto_error(s->ppl.ssh, "Received unexpected packet"
|
ssh_proto_error(s->ppl.ssh, "Received unexpected packet"
|
||||||
" in response to TIS authentication, "
|
" in response to TIS authentication, "
|
||||||
@ -834,6 +844,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
|||||||
} else if (conf_get_bool(s->conf, CONF_try_tis_auth) &&
|
} else if (conf_get_bool(s->conf, CONF_try_tis_auth) &&
|
||||||
(s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
|
(s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
|
||||||
!s->ccard_auth_refused) {
|
!s->ccard_auth_refused) {
|
||||||
|
ssh1_login_setup_tis_scc(s);
|
||||||
s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
|
s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
|
||||||
ppl_logevent("Requested CryptoCard authentication");
|
ppl_logevent("Requested CryptoCard authentication");
|
||||||
pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_CCARD);
|
pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_CCARD);
|
||||||
@ -845,10 +856,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
|||||||
s->ccard_auth_refused = true;
|
s->ccard_auth_refused = true;
|
||||||
continue;
|
continue;
|
||||||
} else if (pktin->type == SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
|
} else if (pktin->type == SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
|
||||||
ptrlen challenge;
|
ptrlen challenge = get_string(pktin);
|
||||||
char *instr_suf, *prompt;
|
|
||||||
|
|
||||||
challenge = get_string(pktin);
|
|
||||||
if (get_err(pktin)) {
|
if (get_err(pktin)) {
|
||||||
ssh_proto_error(s->ppl.ssh, "CryptoCard challenge packet "
|
ssh_proto_error(s->ppl.ssh, "CryptoCard challenge packet "
|
||||||
"was badly formed");
|
"was badly formed");
|
||||||
@ -858,22 +866,28 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
|||||||
s->cur_prompt->to_server = true;
|
s->cur_prompt->to_server = true;
|
||||||
s->cur_prompt->from_server = true;
|
s->cur_prompt->from_server = true;
|
||||||
s->cur_prompt->name = dupstr("SSH CryptoCard authentication");
|
s->cur_prompt->name = dupstr("SSH CryptoCard authentication");
|
||||||
s->cur_prompt->name_reqd = false;
|
|
||||||
/* Prompt heuristic comes from OpenSSH */
|
strbuf *sb = strbuf_new();
|
||||||
if (!memchr(challenge.ptr, '\n', challenge.len)) {
|
put_datapl(sb, PTRLEN_LITERAL("\
|
||||||
instr_suf = dupstr("");
|
-- CryptoCard authentication challenge from server: --------------------------\
|
||||||
prompt = mkstr(challenge);
|
\r\n"));
|
||||||
|
if (s->tis_scc) {
|
||||||
|
stripctrl_retarget(s->tis_scc, BinarySink_UPCAST(sb));
|
||||||
|
put_datapl(s->tis_scc, challenge);
|
||||||
|
stripctrl_retarget(s->tis_scc, NULL);
|
||||||
} else {
|
} else {
|
||||||
instr_suf = mkstr(challenge);
|
put_datapl(sb, challenge);
|
||||||
prompt = dupstr("Response: ");
|
|
||||||
}
|
}
|
||||||
s->cur_prompt->instruction =
|
if (!ptrlen_endswith(challenge, PTRLEN_LITERAL("\n"), NULL))
|
||||||
dupprintf("Using CryptoCard authentication.%s%s",
|
put_datapl(sb, PTRLEN_LITERAL("\r\n"));
|
||||||
(*instr_suf) ? "\n" : "",
|
put_datapl(sb, PTRLEN_LITERAL("\
|
||||||
instr_suf);
|
-- End of CryptoCard authentication challenge from server: -------------------\
|
||||||
|
\r\n"));
|
||||||
|
|
||||||
|
s->cur_prompt->instruction = strbuf_to_str(sb);
|
||||||
s->cur_prompt->instr_reqd = true;
|
s->cur_prompt->instr_reqd = true;
|
||||||
add_prompt(s->cur_prompt, prompt, false);
|
add_prompt(s->cur_prompt, dupstr(
|
||||||
sfree(instr_suf);
|
"CryptoCard authentication response: "), false);
|
||||||
} else {
|
} else {
|
||||||
ssh_proto_error(s->ppl.ssh, "Received unexpected packet"
|
ssh_proto_error(s->ppl.ssh, "Received unexpected packet"
|
||||||
" in response to TIS authentication, "
|
" in response to TIS authentication, "
|
||||||
@ -1085,6 +1099,16 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
|||||||
crFinishV;
|
crFinishV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ssh1_login_setup_tis_scc(struct ssh1_login_state *s)
|
||||||
|
{
|
||||||
|
if (s->tis_scc_initialised)
|
||||||
|
return;
|
||||||
|
s->tis_scc = seat_stripctrl_new(s->ppl.seat, NULL, SIC_KI_PROMPTS);
|
||||||
|
if (s->tis_scc)
|
||||||
|
stripctrl_enable_line_limiting(s->tis_scc);
|
||||||
|
s->tis_scc_initialised = true;
|
||||||
|
}
|
||||||
|
|
||||||
static void ssh1_login_dialog_callback(void *loginv, int ret)
|
static void ssh1_login_dialog_callback(void *loginv, int ret)
|
||||||
{
|
{
|
||||||
struct ssh1_login_state *s = (struct ssh1_login_state *)loginv;
|
struct ssh1_login_state *s = (struct ssh1_login_state *)loginv;
|
||||||
|
110
ssh2userauth.c
110
ssh2userauth.c
@ -81,6 +81,10 @@ struct ssh2_userauth_state {
|
|||||||
StripCtrlChars *banner_scc;
|
StripCtrlChars *banner_scc;
|
||||||
bool banner_scc_initialised;
|
bool banner_scc_initialised;
|
||||||
|
|
||||||
|
StripCtrlChars *ki_scc;
|
||||||
|
bool ki_scc_initialised;
|
||||||
|
bool ki_printed_header;
|
||||||
|
|
||||||
PacketProtocolLayer ppl;
|
PacketProtocolLayer ppl;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -176,6 +180,8 @@ static void ssh2_userauth_free(PacketProtocolLayer *ppl)
|
|||||||
strbuf_free(s->last_methods_string);
|
strbuf_free(s->last_methods_string);
|
||||||
if (s->banner_scc)
|
if (s->banner_scc)
|
||||||
stripctrl_free(s->banner_scc);
|
stripctrl_free(s->banner_scc);
|
||||||
|
if (s->ki_scc)
|
||||||
|
stripctrl_free(s->ki_scc);
|
||||||
sfree(s);
|
sfree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1174,6 +1180,14 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
|||||||
|
|
||||||
ppl_logevent("Attempting keyboard-interactive authentication");
|
ppl_logevent("Attempting keyboard-interactive authentication");
|
||||||
|
|
||||||
|
if (!s->ki_scc_initialised) {
|
||||||
|
s->ki_scc = seat_stripctrl_new(
|
||||||
|
s->ppl.seat, NULL, SIC_KI_PROMPTS);
|
||||||
|
if (s->ki_scc)
|
||||||
|
stripctrl_enable_line_limiting(s->ki_scc);
|
||||||
|
s->ki_scc_initialised = true;
|
||||||
|
}
|
||||||
|
|
||||||
crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
|
crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
|
||||||
if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
|
if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
|
||||||
/* Server is not willing to do keyboard-interactive
|
/* Server is not willing to do keyboard-interactive
|
||||||
@ -1186,12 +1200,15 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->ki_printed_header = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop while the server continues to send INFO_REQUESTs.
|
* Loop while the server continues to send INFO_REQUESTs.
|
||||||
*/
|
*/
|
||||||
while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
|
while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
|
||||||
|
|
||||||
ptrlen name, inst;
|
ptrlen name, inst;
|
||||||
|
strbuf *sb;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1210,52 +1227,78 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
|||||||
*/
|
*/
|
||||||
s->num_prompts = get_uint32(pktin);
|
s->num_prompts = get_uint32(pktin);
|
||||||
for (i = 0; i < s->num_prompts; i++) {
|
for (i = 0; i < s->num_prompts; i++) {
|
||||||
ptrlen prompt;
|
ptrlen prompt = get_string(pktin);
|
||||||
bool echo;
|
bool echo = get_bool(pktin);
|
||||||
static char noprompt[] =
|
|
||||||
"<server failed to send prompt>: ";
|
|
||||||
|
|
||||||
prompt = get_string(pktin);
|
sb = strbuf_new();
|
||||||
echo = get_bool(pktin);
|
|
||||||
if (!prompt.len) {
|
if (!prompt.len) {
|
||||||
prompt.ptr = noprompt;
|
put_datapl(sb, PTRLEN_LITERAL(
|
||||||
prompt.len = lenof(noprompt)-1;
|
"<server failed to send prompt>: "));
|
||||||
|
} else if (s->ki_scc) {
|
||||||
|
stripctrl_retarget(
|
||||||
|
s->ki_scc, BinarySink_UPCAST(sb));
|
||||||
|
put_datapl(s->ki_scc, prompt);
|
||||||
|
stripctrl_retarget(s->ki_scc, NULL);
|
||||||
|
} else {
|
||||||
|
put_datapl(sb, prompt);
|
||||||
}
|
}
|
||||||
add_prompt(s->cur_prompt, mkstr(prompt), echo);
|
add_prompt(s->cur_prompt, strbuf_to_str(sb), echo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make the header strings. This includes the
|
||||||
|
* 'name' (optional dialog-box title) and
|
||||||
|
* 'instruction' from the server.
|
||||||
|
*
|
||||||
|
* First, display our disambiguating header line
|
||||||
|
* if this is the first time round the loop -
|
||||||
|
* _unless_ the server has sent a completely empty
|
||||||
|
* k-i packet with no prompts _or_ text, which
|
||||||
|
* apparently some do. In that situation there's
|
||||||
|
* no need to alert the user that the following
|
||||||
|
* text is server- supplied, because, well, _what_
|
||||||
|
* text?
|
||||||
|
*
|
||||||
|
* We also only do this if we got a stripctrl,
|
||||||
|
* because if we didn't, that suggests this is all
|
||||||
|
* being done via dialog boxes anyway.
|
||||||
|
*/
|
||||||
|
if (!s->ki_printed_header && s->ki_scc &&
|
||||||
|
(s->num_prompts || name.len || inst.len)) {
|
||||||
|
ssh2_userauth_antispoof_msg(
|
||||||
|
s, "Keyboard-interactive authentication "
|
||||||
|
"prompts from server:");
|
||||||
|
s->ki_printed_header = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb = strbuf_new();
|
||||||
if (name.len) {
|
if (name.len) {
|
||||||
/* FIXME: better prefix to distinguish from
|
stripctrl_retarget(s->ki_scc, BinarySink_UPCAST(sb));
|
||||||
* local prompts? */
|
put_datapl(s->ki_scc, name);
|
||||||
s->cur_prompt->name =
|
stripctrl_retarget(s->ki_scc, NULL);
|
||||||
dupprintf("SSH server: %.*s", PTRLEN_PRINTF(name));
|
|
||||||
s->cur_prompt->name_reqd = true;
|
s->cur_prompt->name_reqd = true;
|
||||||
} else {
|
} else {
|
||||||
s->cur_prompt->name =
|
put_datapl(sb, PTRLEN_LITERAL(
|
||||||
dupstr("SSH server authentication");
|
"SSH server authentication"));
|
||||||
s->cur_prompt->name_reqd = false;
|
s->cur_prompt->name_reqd = false;
|
||||||
}
|
}
|
||||||
/* We add a prefix to try to make it clear that a prompt
|
s->cur_prompt->name = strbuf_to_str(sb);
|
||||||
* has come from the server.
|
|
||||||
* FIXME: ugly to print "Using..." in prompt _every_
|
sb = strbuf_new();
|
||||||
* time round. Can this be done more subtly? */
|
if (inst.len) {
|
||||||
/* Special case: for reasons best known to themselves,
|
stripctrl_retarget(s->ki_scc, BinarySink_UPCAST(sb));
|
||||||
* some servers send k-i requests with no prompts and
|
put_datapl(s->ki_scc, inst);
|
||||||
* nothing to display. Keep quiet in this case. */
|
stripctrl_retarget(s->ki_scc, NULL);
|
||||||
if (s->num_prompts || name.len || inst.len) {
|
|
||||||
s->cur_prompt->instruction =
|
|
||||||
dupprintf("Using keyboard-interactive "
|
|
||||||
"authentication.%s%.*s",
|
|
||||||
inst.len ? "\n" : "",
|
|
||||||
PTRLEN_PRINTF(inst));
|
|
||||||
s->cur_prompt->instr_reqd = true;
|
s->cur_prompt->instr_reqd = true;
|
||||||
} else {
|
} else {
|
||||||
s->cur_prompt->instr_reqd = false;
|
s->cur_prompt->instr_reqd = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Display any instructions, and get the user's
|
* Our prompts_t is fully constructed now. Get the
|
||||||
* response(s).
|
* user's response(s).
|
||||||
*/
|
*/
|
||||||
s->userpass_ret = seat_get_userpass_input(
|
s->userpass_ret = seat_get_userpass_input(
|
||||||
s->ppl.seat, s->cur_prompt, NULL);
|
s->ppl.seat, s->cur_prompt, NULL);
|
||||||
@ -1313,6 +1356,13 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print our trailer line, if we printed a header.
|
||||||
|
*/
|
||||||
|
if (s->ki_printed_header)
|
||||||
|
ssh2_userauth_antispoof_msg(
|
||||||
|
s, "End of keyboard-interactive prompts from server");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We should have SUCCESS or FAILURE now.
|
* We should have SUCCESS or FAILURE now.
|
||||||
*/
|
*/
|
||||||
|
44
terminal.c
44
terminal.c
@ -1682,9 +1682,6 @@ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, TermWin *win)
|
|||||||
term->paste_buffer = NULL;
|
term->paste_buffer = NULL;
|
||||||
term->paste_len = 0;
|
term->paste_len = 0;
|
||||||
bufchain_init(&term->inbuf);
|
bufchain_init(&term->inbuf);
|
||||||
bufchain_sink_init(&term->inbuf_bs, &term->inbuf);
|
|
||||||
term->inbuf_scc = stripctrl_new_term(
|
|
||||||
BinarySink_UPCAST(&term->inbuf_bs), false, 0, term);
|
|
||||||
bufchain_init(&term->printer_buf);
|
bufchain_init(&term->printer_buf);
|
||||||
term->printing = term->only_printing = false;
|
term->printing = term->only_printing = false;
|
||||||
term->print_job = NULL;
|
term->print_job = NULL;
|
||||||
@ -1779,7 +1776,6 @@ void term_free(Terminal *term)
|
|||||||
term->beephead = beep->next;
|
term->beephead = beep->next;
|
||||||
sfree(beep);
|
sfree(beep);
|
||||||
}
|
}
|
||||||
stripctrl_free(term->inbuf_scc);
|
|
||||||
bufchain_clear(&term->inbuf);
|
bufchain_clear(&term->inbuf);
|
||||||
if(term->print_job)
|
if(term->print_job)
|
||||||
printer_finish_job(term->print_job);
|
printer_finish_job(term->print_job);
|
||||||
@ -6837,12 +6833,6 @@ size_t term_data(Terminal *term, bool is_stderr, const void *data, size_t len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void term_data_untrusted(Terminal *term, const void *data, size_t len)
|
|
||||||
{
|
|
||||||
put_data(term->inbuf_scc, data, len);
|
|
||||||
term_added_data(term);
|
|
||||||
}
|
|
||||||
|
|
||||||
void term_provide_logctx(Terminal *term, LogContext *logctx)
|
void term_provide_logctx(Terminal *term, LogContext *logctx)
|
||||||
{
|
{
|
||||||
term->logctx = logctx;
|
term->logctx = logctx;
|
||||||
@ -6877,6 +6867,12 @@ struct term_userpass_state {
|
|||||||
size_t pos; /* cursor position */
|
size_t pos; /* cursor position */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Tiny wrapper to make it easier to write lots of little strings */
|
||||||
|
static inline void term_write(Terminal *term, ptrlen data)
|
||||||
|
{
|
||||||
|
term_data(term, false, data.ptr, data.len);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process some terminal data in the course of username/password
|
* Process some terminal data in the course of username/password
|
||||||
* input.
|
* input.
|
||||||
@ -6893,17 +6889,17 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
|||||||
s->done_prompt = false;
|
s->done_prompt = false;
|
||||||
/* We only print the `name' caption if we have to... */
|
/* We only print the `name' caption if we have to... */
|
||||||
if (p->name_reqd && p->name) {
|
if (p->name_reqd && p->name) {
|
||||||
size_t l = strlen(p->name);
|
ptrlen plname = ptrlen_from_asciz(p->name);
|
||||||
term_data_untrusted(term, p->name, l);
|
term_write(term, plname);
|
||||||
if (p->name[l-1] != '\n')
|
if (!ptrlen_endswith(plname, PTRLEN_LITERAL("\n"), NULL))
|
||||||
term_data_untrusted(term, "\n", 1);
|
term_write(term, PTRLEN_LITERAL("\r\n"));
|
||||||
}
|
}
|
||||||
/* ...but we always print any `instruction'. */
|
/* ...but we always print any `instruction'. */
|
||||||
if (p->instruction) {
|
if (p->instruction) {
|
||||||
size_t l = strlen(p->instruction);
|
ptrlen plinst = ptrlen_from_asciz(p->instruction);
|
||||||
term_data_untrusted(term, p->instruction, l);
|
term_write(term, plinst);
|
||||||
if (p->instruction[l-1] != '\n')
|
if (!ptrlen_endswith(plinst, PTRLEN_LITERAL("\n"), NULL))
|
||||||
term_data_untrusted(term, "\n", 1);
|
term_write(term, PTRLEN_LITERAL("\r\n"));
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Zero all the results, in case we abort half-way through.
|
* Zero all the results, in case we abort half-way through.
|
||||||
@ -6921,7 +6917,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
|||||||
bool finished_prompt = false;
|
bool finished_prompt = false;
|
||||||
|
|
||||||
if (!s->done_prompt) {
|
if (!s->done_prompt) {
|
||||||
term_data_untrusted(term, pr->prompt, strlen(pr->prompt));
|
term_write(term, ptrlen_from_asciz(pr->prompt));
|
||||||
s->done_prompt = true;
|
s->done_prompt = true;
|
||||||
s->pos = 0;
|
s->pos = 0;
|
||||||
}
|
}
|
||||||
@ -6937,7 +6933,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
|||||||
switch (c) {
|
switch (c) {
|
||||||
case 10:
|
case 10:
|
||||||
case 13:
|
case 13:
|
||||||
term_data(term, false, "\r\n", 2);
|
term_write(term, PTRLEN_LITERAL("\r\n"));
|
||||||
prompt_ensure_result_size(pr, s->pos + 1);
|
prompt_ensure_result_size(pr, s->pos + 1);
|
||||||
pr->result[s->pos] = '\0';
|
pr->result[s->pos] = '\0';
|
||||||
/* go to next prompt, if any */
|
/* go to next prompt, if any */
|
||||||
@ -6949,7 +6945,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
|||||||
case 127:
|
case 127:
|
||||||
if (s->pos > 0) {
|
if (s->pos > 0) {
|
||||||
if (pr->echo)
|
if (pr->echo)
|
||||||
term_data(term, false, "\b \b", 3);
|
term_write(term, PTRLEN_LITERAL("\b \b"));
|
||||||
s->pos--;
|
s->pos--;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -6957,14 +6953,14 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
|||||||
case 27:
|
case 27:
|
||||||
while (s->pos > 0) {
|
while (s->pos > 0) {
|
||||||
if (pr->echo)
|
if (pr->echo)
|
||||||
term_data(term, false, "\b \b", 3);
|
term_write(term, PTRLEN_LITERAL("\b \b"));
|
||||||
s->pos--;
|
s->pos--;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
case 4:
|
case 4:
|
||||||
/* Immediate abort. */
|
/* Immediate abort. */
|
||||||
term_data(term, false, "\r\n", 2);
|
term_write(term, PTRLEN_LITERAL("\r\n"));
|
||||||
sfree(s);
|
sfree(s);
|
||||||
p->data = NULL;
|
p->data = NULL;
|
||||||
return 0; /* user abort */
|
return 0; /* user abort */
|
||||||
@ -6979,7 +6975,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
|||||||
prompt_ensure_result_size(pr, s->pos + 1);
|
prompt_ensure_result_size(pr, s->pos + 1);
|
||||||
pr->result[s->pos++] = c;
|
pr->result[s->pos++] = c;
|
||||||
if (pr->echo)
|
if (pr->echo)
|
||||||
term_data(term, false, &c, 1);
|
term_write(term, make_ptrlen(&c, 1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -100,8 +100,6 @@ struct terminal_tag {
|
|||||||
termchar basic_erase_char, erase_char;
|
termchar basic_erase_char, erase_char;
|
||||||
|
|
||||||
bufchain inbuf; /* terminal input buffer */
|
bufchain inbuf; /* terminal input buffer */
|
||||||
bufchain_sink inbuf_bs;
|
|
||||||
StripCtrlChars *inbuf_scc;
|
|
||||||
|
|
||||||
pos curs; /* cursor */
|
pos curs; /* cursor */
|
||||||
pos savecurs; /* saved cursor position */
|
pos savecurs; /* saved cursor position */
|
||||||
|
@ -494,22 +494,9 @@ static void console_close(FILE *outfp, int infd)
|
|||||||
fclose(outfp); /* will automatically close infd too */
|
fclose(outfp); /* will automatically close infd too */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void console_prompt_text(FILE *outfp, const char *data, size_t len)
|
static void console_write(FILE *outfp, ptrlen data)
|
||||||
{
|
{
|
||||||
bufchain sanitised;
|
fwrite(data.ptr, 1, data.len, outfp);
|
||||||
bufchain_sink bs;
|
|
||||||
|
|
||||||
bufchain_init(&sanitised);
|
|
||||||
bufchain_sink_init(&bs, &sanitised);
|
|
||||||
StripCtrlChars *scc = stripctrl_new(BinarySink_UPCAST(&bs), false, 0);
|
|
||||||
put_data(scc, data, len);
|
|
||||||
stripctrl_free(scc);
|
|
||||||
|
|
||||||
while (bufchain_size(&sanitised) > 0) {
|
|
||||||
ptrlen sdata = bufchain_prefix(&sanitised);
|
|
||||||
fwrite(sdata.ptr, 1, sdata.len, outfp);
|
|
||||||
bufchain_consume(&sanitised, sdata.len);
|
|
||||||
}
|
|
||||||
fflush(outfp);
|
fflush(outfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,17 +525,17 @@ int console_get_userpass_input(prompts_t *p)
|
|||||||
*/
|
*/
|
||||||
/* We only print the `name' caption if we have to... */
|
/* We only print the `name' caption if we have to... */
|
||||||
if (p->name_reqd && p->name) {
|
if (p->name_reqd && p->name) {
|
||||||
size_t l = strlen(p->name);
|
ptrlen plname = ptrlen_from_asciz(p->name);
|
||||||
console_prompt_text(outfp, p->name, l);
|
console_write(outfp, plname);
|
||||||
if (p->name[l-1] != '\n')
|
if (!ptrlen_endswith(plname, PTRLEN_LITERAL("\n"), NULL))
|
||||||
console_prompt_text(outfp, "\n", 1);
|
console_write(outfp, PTRLEN_LITERAL("\n"));
|
||||||
}
|
}
|
||||||
/* ...but we always print any `instruction'. */
|
/* ...but we always print any `instruction'. */
|
||||||
if (p->instruction) {
|
if (p->instruction) {
|
||||||
size_t l = strlen(p->instruction);
|
ptrlen plinst = ptrlen_from_asciz(p->instruction);
|
||||||
console_prompt_text(outfp, p->instruction, l);
|
console_write(outfp, plinst);
|
||||||
if (p->instruction[l-1] != '\n')
|
if (!ptrlen_endswith(plinst, PTRLEN_LITERAL("\n"), NULL))
|
||||||
console_prompt_text(outfp, "\n", 1);
|
console_write(outfp, PTRLEN_LITERAL("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
|
for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
|
||||||
@ -566,7 +553,7 @@ int console_get_userpass_input(prompts_t *p)
|
|||||||
newmode.c_lflag |= ECHO;
|
newmode.c_lflag |= ECHO;
|
||||||
tcsetattr(infd, TCSANOW, &newmode);
|
tcsetattr(infd, TCSANOW, &newmode);
|
||||||
|
|
||||||
console_prompt_text(outfp, pr->prompt, strlen(pr->prompt));
|
console_write(outfp, ptrlen_from_asciz(pr->prompt));
|
||||||
|
|
||||||
len = 0;
|
len = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
@ -588,7 +575,7 @@ int console_get_userpass_input(prompts_t *p)
|
|||||||
tcsetattr(infd, TCSANOW, &oldmode);
|
tcsetattr(infd, TCSANOW, &oldmode);
|
||||||
|
|
||||||
if (!pr->echo)
|
if (!pr->echo)
|
||||||
console_prompt_text(outfp, "\n", 1);
|
console_write(outfp, PTRLEN_LITERAL("\n"));
|
||||||
|
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
console_close(outfp, infd);
|
console_close(outfp, infd);
|
||||||
|
@ -394,23 +394,10 @@ StripCtrlChars *console_stripctrl_new(
|
|||||||
return stripctrl_new(bs_out, false, 0);
|
return stripctrl_new(bs_out, false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void console_data_untrusted(HANDLE hout, const char *data, size_t len)
|
static void console_write(HANDLE hout, ptrlen data)
|
||||||
{
|
{
|
||||||
DWORD dummy;
|
DWORD dummy;
|
||||||
bufchain sanitised;
|
WriteFile(hout, data.ptr, data.len, &dummy, NULL);
|
||||||
bufchain_sink bs;
|
|
||||||
|
|
||||||
bufchain_init(&sanitised);
|
|
||||||
bufchain_sink_init(&bs, &sanitised);
|
|
||||||
StripCtrlChars *scc = stripctrl_new(BinarySink_UPCAST(&bs), false, 0);
|
|
||||||
put_data(scc, data, len);
|
|
||||||
stripctrl_free(scc);
|
|
||||||
|
|
||||||
while (bufchain_size(&sanitised) > 0) {
|
|
||||||
ptrlen sdata = bufchain_prefix(&sanitised);
|
|
||||||
WriteFile(hout, sdata.ptr, sdata.len, &dummy, NULL);
|
|
||||||
bufchain_consume(&sanitised, sdata.len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int console_get_userpass_input(prompts_t *p)
|
int console_get_userpass_input(prompts_t *p)
|
||||||
@ -459,17 +446,17 @@ int console_get_userpass_input(prompts_t *p)
|
|||||||
*/
|
*/
|
||||||
/* We only print the `name' caption if we have to... */
|
/* We only print the `name' caption if we have to... */
|
||||||
if (p->name_reqd && p->name) {
|
if (p->name_reqd && p->name) {
|
||||||
size_t l = strlen(p->name);
|
ptrlen plname = ptrlen_from_asciz(p->name);
|
||||||
console_data_untrusted(hout, p->name, l);
|
console_write(hout, plname);
|
||||||
if (p->name[l-1] != '\n')
|
if (!ptrlen_endswith(plname, PTRLEN_LITERAL("\n"), NULL))
|
||||||
console_data_untrusted(hout, "\n", 1);
|
console_write(hout, PTRLEN_LITERAL("\n"));
|
||||||
}
|
}
|
||||||
/* ...but we always print any `instruction'. */
|
/* ...but we always print any `instruction'. */
|
||||||
if (p->instruction) {
|
if (p->instruction) {
|
||||||
size_t l = strlen(p->instruction);
|
ptrlen plinst = ptrlen_from_asciz(p->instruction);
|
||||||
console_data_untrusted(hout, p->instruction, l);
|
console_write(hout, plinst);
|
||||||
if (p->instruction[l-1] != '\n')
|
if (!ptrlen_endswith(plinst, PTRLEN_LITERAL("\n"), NULL))
|
||||||
console_data_untrusted(hout, "\n", 1);
|
console_write(hout, PTRLEN_LITERAL("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
|
for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
|
||||||
@ -486,7 +473,7 @@ int console_get_userpass_input(prompts_t *p)
|
|||||||
newmode |= ENABLE_ECHO_INPUT;
|
newmode |= ENABLE_ECHO_INPUT;
|
||||||
SetConsoleMode(hin, newmode);
|
SetConsoleMode(hin, newmode);
|
||||||
|
|
||||||
console_data_untrusted(hout, pr->prompt, strlen(pr->prompt));
|
console_write(hout, ptrlen_from_asciz(pr->prompt));
|
||||||
|
|
||||||
len = 0;
|
len = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
@ -510,10 +497,8 @@ int console_get_userpass_input(prompts_t *p)
|
|||||||
|
|
||||||
SetConsoleMode(hin, savemode);
|
SetConsoleMode(hin, savemode);
|
||||||
|
|
||||||
if (!pr->echo) {
|
if (!pr->echo)
|
||||||
DWORD dummy;
|
console_write(hout, PTRLEN_LITERAL("\r\n"));
|
||||||
WriteFile(hout, "\r\n", 2, &dummy, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len == (size_t)-1) {
|
if (len == (size_t)-1) {
|
||||||
return 0; /* failure due to read error */
|
return 0; /* failure due to read error */
|
||||||
|
Loading…
Reference in New Issue
Block a user