Unbuffer stdin before a client password prompt

The password prompt reads stdin with fgets(), and a buffered stdin
keeps its own copy of the line in libc for the life of the process.
The clients make stdin unbuffered before any stdin I/O, so the typed
password lands only in the buffer ClientUserAuth() wipes.

- the wolfssh app, and the example client, sftp client and scp client
  whether or not -P was given, as keyboard-interactive reads stdin too
- ClientUnbufferStdin() acts once per process and reports a failure
- scripts/sshclient.test sends the session data right after the
  password, with no sleep

Issue: F-15669
pull/1360/merge
John Safranek 2026-10-21 09:17:59 -07:00 committed by philljj
parent 9e206dd2f1
commit b05fe9f4c9
7 changed files with 34 additions and 9 deletions

View File

@ -1270,6 +1270,9 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
#endif
MODES_STORE();
/* No stdio buffer keeps a copy of a typed password. */
if (setvbuf(stdin, NULL, _IONBF, 0) != 0)
fprintf(stderr, "Couldn't unbuffer stdin.\n");
((func_args*)args)->return_code = 0;

View File

@ -1006,6 +1006,10 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
}
myoptind = 0; /* reset for test cases */
#ifdef WOLFSSH_TERM
ClientUnbufferStdin();
#endif
if (username == NULL)
err_sys("client requires a username parameter.");

View File

@ -675,6 +675,18 @@ void ClientClearPassword(void)
#ifdef WOLFSSH_TERM
void ClientUnbufferStdin(void)
{
static int done = 0;
if (!done) {
done = 1;
if (setvbuf(stdin, NULL, _IONBF, 0) != 0)
fprintf(stderr, "Couldn't unbuffer stdin.\n");
}
}
/* type = 2 : shell / execute command settings
* type = 0 : password
* type = 1 : restore default

View File

@ -32,6 +32,10 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc,
void* heap, const char* tpmKeyAuth);
int ClientUseCert(const char* certName, void* heap);
int ClientSetEcho(int type);
/* Makes stdin unbuffered, so no stdio buffer keeps a copy of a typed
* password or response. Call before any other stdin I/O; only the first
* call in a process acts. */
void ClientUnbufferStdin(void);
int ClientUserAuth(byte authType,
WS_UserAuthData* authData, void* ctx);
int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx);

View File

@ -193,6 +193,10 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args)
myoptind = 0; /* reset for test cases */
#ifdef WOLFSSH_TERM
ClientUnbufferStdin();
#endif
if (username == NULL)
err_sys("client requires a username parameter.");

View File

@ -1719,6 +1719,10 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
}
myoptind = 0; /* reset for test cases */
#ifdef WOLFSSH_TERM
ClientUnbufferStdin();
#endif
if (username == NULL)
err_sys("client requires a username parameter.");

View File

@ -136,10 +136,9 @@ wait_for_output() {
# Run the client with its stdin coming from a fifo.
#
# The client answers its prompts with stdio, which buffers everything that
# is ready to be read, so anything written along with a prompt's answer is
# swallowed with it and never reaches the session. Writing each piece only
# once the client has asked for it keeps them in separate reads. The
# The client reads its prompt answers from an unbuffered stdin, so the
# session data can follow the password at once and is left in the fifo for
# the session. Each answer is still written only once its prompt is up. The
# echoserver only ends the session when it receives a 0x03, so every
# session has to send one or both ends wait for the other forever.
#
@ -161,11 +160,6 @@ run_client() {
wait_for_output "Password:" || exit 1
printf 'upthehill\n'
# Let the client consume the password before sending the session
# data. A single read that catches both loses the data.
sleep 2
printf 'hello\003'
) > "$input_file" 2>/dev/null &
input_pid=$!