mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Add an interactive anti-spoofing prompt in Plink.
At the point when we change over the seat's trust status to untrusted for the last time, to finish authentication, Plink will now present a final interactive prompt saying 'Press Return to begin session'. This is a hint that anything after that that resembles an auth prompt should be treated with suspicion, because _PuTTY_ thinks it's finished authenticating. This is of course an annoying inconvenience for interactive users, so I've tried to reduce its impact as much as I can. It doesn't happen in GUI PuTTY at all (because the trust sigil system is used instead); it doesn't happen if you use plink -batch (because then the user already knows that they _never_ expect an interactive prompt); and it doesn't happen if Plink's standard input is being redirected from anywhere other than the terminal / console (because then it would be pointless for the server to try to scam passphrases out of the user anyway, since the user isn't in a position to enter one in response to a spoof prompt). So it should only happen to people who are using Plink in a terminal for interactive login purposes, and that's not _really_ what I ever intended Plink to be used for (which is why it's never had any out-of-band control UI like OpenSSH's ~ system). If anyone _still_ doesn't like this new prompt, it can also be turned off using the new -no-antispoof flag, if the user is willing to knowingly assume the risk.
This commit is contained in:
parent
76d8d363be
commit
514796b7e4
@ -328,6 +328,46 @@ channel.
|
||||
\dt Do not sanitise server data written to Plink's standard output
|
||||
channel.
|
||||
|
||||
\S2{plink-option-antispoof} \I{-no-antispoof}: turn off authentication spoofing protection prompt
|
||||
|
||||
In SSH, some possible server authentication methods require user input
|
||||
(for example, password authentication, or entering a private key
|
||||
passphrase), and others do not (e.g. a private key held in Pageant).
|
||||
|
||||
If you use Plink to run an interactive login session, and if Plink
|
||||
authenticates without needing any user interaction, and if the server
|
||||
is malicious or compromised, it could try to trick you into giving it
|
||||
authentication data that should not go to the server (such as your
|
||||
private key passphrase), by sending what \e{looks} like one of Plink's
|
||||
local prompts, as if Plink had not already authenticated.
|
||||
|
||||
To protect against this, Plink's default policy is to finish the
|
||||
authentication phase with a final trivial prompt looking like this:
|
||||
|
||||
\c Access granted. Press Return to begin session.
|
||||
|
||||
so that if you saw anything that looked like an authentication prompt
|
||||
\e{after} that line, you would know it was not from Plink.
|
||||
|
||||
That extra interactive step is inconvenient. So Plink will turn it off
|
||||
in as many situations as it can:
|
||||
|
||||
\b If Plink's standard input is not pointing at a console or terminal
|
||||
device \dash for example, if you're using Plink as a transport for
|
||||
some automated application like version control \dash then you
|
||||
\e{can't} type passphrases into the server anyway. In that situation,
|
||||
Plink won't try to protect you from the server trying to fool you into
|
||||
doing so.
|
||||
|
||||
\b If Plink is in batch mode (see \k{plink-usage-batch}), then it
|
||||
\e{never} does any interactive authentication. So anything looking
|
||||
like an interactive authentication prompt is automatically suspect,
|
||||
and so Plink omits the anti-spoofing prompt.
|
||||
|
||||
But if you still find the protective prompt inconvenient, and you
|
||||
trust the server not to try a trick like this, you can turn it off
|
||||
using the \cq{-no-antispoof} option.
|
||||
|
||||
\H{plink-batch} Using Plink in \i{batch files} and \i{scripts}
|
||||
|
||||
Once you have set up Plink to be able to log in to a remote server
|
||||
|
2
putty.h
2
putty.h
@ -1926,7 +1926,7 @@ bool have_ssh_host_key(const char *host, int port, const char *keytype);
|
||||
* Exports from console frontends (wincons.c, uxcons.c)
|
||||
* that aren't equivalents to things in windlg.c et al.
|
||||
*/
|
||||
extern bool console_batch_mode;
|
||||
extern bool console_batch_mode, console_antispoof_prompt;
|
||||
int console_get_userpass_input(prompts_t *p);
|
||||
bool is_interactive(void);
|
||||
void console_print_error_msg(const char *prefix, const char *msg);
|
||||
|
@ -474,3 +474,8 @@ void ssh2channel_send_terminal_size_change(SshChannel *sc, int w, int h)
|
||||
put_uint32(pktout, 0); /* pixel height */
|
||||
pq_push(s->ppl.out_pq, pktout);
|
||||
}
|
||||
|
||||
bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s)
|
||||
{
|
||||
return !seat_set_trust_status(s->ppl.seat, false);
|
||||
}
|
||||
|
@ -297,3 +297,8 @@ void ssh2channel_send_terminal_size_change(SshChannel *sc, int w, int h)
|
||||
{
|
||||
unreachable("Should never be called in the server");
|
||||
}
|
||||
|
||||
bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -953,6 +953,41 @@ static void ssh2_connection_process_queue(PacketProtocolLayer *ppl)
|
||||
if (s->connshare)
|
||||
share_activate(s->connshare, s->peer_verstring);
|
||||
|
||||
/*
|
||||
* Signal the seat that authentication is done, so that it can
|
||||
* deploy spoofing defences. If it doesn't have any, deploy our
|
||||
* own fallback one.
|
||||
*
|
||||
* We do this here rather than at the end of userauth, because we
|
||||
* might not have gone through userauth at all (if we're a
|
||||
* connection-sharing downstream).
|
||||
*/
|
||||
if (ssh2_connection_need_antispoof_prompt(s)) {
|
||||
s->antispoof_prompt = new_prompts();
|
||||
s->antispoof_prompt->to_server = true;
|
||||
s->antispoof_prompt->from_server = false;
|
||||
s->antispoof_prompt->name = dupstr("Authentication successful");
|
||||
add_prompt(
|
||||
s->antispoof_prompt,
|
||||
dupstr("Access granted. Press Return to begin session. "), false);
|
||||
s->antispoof_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->antispoof_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->antispoof_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->antispoof_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->antispoof_prompt, s->ppl.user_input);
|
||||
|
||||
if (s->antispoof_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
}
|
||||
free_prompts(s->antispoof_prompt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable port forwardings.
|
||||
*/
|
||||
|
@ -37,6 +37,9 @@ struct ssh2_connection_state {
|
||||
PortFwdManager *portfwdmgr;
|
||||
bool portfwdmgr_configured;
|
||||
|
||||
prompts_t *antispoof_prompt;
|
||||
int antispoof_ret;
|
||||
|
||||
const SftpServerVtable *sftpserver_vt;
|
||||
|
||||
/*
|
||||
@ -228,4 +231,6 @@ ChanopenResult ssh2_connection_parse_channel_open(
|
||||
bool ssh2_connection_parse_global_request(
|
||||
struct ssh2_connection_state *s, ptrlen type, PktIn *pktin);
|
||||
|
||||
bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s);
|
||||
|
||||
#endif /* PUTTY_SSH2CONNECTION_H */
|
||||
|
@ -414,8 +414,26 @@ static int console_askappend(LogPolicy *lp, Filename *filename,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool console_antispoof_prompt = true;
|
||||
bool console_set_trust_status(Seat *seat, bool trusted)
|
||||
{
|
||||
if (console_batch_mode || !is_interactive() || !console_antispoof_prompt) {
|
||||
/*
|
||||
* In batch mode, we don't need to worry about the server
|
||||
* mimicking our interactive authentication, because the user
|
||||
* already knows not to expect any.
|
||||
*
|
||||
* If standard input isn't connected to a terminal, likewise,
|
||||
* because even if the server did send a spoof authentication
|
||||
* prompt, the user couldn't respond to it via the terminal
|
||||
* anyway.
|
||||
*
|
||||
* We also vacuously return success if the user has purposely
|
||||
* disabled the antispoof prompt.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -534,6 +534,8 @@ static void usage(void)
|
||||
"-no-sanitise-stderr, -no-sanitise-stdout\n");
|
||||
printf(" do/don't strip control chars from standard "
|
||||
"output/error\n");
|
||||
printf(" -no-antispoof omit anti-spoofing prompt after "
|
||||
"authentication\n");
|
||||
printf(" -m file read remote command(s) from file\n");
|
||||
printf(" -s remote command is an SSH subsystem (SSH-2 only)\n");
|
||||
printf(" -N don't start a shell/command (SSH-2 only)\n");
|
||||
@ -678,6 +680,8 @@ int main(int argc, char **argv)
|
||||
} else if (!strcmp(p, "-no-sanitise-stderr") ||
|
||||
!strcmp(p, "-no-sanitize-stderr")) {
|
||||
sanitise_stderr = FORCE_OFF;
|
||||
} else if (!strcmp(p, "-no-antispoof")) {
|
||||
console_antispoof_prompt = false;
|
||||
} else if (*p != '-') {
|
||||
strbuf *cmdbuf = strbuf_new();
|
||||
|
||||
|
@ -279,8 +279,31 @@ int console_confirm_weak_cached_hostkey(
|
||||
}
|
||||
}
|
||||
|
||||
bool is_interactive(void)
|
||||
{
|
||||
return is_console_handle(GetStdHandle(STD_INPUT_HANDLE));
|
||||
}
|
||||
|
||||
bool console_antispoof_prompt = true;
|
||||
bool console_set_trust_status(Seat *seat, bool trusted)
|
||||
{
|
||||
if (console_batch_mode || !is_interactive() || !console_antispoof_prompt) {
|
||||
/*
|
||||
* In batch mode, we don't need to worry about the server
|
||||
* mimicking our interactive authentication, because the user
|
||||
* already knows not to expect any.
|
||||
*
|
||||
* If standard input isn't connected to a terminal, likewise,
|
||||
* because even if the server did send a spoof authentication
|
||||
* prompt, the user couldn't respond to it via the terminal
|
||||
* anyway.
|
||||
*
|
||||
* We also vacuously return success if the user has purposely
|
||||
* disabled the antispoof prompt.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -171,6 +171,8 @@ static void usage(void)
|
||||
"-no-sanitise-stderr, -no-sanitise-stdout\n");
|
||||
printf(" do/don't strip control chars from standard "
|
||||
"output/error\n");
|
||||
printf(" -no-antispoof omit anti-spoofing prompt after "
|
||||
"authentication\n");
|
||||
printf(" -m file read remote command(s) from file\n");
|
||||
printf(" -s remote command is an SSH subsystem (SSH-2 only)\n");
|
||||
printf(" -N don't start a shell/command (SSH-2 only)\n");
|
||||
@ -353,6 +355,8 @@ int main(int argc, char **argv)
|
||||
} else if (!strcmp(p, "-no-sanitise-stderr") ||
|
||||
!strcmp(p, "-no-sanitize-stderr")) {
|
||||
sanitise_stderr = FORCE_OFF;
|
||||
} else if (!strcmp(p, "-no-antispoof")) {
|
||||
console_antispoof_prompt = false;
|
||||
} else if (*p != '-') {
|
||||
strbuf *cmdbuf = strbuf_new();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user