1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/windows/console.c

731 lines
22 KiB
C
Raw Normal View History

/*
* console.c - various interactive-prompt routines shared between
* the Windows console PuTTY tools
*/
#include <stdio.h>
#include <stdlib.h>
#include "putty.h"
#include "storage.h"
#include "ssh.h"
#include "console.h"
void cleanup_exit(int code)
{
/*
* Clean up.
*/
sk_cleanup();
random_save_seed();
exit(code);
}
New abstraction 'Seat', to pass to backends. This is a new vtable-based abstraction which is passed to a backend in place of Frontend, and it implements only the subset of the Frontend functions needed by a backend. (Many other Frontend functions still exist, notably the wide range of things called by terminal.c providing platform-independent operations on the GUI terminal window.) The purpose of making it a vtable is that this opens up the possibility of creating a backend as an internal implementation detail of some other activity, by providing just that one backend with a custom Seat that implements the methods differently. For example, this refactoring should make it feasible to directly implement an SSH proxy type, aka the 'jump host' feature supported by OpenSSH, aka 'open a secondary SSH session in MAINCHAN_DIRECT_TCP mode, and then expose the main channel of that as the Socket for the primary connection'. (Which of course you can already do by spawning 'plink -nc' as a separate proxy process, but this would permit it in the _same_ process without anything getting confused.) I've centralised a full set of stub methods in misc.c for the new abstraction, which allows me to get rid of several annoying stubs in the previous code. Also, while I'm here, I've moved a lot of duplicated modalfatalbox() type functions from application main program files into wincons.c / uxcons.c, which I think saves duplication overall. (A minor visible effect is that the prefixes on those console-based fatal error messages will now be more consistent between applications.)
2018-10-11 18:58:42 +00:00
void console_print_error_msg(const char *prefix, const char *msg)
{
fputs(prefix, stderr);
fputs(": ", stderr);
fputs(msg, stderr);
fputc('\n', stderr);
fflush(stderr);
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
/*
* System for getting I/O handles to talk to the console for
* interactive prompts.
*
* In PuTTY 0.81 and before, these prompts used the standard I/O
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
* handles. But this means you can't redirect Plink's actual stdin
* from a sensible data channel without the responses to login prompts
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
* unwantedly being read from it too. Also, if you have a real
* console handle then you can read from it in Unicode mode, which is
* an option not available for any old file handle.
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
*
* However, many versions of PuTTY have worked the old way, so we need
* a method of falling back to it for the sake of whoever's workflow
* it turns out to break. So this structure equivocates between the
* two systems.
*/
static bool conio_use_standard_handles = false;
bool console_set_stdio_prompts(bool newvalue)
{
conio_use_standard_handles = newvalue;
return true;
}
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
static bool conio_use_utf8 = true;
bool set_legacy_charset_handling(bool newvalue)
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
{
conio_use_utf8 = !newvalue;
return true;
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
typedef struct ConsoleIO {
HANDLE hin, hout;
bool need_close_hin, need_close_hout;
bool hin_is_console, hout_is_console;
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
bool utf8;
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
BinarySink_IMPLEMENTATION;
} ConsoleIO;
static void console_write(BinarySink *bs, const void *data, size_t len);
static ConsoleIO *conio_setup(bool utf8, DWORD fallback_output)
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
{
ConsoleIO *conio = snew(ConsoleIO);
conio->hin = conio->hout = INVALID_HANDLE_VALUE;
conio->need_close_hin = conio->need_close_hout = false;
init_winver();
if (osPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
osPlatformId == VER_PLATFORM_WIN32s)
conio->utf8 = false; /* no Unicode support at all */
else
conio->utf8 = utf8 && conio_use_utf8;
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
/*
* First try opening the console itself, so that prompts will go
* there regardless of I/O redirection. We don't do this if the
* user has deliberately requested a fallback to the old
* behaviour. We also don't do it in batch mode, because in that
* situation, any need for an interactive prompt will instead
* noninteractively abort the connection, and in that situation,
* the 'prompt' becomes more in the nature of an error message, so
* it should go to standard error like everything else.
*/
if (!conio_use_standard_handles && !console_batch_mode) {
/*
* If we do open the console, it has to be done separately for
* input and output, with different magic file names.
*
* We need both read and write permission for both handles,
* because read permission is needed to read the console mode
* (in particular, to test if a file handle _is_ a console),
* and write permission to change it.
*/
conio->hin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (conio->hin != INVALID_HANDLE_VALUE)
conio->need_close_hin = true;
conio->hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (conio->hout != INVALID_HANDLE_VALUE)
conio->need_close_hout = true;
}
/*
* Fall back from that to using the standard handles.
* (For prompt output, some callers use STD_ERROR_HANDLE rather
* than STD_OUTPUT_HANDLE, because that has a better chance of
* separating them from session output.)
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
*/
if (conio->hin == INVALID_HANDLE_VALUE)
conio->hin = GetStdHandle(STD_INPUT_HANDLE);
if (conio->hout == INVALID_HANDLE_VALUE)
conio->hout = GetStdHandle(fallback_output);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
DWORD dummy;
conio->hin_is_console = GetConsoleMode(conio->hin, &dummy);
conio->hout_is_console = GetConsoleMode(conio->hout, &dummy);
BinarySink_INIT(conio, console_write);
return conio;
}
static void conio_free(ConsoleIO *conio)
{
if (conio->need_close_hin)
CloseHandle(conio->hin);
if (conio->need_close_hout)
CloseHandle(conio->hout);
sfree(conio);
}
static void console_write(BinarySink *bs, const void *data, size_t len)
{
ConsoleIO *conio = BinarySink_DOWNCAST(bs, ConsoleIO);
if (conio->utf8) {
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
/*
* Convert the UTF-8 input into a wide string.
*/
size_t wlen;
wchar_t *wide = dup_mb_to_wc_c(CP_UTF8, data, len, &wlen);
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
if (conio->hout_is_console) {
/*
* To write UTF-8 to a console, use WriteConsoleW on the
* wide string we've just made.
*/
size_t pos = 0;
DWORD nwritten;
while (pos < wlen && WriteConsoleW(conio->hout, wide+pos, wlen-pos,
&nwritten, NULL))
pos += nwritten;
} else {
/*
* To write a string encoded in UTF-8 to any other file
* handle, the best we can do is to convert it into the
* system code page. This will lose some characters, but
* what else can you do?
*/
size_t clen;
char *sys_cp = dup_wc_to_mb_c(CP_ACP, wide, wlen, "?", &clen);
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
size_t pos = 0;
DWORD nwritten;
while (pos < clen && WriteFile(conio->hout, sys_cp+pos, clen-pos,
&nwritten, NULL))
pos += nwritten;
burnstr(sys_cp);
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
burnwcs(wide);
} else {
/*
* If we're in legacy non-UTF-8 mode, just send the bytes
* we're given to the file handle without trying to be clever.
*/
const char *cdata = (const char *)data;
size_t pos = 0;
DWORD nwritten;
while (pos < len && WriteFile(conio->hout, cdata+pos, len-pos,
&nwritten, NULL))
pos += nwritten;
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
}
static bool console_read_line_to_strbuf(ConsoleIO *conio, bool echo,
strbuf *sb)
{
DWORD savemode;
if (conio->hin_is_console) {
GetConsoleMode(conio->hin, &savemode);
DWORD newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
if (!echo)
newmode &= ~ENABLE_ECHO_INPUT;
else
newmode |= ENABLE_ECHO_INPUT;
SetConsoleMode(conio->hin, newmode);
}
bool toret = false;
while (true) {
if (ptrlen_endswith(ptrlen_from_strbuf(sb),
PTRLEN_LITERAL("\n"), NULL)) {
toret = true;
goto out;
}
if (conio->utf8) {
Rework Unicode conversion APIs to use a BinarySink. The previous mb_to_wc and wc_to_mb had horrible and also buggy APIs. This commit introduces a fresh pair of functions to replace them, which generate output by writing to a BinarySink. So it's now up to the caller to decide whether it wants the output written to a fixed-size buffer with overflow checking (via buffer_sink), or dynamically allocated, or even written directly to some other output channel. Nothing uses the new functions yet. I plan to migrate things over in upcoming commits. What was wrong with the old APIs: they had that awkward undocumented Windows-specific 'flags' parameter that I described in the previous commit and took out of the dup_X_to_Y wrappers. But much worse, the semantics for buffer overflow were not just undocumented but actually inconsistent. dup_wc_to_mb() in utils assumed that the underlying wc_to_mb would fill the buffer nearly full and return the size of data it wrote. In fact, this was untrue in the case where wc_to_mb called WideCharToMultiByte: that returns straight-up failure, setting the Windows error code to ERROR_INSUFFICIENT_BUFFER. It _does_ partially fill the output buffer, but doesn't tell you how much it wrote! What's wrong with the new API: it's a bit awkward to write a sequence of wchar_t in native byte order to a byte-oriented BinarySink, so people using put_mb_to_wc directly have to do some annoying pointer casting. But I think that's less horrible than the previous APIs. Another change: in the new API for wc_to_mb, defchr can be "", but not NULL.
2024-09-24 07:18:48 +00:00
wchar_t wbuf[4097];
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
size_t wlen;
if (conio->hin_is_console) {
/*
* To read UTF-8 from a console, read wide character data
* via ReadConsoleW, and convert it to UTF-8.
*/
DWORD nread;
if (!ReadConsoleW(conio->hin, wbuf, lenof(wbuf), &nread, NULL))
goto out;
wlen = nread;
} else {
/*
* To read UTF-8 from an ordinary file handle, read it
* as normal bytes and then convert from CP_ACP to
* UTF-8, in the reverse of what we did above for
* output.
*/
char buf[4096];
DWORD nread;
if (!ReadFile(conio->hin, buf, lenof(buf), &nread, NULL))
goto out;
Rework Unicode conversion APIs to use a BinarySink. The previous mb_to_wc and wc_to_mb had horrible and also buggy APIs. This commit introduces a fresh pair of functions to replace them, which generate output by writing to a BinarySink. So it's now up to the caller to decide whether it wants the output written to a fixed-size buffer with overflow checking (via buffer_sink), or dynamically allocated, or even written directly to some other output channel. Nothing uses the new functions yet. I plan to migrate things over in upcoming commits. What was wrong with the old APIs: they had that awkward undocumented Windows-specific 'flags' parameter that I described in the previous commit and took out of the dup_X_to_Y wrappers. But much worse, the semantics for buffer overflow were not just undocumented but actually inconsistent. dup_wc_to_mb() in utils assumed that the underlying wc_to_mb would fill the buffer nearly full and return the size of data it wrote. In fact, this was untrue in the case where wc_to_mb called WideCharToMultiByte: that returns straight-up failure, setting the Windows error code to ERROR_INSUFFICIENT_BUFFER. It _does_ partially fill the output buffer, but doesn't tell you how much it wrote! What's wrong with the new API: it's a bit awkward to write a sequence of wchar_t in native byte order to a byte-oriented BinarySink, so people using put_mb_to_wc directly have to do some annoying pointer casting. But I think that's less horrible than the previous APIs. Another change: in the new API for wc_to_mb, defchr can be "", but not NULL.
2024-09-24 07:18:48 +00:00
buffer_sink bs[1];
buffer_sink_init(bs, wbuf, sizeof(wbuf) - sizeof(wchar_t));
put_mb_to_wc(bs, CP_ACP, buf, nread);
assert(!bs->overflowed);
wlen = (wchar_t *)bs->out - wbuf;
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
smemclr(buf, sizeof(buf));
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
Rework Unicode conversion APIs to use a BinarySink. The previous mb_to_wc and wc_to_mb had horrible and also buggy APIs. This commit introduces a fresh pair of functions to replace them, which generate output by writing to a BinarySink. So it's now up to the caller to decide whether it wants the output written to a fixed-size buffer with overflow checking (via buffer_sink), or dynamically allocated, or even written directly to some other output channel. Nothing uses the new functions yet. I plan to migrate things over in upcoming commits. What was wrong with the old APIs: they had that awkward undocumented Windows-specific 'flags' parameter that I described in the previous commit and took out of the dup_X_to_Y wrappers. But much worse, the semantics for buffer overflow were not just undocumented but actually inconsistent. dup_wc_to_mb() in utils assumed that the underlying wc_to_mb would fill the buffer nearly full and return the size of data it wrote. In fact, this was untrue in the case where wc_to_mb called WideCharToMultiByte: that returns straight-up failure, setting the Windows error code to ERROR_INSUFFICIENT_BUFFER. It _does_ partially fill the output buffer, but doesn't tell you how much it wrote! What's wrong with the new API: it's a bit awkward to write a sequence of wchar_t in native byte order to a byte-oriented BinarySink, so people using put_mb_to_wc directly have to do some annoying pointer casting. But I think that's less horrible than the previous APIs. Another change: in the new API for wc_to_mb, defchr can be "", but not NULL.
2024-09-24 07:18:48 +00:00
put_wc_to_mb(sb, CP_UTF8, wbuf, wlen, "");
Add UTF-8 support to the new Windows ConsoleIO system. This allows you to set a flag in conio_setup() which causes the returned ConsoleIO object to interpret all its output as UTF-8, by translating it to UTF-16 and using WriteConsoleW to write it in Unicode. Similarly, input is read using ReadConsoleW and decoded from UTF-16 to UTF-8. This flag is set to false in most places, to avoid making sudden breaking changes. But when we're about to present a prompts_t to the user, it's set from the new 'utf8' flag in that prompt, which in turn is set by the userauth layer in any case where the prompts are going to the server. The idea is that this should be the start of a fix for the long- standing character-set handling bug that strings transmitted during SSH userauth (usernames, passwords, k-i prompts and responses) are all supposed to be in UTF-8, but we've always encoded them in whatever our input system happens to be using, and not done any tidying up on them. We get occasional complaints about this from users whose passwords contain characters that are encoded differently between UTF-8 and their local encoding, but I've never got round to fixing it because it's a large piece of engineering. Indeed, this isn't nearly the end of it. The next step is to add UTF-8 support to all the _other_ ways of presenting a prompts_t, as best we can. Like the previous change to console handling, it seems very likely that this will break someone's workflow. So there's a fallback command-line option '-legacy-charset-handling' to revert to PuTTY's previous behaviour.
2022-11-25 12:57:43 +00:00
smemclr(wbuf, sizeof(wbuf));
} else {
/*
* If we're in legacy non-UTF-8 mode, just read bytes
* directly from the file handle into the output strbuf.
*/
char buf[4096];
DWORD nread;
if (!ReadFile(conio->hin, buf, lenof(buf), &nread, NULL))
goto out;
put_data(sb, buf, nread);
smemclr(buf, sizeof(buf));
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
}
out:
if (!echo)
put_datalit(conio, "\r\n");
if (conio->hin_is_console)
SetConsoleMode(conio->hin, savemode);
return toret;
}
static char *console_read_line(ConsoleIO *conio, bool echo)
{
strbuf *sb = strbuf_new_nm();
if (!console_read_line_to_strbuf(conio, echo, sb)) {
strbuf_free(sb);
return NULL;
} else {
return strbuf_to_str(sb);
}
}
typedef enum {
RESPONSE_ABANDON,
RESPONSE_YES,
RESPONSE_NO,
RESPONSE_INFO,
RESPONSE_UNRECOGNISED
} ResponseType;
static ResponseType parse_and_free_response(char *line)
{
if (!line)
return RESPONSE_ABANDON;
ResponseType toret;
switch (line[0]) {
/* In case of misplaced reflexes from another program,
* recognise 'q' as 'abandon connection' as well as the
* advertised 'just press Return' */
case 'q':
case 'Q':
case '\n':
case '\r':
case '\0':
toret = RESPONSE_ABANDON;
break;
case 'y':
case 'Y':
toret = RESPONSE_YES;
break;
case 'n':
case 'N':
toret = RESPONSE_NO;
break;
case 'i':
case 'I':
toret = RESPONSE_INFO;
break;
default:
toret = RESPONSE_UNRECOGNISED;
break;
}
burnstr(line);
return toret;
}
/*
* Helper function to print the message from a SeatDialogText. Returns
* the final prompt to print on the input line, or NULL if a
* batch-mode abort is needed. In the latter case it will have printed
* the abort text already.
*/
static const char *console_print_seatdialogtext(
ConsoleIO *conio, SeatDialogText *text)
{
const char *prompt = NULL;
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
for (SeatDialogTextItem *item = text->items,
*end = item+text->nitems; item < end; item++) {
switch (item->type) {
case SDT_PARA:
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
wordwrap(BinarySink_UPCAST(conio),
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
ptrlen_from_asciz(item->text), 60);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_byte(conio, '\n');
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
break;
case SDT_DISPLAY:
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_fmt(conio, " %s\n", item->text);
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
break;
case SDT_SCARY_HEADING:
/* Can't change font size or weight in this context */
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_fmt(conio, "%s\n", item->text);
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
break;
case SDT_BATCH_ABORT:
if (console_batch_mode) {
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_fmt(conio, "%s\n", item->text);
return NULL;
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
}
break;
case SDT_PROMPT:
prompt = item->text;
break;
default:
break;
}
}
assert(prompt); /* something in the SeatDialogText should have set this */
return prompt;
}
SeatPromptResult console_confirm_ssh_host_key(
Seat *seat, const char *host, int port, const char *keytype,
char *keystr, SeatDialogText *text, HelpCtx helpctx,
void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
{
ConsoleIO *conio = conio_setup(false, STD_ERROR_HANDLE);
SeatPromptResult result;
const char *prompt = console_print_seatdialogtext(conio, text);
if (!prompt) {
result = SPR_SW_ABORT("Cannot confirm a host key in batch mode");
goto out;
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
ResponseType response;
while (true) {
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_fmt(conio, "%s (y/n, Return cancels connection, i for more info) ",
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
prompt);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
response = parse_and_free_response(console_read_line(conio, true));
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
if (response == RESPONSE_INFO) {
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
for (SeatDialogTextItem *item = text->items,
*end = item+text->nitems; item < end; item++) {
switch (item->type) {
case SDT_MORE_INFO_KEY:
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_dataz(conio, item->text);
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
break;
case SDT_MORE_INFO_VALUE_SHORT:
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_fmt(conio, ": %s\n", item->text);
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
break;
case SDT_MORE_INFO_VALUE_BLOB:
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_fmt(conio, ":\n%s\n", item->text);
Centralise most details of host-key prompting. The text of the host key warnings was replicated in three places: the Windows rc file, the GTK dialog setup function, and the console.c shared between both platforms' CLI tools. Now it lives in just one place, namely ssh/common.c where the rest of the centralised host-key checking is done, so it'll be easier to adjust the wording in future. This comes with some extra automation. Paragraph wrapping is no longer done by hand in any version of these prompts. (Previously we let GTK do the wrapping on GTK, but on Windows the resource file contained a bunch of pre-wrapped LTEXT lines, and console.c had pre-wrapped terminal messages.) And the dialog heights in Windows are determined automatically based on the amount of stuff in the window. The main idea of all this is that it'll be easier to set up more elaborate kinds of host key prompt that deal with certificates (if, e.g., a server sends us a certified host key which we don't trust the CA for). But there are side benefits of this refactoring too: each tool now reliably inserts its own appname in the prompts, and also, on Windows the entire prompt text is copy-pastable. Details of implementation: there's a new type SeatDialogText which holds a set of (type, string) pairs describing the contents of a prompt. Type codes distinguish ordinary text paragraphs, paragraphs to be displayed prominently (like key fingerprints), the extra-bold scary title at the top of the 'host key changed' version of the dialog, and the various information that lives in the subsidiary 'more info' box. ssh/common.c constructs this, and passes it to the Seat to present the actual prompt. In order to deal with the different UI for answering the prompt, I've added an extra Seat method 'prompt_descriptions' which returns some snippets of text to interpolate into the messages. ssh/common.c calls that while it's still constructing the text, and incorporates the resulting snippets into the SeatDialogText. For the moment, this refactoring only affects the host key prompts. The warnings about outmoded crypto are still done the old-fashioned way; they probably ought to be similarly refactored to use this new SeatDialogText system, but it's not immediately critical for the purpose I have right now.
2022-07-07 16:25:15 +00:00
break;
default:
break;
}
}
} else {
break;
}
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
if (response == RESPONSE_YES || response == RESPONSE_NO) {
if (response == RESPONSE_YES)
store_host_key(seat, host, port, keytype, keystr);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
result = SPR_OK;
} else {
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_dataz(conio, console_abandoned_msg);
result = SPR_USER_ABORT;
}
out:
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
conio_free(conio);
return result;
}
Richer data type for interactive prompt results. All the seat functions that request an interactive prompt of some kind to the user - both the main seat_get_userpass_input and the various confirmation dialogs for things like host keys - were using a simple int return value, with the general semantics of 0 = "fail", 1 = "proceed" (and in the case of seat_get_userpass_input, answers to the prompts were provided), and -1 = "request in progress, wait for a callback". In this commit I change all those functions' return types to a new struct called SeatPromptResult, whose primary field is an enum replacing those simple integer values. The main purpose is that the enum has not three but _four_ values: the "fail" result has been split into 'user abort' and 'software abort'. The distinction is that a user abort occurs as a result of an interactive UI action, such as the user clicking 'cancel' in a dialog box or hitting ^D or ^C at a terminal password prompt - and therefore, there's no need to display an error message telling the user that the interactive operation has failed, because the user already knows, because they _did_ it. 'Software abort' is from any other cause, where PuTTY is the first to know there was a problem, and has to tell the user. We already had this 'user abort' vs 'software abort' distinction in other parts of the code - the SSH backend has separate termination functions which protocol layers can call. But we assumed that any failure from an interactive prompt request fell into the 'user abort' category, which is not true. A couple of examples: if you configure a host key fingerprint in your saved session via the SSH > Host keys pane, and the server presents a host key that doesn't match it, then verify_ssh_host_key would report that the user had aborted the connection, and feel no need to tell the user what had gone wrong! Similarly, if a password provided on the command line was not accepted, then (after I fixed the semantics of that in the previous commit) the same wrong handling would occur. So now, those Seat prompt functions too can communicate whether the user or the software originated a connection abort. And in the latter case, we also provide an error message to present to the user. Result: in those two example cases (and others), error messages should no longer go missing. Implementation note: to avoid the hassle of having the error message in a SeatPromptResult being a dynamically allocated string (and hence, every recipient of one must always check whether it's non-NULL and free it on every exit path, plus being careful about copying the struct around), I've instead arranged that the structure contains a function pointer and a couple of parameters, so that the string form of the message can be constructed on demand. That way, the only users who need to free it are the ones who actually _asked_ for it in the first place, which is a much smaller set. (This is one of the rare occasions that I regret not having C++'s extra features available in this code base - a unique_ptr or shared_ptr to a string would have been just the thing here, and the compiler would have done all the hard work for me of remembering where to insert the frees!)
2021-12-28 17:52:00 +00:00
SeatPromptResult console_confirm_weak_crypto_primitive(
Seat *seat, SeatDialogText *text,
Richer data type for interactive prompt results. All the seat functions that request an interactive prompt of some kind to the user - both the main seat_get_userpass_input and the various confirmation dialogs for things like host keys - were using a simple int return value, with the general semantics of 0 = "fail", 1 = "proceed" (and in the case of seat_get_userpass_input, answers to the prompts were provided), and -1 = "request in progress, wait for a callback". In this commit I change all those functions' return types to a new struct called SeatPromptResult, whose primary field is an enum replacing those simple integer values. The main purpose is that the enum has not three but _four_ values: the "fail" result has been split into 'user abort' and 'software abort'. The distinction is that a user abort occurs as a result of an interactive UI action, such as the user clicking 'cancel' in a dialog box or hitting ^D or ^C at a terminal password prompt - and therefore, there's no need to display an error message telling the user that the interactive operation has failed, because the user already knows, because they _did_ it. 'Software abort' is from any other cause, where PuTTY is the first to know there was a problem, and has to tell the user. We already had this 'user abort' vs 'software abort' distinction in other parts of the code - the SSH backend has separate termination functions which protocol layers can call. But we assumed that any failure from an interactive prompt request fell into the 'user abort' category, which is not true. A couple of examples: if you configure a host key fingerprint in your saved session via the SSH > Host keys pane, and the server presents a host key that doesn't match it, then verify_ssh_host_key would report that the user had aborted the connection, and feel no need to tell the user what had gone wrong! Similarly, if a password provided on the command line was not accepted, then (after I fixed the semantics of that in the previous commit) the same wrong handling would occur. So now, those Seat prompt functions too can communicate whether the user or the software originated a connection abort. And in the latter case, we also provide an error message to present to the user. Result: in those two example cases (and others), error messages should no longer go missing. Implementation note: to avoid the hassle of having the error message in a SeatPromptResult being a dynamically allocated string (and hence, every recipient of one must always check whether it's non-NULL and free it on every exit path, plus being careful about copying the struct around), I've instead arranged that the structure contains a function pointer and a couple of parameters, so that the string form of the message can be constructed on demand. That way, the only users who need to free it are the ones who actually _asked_ for it in the first place, which is a much smaller set. (This is one of the rare occasions that I regret not having C++'s extra features available in this code base - a unique_ptr or shared_ptr to a string would have been just the thing here, and the compiler would have done all the hard work for me of remembering where to insert the frees!)
2021-12-28 17:52:00 +00:00
void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
{
ConsoleIO *conio = conio_setup(false, STD_ERROR_HANDLE);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
SeatPromptResult result;
const char *prompt = console_print_seatdialogtext(conio, text);
if (!prompt) {
result = SPR_SW_ABORT("Cannot confirm a weak crypto primitive "
"in batch mode");
goto out;
}
put_fmt(conio, "%s (y/n) ", prompt);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
ResponseType response = parse_and_free_response(
console_read_line(conio, true));
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
if (response == RESPONSE_YES) {
result = SPR_OK;
} else {
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_dataz(conio, console_abandoned_msg);
result = SPR_USER_ABORT;
}
out:
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
conio_free(conio);
return result;
}
Richer data type for interactive prompt results. All the seat functions that request an interactive prompt of some kind to the user - both the main seat_get_userpass_input and the various confirmation dialogs for things like host keys - were using a simple int return value, with the general semantics of 0 = "fail", 1 = "proceed" (and in the case of seat_get_userpass_input, answers to the prompts were provided), and -1 = "request in progress, wait for a callback". In this commit I change all those functions' return types to a new struct called SeatPromptResult, whose primary field is an enum replacing those simple integer values. The main purpose is that the enum has not three but _four_ values: the "fail" result has been split into 'user abort' and 'software abort'. The distinction is that a user abort occurs as a result of an interactive UI action, such as the user clicking 'cancel' in a dialog box or hitting ^D or ^C at a terminal password prompt - and therefore, there's no need to display an error message telling the user that the interactive operation has failed, because the user already knows, because they _did_ it. 'Software abort' is from any other cause, where PuTTY is the first to know there was a problem, and has to tell the user. We already had this 'user abort' vs 'software abort' distinction in other parts of the code - the SSH backend has separate termination functions which protocol layers can call. But we assumed that any failure from an interactive prompt request fell into the 'user abort' category, which is not true. A couple of examples: if you configure a host key fingerprint in your saved session via the SSH > Host keys pane, and the server presents a host key that doesn't match it, then verify_ssh_host_key would report that the user had aborted the connection, and feel no need to tell the user what had gone wrong! Similarly, if a password provided on the command line was not accepted, then (after I fixed the semantics of that in the previous commit) the same wrong handling would occur. So now, those Seat prompt functions too can communicate whether the user or the software originated a connection abort. And in the latter case, we also provide an error message to present to the user. Result: in those two example cases (and others), error messages should no longer go missing. Implementation note: to avoid the hassle of having the error message in a SeatPromptResult being a dynamically allocated string (and hence, every recipient of one must always check whether it's non-NULL and free it on every exit path, plus being careful about copying the struct around), I've instead arranged that the structure contains a function pointer and a couple of parameters, so that the string form of the message can be constructed on demand. That way, the only users who need to free it are the ones who actually _asked_ for it in the first place, which is a much smaller set. (This is one of the rare occasions that I regret not having C++'s extra features available in this code base - a unique_ptr or shared_ptr to a string would have been just the thing here, and the compiler would have done all the hard work for me of remembering where to insert the frees!)
2021-12-28 17:52:00 +00:00
SeatPromptResult console_confirm_weak_cached_hostkey(
Seat *seat, SeatDialogText *text,
Richer data type for interactive prompt results. All the seat functions that request an interactive prompt of some kind to the user - both the main seat_get_userpass_input and the various confirmation dialogs for things like host keys - were using a simple int return value, with the general semantics of 0 = "fail", 1 = "proceed" (and in the case of seat_get_userpass_input, answers to the prompts were provided), and -1 = "request in progress, wait for a callback". In this commit I change all those functions' return types to a new struct called SeatPromptResult, whose primary field is an enum replacing those simple integer values. The main purpose is that the enum has not three but _four_ values: the "fail" result has been split into 'user abort' and 'software abort'. The distinction is that a user abort occurs as a result of an interactive UI action, such as the user clicking 'cancel' in a dialog box or hitting ^D or ^C at a terminal password prompt - and therefore, there's no need to display an error message telling the user that the interactive operation has failed, because the user already knows, because they _did_ it. 'Software abort' is from any other cause, where PuTTY is the first to know there was a problem, and has to tell the user. We already had this 'user abort' vs 'software abort' distinction in other parts of the code - the SSH backend has separate termination functions which protocol layers can call. But we assumed that any failure from an interactive prompt request fell into the 'user abort' category, which is not true. A couple of examples: if you configure a host key fingerprint in your saved session via the SSH > Host keys pane, and the server presents a host key that doesn't match it, then verify_ssh_host_key would report that the user had aborted the connection, and feel no need to tell the user what had gone wrong! Similarly, if a password provided on the command line was not accepted, then (after I fixed the semantics of that in the previous commit) the same wrong handling would occur. So now, those Seat prompt functions too can communicate whether the user or the software originated a connection abort. And in the latter case, we also provide an error message to present to the user. Result: in those two example cases (and others), error messages should no longer go missing. Implementation note: to avoid the hassle of having the error message in a SeatPromptResult being a dynamically allocated string (and hence, every recipient of one must always check whether it's non-NULL and free it on every exit path, plus being careful about copying the struct around), I've instead arranged that the structure contains a function pointer and a couple of parameters, so that the string form of the message can be constructed on demand. That way, the only users who need to free it are the ones who actually _asked_ for it in the first place, which is a much smaller set. (This is one of the rare occasions that I regret not having C++'s extra features available in this code base - a unique_ptr or shared_ptr to a string would have been just the thing here, and the compiler would have done all the hard work for me of remembering where to insert the frees!)
2021-12-28 17:52:00 +00:00
void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
{
ConsoleIO *conio = conio_setup(false, STD_ERROR_HANDLE);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
SeatPromptResult result;
const char *prompt = console_print_seatdialogtext(conio, text);
if (!prompt)
Richer data type for interactive prompt results. All the seat functions that request an interactive prompt of some kind to the user - both the main seat_get_userpass_input and the various confirmation dialogs for things like host keys - were using a simple int return value, with the general semantics of 0 = "fail", 1 = "proceed" (and in the case of seat_get_userpass_input, answers to the prompts were provided), and -1 = "request in progress, wait for a callback". In this commit I change all those functions' return types to a new struct called SeatPromptResult, whose primary field is an enum replacing those simple integer values. The main purpose is that the enum has not three but _four_ values: the "fail" result has been split into 'user abort' and 'software abort'. The distinction is that a user abort occurs as a result of an interactive UI action, such as the user clicking 'cancel' in a dialog box or hitting ^D or ^C at a terminal password prompt - and therefore, there's no need to display an error message telling the user that the interactive operation has failed, because the user already knows, because they _did_ it. 'Software abort' is from any other cause, where PuTTY is the first to know there was a problem, and has to tell the user. We already had this 'user abort' vs 'software abort' distinction in other parts of the code - the SSH backend has separate termination functions which protocol layers can call. But we assumed that any failure from an interactive prompt request fell into the 'user abort' category, which is not true. A couple of examples: if you configure a host key fingerprint in your saved session via the SSH > Host keys pane, and the server presents a host key that doesn't match it, then verify_ssh_host_key would report that the user had aborted the connection, and feel no need to tell the user what had gone wrong! Similarly, if a password provided on the command line was not accepted, then (after I fixed the semantics of that in the previous commit) the same wrong handling would occur. So now, those Seat prompt functions too can communicate whether the user or the software originated a connection abort. And in the latter case, we also provide an error message to present to the user. Result: in those two example cases (and others), error messages should no longer go missing. Implementation note: to avoid the hassle of having the error message in a SeatPromptResult being a dynamically allocated string (and hence, every recipient of one must always check whether it's non-NULL and free it on every exit path, plus being careful about copying the struct around), I've instead arranged that the structure contains a function pointer and a couple of parameters, so that the string form of the message can be constructed on demand. That way, the only users who need to free it are the ones who actually _asked_ for it in the first place, which is a much smaller set. (This is one of the rare occasions that I regret not having C++'s extra features available in this code base - a unique_ptr or shared_ptr to a string would have been just the thing here, and the compiler would have done all the hard work for me of remembering where to insert the frees!)
2021-12-28 17:52:00 +00:00
return SPR_SW_ABORT("Cannot confirm a weak cached host key "
"in batch mode");
put_fmt(conio, "%s (y/n) ", prompt);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
ResponseType response = parse_and_free_response(
console_read_line(conio, true));
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
if (response == RESPONSE_YES) {
result = SPR_OK;
} else {
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_dataz(conio, console_abandoned_msg);
result = SPR_USER_ABORT;
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
conio_free(conio);
return result;
}
bool is_interactive(void)
{
return is_console_handle(GetStdHandle(STD_INPUT_HANDLE));
}
bool console_antispoof_prompt = true;
void console_set_trust_status(Seat *seat, bool trusted)
{
/* Do nothing in response to a change of trust status, because
* there's nothing we can do in a console environment. However,
* the query function below will make a fiddly decision about
* whether to tell the backend to enable fallback handling. */
}
bool console_can_set_trust_status(Seat *seat)
{
New Seat query, has_mixed_input_stream(). (TL;DR: to suppress redundant 'Press Return to begin session' prompts in between hops of a jump-host configuration, in Plink.) This new query method directly asks the Seat the question: is the same stream of input used to provide responses to interactive login prompts, and the session input provided after login concludes? It's used to suppress the last-ditch anti-spoofing defence in Plink of interactively asking 'Access granted. Press Return to begin session', on the basis that any such spoofing attack works by confusing the user about what's a legit login prompt before the session begins and what's sent by the server after the main session begins - so if those two things take input from different places, the user can't be confused. This doesn't change the existing behaviour of Plink, which was already suppressing the antispoof prompt in cases where its standard input was redirected from something other than a terminal. But previously it was doing it within the can_set_trust_status() seat query, and I've now moved it out into a separate query function. The reason why these need to be separate is for SshProxy, which needs to give an unusual combination of answers when run inside Plink. For can_set_trust_status(), it needs to return whatever the parent Seat returns, so that all the login prompts for a string of proxy connections in session will be antispoofed the same way. But you only want that final 'Access granted' prompt to happen _once_, after all the proxy connection setup phases are done, because up until then you're still in the safe hands of PuTTY itself presenting an unbroken sequence of legit login prompts (even if they come from a succession of different servers). Hence, SshProxy unconditionally returns 'no' to the query of whether it has a single mixed input stream, because indeed, it never does - for purposes of session input it behaves like an always-redirected Plink, no matter what kind of real Seat it ends up sending its pre-session login prompts to.
2021-11-06 14:33:03 +00:00
if (console_batch_mode) {
/*
* 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.
*/
return true;
}
return false;
}
New Seat query, has_mixed_input_stream(). (TL;DR: to suppress redundant 'Press Return to begin session' prompts in between hops of a jump-host configuration, in Plink.) This new query method directly asks the Seat the question: is the same stream of input used to provide responses to interactive login prompts, and the session input provided after login concludes? It's used to suppress the last-ditch anti-spoofing defence in Plink of interactively asking 'Access granted. Press Return to begin session', on the basis that any such spoofing attack works by confusing the user about what's a legit login prompt before the session begins and what's sent by the server after the main session begins - so if those two things take input from different places, the user can't be confused. This doesn't change the existing behaviour of Plink, which was already suppressing the antispoof prompt in cases where its standard input was redirected from something other than a terminal. But previously it was doing it within the can_set_trust_status() seat query, and I've now moved it out into a separate query function. The reason why these need to be separate is for SshProxy, which needs to give an unusual combination of answers when run inside Plink. For can_set_trust_status(), it needs to return whatever the parent Seat returns, so that all the login prompts for a string of proxy connections in session will be antispoofed the same way. But you only want that final 'Access granted' prompt to happen _once_, after all the proxy connection setup phases are done, because up until then you're still in the safe hands of PuTTY itself presenting an unbroken sequence of legit login prompts (even if they come from a succession of different servers). Hence, SshProxy unconditionally returns 'no' to the query of whether it has a single mixed input stream, because indeed, it never does - for purposes of session input it behaves like an always-redirected Plink, no matter what kind of real Seat it ends up sending its pre-session login prompts to.
2021-11-06 14:33:03 +00:00
bool console_has_mixed_input_stream(Seat *seat)
{
if (!is_interactive() || !console_antispoof_prompt) {
/*
* If standard input isn't connected to a terminal, then even
* if the server did send a spoof authentication prompt, the
* user couldn't respond to it via the terminal anyway.
*
* We also pretend this is true if the user has purposely
* disabled the antispoof prompt.
*/
return false;
}
return true;
}
/*
* Ask whether to wipe a session log file before writing to it.
* Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
*/
int console_askappend(LogPolicy *lp, Filename *filename,
void (*callback)(void *ctx, int result), void *ctx)
{
static const char msgtemplate[] =
"The session log file \"%.*s\" already exists.\n"
"You can overwrite it with a new session log,\n"
"append your session log to the end of it,\n"
"or disable session logging for this session.\n"
"Enter \"y\" to wipe the file, \"n\" to append to it,\n"
"or just press Return to disable logging.\n"
"Wipe the log file? (y/n, Return cancels logging) ";
static const char msgtemplate_batch[] =
"The session log file \"%.*s\" already exists.\n"
"Logging will not be enabled.\n";
ConsoleIO *conio = conio_setup(true, STD_ERROR_HANDLE);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
int result;
if (console_batch_mode) {
Some support for wide-character filenames in Windows. The Windows version of the Filename structure now contains three versions of the pathname, in UTF-16, UTF-8 and the system code page. Callers can use whichever is most convenient. All uses of filenames for actually opening files now use the UTF-16 version, which means they can tolerate 'exotic' filenames, by which I mean those including Unicode characters outside the host system's CP_ACP default code page. Other uses of Filename structures inside the 'windows' subdirectory do something appropriate, e.g. when printing a filename inside a message box or a console message, we use the UTF-8 version of the filename with the UTF-8 version of the appropriate API. There are three remaining pieces to full Unicode filename support: One is that the cross-platform code has many calls to filename_to_str(), embodying the assumption that a file name can be reliably converted into the unspecified current character set; those will all need changing in some way. Another is that write_setting_filename(), in windows/storage.c, still saves filenames to the Registry as an ordinary REG_SZ in the system code page. So even if an exotic filename were stored in a Conf, that Conf couldn't round-trip via the Registry and back without corrupting that filename by coercing it back to a string that fits in CP_ACP and therefore doesn't represent the same file. This can't be fixed without a compatibility break in the storage format, and I don't want to make a minimal change in that area: if we're going to break compatibility, then we should break it good and hard (the Nanny Ogg principle), and devise a completely fresh storage representation that fixes as many other legacy problems as possible at the same time. So that's my plan, not yet started. The final point, much more obviously, is that we're still short of methods to _construct_ any Filename structures using a Unicode input string! It should now work to enter one in the GUI configurer (either by manual text input or via the file selector), but it won't round-trip through a save and load (as discussed above), and there's still no way to specify one on the command line (the groundwork is laid by commit 10e1ac7752de928 but not yet linked up). But this is a start.
2023-05-28 10:30:59 +00:00
put_fmt(conio, msgtemplate_batch, FILENAME_MAX, filename->utf8path);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
result = 0;
goto out;
}
Some support for wide-character filenames in Windows. The Windows version of the Filename structure now contains three versions of the pathname, in UTF-16, UTF-8 and the system code page. Callers can use whichever is most convenient. All uses of filenames for actually opening files now use the UTF-16 version, which means they can tolerate 'exotic' filenames, by which I mean those including Unicode characters outside the host system's CP_ACP default code page. Other uses of Filename structures inside the 'windows' subdirectory do something appropriate, e.g. when printing a filename inside a message box or a console message, we use the UTF-8 version of the filename with the UTF-8 version of the appropriate API. There are three remaining pieces to full Unicode filename support: One is that the cross-platform code has many calls to filename_to_str(), embodying the assumption that a file name can be reliably converted into the unspecified current character set; those will all need changing in some way. Another is that write_setting_filename(), in windows/storage.c, still saves filenames to the Registry as an ordinary REG_SZ in the system code page. So even if an exotic filename were stored in a Conf, that Conf couldn't round-trip via the Registry and back without corrupting that filename by coercing it back to a string that fits in CP_ACP and therefore doesn't represent the same file. This can't be fixed without a compatibility break in the storage format, and I don't want to make a minimal change in that area: if we're going to break compatibility, then we should break it good and hard (the Nanny Ogg principle), and devise a completely fresh storage representation that fixes as many other legacy problems as possible at the same time. So that's my plan, not yet started. The final point, much more obviously, is that we're still short of methods to _construct_ any Filename structures using a Unicode input string! It should now work to enter one in the GUI configurer (either by manual text input or via the file selector), but it won't round-trip through a save and load (as discussed above), and there's still no way to specify one on the command line (the groundwork is laid by commit 10e1ac7752de928 but not yet linked up). But this is a start.
2023-05-28 10:30:59 +00:00
put_fmt(conio, msgtemplate, FILENAME_MAX, filename->utf8path);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
ResponseType response = parse_and_free_response(
console_read_line(conio, true));
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
if (response == RESPONSE_YES)
result = 2;
else if (response == RESPONSE_NO)
result = 1;
else
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
result = 0;
out:
conio_free(conio);
return result;
}
/*
* Warn about the obsolescent key file format.
*
* Uniquely among these functions, this one does _not_ expect a
* frontend handle. This means that if PuTTY is ported to a
* platform which requires frontend handles, this function will be
* an anomaly. Fortunately, the problem it addresses will not have
* been present on that platform, so it can plausibly be
* implemented as an empty function.
*/
void old_keyfile_warning(void)
{
static const char message[] =
"You are loading an SSH-2 private key which has an\n"
"old version of the file format. This means your key\n"
"file is not fully tamperproof. Future versions of\n"
"PuTTY may stop supporting this private key format,\n"
"so we recommend you convert your key to the new\n"
"format.\n"
"\n"
"Once the key is loaded into PuTTYgen, you can perform\n"
"this conversion simply by saving it again.\n";
fputs(message, stderr);
}
/*
* Display the fingerprints of the PGP Master Keys to the user.
*/
void pgp_fingerprints(void)
{
fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n"
"be used to establish a trust path from this executable to another\n"
"one. See the manual for more information.\n"
"(Note: these fingerprints have nothing to do with SSH!)\n"
"\n"
"PuTTY Master Key as of " PGP_MASTER_KEY_YEAR
" (" PGP_MASTER_KEY_DETAILS "):\n"
" " PGP_MASTER_KEY_FP "\n\n"
"Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR
", " PGP_PREV_MASTER_KEY_DETAILS "):\n"
" " PGP_PREV_MASTER_KEY_FP "\n", stdout);
}
void console_logging_error(LogPolicy *lp, const char *string)
{
Refactor the LogContext type. LogContext is now the owner of the logevent() function that back ends and so forth are constantly calling. Previously, logevent was owned by the Frontend, which would store the message into its list for the GUI Event Log dialog (or print it to standard error, or whatever) and then pass it _back_ to LogContext to write to the currently open log file. Now it's the other way round: LogContext gets the message from the back end first, writes it to its log file if it feels so inclined, and communicates it back to the front end. This means that lots of parts of the back end system no longer need to have a pointer to a full-on Frontend; the only thing they needed it for was logging, so now they just have a LogContext (which many of them had to have anyway, e.g. for logging SSH packets or session traffic). LogContext itself also doesn't get a full Frontend pointer any more: it now talks back to the front end via a little vtable of its own called LogPolicy, which contains the method that passes Event Log entries through, the old askappend() function that decides whether to truncate a pre-existing log file, and an emergency function for printing an especially prominent message if the log file can't be created. One minor nice effect of this is that console and GUI apps can implement that last function subtly differently, so that Unix console apps can write it with a plain \n instead of the \r\n (harmless but inelegant) that the old centralised implementation generated. One other consequence of this is that the LogContext has to be provided to backend_init() so that it's available to backends from the instant of creation, rather than being provided via a separate API call a couple of function calls later, because backends have typically started doing things that need logging (like making network connections) before the call to backend_provide_logctx. Fortunately, there's no case in the whole code base where we don't already have logctx by the time we make a backend (so I don't actually remember why I ever delayed providing one). So that shortens the backend API by one function, which is always nice. While I'm tidying up, I've also moved the printf-style logeventf() and the handy logevent_and_free() into logging.c, instead of having copies of them scattered around other places. This has also let me remove some stub functions from a couple of outlying applications like Pageant. Finally, I've removed the pointless "_tag" at the end of LogContext's official struct name.
2018-10-10 18:26:18 +00:00
/* Ordinary Event Log entries are displayed in the same way as
* logging errors, but only in verbose mode */
fprintf(stderr, "%s\n", string);
fflush(stderr);
}
void console_eventlog(LogPolicy *lp, const char *string)
{
Refactor the LogContext type. LogContext is now the owner of the logevent() function that back ends and so forth are constantly calling. Previously, logevent was owned by the Frontend, which would store the message into its list for the GUI Event Log dialog (or print it to standard error, or whatever) and then pass it _back_ to LogContext to write to the currently open log file. Now it's the other way round: LogContext gets the message from the back end first, writes it to its log file if it feels so inclined, and communicates it back to the front end. This means that lots of parts of the back end system no longer need to have a pointer to a full-on Frontend; the only thing they needed it for was logging, so now they just have a LogContext (which many of them had to have anyway, e.g. for logging SSH packets or session traffic). LogContext itself also doesn't get a full Frontend pointer any more: it now talks back to the front end via a little vtable of its own called LogPolicy, which contains the method that passes Event Log entries through, the old askappend() function that decides whether to truncate a pre-existing log file, and an emergency function for printing an especially prominent message if the log file can't be created. One minor nice effect of this is that console and GUI apps can implement that last function subtly differently, so that Unix console apps can write it with a plain \n instead of the \r\n (harmless but inelegant) that the old centralised implementation generated. One other consequence of this is that the LogContext has to be provided to backend_init() so that it's available to backends from the instant of creation, rather than being provided via a separate API call a couple of function calls later, because backends have typically started doing things that need logging (like making network connections) before the call to backend_provide_logctx. Fortunately, there's no case in the whole code base where we don't already have logctx by the time we make a backend (so I don't actually remember why I ever delayed providing one). So that shortens the backend API by one function, which is always nice. While I'm tidying up, I've also moved the printf-style logeventf() and the handy logevent_and_free() into logging.c, instead of having copies of them scattered around other places. This has also let me remove some stub functions from a couple of outlying applications like Pageant. Finally, I've removed the pointless "_tag" at the end of LogContext's official struct name.
2018-10-10 18:26:18 +00:00
/* Ordinary Event Log entries are displayed in the same way as
* logging errors, but only in verbose mode */
Remove FLAG_VERBOSE. The global 'int flags' has always been an ugly feature of this code base, and I suddenly thought that perhaps it's time to start throwing it out, one flag at a time, until it's totally unused. My first target is FLAG_VERBOSE. This was usually set by cmdline.c when it saw a -v option on the program's command line, except that GUI PuTTY itself sets it unconditionally on startup. And then various bits of the code would check it in order to decide whether to print a given message. In the current system of front-end abstraction traits, there's no _one_ place that I can move it to. But there are two: every place that checked FLAG_VERBOSE has access to either a Seat or a LogPolicy. So now each of those traits has a query method for 'do I want verbose messages?'. A good effect of this is that subsidiary Seats, like the ones used in Uppity for the main SSH server module itself and the server end of shell channels, now get to have their own verbosity setting instead of inheriting the one global one. In fact I don't expect any code using those Seats to be generating any messages at all, but if that changes later, we'll have a way to control it. (Who knows, perhaps logging in Uppity might become a thing.) As part of this cleanup, I've added a new flag to cmdline_tooltype, called TOOLTYPE_NO_VERBOSE_OPTION. The unconditionally-verbose tools now set that, and it has the effect of making cmdline.c disallow -v completely. So where 'putty -v' would previously have been silently ignored ("I was already verbose"), it's now an error, reminding you that that option doesn't actually do anything. Finally, the 'default_logpolicy' provided by uxcons.c and wincons.c (with identical definitions) has had to move into a new file of its own, because now it has to ask cmdline.c for the verbosity setting as well as asking console.c for the rest of its methods. So there's a new file clicons.c which can only be included by programs that link against both cmdline.c _and_ one of the *cons.c, and I've renamed the logpolicy to reflect that.
2020-01-30 06:40:21 +00:00
if (lp_verbose(lp))
Refactor the LogContext type. LogContext is now the owner of the logevent() function that back ends and so forth are constantly calling. Previously, logevent was owned by the Frontend, which would store the message into its list for the GUI Event Log dialog (or print it to standard error, or whatever) and then pass it _back_ to LogContext to write to the currently open log file. Now it's the other way round: LogContext gets the message from the back end first, writes it to its log file if it feels so inclined, and communicates it back to the front end. This means that lots of parts of the back end system no longer need to have a pointer to a full-on Frontend; the only thing they needed it for was logging, so now they just have a LogContext (which many of them had to have anyway, e.g. for logging SSH packets or session traffic). LogContext itself also doesn't get a full Frontend pointer any more: it now talks back to the front end via a little vtable of its own called LogPolicy, which contains the method that passes Event Log entries through, the old askappend() function that decides whether to truncate a pre-existing log file, and an emergency function for printing an especially prominent message if the log file can't be created. One minor nice effect of this is that console and GUI apps can implement that last function subtly differently, so that Unix console apps can write it with a plain \n instead of the \r\n (harmless but inelegant) that the old centralised implementation generated. One other consequence of this is that the LogContext has to be provided to backend_init() so that it's available to backends from the instant of creation, rather than being provided via a separate API call a couple of function calls later, because backends have typically started doing things that need logging (like making network connections) before the call to backend_provide_logctx. Fortunately, there's no case in the whole code base where we don't already have logctx by the time we make a backend (so I don't actually remember why I ever delayed providing one). So that shortens the backend API by one function, which is always nice. While I'm tidying up, I've also moved the printf-style logeventf() and the handy logevent_and_free() into logging.c, instead of having copies of them scattered around other places. This has also let me remove some stub functions from a couple of outlying applications like Pageant. Finally, I've removed the pointless "_tag" at the end of LogContext's official struct name.
2018-10-10 18:26:18 +00:00
console_logging_error(lp, string);
}
StripCtrlChars *console_stripctrl_new(
Seat *seat, BinarySink *bs_out, SeatInteractionContext sic)
{
return stripctrl_new(bs_out, false, 0);
}
Richer data type for interactive prompt results. All the seat functions that request an interactive prompt of some kind to the user - both the main seat_get_userpass_input and the various confirmation dialogs for things like host keys - were using a simple int return value, with the general semantics of 0 = "fail", 1 = "proceed" (and in the case of seat_get_userpass_input, answers to the prompts were provided), and -1 = "request in progress, wait for a callback". In this commit I change all those functions' return types to a new struct called SeatPromptResult, whose primary field is an enum replacing those simple integer values. The main purpose is that the enum has not three but _four_ values: the "fail" result has been split into 'user abort' and 'software abort'. The distinction is that a user abort occurs as a result of an interactive UI action, such as the user clicking 'cancel' in a dialog box or hitting ^D or ^C at a terminal password prompt - and therefore, there's no need to display an error message telling the user that the interactive operation has failed, because the user already knows, because they _did_ it. 'Software abort' is from any other cause, where PuTTY is the first to know there was a problem, and has to tell the user. We already had this 'user abort' vs 'software abort' distinction in other parts of the code - the SSH backend has separate termination functions which protocol layers can call. But we assumed that any failure from an interactive prompt request fell into the 'user abort' category, which is not true. A couple of examples: if you configure a host key fingerprint in your saved session via the SSH > Host keys pane, and the server presents a host key that doesn't match it, then verify_ssh_host_key would report that the user had aborted the connection, and feel no need to tell the user what had gone wrong! Similarly, if a password provided on the command line was not accepted, then (after I fixed the semantics of that in the previous commit) the same wrong handling would occur. So now, those Seat prompt functions too can communicate whether the user or the software originated a connection abort. And in the latter case, we also provide an error message to present to the user. Result: in those two example cases (and others), error messages should no longer go missing. Implementation note: to avoid the hassle of having the error message in a SeatPromptResult being a dynamically allocated string (and hence, every recipient of one must always check whether it's non-NULL and free it on every exit path, plus being careful about copying the struct around), I've instead arranged that the structure contains a function pointer and a couple of parameters, so that the string form of the message can be constructed on demand. That way, the only users who need to free it are the ones who actually _asked_ for it in the first place, which is a much smaller set. (This is one of the rare occasions that I regret not having C++'s extra features available in this code base - a unique_ptr or shared_ptr to a string would have been just the thing here, and the compiler would have done all the hard work for me of remembering where to insert the frees!)
2021-12-28 17:52:00 +00:00
SeatPromptResult console_get_userpass_input(prompts_t *p)
{
ConsoleIO *conio = conio_setup(p->utf8, STD_OUTPUT_HANDLE);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
SeatPromptResult result;
size_t curr_prompt;
/*
* Zero all the results, in case we abort half-way through.
*/
{
int i;
for (i = 0; i < (int)p->n_prompts; i++)
prompt_set_result(p->prompts[i], "");
}
/*
* The prompts_t might contain a message to be displayed but no
* actual prompt. More usually, though, it will contain
* questions that the user needs to answer, in which case we
* need to ensure that we're able to get the answers.
*/
if (p->n_prompts) {
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
if (console_batch_mode) {
result = SPR_SW_ABORT("Cannot answer interactive prompts "
"in batch mode");
goto out;
}
}
/*
* Preamble.
*/
/* We only print the `name' caption if we have to... */
if (p->name_reqd && p->name) {
ptrlen plname = ptrlen_from_asciz(p->name);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_datapl(conio, plname);
if (!ptrlen_endswith(plname, PTRLEN_LITERAL("\n"), NULL))
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_datalit(conio, "\n");
}
/* ...but we always print any `instruction'. */
if (p->instruction) {
ptrlen plinst = ptrlen_from_asciz(p->instruction);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_datapl(conio, plinst);
if (!ptrlen_endswith(plinst, PTRLEN_LITERAL("\n"), NULL))
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_datalit(conio, "\n");
}
for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
prompt_t *pr = p->prompts[curr_prompt];
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
put_dataz(conio, pr->prompt);
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
if (!console_read_line_to_strbuf(conio, pr->echo, pr->result)) {
result = make_spr_sw_abort_winerror(
"Error reading from console", GetLastError());
goto out;
} else if (!pr->result->len) {
/* Regard EOF on the terminal as a deliberate user-abort */
result = SPR_USER_ABORT;
goto out;
} else {
if (strbuf_chomp(pr->result, '\n')) {
strbuf_chomp(pr->result, '\r');
}
}
}
New system for reading prompts from the console. Until now, the command-line PuTTY tools (PSCP, PSFTP and Plink) have presented all the kinds of interactive prompt (password/passphrase, host key, the assorted weak-crypto warnings, 'append to log file?') on standard error, and read the responses from standard input. This is unfortunate because if you're redirecting their standard input (especially likely with Plink) then the prompt responses will consume some of the intended session data. It would be better to present the prompts _on the console_, even if that's not where stdin or stderr point. On Unix, we've been doing this for ages, by opening /dev/tty directly. On Windows, we didn't, because I didn't know how. But I've recently found out: you can open the magic file names CONIN$ and CONOUT$, which will point at your actual console, if one is available. So now, if it's possible, the command-line tools will do that. But if the attempt to open CONIN$ and CONOUT$ fails, they'll fall back to the old behaviour (in particular, if no console is available at all). In order to make this happen consistently across all the prompt types, I've introduced a new object called ConsoleIO, which holds whatever file handles are necessary, knows whether to close them afterwards (yes if they were obtained by opening CONFOO$, no if they're the standard I/O handles), and presents a BinarySink API to write to them and a custom API to read a line of text. This seems likely to break _someone's_ workflow. So I've added an option '-legacy-stdio-prompts' to restore the old behaviour.
2022-11-24 12:46:25 +00:00
result = SPR_OK;
out:
conio_free(conio);
return result;
}