mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-08 08:58:00 +00:00
Complete rework of terminal userpass input system.
The system for handling seat_get_userpass_input has always been structured differently between GUI PuTTY and CLI tools like Plink. In the CLI tools, password input is read directly from the OS terminal/console device by console_get_userpass_input; this means that you need to ensure the same terminal input data _hasn't_ already been consumed by the main event loop and sent on to the backend. This is achieved by the backend_sendok() method, which tells the event loop when the backend has finished issuing password prompts, and hence, when it's safe to start passing standard input to backend_send(). But in the GUI tools, input generated by the terminal window has always been sent straight to backend_send(), regardless of whether backend_sendok() says it wants it. So the terminal-based implementation of username and password prompts has to work by consuming input data that had _already_ been passed to the backend - hence, any backend that needs to do that must keep its input on a bufchain, and pass that bufchain to seat_get_userpass_input. It's awkward that these two totally different systems coexist in the first place. And now that SSH proxying needs to present interactive prompts of its own, it's clear which one should win: the CLI style is the Right Thing. So this change reworks the GUI side of the mechanism to be more similar: terminal data now goes into a queue in the Ldisc, and is not sent on to the backend until the backend says it's ready for it via backend_sendok(). So terminal-based userpass prompts can now consume data directly from that queue during the connection setup stage. As a result, the 'bufchain *' parameter has vanished from all the userpass_input functions (both the official implementations of the Seat trait method, and term_get_userpass_input() to which some of those implementations delegate). The only function that actually used that bufchain, namely term_get_userpass_input(), now instead reads from the ldisc's input queue via a couple of new Ldisc functions. (Not _trivial_ functions, since input buffered by Ldisc can be a mixture of raw bytes and session specials like SS_EOL! The input queue inside Ldisc is a bufchain containing a fiddly binary encoding that can represent an arbitrary interleaving of those things.) This greatly simplifies the calls to seat_get_userpass_input in backends, which now don't have to mess about with passing their own user_input bufchain around, or toggling their want_user_input flag back and forth to request data to put on to that bufchain. But the flip side is that now there has to be some _other_ method for notifying the terminal when there's more input to be consumed during an interactive prompt, and for notifying the backend when prompt input has finished so that it can proceed to the next stage of the protocol. This is done by a pair of extra callbacks: when more data is put on to Ldisc's input queue, it triggers a call to term_get_userpass_input, and when term_get_userpass_input finishes, it calls a callback function provided in the prompts_t. Therefore, any use of a prompts_t which *might* be asynchronous must fill in the latter callback when setting up the prompts_t. In SSH, the callback is centralised into a common PPL helper function, which reinvokes the same PPL's process_queue coroutine; in rlogin we have to set it up ourselves. I'm sorry for this large and sprawling patch: I tried fairly hard to break it up into individually comprehensible sub-patches, but I just couldn't tease out any part of it that would stand sensibly alone.
This commit is contained in:
parent
9f0e7d2915
commit
cd8a7181fd
@ -119,6 +119,11 @@ static const TermWinVtable fuzz_termwin_vt = {
|
||||
|
||||
void ldisc_send(Ldisc *ldisc, const void *buf, int len, bool interactive) {}
|
||||
void ldisc_echoedit_update(Ldisc *ldisc) {}
|
||||
bool ldisc_has_input_buffered(Ldisc *ldisc) { return false; }
|
||||
LdiscInputToken ldisc_get_input_token(Ldisc *ldisc)
|
||||
{ unreachable("This fake ldisc never has any buffered input"); }
|
||||
void ldisc_enable_prompt_callback(Ldisc *ldisc, prompts_t *p)
|
||||
{ unreachable("This fake ldisc should never be used for user/pass prompts"); }
|
||||
void modalfatalbox(const char *fmt, ...) { exit(0); }
|
||||
void nonfatal(const char *fmt, ...) { }
|
||||
|
||||
|
250
ldisc.c
250
ldisc.c
@ -17,6 +17,46 @@ struct Ldisc_tag {
|
||||
Backend *backend;
|
||||
Seat *seat;
|
||||
|
||||
/*
|
||||
* When the backend is not reporting true from sendok(), terminal
|
||||
* input that comes here is stored in this bufchain instead. When
|
||||
* the backend later decides it wants session input, we empty the
|
||||
* queue in ldisc_check_sendok_callback(), passing its contents on
|
||||
* to the backend. Before then, we also provide data from this
|
||||
* queue to term_get_userpass_input() via ldisc_get_input_token(),
|
||||
* to be interpreted as user responses to username and password
|
||||
* prompts during authentication.
|
||||
*
|
||||
* Unfortunately, the data stored in this queue is not all of the
|
||||
* same type: our output to the backend consists of both raw bytes
|
||||
* sent to backend_send(), and also session specials such as
|
||||
* SS_EOL and SS_EC. So we have to encode our queued data in a way
|
||||
* that can represent both.
|
||||
*
|
||||
* The encoding is private to this source file, so we can change
|
||||
* it if necessary and only have to worry about the encode and
|
||||
* decode functions here. Currently, it is:
|
||||
*
|
||||
* - Bytes other than 0xFF are stored literally.
|
||||
* - The byte 0xFF itself is stored as 0xFF 0xFF.
|
||||
* - A session special (code, arg) is stored as 0xFF, followed by
|
||||
* a big-endian 4-byte integer containing code, followed by
|
||||
* another big-endian 4-byte integer containing arg.
|
||||
*
|
||||
* (This representation relies on session special codes being at
|
||||
* most 0xFEFFFFFF when represented in 32 bits, so that the first
|
||||
* byte of the 'code' integer can't be confused with the 0xFF
|
||||
* followup byte indicating a literal 0xFF, But since session
|
||||
* special codes are defined by an enum counting up from zero, and
|
||||
* there are only a couple of dozen of them, that shouldn't be a
|
||||
* problem! Even so, just in case, an assertion checks that at
|
||||
* encode time.)
|
||||
*/
|
||||
bufchain input_queue;
|
||||
|
||||
IdempotentCallback input_queue_callback;
|
||||
prompts_t *prompts;
|
||||
|
||||
/*
|
||||
* Values cached out of conf.
|
||||
*/
|
||||
@ -87,6 +127,8 @@ static void bsb(Ldisc *ldisc, int n)
|
||||
c_write(ldisc, "\010 \010", 3);
|
||||
}
|
||||
|
||||
static void ldisc_input_queue_callback(void *ctx);
|
||||
|
||||
#define CTRL(x) (x^'@')
|
||||
#define KCTRL(x) ((x^'@') | 0x100)
|
||||
|
||||
@ -103,6 +145,14 @@ Ldisc *ldisc_create(Conf *conf, Terminal *term, Backend *backend, Seat *seat)
|
||||
ldisc->term = term;
|
||||
ldisc->seat = seat;
|
||||
|
||||
bufchain_init(&ldisc->input_queue);
|
||||
|
||||
ldisc->prompts = NULL;
|
||||
ldisc->input_queue_callback.fn = ldisc_input_queue_callback;
|
||||
ldisc->input_queue_callback.ctx = ldisc;
|
||||
ldisc->input_queue_callback.queued = false;
|
||||
bufchain_set_callback(&ldisc->input_queue, &ldisc->input_queue_callback);
|
||||
|
||||
ldisc_configure(ldisc, conf);
|
||||
|
||||
/* Link ourselves into the backend and the terminal */
|
||||
@ -125,12 +175,14 @@ void ldisc_configure(Ldisc *ldisc, Conf *conf)
|
||||
|
||||
void ldisc_free(Ldisc *ldisc)
|
||||
{
|
||||
bufchain_clear(&ldisc->input_queue);
|
||||
if (ldisc->term)
|
||||
ldisc->term->ldisc = NULL;
|
||||
if (ldisc->backend)
|
||||
backend_provide_ldisc(ldisc->backend, NULL);
|
||||
if (ldisc->buf)
|
||||
sfree(ldisc->buf);
|
||||
delete_callbacks_for_context(ldisc);
|
||||
sfree(ldisc);
|
||||
}
|
||||
|
||||
@ -139,8 +191,169 @@ void ldisc_echoedit_update(Ldisc *ldisc)
|
||||
seat_echoedit_update(ldisc->seat, ECHOING, EDITING);
|
||||
}
|
||||
|
||||
void ldisc_enable_prompt_callback(Ldisc *ldisc, prompts_t *prompts)
|
||||
{
|
||||
/*
|
||||
* Called by the terminal to indicate that there's a prompts_t
|
||||
* currently in flight, or to indicate that one has just finished
|
||||
* (by passing NULL). When ldisc->prompts is not null, we notify
|
||||
* the terminal whenever new data arrives in our input queue, so
|
||||
* that it can continue the interactive prompting process.
|
||||
*/
|
||||
ldisc->prompts = prompts;
|
||||
}
|
||||
|
||||
static void ldisc_input_queue_callback(void *ctx)
|
||||
{
|
||||
/*
|
||||
* Toplevel callback that is triggered whenever the input queue
|
||||
* lengthens. If we're currently processing an interactive prompt,
|
||||
* we call back the Terminal to tell it to do some more stuff with
|
||||
* that prompt based on the new input.
|
||||
*/
|
||||
Ldisc *ldisc = (Ldisc *)ctx;
|
||||
if (ldisc->term && ldisc->prompts) {
|
||||
/*
|
||||
* The integer return value from this call is discarded,
|
||||
* because we have no channel to pass it on to the backend
|
||||
* that originally wanted it. But that's OK, because if the
|
||||
* return value is >= 0 (that is, the prompts are either
|
||||
* completely filled in, or aborted by the user), then the
|
||||
* terminal will notify the callback in the prompts_t, and
|
||||
* when that calls term_get_userpass_input again, it will
|
||||
* return the same answer again.
|
||||
*/
|
||||
term_get_userpass_input(ldisc->term, ldisc->prompts);
|
||||
}
|
||||
}
|
||||
|
||||
static void ldisc_to_backend_raw(
|
||||
Ldisc *ldisc, const void *vbuf, size_t len)
|
||||
{
|
||||
if (backend_sendok(ldisc->backend)) {
|
||||
backend_send(ldisc->backend, vbuf, len);
|
||||
} else {
|
||||
const char *buf = (const char *)vbuf;
|
||||
while (len > 0) {
|
||||
/*
|
||||
* Encode raw data in input_queue, by storing large chunks
|
||||
* as long as they don't include 0xFF, and pausing every
|
||||
* time they do to escape it.
|
||||
*/
|
||||
const char *ff = memchr(buf, '\xFF', len);
|
||||
size_t this_len = ff ? ff - buf : len;
|
||||
if (this_len > 0) {
|
||||
bufchain_add(&ldisc->input_queue, buf, len);
|
||||
} else {
|
||||
bufchain_add(&ldisc->input_queue, "\xFF\xFF", 2);
|
||||
this_len = 1;
|
||||
}
|
||||
buf += this_len;
|
||||
len -= this_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ldisc_to_backend_special(
|
||||
Ldisc *ldisc, SessionSpecialCode code, int arg)
|
||||
{
|
||||
if (backend_sendok(ldisc->backend)) {
|
||||
backend_special(ldisc->backend, code, arg);
|
||||
} else {
|
||||
/*
|
||||
* Encode a session special in input_queue.
|
||||
*/
|
||||
unsigned char data[9];
|
||||
data[0] = 0xFF;
|
||||
PUT_32BIT_MSB_FIRST(data+1, code);
|
||||
PUT_32BIT_MSB_FIRST(data+5, arg);
|
||||
assert(data[1] != 0xFF &&
|
||||
"SessionSpecialCode encoding collides with FF FF escape");
|
||||
bufchain_add(&ldisc->input_queue, data, 9);
|
||||
}
|
||||
}
|
||||
|
||||
bool ldisc_has_input_buffered(Ldisc *ldisc)
|
||||
{
|
||||
return bufchain_size(&ldisc->input_queue) > 0;
|
||||
}
|
||||
|
||||
LdiscInputToken ldisc_get_input_token(Ldisc *ldisc)
|
||||
{
|
||||
assert(bufchain_size(&ldisc->input_queue) > 0 &&
|
||||
"You're not supposed to call this unless there is buffered input!");
|
||||
|
||||
LdiscInputToken tok;
|
||||
|
||||
char c;
|
||||
bufchain_fetch_consume(&ldisc->input_queue, &c, 1);
|
||||
if (c != '\xFF') {
|
||||
/* A literal non-FF byte */
|
||||
tok.is_special = false;
|
||||
tok.chr = c;
|
||||
return tok;
|
||||
} else {
|
||||
char data[8];
|
||||
|
||||
/* See if the byte after the FF is also FF, indicating a literal FF */
|
||||
bufchain_fetch_consume(&ldisc->input_queue, data, 1);
|
||||
if (data[0] == '\xFF') {
|
||||
tok.is_special = false;
|
||||
tok.chr = '\xFF';
|
||||
return tok;
|
||||
}
|
||||
|
||||
/* If not, get the rest of an 8-byte chunk and decode a special */
|
||||
bufchain_fetch_consume(&ldisc->input_queue, data+1, 7);
|
||||
tok.is_special = true;
|
||||
tok.code = GET_32BIT_MSB_FIRST(data);
|
||||
tok.arg = toint(GET_32BIT_MSB_FIRST(data+4));
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
|
||||
static void ldisc_check_sendok_callback(void *ctx)
|
||||
{
|
||||
Ldisc *ldisc = (Ldisc *)ctx;
|
||||
|
||||
if (!(ldisc->backend && backend_sendok(ldisc->backend)))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Flush the ldisc input queue into the backend, which is now
|
||||
* willing to receive the data.
|
||||
*/
|
||||
while (bufchain_size(&ldisc->input_queue) > 0) {
|
||||
/*
|
||||
* Process either a chunk of non-special data, or an FF
|
||||
* escape, depending on whether the first thing we see is an
|
||||
* FF byte.
|
||||
*/
|
||||
ptrlen data = bufchain_prefix(&ldisc->input_queue);
|
||||
const char *ff = memchr(data.ptr, '\xFF', data.len);
|
||||
if (ff != data.ptr) {
|
||||
/* Send a maximal block of data not containing any
|
||||
* difficult bytes. */
|
||||
if (ff)
|
||||
data.len = ff - (const char *)data.ptr;
|
||||
backend_send(ldisc->backend, data.ptr, data.len);
|
||||
bufchain_consume(&ldisc->input_queue, data.len);
|
||||
} else {
|
||||
/* Decode either a special or an escaped FF byte. The
|
||||
* easiest way to do this is to reuse the decoding code
|
||||
* already in ldisc_get_input_token. */
|
||||
LdiscInputToken tok = ldisc_get_input_token(ldisc);
|
||||
if (tok.is_special)
|
||||
backend_special(ldisc->backend, tok.code, tok.arg);
|
||||
else
|
||||
backend_send(ldisc->backend, &tok.chr, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ldisc_check_sendok(Ldisc *ldisc)
|
||||
{
|
||||
queue_toplevel_callback(ldisc_check_sendok_callback, ldisc);
|
||||
}
|
||||
|
||||
void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
|
||||
@ -225,7 +438,7 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
}
|
||||
backend_special(ldisc->backend, SS_EL, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_EL, 0);
|
||||
/*
|
||||
* We don't send IP, SUSP or ABORT if the user has
|
||||
* configured telnet specials off! This breaks
|
||||
@ -234,11 +447,11 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
|
||||
if (!ldisc->telnet_keyboard)
|
||||
goto default_case;
|
||||
if (c == CTRL('C'))
|
||||
backend_special(ldisc->backend, SS_IP, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_IP, 0);
|
||||
if (c == CTRL('Z'))
|
||||
backend_special(ldisc->backend, SS_SUSP, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_SUSP, 0);
|
||||
if (c == CTRL('\\'))
|
||||
backend_special(ldisc->backend, SS_ABORT, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_ABORT, 0);
|
||||
break;
|
||||
case CTRL('R'): /* redraw line */
|
||||
if (ECHOING) {
|
||||
@ -253,9 +466,9 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
|
||||
break;
|
||||
case CTRL('D'): /* logout or send */
|
||||
if (ldisc->buflen == 0) {
|
||||
backend_special(ldisc->backend, SS_EOF, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_EOF, 0);
|
||||
} else {
|
||||
backend_send(ldisc->backend, ldisc->buf, ldisc->buflen);
|
||||
ldisc_to_backend_raw(ldisc, ldisc->buf, ldisc->buflen);
|
||||
ldisc->buflen = 0;
|
||||
}
|
||||
break;
|
||||
@ -291,14 +504,13 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
|
||||
/* FALLTHROUGH */
|
||||
case KCTRL('M'): /* send with newline */
|
||||
if (ldisc->buflen > 0)
|
||||
backend_send(ldisc->backend,
|
||||
ldisc->buf, ldisc->buflen);
|
||||
ldisc_to_backend_raw(ldisc, ldisc->buf, ldisc->buflen);
|
||||
if (ldisc->protocol == PROT_RAW)
|
||||
backend_send(ldisc->backend, "\r\n", 2);
|
||||
ldisc_to_backend_raw(ldisc, "\r\n", 2);
|
||||
else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
|
||||
backend_special(ldisc->backend, SS_EOL, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_EOL, 0);
|
||||
else
|
||||
backend_send(ldisc->backend, "\r", 1);
|
||||
ldisc_to_backend_raw(ldisc, "\r", 1);
|
||||
if (ECHOING)
|
||||
c_write(ldisc, "\r\n", 2);
|
||||
ldisc->buflen = 0;
|
||||
@ -317,7 +529,7 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
|
||||
}
|
||||
} else {
|
||||
if (ldisc->buflen != 0) {
|
||||
backend_send(ldisc->backend, ldisc->buf, ldisc->buflen);
|
||||
ldisc_to_backend_raw(ldisc, ldisc->buf, ldisc->buflen);
|
||||
while (ldisc->buflen > 0) {
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
@ -330,33 +542,33 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
|
||||
switch (buf[0]) {
|
||||
case CTRL('M'):
|
||||
if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
|
||||
backend_special(ldisc->backend, SS_EOL, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_EOL, 0);
|
||||
else
|
||||
backend_send(ldisc->backend, "\r", 1);
|
||||
ldisc_to_backend_raw(ldisc, "\r", 1);
|
||||
break;
|
||||
case CTRL('?'):
|
||||
case CTRL('H'):
|
||||
if (ldisc->telnet_keyboard) {
|
||||
backend_special(ldisc->backend, SS_EC, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_EC, 0);
|
||||
break;
|
||||
}
|
||||
case CTRL('C'):
|
||||
if (ldisc->telnet_keyboard) {
|
||||
backend_special(ldisc->backend, SS_IP, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_IP, 0);
|
||||
break;
|
||||
}
|
||||
case CTRL('Z'):
|
||||
if (ldisc->telnet_keyboard) {
|
||||
backend_special(ldisc->backend, SS_SUSP, 0);
|
||||
ldisc_to_backend_special(ldisc, SS_SUSP, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
backend_send(ldisc->backend, buf, len);
|
||||
ldisc_to_backend_raw(ldisc, buf, len);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
backend_send(ldisc->backend, buf, len);
|
||||
ldisc_to_backend_raw(ldisc, buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
5
noterm.c
5
noterm.c
@ -9,3 +9,8 @@
|
||||
void term_nopaste(Terminal *term)
|
||||
{
|
||||
}
|
||||
|
||||
int term_get_userpass_input(Terminal *term, prompts_t *p)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ struct Rlogin {
|
||||
|
||||
static void rlogin_startup(Rlogin *rlogin, int prompt_result,
|
||||
const char *ruser);
|
||||
static void rlogin_try_username_prompt(void *ctx);
|
||||
|
||||
static void c_write(Rlogin *rlogin, const void *buf, size_t len)
|
||||
{
|
||||
@ -80,18 +81,11 @@ static void rlogin_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
|
||||
rlogin->prompt->to_server = true;
|
||||
rlogin->prompt->from_server = false;
|
||||
rlogin->prompt->name = dupstr("Rlogin login name");
|
||||
rlogin->prompt->callback = rlogin_try_username_prompt;
|
||||
rlogin->prompt->callback_ctx = rlogin;
|
||||
add_prompt(rlogin->prompt, dupstr("rlogin username: "), true);
|
||||
|
||||
int ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt,
|
||||
NULL);
|
||||
if (ret >= 0) {
|
||||
/* Next terminal output will come from server */
|
||||
seat_set_trust_status(rlogin->seat, false);
|
||||
rlogin_startup(rlogin, ret, prompt_get_result_ref(
|
||||
rlogin->prompt->prompts[0]));
|
||||
}
|
||||
rlogin_try_username_prompt(rlogin);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,45 +294,36 @@ static void rlogin_reconfig(Backend *be, Conf *conf)
|
||||
{
|
||||
}
|
||||
|
||||
static void rlogin_try_username_prompt(void *ctx)
|
||||
{
|
||||
Rlogin *rlogin = (Rlogin *)ctx;
|
||||
|
||||
int ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
/* Next terminal output will come from server */
|
||||
seat_set_trust_status(rlogin->seat, false);
|
||||
|
||||
/* Send the rlogin setup protocol data, and then we're ready to
|
||||
* start receiving normal input to send down the wire, which
|
||||
* rlogin_startup will signal to rlogin_sendok by nulling out
|
||||
* rlogin->prompt. */
|
||||
rlogin_startup(
|
||||
rlogin, ret, prompt_get_result_ref(rlogin->prompt->prompts[0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to send data down the rlogin connection.
|
||||
*/
|
||||
static void rlogin_send(Backend *be, const char *buf, size_t len)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
bufchain bc;
|
||||
|
||||
if (rlogin->s == NULL)
|
||||
return;
|
||||
|
||||
bufchain_init(&bc);
|
||||
bufchain_add(&bc, buf, len);
|
||||
|
||||
if (rlogin->prompt) {
|
||||
/*
|
||||
* We're still prompting for a username, and aren't talking
|
||||
* directly to the network connection yet.
|
||||
*/
|
||||
int ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, &bc);
|
||||
if (ret >= 0) {
|
||||
/* Next terminal output will come from server */
|
||||
seat_set_trust_status(rlogin->seat, false);
|
||||
rlogin_startup(rlogin, ret, prompt_get_result_ref(
|
||||
rlogin->prompt->prompts[0]));
|
||||
/* that nulls out rlogin->prompt, so then we'll start sending
|
||||
* data down the wire in the obvious way */
|
||||
}
|
||||
}
|
||||
|
||||
if (!rlogin->prompt) {
|
||||
while (bufchain_size(&bc) > 0) {
|
||||
ptrlen data = bufchain_prefix(&bc);
|
||||
rlogin->bufsize = sk_write(rlogin->s, data.ptr, data.len);
|
||||
bufchain_consume(&bc, len);
|
||||
}
|
||||
}
|
||||
|
||||
bufchain_clear(&bc);
|
||||
rlogin->bufsize = sk_write(rlogin->s, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -398,8 +383,12 @@ static bool rlogin_connected(Backend *be)
|
||||
|
||||
static bool rlogin_sendok(Backend *be)
|
||||
{
|
||||
/*
|
||||
* We only want to receive input data if the socket is connected
|
||||
* and we're not still at the username prompt stage.
|
||||
*/
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
return rlogin->socket_connected;
|
||||
return rlogin->socket_connected && !rlogin->prompt;
|
||||
}
|
||||
|
||||
static void rlogin_unthrottle(Backend *be, size_t backlog)
|
||||
|
60
putty.h
60
putty.h
@ -741,6 +741,11 @@ extern const int be_default_protocol;
|
||||
*/
|
||||
extern const char *const appname;
|
||||
|
||||
/*
|
||||
* Used by callback.c; declared up here so that prompts_t can use it
|
||||
*/
|
||||
typedef void (*toplevel_callback_fn_t)(void *ctx);
|
||||
|
||||
/*
|
||||
* Mechanism for getting text strings such as usernames and passwords
|
||||
* from the front-end.
|
||||
@ -790,6 +795,17 @@ typedef struct {
|
||||
prompt_t **prompts;
|
||||
void *data; /* slot for housekeeping data, managed by
|
||||
* seat_get_userpass_input(); initially NULL */
|
||||
int idata; /* another slot private to the implementation */
|
||||
|
||||
/*
|
||||
* Callback you can fill in to be notified when all the prompts'
|
||||
* responses are available. After you receive this notification, a
|
||||
* further call to the get_userpass_input function will return the
|
||||
* final state of the prompts system, which is guaranteed not to
|
||||
* be negative for 'still ongoing'.
|
||||
*/
|
||||
toplevel_callback_fn_t callback;
|
||||
void *callback_ctx;
|
||||
} prompts_t;
|
||||
prompts_t *new_prompts(void);
|
||||
void add_prompt(prompts_t *p, char *promptstr, bool echo);
|
||||
@ -907,13 +923,7 @@ struct SeatVtable {
|
||||
|
||||
/*
|
||||
* Try to get answers from a set of interactive login prompts. The
|
||||
* prompts are provided in 'p'; the bufchain 'input' holds the
|
||||
* data currently outstanding in the session's normal standard-
|
||||
* input channel. Seats may implement this function by consuming
|
||||
* data from 'input' (e.g. password prompts in GUI PuTTY,
|
||||
* displayed in the same terminal as the subsequent session), or
|
||||
* by doing something entirely different (e.g. directly
|
||||
* interacting with standard I/O, or putting up a dialog box).
|
||||
* prompts are provided in 'p'.
|
||||
*
|
||||
* A positive return value means that all prompts have had answers
|
||||
* filled in. A zero return means that the user performed a
|
||||
@ -939,7 +949,7 @@ struct SeatVtable {
|
||||
* ever do want to move password prompts into a dialog box, I'll
|
||||
* want a backend method for sending that notification.)
|
||||
*/
|
||||
int (*get_userpass_input)(Seat *seat, prompts_t *p, bufchain *input);
|
||||
int (*get_userpass_input)(Seat *seat, prompts_t *p);
|
||||
|
||||
/*
|
||||
* Notify the seat that the main session channel has been
|
||||
@ -1155,9 +1165,8 @@ static inline bool seat_eof(Seat *seat)
|
||||
{ return seat->vt->eof(seat); }
|
||||
static inline void seat_sent(Seat *seat, size_t bufsize)
|
||||
{ seat->vt->sent(seat, bufsize); }
|
||||
static inline int seat_get_userpass_input(
|
||||
Seat *seat, prompts_t *p, bufchain *input)
|
||||
{ return seat->vt->get_userpass_input(seat, p, input); }
|
||||
static inline int seat_get_userpass_input(Seat *seat, prompts_t *p)
|
||||
{ return seat->vt->get_userpass_input(seat, p); }
|
||||
static inline void seat_notify_session_started(Seat *seat)
|
||||
{ seat->vt->notify_session_started(seat); }
|
||||
static inline void seat_notify_remote_exit(Seat *seat)
|
||||
@ -1233,7 +1242,7 @@ size_t nullseat_output(
|
||||
Seat *seat, bool is_stderr, const void *data, size_t len);
|
||||
bool nullseat_eof(Seat *seat);
|
||||
void nullseat_sent(Seat *seat, size_t bufsize);
|
||||
int nullseat_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input);
|
||||
int nullseat_get_userpass_input(Seat *seat, prompts_t *p);
|
||||
void nullseat_notify_session_started(Seat *seat);
|
||||
void nullseat_notify_remote_exit(Seat *seat);
|
||||
void nullseat_notify_remote_disconnect(Seat *seat);
|
||||
@ -1292,7 +1301,7 @@ bool console_can_set_trust_status(Seat *seat);
|
||||
/*
|
||||
* Other centralised seat functions.
|
||||
*/
|
||||
int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input);
|
||||
int filexfer_get_userpass_input(Seat *seat, prompts_t *p);
|
||||
bool cmdline_seat_verbose(Seat *seat);
|
||||
|
||||
/*
|
||||
@ -1888,7 +1897,7 @@ void term_provide_backend(Terminal *term, Backend *backend);
|
||||
void term_provide_logctx(Terminal *term, LogContext *logctx);
|
||||
void term_set_focus(Terminal *term, bool has_focus);
|
||||
char *term_get_ttymode(Terminal *term, const char *mode);
|
||||
int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input);
|
||||
int term_get_userpass_input(Terminal *term, prompts_t *p);
|
||||
void term_set_trust_status(Terminal *term, bool trusted);
|
||||
void term_keyinput(Terminal *, int codepage, const void *buf, int len);
|
||||
void term_keyinputw(Terminal *, const wchar_t * widebuf, int len);
|
||||
@ -2055,6 +2064,28 @@ void ldisc_configure(Ldisc *, Conf *);
|
||||
void ldisc_free(Ldisc *);
|
||||
void ldisc_send(Ldisc *, const void *buf, int len, bool interactive);
|
||||
void ldisc_echoedit_update(Ldisc *);
|
||||
typedef struct LdiscInputToken {
|
||||
/*
|
||||
* Structure that encodes any single item of data that Ldisc can
|
||||
* buffer: either a single character of raw data, or a session
|
||||
* special.
|
||||
*/
|
||||
bool is_special;
|
||||
union {
|
||||
struct {
|
||||
/* if is_special == false */
|
||||
char chr;
|
||||
};
|
||||
struct {
|
||||
/* if is_special == true */
|
||||
SessionSpecialCode code;
|
||||
int arg;
|
||||
};
|
||||
};
|
||||
} LdiscInputToken;
|
||||
bool ldisc_has_input_buffered(Ldisc *);
|
||||
LdiscInputToken ldisc_get_input_token(Ldisc *); /* asserts there is input */
|
||||
void ldisc_enable_prompt_callback(Ldisc *, prompts_t *);
|
||||
void ldisc_check_sendok(Ldisc *);
|
||||
|
||||
/*
|
||||
@ -2472,7 +2503,6 @@ unsigned long timing_last_clock(void);
|
||||
* loop, as in PSFTP, for example - if a callback has run then perhaps
|
||||
* it might have done whatever the loop's caller was waiting for.
|
||||
*/
|
||||
typedef void (*toplevel_callback_fn_t)(void *ctx);
|
||||
void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx);
|
||||
bool run_toplevel_callbacks(void);
|
||||
bool toplevel_callback_pending(void);
|
||||
|
13
ssh/common.c
13
ssh/common.c
@ -737,6 +737,19 @@ size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl)
|
||||
return ppl->out_pq->pqb.total_size;
|
||||
}
|
||||
|
||||
static void ssh_ppl_prompts_callback(void *ctx)
|
||||
{
|
||||
ssh_ppl_process_queue((PacketProtocolLayer *)ctx);
|
||||
}
|
||||
|
||||
prompts_t *ssh_ppl_new_prompts(PacketProtocolLayer *ppl)
|
||||
{
|
||||
prompts_t *p = new_prompts();
|
||||
p->callback = ssh_ppl_prompts_callback;
|
||||
p->callback_ctx = ppl;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Common helper functions for clients and implementations of
|
||||
* BinaryPacketProtocol.
|
||||
|
@ -370,7 +370,7 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl)
|
||||
* connection-sharing downstream).
|
||||
*/
|
||||
if (ssh1_connection_need_antispoof_prompt(s)) {
|
||||
s->antispoof_prompt = new_prompts();
|
||||
s->antispoof_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
s->antispoof_prompt->to_server = true;
|
||||
s->antispoof_prompt->from_server = false;
|
||||
s->antispoof_prompt->name = dupstr("Authentication successful");
|
||||
@ -378,19 +378,11 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl)
|
||||
s->antispoof_prompt,
|
||||
dupstr("Access granted. Press Return to begin session. "), false);
|
||||
s->antispoof_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->antispoof_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->antispoof_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->antispoof_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->antispoof_prompt, s->ppl.user_input);
|
||||
|
||||
if (s->antispoof_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->ppl.seat, s->antispoof_prompt);
|
||||
while (s->antispoof_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->antispoof_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->antispoof_prompt);
|
||||
}
|
||||
free_prompts(s->antispoof_prompt);
|
||||
s->antispoof_prompt = NULL;
|
||||
|
@ -982,7 +982,7 @@ static void ssh2_connection_process_queue(PacketProtocolLayer *ppl)
|
||||
* connection-sharing downstream).
|
||||
*/
|
||||
if (ssh2_connection_need_antispoof_prompt(s)) {
|
||||
s->antispoof_prompt = new_prompts();
|
||||
s->antispoof_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
s->antispoof_prompt->to_server = true;
|
||||
s->antispoof_prompt->from_server = false;
|
||||
s->antispoof_prompt->name = dupstr("Authentication successful");
|
||||
@ -990,19 +990,11 @@ static void ssh2_connection_process_queue(PacketProtocolLayer *ppl)
|
||||
s->antispoof_prompt,
|
||||
dupstr("Access granted. Press Return to begin session. "), false);
|
||||
s->antispoof_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->antispoof_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->antispoof_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->antispoof_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->antispoof_prompt, s->ppl.user_input);
|
||||
|
||||
if (s->antispoof_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->ppl.seat, s->antispoof_prompt);
|
||||
while (s->antispoof_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->antispoof_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->antispoof_prompt);
|
||||
}
|
||||
free_prompts(s->antispoof_prompt);
|
||||
s->antispoof_prompt = NULL;
|
||||
|
56
ssh/login1.c
56
ssh/login1.c
@ -404,25 +404,16 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
||||
ppl_logevent("Successfully started encryption");
|
||||
|
||||
if ((s->username = get_remote_username(s->conf)) == NULL) {
|
||||
s->cur_prompt = new_prompts();
|
||||
s->cur_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
s->cur_prompt->to_server = true;
|
||||
s->cur_prompt->from_server = false;
|
||||
s->cur_prompt->name = dupstr("SSH login name");
|
||||
add_prompt(s->cur_prompt, dupstr("login as: "), true);
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->userpass_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, s->ppl.user_input);
|
||||
|
||||
if (s->userpass_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->userpass_ret = seat_get_userpass_input(s->ppl.seat, s->cur_prompt);
|
||||
while (s->userpass_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
}
|
||||
if (!s->userpass_ret) {
|
||||
/*
|
||||
@ -707,7 +698,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
||||
ppl_printf("No passphrase required.\r\n");
|
||||
passphrase = NULL;
|
||||
} else {
|
||||
s->cur_prompt = new_prompts();
|
||||
s->cur_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
s->cur_prompt->to_server = false;
|
||||
s->cur_prompt->from_server = false;
|
||||
s->cur_prompt->name = dupstr("SSH key passphrase");
|
||||
@ -715,19 +706,11 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
||||
dupprintf("Passphrase for key \"%s\": ",
|
||||
s->publickey_comment), false);
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->userpass_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, s->ppl.user_input);
|
||||
|
||||
if (s->userpass_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
while (s->userpass_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
}
|
||||
if (!s->userpass_ret) {
|
||||
/* Failed to get a passphrase. Terminate. */
|
||||
@ -846,7 +829,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
||||
/*
|
||||
* Otherwise, try various forms of password-like authentication.
|
||||
*/
|
||||
s->cur_prompt = new_prompts();
|
||||
s->cur_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
|
||||
if (conf_get_bool(s->conf, CONF_try_tis_auth) &&
|
||||
(s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
|
||||
@ -977,20 +960,11 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
|
||||
* or CryptoCard exchange if we're doing TIS or CryptoCard
|
||||
* authentication.
|
||||
*/
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->userpass_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, s->ppl.user_input);
|
||||
|
||||
if (s->userpass_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->userpass_ret = seat_get_userpass_input(s->ppl.seat, s->cur_prompt);
|
||||
while (s->userpass_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
}
|
||||
if (!s->userpass_ret) {
|
||||
/*
|
||||
|
@ -154,6 +154,11 @@ void ssh2_transport_notify_auth_done(PacketProtocolLayer *ssh2_transport_ptr);
|
||||
* be handled by ssh2connection. */
|
||||
bool ssh2_common_filter_queue(PacketProtocolLayer *ppl);
|
||||
|
||||
/* Method for making a prompts_t in such a way that it will install a
|
||||
* callback that causes this PPL's process_queue method to be called
|
||||
* when asynchronous prompt input completes. */
|
||||
prompts_t *ssh_ppl_new_prompts(PacketProtocolLayer *ppl);
|
||||
|
||||
/* Methods for ssh1login to pass protocol flags to ssh1connection */
|
||||
void ssh1_connection_set_protoflags(
|
||||
PacketProtocolLayer *ppl, int local, int remote);
|
||||
|
@ -442,25 +442,17 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
||||
* it again.
|
||||
*/
|
||||
} else if ((s->username = s->default_username) == NULL) {
|
||||
s->cur_prompt = new_prompts();
|
||||
s->cur_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
s->cur_prompt->to_server = true;
|
||||
s->cur_prompt->from_server = false;
|
||||
s->cur_prompt->name = dupstr("SSH login name");
|
||||
add_prompt(s->cur_prompt, dupstr("login as: "), true);
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->userpass_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, s->ppl.user_input);
|
||||
|
||||
if (s->userpass_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
while (s->userpass_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
}
|
||||
if (!s->userpass_ret) {
|
||||
/*
|
||||
@ -913,7 +905,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
||||
/*
|
||||
* Get a passphrase from the user.
|
||||
*/
|
||||
s->cur_prompt = new_prompts();
|
||||
s->cur_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
s->cur_prompt->to_server = false;
|
||||
s->cur_prompt->from_server = false;
|
||||
s->cur_prompt->name = dupstr("SSH key passphrase");
|
||||
@ -922,20 +914,11 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
||||
s->publickey_comment),
|
||||
false);
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->userpass_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt,
|
||||
s->ppl.user_input);
|
||||
|
||||
if (s->userpass_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
while (s->userpass_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
}
|
||||
if (!s->userpass_ret) {
|
||||
/* Failed to get a passphrase. Terminate. */
|
||||
@ -1304,7 +1287,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
||||
name = get_string(pktin);
|
||||
inst = get_string(pktin);
|
||||
get_string(pktin); /* skip language tag */
|
||||
s->cur_prompt = new_prompts();
|
||||
s->cur_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
s->cur_prompt->to_server = true;
|
||||
s->cur_prompt->from_server = true;
|
||||
|
||||
@ -1408,19 +1391,11 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
||||
* user's response(s).
|
||||
*/
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->userpass_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, s->ppl.user_input);
|
||||
|
||||
if (s->userpass_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
while (s->userpass_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
}
|
||||
if (!s->userpass_ret) {
|
||||
/*
|
||||
@ -1486,7 +1461,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
||||
|
||||
s->ppl.bpp->pls->actx = SSH2_PKTCTX_PASSWORD;
|
||||
|
||||
s->cur_prompt = new_prompts();
|
||||
s->cur_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
s->cur_prompt->to_server = true;
|
||||
s->cur_prompt->from_server = false;
|
||||
s->cur_prompt->name = dupstr("SSH password");
|
||||
@ -1495,19 +1470,11 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
||||
false);
|
||||
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->userpass_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, s->ppl.user_input);
|
||||
|
||||
if (s->userpass_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
while (s->userpass_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
}
|
||||
if (!s->userpass_ret) {
|
||||
/*
|
||||
@ -1581,7 +1548,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
||||
|
||||
prompt = get_string(pktin);
|
||||
|
||||
s->cur_prompt = new_prompts();
|
||||
s->cur_prompt = ssh_ppl_new_prompts(&s->ppl);
|
||||
s->cur_prompt->to_server = true;
|
||||
s->cur_prompt->from_server = false;
|
||||
s->cur_prompt->name = dupstr("New SSH password");
|
||||
@ -1613,20 +1580,11 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
|
||||
*/
|
||||
while (!got_new) {
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt, NULL);
|
||||
while (1) {
|
||||
while (s->userpass_ret < 0 &&
|
||||
bufchain_size(s->ppl.user_input) > 0)
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt,
|
||||
s->ppl.user_input);
|
||||
|
||||
if (s->userpass_ret >= 0)
|
||||
break;
|
||||
|
||||
s->want_user_input = true;
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
while (s->userpass_ret < 0) {
|
||||
crReturnV;
|
||||
s->want_user_input = false;
|
||||
s->userpass_ret = seat_get_userpass_input(
|
||||
s->ppl.seat, s->cur_prompt);
|
||||
}
|
||||
if (!s->userpass_ret) {
|
||||
/*
|
||||
|
@ -276,8 +276,7 @@ static void sshproxy_notify_remote_disconnect(Seat *seat)
|
||||
sshproxy_eof(seat);
|
||||
}
|
||||
|
||||
static int sshproxy_get_userpass_input(Seat *seat, prompts_t *p,
|
||||
bufchain *input)
|
||||
static int sshproxy_get_userpass_input(Seat *seat, prompts_t *p)
|
||||
{
|
||||
/*
|
||||
* TODO: if we had access to the outer Seat, we could pass on this
|
||||
|
54
terminal.c
54
terminal.c
@ -7519,18 +7519,45 @@ static inline void term_write(Terminal *term, ptrlen data)
|
||||
term_data(term, false, data.ptr, data.len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal that a prompts_t is done. This involves sending a
|
||||
* notification to the caller, and also turning off our own callback
|
||||
* that listens for more data arriving in the ldisc's input queue.
|
||||
*/
|
||||
static inline int signal_prompts_t(Terminal *term, prompts_t *p, int result)
|
||||
{
|
||||
assert(p->callback && "Asynchronous userpass input requires a callback");
|
||||
queue_toplevel_callback(p->callback, p->callback_ctx);
|
||||
ldisc_enable_prompt_callback(term->ldisc, NULL);
|
||||
p->idata = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process some terminal data in the course of username/password
|
||||
* input.
|
||||
*/
|
||||
int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
||||
int term_get_userpass_input(Terminal *term, prompts_t *p)
|
||||
{
|
||||
if (!term->ldisc) {
|
||||
/* Can't handle interactive prompts without an ldisc */
|
||||
return signal_prompts_t(term, p, 0);
|
||||
}
|
||||
|
||||
if (p->idata >= 0) {
|
||||
/* We've already finished these prompts, so return the same
|
||||
* result again */
|
||||
return p->idata;
|
||||
}
|
||||
|
||||
struct term_userpass_state *s = (struct term_userpass_state *)p->data;
|
||||
|
||||
if (!s) {
|
||||
/*
|
||||
* First call. Set some stuff up.
|
||||
*/
|
||||
p->data = s = snew(struct term_userpass_state);
|
||||
p->idata = -1;
|
||||
s->curr_prompt = 0;
|
||||
s->done_prompt = false;
|
||||
/* We only print the `name' caption if we have to... */
|
||||
@ -7569,12 +7596,26 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
||||
|
||||
/* Breaking out here ensures that the prompt is printed even
|
||||
* if we're now waiting for user data. */
|
||||
if (!input || !bufchain_size(input)) break;
|
||||
if (!ldisc_has_input_buffered(term->ldisc))
|
||||
break;
|
||||
|
||||
/* FIXME: should we be using local-line-editing code instead? */
|
||||
while (!finished_prompt && bufchain_size(input) > 0) {
|
||||
while (!finished_prompt && ldisc_has_input_buffered(term->ldisc)) {
|
||||
LdiscInputToken tok = ldisc_get_input_token(term->ldisc);
|
||||
|
||||
char c;
|
||||
bufchain_fetch_consume(input, &c, 1);
|
||||
if (tok.is_special) {
|
||||
switch (tok.code) {
|
||||
case SS_EOL: c = 13; break;
|
||||
case SS_EC: c = 8; break;
|
||||
case SS_IP: c = 3; break;
|
||||
case SS_EOF: c = 3; break;
|
||||
default: continue;
|
||||
}
|
||||
} else {
|
||||
c = tok.chr;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 10:
|
||||
case 13:
|
||||
@ -7606,7 +7647,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
||||
term_write(term, PTRLEN_LITERAL("\r\n"));
|
||||
sfree(s);
|
||||
p->data = NULL;
|
||||
return 0; /* user abort */
|
||||
return signal_prompts_t(term, p, 0); /* user abort */
|
||||
default:
|
||||
/*
|
||||
* This simplistic check for printability is disabled
|
||||
@ -7626,11 +7667,12 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input)
|
||||
}
|
||||
|
||||
if (s->curr_prompt < p->n_prompts) {
|
||||
ldisc_enable_prompt_callback(term->ldisc, p);
|
||||
return -1; /* more data required */
|
||||
} else {
|
||||
sfree(s);
|
||||
p->data = NULL;
|
||||
return +1; /* all done */
|
||||
return signal_prompts_t(term, p, +1); /* all done */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,7 @@ static bool plink_eof(Seat *seat)
|
||||
return false; /* do not respond to incoming EOF with outgoing */
|
||||
}
|
||||
|
||||
static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input)
|
||||
static int plink_get_userpass_input(Seat *seat, prompts_t *p)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p);
|
||||
|
@ -63,7 +63,7 @@ Filename *platform_default_filename(const char *name)
|
||||
return filename_from_str("");
|
||||
}
|
||||
|
||||
int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input)
|
||||
int filexfer_get_userpass_input(Seat *seat, prompts_t *p)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p);
|
||||
|
@ -331,14 +331,13 @@ static bool gtk_seat_eof(Seat *seat)
|
||||
return true; /* do respond to incoming EOF with outgoing */
|
||||
}
|
||||
|
||||
static int gtk_seat_get_userpass_input(Seat *seat, prompts_t *p,
|
||||
bufchain *input)
|
||||
static int gtk_seat_get_userpass_input(Seat *seat, prompts_t *p)
|
||||
{
|
||||
GtkFrontend *inst = container_of(seat, GtkFrontend, seat);
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p);
|
||||
if (ret == -1)
|
||||
ret = term_get_userpass_input(inst->term, p, input);
|
||||
ret = term_get_userpass_input(inst->term, p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,7 @@ size_t nullseat_output(
|
||||
Seat *seat, bool is_stderr, const void *data, size_t len) { return 0; }
|
||||
bool nullseat_eof(Seat *seat) { return true; }
|
||||
void nullseat_sent(Seat *seat, size_t bufsize) {}
|
||||
int nullseat_get_userpass_input(
|
||||
Seat *seat, prompts_t *p, bufchain *input) { return 0; }
|
||||
int nullseat_get_userpass_input(Seat *seat, prompts_t *p) { return 0; }
|
||||
void nullseat_notify_session_started(Seat *seat) {}
|
||||
void nullseat_notify_remote_exit(Seat *seat) {}
|
||||
void nullseat_notify_remote_disconnect(Seat *seat) {}
|
||||
|
@ -14,6 +14,8 @@ prompts_t *new_prompts(void)
|
||||
p->to_server = true; /* to be on the safe side */
|
||||
p->name = p->instruction = NULL;
|
||||
p->name_reqd = p->instr_reqd = false;
|
||||
p->callback = NULL;
|
||||
p->callback_ctx = NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -177,8 +177,7 @@ static bool tempseat_can_set_trust_status(Seat *seat)
|
||||
* for the network connection.
|
||||
*/
|
||||
|
||||
static int tempseat_get_userpass_input(Seat *seat, prompts_t *p,
|
||||
bufchain *input)
|
||||
static int tempseat_get_userpass_input(Seat *seat, prompts_t *p)
|
||||
{
|
||||
/*
|
||||
* Interactive prompts of this nature are a thing that a backend
|
||||
|
@ -64,7 +64,7 @@ static bool plink_eof(Seat *seat)
|
||||
return false; /* do not respond to incoming EOF with outgoing */
|
||||
}
|
||||
|
||||
static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input)
|
||||
static int plink_get_userpass_input(Seat *seat, prompts_t *p)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p);
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "ssh.h"
|
||||
#include "security-api.h"
|
||||
|
||||
int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input)
|
||||
int filexfer_get_userpass_input(Seat *seat, prompts_t *p)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p);
|
||||
|
@ -317,8 +317,7 @@ static StripCtrlChars *win_seat_stripctrl_new(
|
||||
static size_t win_seat_output(
|
||||
Seat *seat, bool is_stderr, const void *, size_t);
|
||||
static bool win_seat_eof(Seat *seat);
|
||||
static int win_seat_get_userpass_input(
|
||||
Seat *seat, prompts_t *p, bufchain *input);
|
||||
static int win_seat_get_userpass_input(Seat *seat, prompts_t *p);
|
||||
static void win_seat_notify_remote_exit(Seat *seat);
|
||||
static void win_seat_connection_fatal(Seat *seat, const char *msg);
|
||||
static void win_seat_update_specials_menu(Seat *seat);
|
||||
@ -5746,13 +5745,12 @@ static bool win_seat_eof(Seat *seat)
|
||||
return true; /* do respond to incoming EOF with outgoing */
|
||||
}
|
||||
|
||||
static int win_seat_get_userpass_input(
|
||||
Seat *seat, prompts_t *p, bufchain *input)
|
||||
static int win_seat_get_userpass_input(Seat *seat, prompts_t *p)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p);
|
||||
if (ret == -1)
|
||||
ret = term_get_userpass_input(term, p, input);
|
||||
ret = term_get_userpass_input(term, p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user