mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 09:27:59 +00:00
Revamp SSH authentication code so that user interaction is more
abstracted out; replace loops structured around a single interaction per loop with less tortuous code (fixes: `ki-multiprompt-crash', `ssh1-bad-passphrase-crash'; makes `ssh2-password-expiry' and `proxy-password-prompt' easier). The new interaction abstraction has a lot of fields that are unused in the current code (things like window captions); this is groundwork for `gui-auth'. However, ssh.c still writes directly to stderr; that may want to be fixed. In the GUI apps, user interaction is moved to terminal.c. This should make it easier to fix things like UTF-8 username entry, although I haven't attempted to do so. Also, control character filtering can be tailored to be appropriate for individual front-ends; so far I don't promise anything other than not having made it any worse. I've tried to test this fairly exhaustively (although Mac stuff is untested, as usual). It all seems to basically work, but I bet there are new bugs. (One I know about is that you can no longer make the PuTTY window go away with a ^D at the password prompt; this should be fixed.) [originally from svn r6437] [this svn revision also touched putty-wishlist]
This commit is contained in:
parent
d6c13bd154
commit
8719f92c14
92
cmdgen.c
92
cmdgen.c
@ -25,9 +25,10 @@
|
||||
* order to avoid depleting the test system's /dev/random
|
||||
* unnecessarily.
|
||||
*
|
||||
* - Calls to console_get_line() are replaced with the diagnostic
|
||||
* function below, so that I can run tests in an automated
|
||||
* manner and provide their interactive passphrase inputs.
|
||||
* - Calls to console_get_userpass_input() are replaced with the
|
||||
* diagnostic function below, so that I can run tests in an
|
||||
* automated manner and provide their interactive passphrase
|
||||
* inputs.
|
||||
*
|
||||
* - main() is renamed to cmdgen_main(); at the bottom of the file
|
||||
* I define another main() which calls the former repeatedly to
|
||||
@ -40,19 +41,23 @@ char *get_random_data(int len)
|
||||
memset(buf, 'x', len);
|
||||
return buf;
|
||||
}
|
||||
#define console_get_line console_get_line_diagnostic
|
||||
#define console_get_userpass_input console_get_userpass_input_diagnostic
|
||||
int nprompts, promptsgot;
|
||||
const char *prompts[3];
|
||||
int console_get_line(const char *prompt, char *str, int maxlen, int is_pw)
|
||||
int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
if (promptsgot < nprompts) {
|
||||
assert(strlen(prompts[promptsgot]) < maxlen);
|
||||
strcpy(str, prompts[promptsgot++]);
|
||||
return TRUE;
|
||||
} else {
|
||||
promptsgot++; /* track number of requests anyway */
|
||||
return FALSE;
|
||||
size_t i;
|
||||
int ret = 1;
|
||||
for (i = 0; i < p->n_prompts; i++) {
|
||||
if (promptsgot < nprompts) {
|
||||
assert(strlen(prompts[promptsgot]) < p->prompts[i]->result_len);
|
||||
strcpy(p->prompts[i]->result, prompts[promptsgot++]);
|
||||
} else {
|
||||
promptsgot++; /* track number of requests anyway */
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#define main cmdgen_main
|
||||
#endif
|
||||
@ -670,11 +675,20 @@ int main(int argc, char **argv)
|
||||
* If so, ask for a passphrase.
|
||||
*/
|
||||
if (encrypted && load_encrypted) {
|
||||
passphrase = snewn(512, char);
|
||||
if (!console_get_line("Enter passphrase to load key: ",
|
||||
passphrase, 512, TRUE)) {
|
||||
prompts_t *p = new_prompts(NULL);
|
||||
int ret;
|
||||
p->to_server = FALSE;
|
||||
p->name = dupstr("SSH key passphrase");
|
||||
add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE, 512);
|
||||
ret = console_get_userpass_input(p, NULL, 0);
|
||||
assert(ret >= 0);
|
||||
if (!ret) {
|
||||
free_prompts(p);
|
||||
perror("puttygen: unable to read passphrase");
|
||||
return 1;
|
||||
} else {
|
||||
passphrase = dupstr(p->prompts[0]->result);
|
||||
free_prompts(p);
|
||||
}
|
||||
} else {
|
||||
passphrase = NULL;
|
||||
@ -785,31 +799,35 @@ int main(int argc, char **argv)
|
||||
* we have just generated a key.
|
||||
*/
|
||||
if (change_passphrase || keytype != NOKEYGEN) {
|
||||
char *passphrase2;
|
||||
prompts_t *p = new_prompts(NULL);
|
||||
int ret;
|
||||
|
||||
if (passphrase) {
|
||||
memset(passphrase, 0, strlen(passphrase));
|
||||
sfree(passphrase);
|
||||
}
|
||||
|
||||
passphrase = snewn(512, char);
|
||||
passphrase2 = snewn(512, char);
|
||||
if (!console_get_line("Enter passphrase to save key: ",
|
||||
passphrase, 512, TRUE) ||
|
||||
!console_get_line("Re-enter passphrase to verify: ",
|
||||
passphrase2, 512, TRUE)) {
|
||||
p->to_server = FALSE;
|
||||
p->name = dupstr("New SSH key passphrase");
|
||||
add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE, 512);
|
||||
add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE, 512);
|
||||
ret = console_get_userpass_input(p, NULL, 0);
|
||||
assert(ret >= 0);
|
||||
if (!ret) {
|
||||
free_prompts(p);
|
||||
perror("puttygen: unable to read new passphrase");
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(passphrase, passphrase2)) {
|
||||
fprintf(stderr, "puttygen: passphrases do not match\n");
|
||||
return 1;
|
||||
}
|
||||
memset(passphrase2, 0, strlen(passphrase2));
|
||||
sfree(passphrase2);
|
||||
if (!*passphrase) {
|
||||
sfree(passphrase);
|
||||
passphrase = NULL;
|
||||
} else {
|
||||
if (strcmp(p->prompts[0]->result, p->prompts[1]->result)) {
|
||||
free_prompts(p);
|
||||
fprintf(stderr, "puttygen: passphrases do not match\n");
|
||||
return 1;
|
||||
}
|
||||
if (passphrase) {
|
||||
memset(passphrase, 0, strlen(passphrase));
|
||||
sfree(passphrase);
|
||||
}
|
||||
passphrase = dupstr(p->prompts[0]->result);
|
||||
free_prompts(p);
|
||||
if (!*passphrase) {
|
||||
sfree(passphrase);
|
||||
passphrase = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
45
cmdline.c
45
cmdline.c
@ -63,23 +63,40 @@ void cmdline_cleanup(void)
|
||||
if (need_save) { cmdline_save_param(p, value, pri); return ret; } \
|
||||
} while (0)
|
||||
|
||||
char *cmdline_password = NULL;
|
||||
static char *cmdline_password = NULL;
|
||||
|
||||
/*
|
||||
* Similar interface to get_userpass_input(), except that here a -1
|
||||
* return means that we aren't capable of processing the prompt and
|
||||
* someone else should do it.
|
||||
*/
|
||||
int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen) {
|
||||
|
||||
static int cmdline_get_line(const char *prompt, char *str,
|
||||
int maxlen, int is_pw)
|
||||
{
|
||||
static int tried_once = 0;
|
||||
|
||||
assert(is_pw && cmdline_password);
|
||||
|
||||
if (tried_once) {
|
||||
return 0;
|
||||
} else {
|
||||
strncpy(str, cmdline_password, maxlen);
|
||||
str[maxlen - 1] = '\0';
|
||||
tried_once = 1;
|
||||
return 1;
|
||||
/*
|
||||
* We only handle prompts which don't echo (which we assume to be
|
||||
* passwords), and (currently) we only cope with a password prompt
|
||||
* that comes in a prompt-set on its own.
|
||||
*/
|
||||
if (!cmdline_password || in || p->n_prompts != 1 || p->prompts[0]->echo) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've tried once, return utter failure (no more passwords left
|
||||
* to try).
|
||||
*/
|
||||
if (tried_once)
|
||||
return 0;
|
||||
|
||||
strncpy(p->prompts[0]->result, cmdline_password,
|
||||
p->prompts[0]->result_len);
|
||||
p->prompts[0]->result[p->prompts[0]->result_len-1] = '\0';
|
||||
memset(cmdline_password, 0, strlen(cmdline_password));
|
||||
tried_once = 1;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -272,8 +289,6 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
|
||||
RETURN(2);
|
||||
UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
|
||||
cmdline_password = value;
|
||||
ssh_get_line = cmdline_get_line;
|
||||
ssh_getline_pw_only = TRUE;
|
||||
}
|
||||
|
||||
if (!strcmp(p, "-A")) {
|
||||
|
@ -1898,6 +1898,12 @@ int from_backend(void *frontend, int is_stderr, const char *data, int len)
|
||||
return term_data(s->term, is_stderr, data, len);
|
||||
}
|
||||
|
||||
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
Session *s = p->frontend;
|
||||
return term_get_userpass_input(s->term, p, in, inlen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Emacs magic:
|
||||
* Local Variables:
|
||||
|
@ -893,6 +893,13 @@ int from_backend(void *frontend, int is_stderr, const char *data, int len)
|
||||
return [win fromBackend:data len:len isStderr:is_stderr];
|
||||
}
|
||||
|
||||
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
SessionWindow *win = (SessionWindow *)p->frontend;
|
||||
Terminal *term = [win term];
|
||||
return term_get_userpass_input(term, p, in, inlen);
|
||||
}
|
||||
|
||||
void frontend_keypress(void *handle)
|
||||
{
|
||||
/* FIXME */
|
||||
|
40
misc.c
40
misc.c
@ -87,6 +87,46 @@ char ctrlparse(char *s, char **next)
|
||||
return c;
|
||||
}
|
||||
|
||||
prompts_t *new_prompts(void *frontend)
|
||||
{
|
||||
prompts_t *p = snew(prompts_t);
|
||||
p->prompts = NULL;
|
||||
p->n_prompts = 0;
|
||||
p->frontend = frontend;
|
||||
p->data = NULL;
|
||||
p->to_server = TRUE; /* to be on the safe side */
|
||||
p->name = p->instruction = NULL;
|
||||
p->name_reqd = p->instr_reqd = FALSE;
|
||||
return p;
|
||||
}
|
||||
void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len)
|
||||
{
|
||||
prompt_t *pr = snew(prompt_t);
|
||||
unsigned char *result = snewn(len, unsigned char);
|
||||
pr->prompt = promptstr;
|
||||
pr->echo = echo;
|
||||
pr->result = result;
|
||||
pr->result_len = len;
|
||||
p->n_prompts++;
|
||||
p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *);
|
||||
p->prompts[p->n_prompts-1] = pr;
|
||||
}
|
||||
void free_prompts(prompts_t *p)
|
||||
{
|
||||
size_t i;
|
||||
for (i=0; i < p->n_prompts; i++) {
|
||||
prompt_t *pr = p->prompts[i];
|
||||
memset(pr->result, 0, pr->result_len); /* burn the evidence */
|
||||
sfree(pr->result);
|
||||
sfree(pr->prompt);
|
||||
sfree(pr);
|
||||
}
|
||||
sfree(p->prompts);
|
||||
sfree(p->name);
|
||||
sfree(p->instruction);
|
||||
sfree(p);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* String handling routines.
|
||||
*/
|
||||
|
10
pscp.c
10
pscp.c
@ -220,6 +220,15 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen)
|
||||
|
||||
return 0;
|
||||
}
|
||||
int from_backend_untrusted(void *frontend_handle, const char *data, int len)
|
||||
{
|
||||
/*
|
||||
* No "untrusted" output should get here (the way the code is
|
||||
* currently, it's all diverted by FLAG_STDERR).
|
||||
*/
|
||||
assert(!"Unexpected call to from_backend_untrusted()");
|
||||
return 0; /* not reached */
|
||||
}
|
||||
static int ssh_scp_recv(unsigned char *buf, int len)
|
||||
{
|
||||
outptr = buf;
|
||||
@ -2204,7 +2213,6 @@ int psftp_main(int argc, char *argv[])
|
||||
#endif
|
||||
;
|
||||
cmdline_tooltype = TOOLTYPE_FILETRANSFER;
|
||||
ssh_get_line = &console_get_line;
|
||||
sk_init();
|
||||
|
||||
/* Load Default Settings before doing anything else. */
|
||||
|
27
psftp.c
27
psftp.c
@ -2523,6 +2523,15 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen)
|
||||
|
||||
return 0;
|
||||
}
|
||||
int from_backend_untrusted(void *frontend_handle, const char *data, int len)
|
||||
{
|
||||
/*
|
||||
* No "untrusted" output should get here (the way the code is
|
||||
* currently, it's all diverted by FLAG_STDERR).
|
||||
*/
|
||||
assert(!"Unexpected call to from_backend_untrusted()");
|
||||
return 0; /* not reached */
|
||||
}
|
||||
int sftp_recvdata(char *buf, int len)
|
||||
{
|
||||
outptr = (unsigned char *) buf;
|
||||
@ -2714,14 +2723,21 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
|
||||
cfg.username[sizeof(cfg.username) - 1] = '\0';
|
||||
}
|
||||
if (!cfg.username[0]) {
|
||||
if (!console_get_line("login as: ",
|
||||
cfg.username, sizeof(cfg.username), FALSE)) {
|
||||
/* FIXME: leave this to ssh.c? */
|
||||
int ret;
|
||||
prompts_t *p = new_prompts(NULL);
|
||||
p->to_server = TRUE;
|
||||
p->name = dupstr("SSH login name");
|
||||
add_prompt(p, dupstr("login as: "), TRUE, lenof(cfg.username));
|
||||
ret = get_userpass_input(p, NULL, 0);
|
||||
assert(ret >= 0);
|
||||
if (!ret) {
|
||||
free_prompts(p);
|
||||
fprintf(stderr, "psftp: no username, aborting\n");
|
||||
cleanup_exit(1);
|
||||
} else {
|
||||
int len = strlen(cfg.username);
|
||||
if (cfg.username[len - 1] == '\n')
|
||||
cfg.username[len - 1] = '\0';
|
||||
memcpy(cfg.username, p->prompts[0]->result, lenof(cfg.username));
|
||||
free_prompts(p);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2819,7 +2835,6 @@ int psftp_main(int argc, char *argv[])
|
||||
#endif
|
||||
;
|
||||
cmdline_tooltype = TOOLTYPE_FILETRANSFER;
|
||||
ssh_get_line = &console_get_line;
|
||||
sk_init();
|
||||
|
||||
userhost = user = NULL;
|
||||
|
72
putty.h
72
putty.h
@ -611,6 +611,52 @@ GLOBAL int loaded_session;
|
||||
|
||||
struct RSAKey; /* be a little careful of scope */
|
||||
|
||||
/*
|
||||
* Mechanism for getting text strings such as usernames and passwords
|
||||
* from the front-end.
|
||||
* The fields are mostly modelled after SSH's keyboard-interactive auth.
|
||||
* FIXME We should probably mandate a character set/encoding (probably UTF-8).
|
||||
*
|
||||
* Since many of the pieces of text involved may be chosen by the server,
|
||||
* the caller must take care to ensure that the server can't spoof locally-
|
||||
* generated prompts such as key passphrase prompts. Some ground rules:
|
||||
* - If the front-end needs to truncate a string, it should lop off the
|
||||
* end.
|
||||
* - The front-end should filter out any dangerous characters and
|
||||
* generally not trust the strings. (But \n is required to behave
|
||||
* vaguely sensibly, at least in `instruction', and ideally in
|
||||
* `prompt[]' too.)
|
||||
*/
|
||||
typedef struct {
|
||||
char *prompt;
|
||||
int echo;
|
||||
char *result; /* allocated/freed by caller */
|
||||
size_t result_len;
|
||||
} prompt_t;
|
||||
typedef struct {
|
||||
/*
|
||||
* Indicates whether the information entered is to be used locally
|
||||
* (for instance a key passphrase prompt), or is destined for the wire.
|
||||
* This is a hint only; the front-end is at liberty not to use this
|
||||
* information (so the caller should ensure that the supplied text is
|
||||
* sufficient).
|
||||
*/
|
||||
int to_server;
|
||||
char *name; /* Short description, perhaps for dialog box title */
|
||||
int name_reqd; /* Display of `name' required or optional? */
|
||||
char *instruction; /* Long description, maybe with embedded newlines */
|
||||
int instr_reqd; /* Display of `instruction' required or optional? */
|
||||
size_t n_prompts;
|
||||
prompt_t **prompts;
|
||||
void *frontend;
|
||||
void *data; /* slot for housekeeping data, managed by
|
||||
* get_userpass_input(); initially NULL */
|
||||
} prompts_t;
|
||||
prompts_t *new_prompts(void *frontend);
|
||||
void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len);
|
||||
/* Burn the evidence. (Assumes _all_ strings want free()ing.) */
|
||||
void free_prompts(prompts_t *p);
|
||||
|
||||
/*
|
||||
* Exports from the front end.
|
||||
*/
|
||||
@ -652,10 +698,17 @@ void ldisc_update(void *frontend, int echo, int edit);
|
||||
* shutdown. */
|
||||
void update_specials_menu(void *frontend);
|
||||
int from_backend(void *frontend, int is_stderr, const char *data, int len);
|
||||
int from_backend_untrusted(void *frontend, const char *data, int len);
|
||||
void notify_remote_exit(void *frontend);
|
||||
/* Get a sensible value for a tty mode. NULL return = don't set.
|
||||
* Otherwise, returned value should be freed by caller. */
|
||||
char *get_ttymode(void *frontend, const char *mode);
|
||||
/*
|
||||
* >0 = `got all results, carry on'
|
||||
* 0 = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?)
|
||||
* <0 = `please call back later with more in/inlen'
|
||||
*/
|
||||
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen);
|
||||
#define OPTIMISE_IS_SCROLL 1
|
||||
|
||||
void set_iconic(void *frontend, int iconic);
|
||||
@ -745,12 +798,15 @@ void term_copyall(Terminal *);
|
||||
void term_reconfig(Terminal *, Config *);
|
||||
void term_seen_key_event(Terminal *);
|
||||
int term_data(Terminal *, int is_stderr, const char *data, int len);
|
||||
int term_data_untrusted(Terminal *, const char *data, int len);
|
||||
void term_provide_resize_fn(Terminal *term,
|
||||
void (*resize_fn)(void *, int, int),
|
||||
void *resize_ctx);
|
||||
void term_provide_logctx(Terminal *term, void *logctx);
|
||||
void term_set_focus(Terminal *term, int has_focus);
|
||||
char *term_get_ttymode(Terminal *term, const char *mode);
|
||||
int term_get_userpass_input(Terminal *term, prompts_t *p,
|
||||
unsigned char *in, int inlen);
|
||||
|
||||
/*
|
||||
* Exports from logging.c.
|
||||
@ -800,14 +856,8 @@ extern Backend rlogin_backend;
|
||||
extern Backend telnet_backend;
|
||||
|
||||
/*
|
||||
* Exports from ssh.c. (NB the getline variables have to be GLOBAL
|
||||
* so that PuTTYtel will still compile - otherwise it would depend
|
||||
* on ssh.c.)
|
||||
* Exports from ssh.c.
|
||||
*/
|
||||
|
||||
GLOBAL int (*ssh_get_line) (const char *prompt, char *str, int maxlen,
|
||||
int is_pw);
|
||||
GLOBAL int ssh_getline_pw_only;
|
||||
extern Backend ssh_backend;
|
||||
|
||||
/*
|
||||
@ -952,11 +1002,11 @@ int askappend(void *frontend, Filename filename,
|
||||
void (*callback)(void *ctx, int result), void *ctx);
|
||||
|
||||
/*
|
||||
* Exports from console.c (that aren't equivalents to things in
|
||||
* windlg.c).
|
||||
* Exports from console frontends (wincons.c, uxcons.c)
|
||||
* that aren't equivalents to things in windlg.c et al.
|
||||
*/
|
||||
extern int console_batch_mode;
|
||||
int console_get_line(const char *prompt, char *str, int maxlen, int is_pw);
|
||||
int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen);
|
||||
void console_provide_logctx(void *logctx);
|
||||
int is_interactive(void);
|
||||
|
||||
@ -980,7 +1030,7 @@ void printer_finish_job(printer_job *);
|
||||
int cmdline_process_param(char *, char *, int, Config *);
|
||||
void cmdline_run_saved(Config *);
|
||||
void cmdline_cleanup(void);
|
||||
extern char *cmdline_password;
|
||||
int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen);
|
||||
#define TOOLTYPE_FILETRANSFER 1
|
||||
#define TOOLTYPE_NONNETWORK 2
|
||||
extern int cmdline_tooltype;
|
||||
|
143
terminal.c
143
terminal.c
@ -6257,6 +6257,24 @@ int term_data(Terminal *term, int is_stderr, const char *data, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write untrusted data to the terminal.
|
||||
* The only control character that should be honoured is \n (which
|
||||
* will behave as a CRLF).
|
||||
*/
|
||||
int term_data_untrusted(Terminal *term, const char *data, int len)
|
||||
{
|
||||
int i;
|
||||
/* FIXME: more sophisticated checking? */
|
||||
for (i = 0; i < len; i++) {
|
||||
if (data[i] == '\n')
|
||||
term_data(term, 1, "\r\n", 2);
|
||||
else if (data[i] & 0x60)
|
||||
term_data(term, 1, data + i, 1);
|
||||
}
|
||||
return 0; /* assumes that term_data() always returns 0 */
|
||||
}
|
||||
|
||||
void term_provide_logctx(Terminal *term, void *logctx)
|
||||
{
|
||||
term->logctx = logctx;
|
||||
@ -6281,3 +6299,128 @@ char *term_get_ttymode(Terminal *term, const char *mode)
|
||||
/* FIXME: perhaps we should set ONLCR based on cfg.lfhascr as well? */
|
||||
return dupstr(val);
|
||||
}
|
||||
|
||||
struct term_userpass_state {
|
||||
size_t curr_prompt;
|
||||
int done_prompt; /* printed out prompt yet? */
|
||||
size_t pos; /* cursor position */
|
||||
};
|
||||
|
||||
/*
|
||||
* Process some terminal data in the course of username/password
|
||||
* input.
|
||||
*/
|
||||
int term_get_userpass_input(Terminal *term, prompts_t *p,
|
||||
unsigned char *in, int inlen)
|
||||
{
|
||||
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);
|
||||
s->curr_prompt = 0;
|
||||
s->done_prompt = 0;
|
||||
/* We only print the `name' caption if we have to... */
|
||||
if (p->name_reqd && p->name) {
|
||||
size_t l = strlen(p->name);
|
||||
term_data_untrusted(term, p->name, l);
|
||||
if (p->name[l-1] != '\n')
|
||||
term_data_untrusted(term, "\n", 1);
|
||||
}
|
||||
/* ...but we always print any `instruction'. */
|
||||
if (p->instruction) {
|
||||
size_t l = strlen(p->instruction);
|
||||
term_data_untrusted(term, p->instruction, l);
|
||||
if (p->instruction[l-1] != '\n')
|
||||
term_data_untrusted(term, "\n", 1);
|
||||
}
|
||||
/*
|
||||
* Zero all the results, in case we abort half-way through.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < p->n_prompts; i++)
|
||||
memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);
|
||||
}
|
||||
}
|
||||
|
||||
while (s->curr_prompt < p->n_prompts) {
|
||||
|
||||
prompt_t *pr = p->prompts[s->curr_prompt];
|
||||
int finished_prompt = 0;
|
||||
|
||||
if (!s->done_prompt) {
|
||||
term_data_untrusted(term, pr->prompt, strlen(pr->prompt));
|
||||
s->done_prompt = 1;
|
||||
s->pos = 0;
|
||||
}
|
||||
|
||||
/* Breaking out here ensures that the prompt is printed even
|
||||
* if we're now waiting for user data. */
|
||||
if (!in || !inlen) break;
|
||||
|
||||
/* FIXME: should we be using local-line-editing code instead? */
|
||||
while (!finished_prompt && inlen) {
|
||||
char c = *in++;
|
||||
inlen--;
|
||||
switch (c) {
|
||||
case 10:
|
||||
case 13:
|
||||
term_data(term, 0, "\r\n", 2);
|
||||
pr->result[s->pos] = '\0';
|
||||
pr->result[pr->result_len - 1] = '\0';
|
||||
/* go to next prompt, if any */
|
||||
s->curr_prompt++;
|
||||
s->done_prompt = 0;
|
||||
finished_prompt = 1; /* break out */
|
||||
break;
|
||||
case 8:
|
||||
case 127:
|
||||
if (s->pos > 0) {
|
||||
if (pr->echo)
|
||||
term_data(term, 0, "\b \b", 3);
|
||||
s->pos--;
|
||||
}
|
||||
break;
|
||||
case 21:
|
||||
case 27:
|
||||
while (s->pos > 0) {
|
||||
if (pr->echo)
|
||||
term_data(term, 0, "\b \b", 3);
|
||||
s->pos--;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
/* Immediate abort. */
|
||||
term_data(term, 0, "\r\n", 2);
|
||||
sfree(s);
|
||||
return 0; /* user abort */
|
||||
default:
|
||||
/*
|
||||
* This simplistic check for printability is disabled
|
||||
* when we're doing password input, because some people
|
||||
* have control characters in their passwords.
|
||||
*/
|
||||
if ((!pr->echo ||
|
||||
(c >= ' ' && c <= '~') ||
|
||||
((unsigned char) c >= 160))
|
||||
&& s->pos < pr->result_len - 1) {
|
||||
pr->result[s->pos++] = c;
|
||||
if (pr->echo)
|
||||
term_data(term, 0, &c, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (s->curr_prompt < p->n_prompts) {
|
||||
return -1; /* more data required */
|
||||
} else {
|
||||
sfree(s);
|
||||
return +1; /* all done */
|
||||
}
|
||||
}
|
||||
|
@ -189,6 +189,22 @@ int from_backend(void *frontend, int is_stderr, const char *data, int len)
|
||||
return term_data(inst->term, is_stderr, data, len);
|
||||
}
|
||||
|
||||
int from_backend_untrusted(void *frontend, const char *data, int len)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)frontend;
|
||||
return term_data_untrusted(inst->term, data, len);
|
||||
}
|
||||
|
||||
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)p->frontend;
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p, in, inlen);
|
||||
if (ret == -1)
|
||||
ret = term_get_userpass_input(inst->term, p, in, inlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void logevent(void *frontend, const char *string)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)frontend;
|
||||
|
@ -280,41 +280,81 @@ void logevent(void *frontend, const char *string)
|
||||
log_eventlog(console_logctx, string);
|
||||
}
|
||||
|
||||
int console_get_line(const char *prompt, char *str,
|
||||
int maxlen, int is_pw)
|
||||
static void console_data_untrusted(const char *data, int len)
|
||||
{
|
||||
struct termios oldmode, newmode;
|
||||
int i;
|
||||
for (i = 0; i < len; i++)
|
||||
if ((data[i] & 0x60) || (data[i] == '\n'))
|
||||
fputc(data[i], stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if (console_batch_mode) {
|
||||
if (maxlen > 0)
|
||||
str[0] = '\0';
|
||||
int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
size_t curr_prompt;
|
||||
|
||||
/*
|
||||
* Zero all the results, in case we abort half-way through.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < p->n_prompts; i++)
|
||||
memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);
|
||||
}
|
||||
|
||||
if (console_batch_mode)
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Preamble.
|
||||
*/
|
||||
/* We only print the `name' caption if we have to... */
|
||||
if (p->name_reqd && p->name) {
|
||||
size_t l = strlen(p->name);
|
||||
console_data_untrusted(p->name, l);
|
||||
if (p->name[l-1] != '\n')
|
||||
console_data_untrusted("\n", 1);
|
||||
}
|
||||
/* ...but we always print any `instruction'. */
|
||||
if (p->instruction) {
|
||||
size_t l = strlen(p->instruction);
|
||||
console_data_untrusted(p->instruction, l);
|
||||
if (p->instruction[l-1] != '\n')
|
||||
console_data_untrusted("\n", 1);
|
||||
}
|
||||
|
||||
for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
|
||||
|
||||
struct termios oldmode, newmode;
|
||||
int i;
|
||||
prompt_t *pr = p->prompts[curr_prompt];
|
||||
|
||||
tcgetattr(0, &oldmode);
|
||||
newmode = oldmode;
|
||||
newmode.c_lflag |= ISIG | ICANON;
|
||||
if (is_pw)
|
||||
if (!pr->echo)
|
||||
newmode.c_lflag &= ~ECHO;
|
||||
else
|
||||
newmode.c_lflag |= ECHO;
|
||||
tcsetattr(0, TCSANOW, &newmode);
|
||||
|
||||
fputs(prompt, stdout);
|
||||
fflush(stdout);
|
||||
i = read(0, str, maxlen - 1);
|
||||
console_data_untrusted(pr->prompt, strlen(pr->prompt));
|
||||
|
||||
i = read(0, pr->result, pr->result_len - 1);
|
||||
|
||||
tcsetattr(0, TCSANOW, &oldmode);
|
||||
|
||||
if (i > 0 && str[i-1] == '\n')
|
||||
if (i > 0 && pr->result[i-1] == '\n')
|
||||
i--;
|
||||
str[i] = '\0';
|
||||
pr->result[i] = '\0';
|
||||
|
||||
if (is_pw)
|
||||
if (!pr->echo)
|
||||
fputs("\n", stdout);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1; /* success */
|
||||
|
||||
}
|
||||
|
||||
void frontend_keypress(void *handle)
|
||||
|
@ -394,6 +394,25 @@ int from_backend(void *frontend_handle, int is_stderr,
|
||||
return osize + esize;
|
||||
}
|
||||
|
||||
int from_backend_untrusted(void *frontend_handle, const char *data, int len)
|
||||
{
|
||||
/*
|
||||
* No "untrusted" output should get here (the way the code is
|
||||
* currently, it's all diverted by FLAG_STDERR).
|
||||
*/
|
||||
assert(!"Unexpected call to from_backend_untrusted()");
|
||||
return 0; /* not reached */
|
||||
}
|
||||
|
||||
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p, in, inlen);
|
||||
if (ret == -1)
|
||||
ret = console_get_userpass_input(p, in, inlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle data from a local tty in PARMRK format.
|
||||
*/
|
||||
@ -534,8 +553,6 @@ int main(int argc, char **argv)
|
||||
void *ldisc, *logctx;
|
||||
long now;
|
||||
|
||||
ssh_get_line = console_get_line;
|
||||
|
||||
fdlist = NULL;
|
||||
fdcount = fdsize = 0;
|
||||
/*
|
||||
|
@ -70,6 +70,15 @@ Filename platform_default_filename(const char *name)
|
||||
|
||||
char *get_ttymode(void *frontend, const char *mode) { return NULL; }
|
||||
|
||||
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p, in, inlen);
|
||||
if (ret == -1)
|
||||
ret = console_get_userpass_input(p, in, inlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stubs for the GUI feedback mechanism in Windows PSCP.
|
||||
*/
|
||||
|
@ -298,48 +298,90 @@ void logevent(void *frontend, const char *string)
|
||||
log_eventlog(console_logctx, string);
|
||||
}
|
||||
|
||||
int console_get_line(const char *prompt, char *str,
|
||||
int maxlen, int is_pw)
|
||||
static void console_data_untrusted(HANDLE hout, const char *data, int len)
|
||||
{
|
||||
DWORD dummy;
|
||||
/* FIXME: control-character filtering */
|
||||
WriteFile(hout, data, len, &dummy, NULL);
|
||||
}
|
||||
|
||||
int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
HANDLE hin, hout;
|
||||
DWORD savemode, newmode, i;
|
||||
size_t curr_prompt;
|
||||
|
||||
if (console_batch_mode) {
|
||||
if (maxlen > 0)
|
||||
str[0] = '\0';
|
||||
/*
|
||||
* Zero all the results, in case we abort half-way through.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < p->n_prompts; i++)
|
||||
memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);
|
||||
}
|
||||
|
||||
if (console_batch_mode)
|
||||
return 0;
|
||||
} else {
|
||||
hin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
hout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
|
||||
fprintf(stderr, "Cannot get standard input/output handles\n");
|
||||
cleanup_exit(1);
|
||||
}
|
||||
|
||||
hin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
hout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
|
||||
fprintf(stderr, "Cannot get standard input/output handles\n");
|
||||
cleanup_exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Preamble.
|
||||
*/
|
||||
/* We only print the `name' caption if we have to... */
|
||||
if (p->name_reqd && p->name) {
|
||||
size_t l = strlen(p->name);
|
||||
console_data_untrusted(hout, p->name, l);
|
||||
if (p->name[l-1] != '\n')
|
||||
console_data_untrusted(hout, "\n", 1);
|
||||
}
|
||||
/* ...but we always print any `instruction'. */
|
||||
if (p->instruction) {
|
||||
size_t l = strlen(p->instruction);
|
||||
console_data_untrusted(hout, p->instruction, l);
|
||||
if (p->instruction[l-1] != '\n')
|
||||
console_data_untrusted(hout, "\n", 1);
|
||||
}
|
||||
|
||||
for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
|
||||
|
||||
DWORD savemode, newmode, i = 0;
|
||||
prompt_t *pr = p->prompts[curr_prompt];
|
||||
BOOL r;
|
||||
|
||||
GetConsoleMode(hin, &savemode);
|
||||
newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
|
||||
if (is_pw)
|
||||
if (!pr->echo)
|
||||
newmode &= ~ENABLE_ECHO_INPUT;
|
||||
else
|
||||
newmode |= ENABLE_ECHO_INPUT;
|
||||
SetConsoleMode(hin, newmode);
|
||||
|
||||
WriteFile(hout, prompt, strlen(prompt), &i, NULL);
|
||||
ReadFile(hin, str, maxlen - 1, &i, NULL);
|
||||
console_data_untrusted(hout, pr->prompt, strlen(pr->prompt));
|
||||
|
||||
r = ReadFile(hin, pr->result, pr->result_len - 1, &i, NULL);
|
||||
|
||||
SetConsoleMode(hin, savemode);
|
||||
|
||||
if ((int) i > maxlen)
|
||||
i = maxlen - 1;
|
||||
if ((int) i > pr->result_len)
|
||||
i = pr->result_len - 1;
|
||||
else
|
||||
i = i - 2;
|
||||
str[i] = '\0';
|
||||
pr->result[i] = '\0';
|
||||
|
||||
if (is_pw)
|
||||
WriteFile(hout, "\r\n", 2, &i, NULL);
|
||||
if (!pr->echo) {
|
||||
DWORD dummy;
|
||||
WriteFile(hout, "\r\n", 2, &dummy, NULL);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1; /* success */
|
||||
|
||||
}
|
||||
|
||||
void frontend_keypress(void *handle)
|
||||
|
@ -5069,6 +5069,20 @@ int from_backend(void *frontend, int is_stderr, const char *data, int len)
|
||||
return term_data(term, is_stderr, data, len);
|
||||
}
|
||||
|
||||
int from_backend_untrusted(void *frontend, const char *data, int len)
|
||||
{
|
||||
return term_data_untrusted(term, data, len);
|
||||
}
|
||||
|
||||
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p, in, inlen);
|
||||
if (ret == -1)
|
||||
ret = term_get_userpass_input(term, p, in, inlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void agent_schedule_callback(void (*callback)(void *, void *, int),
|
||||
void *callback_ctx, void *data, int len)
|
||||
{
|
||||
|
@ -189,6 +189,25 @@ int from_backend(void *frontend_handle, int is_stderr,
|
||||
return osize + esize;
|
||||
}
|
||||
|
||||
int from_backend_untrusted(void *frontend_handle, const char *data, int len)
|
||||
{
|
||||
/*
|
||||
* No "untrusted" output should get here (the way the code is
|
||||
* currently, it's all diverted by FLAG_STDERR).
|
||||
*/
|
||||
assert(!"Unexpected call to from_backend_untrusted()");
|
||||
return 0; /* not reached */
|
||||
}
|
||||
|
||||
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p, in, inlen);
|
||||
if (ret == -1)
|
||||
ret = console_get_userpass_input(p, in, inlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DWORD main_thread_id;
|
||||
|
||||
void agent_schedule_callback(void (*callback)(void *, void *, int),
|
||||
@ -285,8 +304,6 @@ int main(int argc, char **argv)
|
||||
int use_subsystem = 0;
|
||||
long now, next;
|
||||
|
||||
ssh_get_line = console_get_line;
|
||||
|
||||
sklist = NULL;
|
||||
skcount = sksize = 0;
|
||||
/*
|
||||
|
@ -127,6 +127,15 @@ void gui_enable(char *arg)
|
||||
|
||||
char *get_ttymode(void *frontend, const char *mode) { return NULL; }
|
||||
|
||||
int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
|
||||
{
|
||||
int ret;
|
||||
ret = cmdline_get_passwd_input(p, in, inlen);
|
||||
if (ret == -1)
|
||||
ret = console_get_userpass_input(p, in, inlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* File access abstraction.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user