diff --git a/cgtest.c b/cgtest.c index ba87892c..f0655b98 100644 --- a/cgtest.c +++ b/cgtest.c @@ -65,10 +65,10 @@ char *get_random_data_diagnostic(int len, const char *device) static int nprompts, promptsgot; static const char *prompts[3]; -int console_get_userpass_input_diagnostic(prompts_t *p) +SeatPromptResult console_get_userpass_input_diagnostic(prompts_t *p) { size_t i; - int ret = 1; + SeatPromptResult ret = SPR_OK; for (i = 0; i < p->n_prompts; i++) { if (promptsgot < nprompts) { prompt_set_result(p->prompts[i], prompts[promptsgot++]); @@ -77,7 +77,7 @@ int console_get_userpass_input_diagnostic(prompts_t *p) p->prompts[i]->prompt, p->prompts[i]->result->s); } else { promptsgot++; /* track number of requests anyway */ - ret = 0; + ret = SPR_SW_ABORT("preloaded prompt unavailable in cgtest"); if (cgtest_verbose) printf(" prompt \"%s\": no response preloaded\n", p->prompts[i]->prompt); diff --git a/cmdgen.c b/cmdgen.c index be40fe1d..e0006efc 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -214,6 +214,15 @@ static char *readpassphrase(const char *filename) #define DEFAULT_RSADSA_BITS 2048 +static void spr_error(SeatPromptResult spr) +{ + if (spr.kind == SPRK_SW_ABORT) { + char *err = spr_get_error_message(spr); + fprintf(stderr, "puttygen: unable to read passphrase: %s", err); + sfree(err); + } +} + /* For Unix in particular, but harmless if this main() is reused elsewhere */ const bool buildinfo_gtk_relevant = false; @@ -941,16 +950,16 @@ int main(int argc, char **argv) if (encrypted && load_encrypted) { if (!old_passphrase) { prompts_t *p = new_prompts(); - int ret; + SeatPromptResult spr; p->to_server = false; p->from_server = false; p->name = dupstr("SSH key passphrase"); add_prompt(p, dupstr("Enter passphrase to load key: "), false); - ret = console_get_userpass_input(p); - assert(ret >= 0); - if (!ret) { + spr = console_get_userpass_input(p); + assert(spr.kind != SPRK_INCOMPLETE); + if (spr_is_abort(spr)) { free_prompts(p); - perror("puttygen: unable to read passphrase"); + spr_error(spr); RETURN(1); } else { old_passphrase = prompt_get_result(p->prompts[0]); @@ -1090,18 +1099,18 @@ int main(int argc, char **argv) if (!new_passphrase && (change_passphrase || (keytype != NOKEYGEN && outtype != TEXT))) { prompts_t *p = new_prompts(); - int ret; + SeatPromptResult spr; p->to_server = false; p->from_server = false; p->name = dupstr("New SSH key passphrase"); add_prompt(p, dupstr("Enter passphrase to save key: "), false); add_prompt(p, dupstr("Re-enter passphrase to verify: "), false); - ret = console_get_userpass_input(p); - assert(ret >= 0); - if (!ret) { + spr = console_get_userpass_input(p); + assert(spr.kind != SPRK_INCOMPLETE); + if (spr_is_abort(spr)) { free_prompts(p); - perror("puttygen: unable to read new passphrase"); + spr_error(spr); RETURN(1); } else { if (strcmp(prompt_get_result_ref(p->prompts[0]), diff --git a/cmdline.c b/cmdline.c index d87c14d0..c39c8ad2 100644 --- a/cmdline.c +++ b/cmdline.c @@ -81,7 +81,7 @@ void cmdline_cleanup(void) * -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) +SeatPromptResult cmdline_get_passwd_input(prompts_t *p) { static bool tried_once = false; @@ -91,7 +91,7 @@ int cmdline_get_passwd_input(prompts_t *p) * that comes in a prompt-set on its own. */ if (p->n_prompts != 1 || p->prompts[0]->echo) { - return -1; + return SPR_INCOMPLETE; } /* @@ -99,7 +99,7 @@ int cmdline_get_passwd_input(prompts_t *p) * to try). */ if (tried_once) - return 0; + return SPR_SW_ABORT("Configured password was not accepted"); /* * If we never had a password available in the first place, we @@ -108,14 +108,14 @@ int cmdline_get_passwd_input(prompts_t *p) * we'll still remember that we _used_ to have one.) */ if (!cmdline_password) - return -1; + return SPR_INCOMPLETE; prompt_set_result(p->prompts[0], cmdline_password); smemclr(cmdline_password, strlen(cmdline_password)); sfree(cmdline_password); cmdline_password = NULL; tried_once = true; - return 1; + return SPR_OK; } static bool cmdline_check_unavailable(int flag, const char *p) diff --git a/defs.h b/defs.h index 039341fe..45bc90f3 100644 --- a/defs.h +++ b/defs.h @@ -114,6 +114,7 @@ typedef struct LogPolicyVtable LogPolicyVtable; typedef struct Seat Seat; typedef struct SeatVtable SeatVtable; +typedef struct SeatPromptResult SeatPromptResult; typedef struct TermWin TermWin; typedef struct TermWinVtable TermWinVtable; diff --git a/otherbackends/rlogin.c b/otherbackends/rlogin.c index 59a833a0..37087257 100644 --- a/otherbackends/rlogin.c +++ b/otherbackends/rlogin.c @@ -35,7 +35,7 @@ struct Rlogin { Interactor interactor; }; -static void rlogin_startup(Rlogin *rlogin, int prompt_result, +static void rlogin_startup(Rlogin *rlogin, SeatPromptResult spr, const char *ruser); static void rlogin_try_username_prompt(void *ctx); @@ -65,7 +65,7 @@ static void rlogin_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, */ /* Next terminal output will come from server */ seat_set_trust_status(rlogin->seat, false); - rlogin_startup(rlogin, 1, ruser); + rlogin_startup(rlogin, SPR_OK, ruser); sfree(ruser); } else { /* @@ -160,17 +160,25 @@ static void rlogin_sent(Plug *plug, size_t bufsize) seat_sent(rlogin->seat, rlogin->bufsize); } -static void rlogin_startup(Rlogin *rlogin, int prompt_result, +static void rlogin_startup(Rlogin *rlogin, SeatPromptResult spr, const char *ruser) { char z = 0; char *p; - if (prompt_result == 0) { + if (spr.kind == SPRK_USER_ABORT) { /* User aborted at the username prompt. */ sk_close(rlogin->s); rlogin->s = NULL; seat_notify_remote_exit(rlogin->seat); + } else if (spr.kind == SPRK_SW_ABORT) { + /* Something else went wrong at the username prompt, so we + * have to show some kind of error. */ + sk_close(rlogin->s); + rlogin->s = NULL; + char *err = spr_get_error_message(spr); + seat_connection_fatal(rlogin->seat, "%s", err); + sfree(err); } else { sk_write(rlogin->s, &z, 1); p = conf_get_str(rlogin->conf, CONF_localusername); @@ -332,9 +340,9 @@ static void rlogin_try_username_prompt(void *ctx) { Rlogin *rlogin = (Rlogin *)ctx; - int ret = seat_get_userpass_input( + SeatPromptResult spr = seat_get_userpass_input( interactor_announce(&rlogin->interactor), rlogin->prompt); - if (ret < 0) + if (spr.kind == SPRK_INCOMPLETE) return; /* Next terminal output will come from server */ @@ -345,7 +353,7 @@ static void rlogin_try_username_prompt(void *ctx) * rlogin_startup will signal to rlogin_sendok by nulling out * rlogin->prompt. */ rlogin_startup( - rlogin, ret, prompt_get_result_ref(rlogin->prompt->prompts[0])); + rlogin, spr, prompt_get_result_ref(rlogin->prompt->prompts[0])); } /* diff --git a/proxy/http.c b/proxy/http.c index e8c66400..f788e52c 100644 --- a/proxy/http.c +++ b/proxy/http.c @@ -655,12 +655,12 @@ static void proxy_http_process_queue(ProxyNegotiator *pn) add_prompt(s->prompts, dupstr("Proxy password: "), false); while (true) { - int prompt_result = seat_get_userpass_input( + SeatPromptResult spr = seat_get_userpass_input( interactor_announce(pn->itr), s->prompts); - if (prompt_result > 0) { + if (spr.kind == SPRK_OK) { break; - } else if (prompt_result == 0) { - pn->aborted = true; + } else if (spr_is_abort(spr)) { + proxy_spr_abort(pn, spr); crStopV; } crReturnV; diff --git a/proxy/local.c b/proxy/local.c index 278d0b12..3b2d130c 100644 --- a/proxy/local.c +++ b/proxy/local.c @@ -149,16 +149,22 @@ static void local_proxy_opener_coroutine(void *vctx) } while (true) { - int prompt_result = seat_get_userpass_input( + SeatPromptResult spr = seat_get_userpass_input( interactor_announce(&lp->interactor), lp->prompts); - if (prompt_result > 0) { + if (spr.kind == SPRK_OK) { break; - } else if (prompt_result == 0) { + } else if (spr.kind == SPRK_USER_ABORT) { local_proxy_opener_cleanup_interactor(lp); plug_closing_user_abort(lp->plug); /* That will have freed us, so we must just return * without calling any crStop */ return; + } else if (spr.kind == SPRK_SW_ABORT) { + local_proxy_opener_cleanup_interactor(lp); + char *err = spr_get_error_message(spr); + plug_closing_error(lp->plug, err); + sfree(err); + return; /* without crStop, as above */ } crReturnV; } diff --git a/proxy/proxy.c b/proxy/proxy.c index b228bdb7..36105097 100644 --- a/proxy/proxy.c +++ b/proxy/proxy.c @@ -463,6 +463,16 @@ prompts_t *proxy_new_prompts(ProxySocket *ps) return prs; } +void proxy_spr_abort(ProxyNegotiator *pn, SeatPromptResult spr) +{ + if (spr.kind == SPRK_SW_ABORT) { + pn->error = spr_get_error_message(spr); + } else { + assert(spr.kind == SPRK_USER_ABORT); + pn->aborted = true; + } +} + Socket *new_connection(SockAddr *addr, const char *hostname, int port, bool privport, bool oobinline, bool nodelay, bool keepalive, diff --git a/proxy/proxy.h b/proxy/proxy.h index aacc1cb1..ca4a293f 100644 --- a/proxy/proxy.h +++ b/proxy/proxy.h @@ -91,10 +91,11 @@ extern const ProxyNegotiatorVT socks5_proxy_negotiator_vt; extern const ProxyNegotiatorVT telnet_proxy_negotiator_vt; /* - * Centralised function to allow ProxyNegotiators to get hold of a - * prompts_t. + * Centralised functions to allow ProxyNegotiators to get hold of a + * prompts_t, and to deal with SeatPromptResults coming back. */ prompts_t *proxy_new_prompts(ProxySocket *ps); +void proxy_spr_abort(ProxyNegotiator *pn, SeatPromptResult spr); /* * This may be reused by local-command proxies on individual diff --git a/proxy/socks5.c b/proxy/socks5.c index eb858b37..66797440 100644 --- a/proxy/socks5.c +++ b/proxy/socks5.c @@ -191,12 +191,12 @@ static void proxy_socks5_process_queue(ProxyNegotiator *pn) } while (true) { - int prompt_result = seat_get_userpass_input( + SeatPromptResult spr = seat_get_userpass_input( interactor_announce(pn->itr), s->prompts); - if (prompt_result > 0) { + if (spr.kind == SPRK_OK) { break; - } else if (prompt_result == 0) { - pn->aborted = true; + } else if (spr_is_abort(spr)) { + proxy_spr_abort(pn, spr); crStopV; } crReturnV; diff --git a/proxy/sshproxy.c b/proxy/sshproxy.c index 49d1ea98..bc514452 100644 --- a/proxy/sshproxy.c +++ b/proxy/sshproxy.c @@ -330,7 +330,7 @@ static void sshproxy_notify_remote_disconnect(Seat *seat) queue_toplevel_callback(sshproxy_notify_remote_disconnect_callback, sp); } -static int sshproxy_get_userpass_input(Seat *seat, prompts_t *p) +static SeatPromptResult sshproxy_get_userpass_input(Seat *seat, prompts_t *p) { SshProxy *sp = container_of(seat, SshProxy, seat); @@ -349,7 +349,8 @@ static int sshproxy_get_userpass_input(Seat *seat, prompts_t *p) */ sshproxy_error(sp, "Unable to provide interactive authentication " "requested by proxy SSH connection"); - return 0; + return SPR_SW_ABORT("Noninteractive SSH proxy cannot perform " + "interactive authentication"); } static void sshproxy_connection_fatal_callback(void *vctx) @@ -368,10 +369,10 @@ static void sshproxy_connection_fatal(Seat *seat, const char *message) } } -static int sshproxy_confirm_ssh_host_key( +static SeatPromptResult sshproxy_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { SshProxy *sp = container_of(seat, SshProxy, seat); @@ -390,12 +391,12 @@ static int sshproxy_confirm_ssh_host_key( * option in the absence of interactive confirmation, i.e. abort * the connection. */ - return 0; + return SPR_SW_ABORT("Noninteractive SSH proxy cannot confirm host key"); } -static int sshproxy_confirm_weak_crypto_primitive( +static SeatPromptResult sshproxy_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { SshProxy *sp = container_of(seat, SshProxy, seat); @@ -415,12 +416,13 @@ static int sshproxy_confirm_weak_crypto_primitive( sshproxy_error(sp, "First %s supported by server is %s, below warning " "threshold. Abandoning proxy SSH connection.", algtype, algname); - return 0; + return SPR_SW_ABORT("Noninteractive SSH proxy cannot confirm " + "weak crypto primitive"); } -static int sshproxy_confirm_weak_cached_hostkey( +static SeatPromptResult sshproxy_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { SshProxy *sp = container_of(seat, SshProxy, seat); @@ -440,7 +442,8 @@ static int sshproxy_confirm_weak_cached_hostkey( sshproxy_error(sp, "First host key type stored for server is %s, below " "warning threshold. Abandoning proxy SSH connection.", algname); - return 0; + return SPR_SW_ABORT("Noninteractive SSH proxy cannot confirm " + "weak cached host key"); } static StripCtrlChars *sshproxy_stripctrl_new( diff --git a/proxy/telnet.c b/proxy/telnet.c index 1b4a5326..242c4025 100644 --- a/proxy/telnet.c +++ b/proxy/telnet.c @@ -288,12 +288,12 @@ static void proxy_telnet_process_queue(ProxyNegotiator *pn) crReturnV; while (true) { - int prompt_result = seat_get_userpass_input( + SeatPromptResult spr = seat_get_userpass_input( interactor_announce(pn->itr), s->prompts); - if (prompt_result > 0) { + if (spr.kind == SPRK_OK) { break; - } else if (prompt_result == 0) { - pn->aborted = true; + } else if (spr_is_abort(spr)) { + proxy_spr_abort(pn, spr); crStopV; } crReturnV; diff --git a/putty.h b/putty.h index c4e8d6a1..edb5eb4c 100644 --- a/putty.h +++ b/putty.h @@ -861,6 +861,82 @@ extern const char *const appname; */ typedef void (*toplevel_callback_fn_t)(void *ctx); +/* Enum of result types in SeatPromptResult below */ +typedef enum SeatPromptResultKind { + /* Answer not yet available at all; either try again later or wait + * for a callback (depending on the request's API) */ + SPRK_INCOMPLETE, + + /* We're abandoning the connection because the user interactively + * told us to. (Hence, no need to present an error message + * telling the user we're doing that: they already know.) */ + SPRK_USER_ABORT, + + /* We're abandoning the connection for some other reason (e.g. we + * were unable to present the prompt at all, or a batch-mode + * configuration told us to give the answer no). This may + * ultimately have stemmed from some user configuration, but they + * didn't _tell us right now_ to abandon this connection, so we + * still need to inform them that we've done so. */ + SPRK_SW_ABORT, + + /* We're proceeding with the connection and have all requested + * information (if any) */ + SPRK_OK +} SeatPromptResultKind; + +/* Small struct to present the results of interactive requests from + * backend to Seat (see below) */ +struct SeatPromptResult { + SeatPromptResultKind kind; + + /* + * In the case of SPRK_SW_ABORT, the frontend provides an error + * message to present to the user. But dynamically allocating it + * up front would mean having to make sure it got freed at any + * call site where one of these structs is received (and freed + * _once_ no matter how many times the struct is copied). So + * instead we provide a function that will generate the error + * message into a BinarySink. + */ + void (*errfn)(SeatPromptResult, BinarySink *); + + /* + * And some fields the error function can use to construct the + * message (holding, e.g. an OS error code). + */ + const char *errdata_lit; /* statically allocated, e.g. a string literal */ + unsigned errdata_u; +}; + +/* Helper function to construct the simple versions of these + * structures inline */ +static inline SeatPromptResult make_spr_simple(SeatPromptResultKind kind) +{ + SeatPromptResult spr; + spr.kind = kind; + spr.errdata_lit = NULL; + return spr; +} + +/* Most common constructor function for SPRK_SW_ABORT errors */ +SeatPromptResult make_spr_sw_abort_static(const char *); + +/* Convenience macros wrapping those constructors in turn */ +#define SPR_INCOMPLETE make_spr_simple(SPRK_INCOMPLETE) +#define SPR_USER_ABORT make_spr_simple(SPRK_USER_ABORT) +#define SPR_SW_ABORT(lit) make_spr_sw_abort_static(lit) +#define SPR_OK make_spr_simple(SPRK_OK) + +/* Query function that folds both kinds of abort together */ +static inline bool spr_is_abort(SeatPromptResult spr) +{ + return spr.kind == SPRK_USER_ABORT || spr.kind == SPRK_SW_ABORT; +} + +/* Function to return a dynamically allocated copy of the error message */ +char *spr_get_error_message(SeatPromptResult spr); + /* * Mechanism for getting text strings such as usernames and passwords * from the front-end. @@ -911,7 +987,7 @@ struct prompts_t { 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 */ + SeatPromptResult spr; /* some implementations need to cache one of these */ /* * Callback you can fill in to be notified when all the prompts' @@ -1062,13 +1138,8 @@ struct SeatVtable { * Try to get answers from a set of interactive login prompts. The * 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 - * deliberate 'cancel' UI action. A negative return means that no - * answer can be given yet but please try again later. - * - * (FIXME: it would be nice to distinguish two classes of cancel - * action, so the user could specify 'I want to abandon this + * (FIXME: it would be nice to distinguish two classes of user- + * abort action, so the user could specify 'I want to abandon this * entire attempt to start a session' or the milder 'I want to * abandon this particular form of authentication and fall back to * a different one' - e.g. if you turn out not to be able to @@ -1076,7 +1147,7 @@ struct SeatVtable { * fall back to password auth rather than aborting the whole * session.) */ - int (*get_userpass_input)(Seat *seat, prompts_t *p); + SeatPromptResult (*get_userpass_input)(Seat *seat, prompts_t *p); /* * Notify the seat that the main session channel has been @@ -1185,10 +1256,11 @@ struct SeatVtable { * back via the provided function with a result that's either 0 * or +1'. */ - int (*confirm_ssh_host_key)( + SeatPromptResult (*confirm_ssh_host_key)( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, - bool mismatch, void (*callback)(void *ctx, int result), void *ctx); + bool mismatch, void (*callback)(void *ctx, SeatPromptResult result), + void *ctx); /* * Check with the seat whether it's OK to use a cryptographic @@ -1196,9 +1268,9 @@ struct SeatVtable { * the input Conf. Return values are the same as * confirm_ssh_host_key above. */ - int (*confirm_weak_crypto_primitive)( + SeatPromptResult (*confirm_weak_crypto_primitive)( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx); + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); /* * Variant form of confirm_weak_crypto_primitive, which prints a @@ -1211,9 +1283,9 @@ struct SeatVtable { * threshold is available that we don't have cached. 'betteralgs' * lists the better algorithm(s). */ - int (*confirm_weak_cached_hostkey)( + SeatPromptResult (*confirm_weak_cached_hostkey)( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx); + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); /* * Indicates whether the seat is expecting to interact with the @@ -1319,8 +1391,8 @@ static inline void seat_sent(Seat *seat, size_t bufsize) static inline size_t seat_banner( InteractionReadySeat iseat, const void *data, size_t len) { return iseat.seat->vt->banner(iseat.seat, data, len); } -static inline int seat_get_userpass_input(InteractionReadySeat iseat, - prompts_t *p) +static inline SeatPromptResult seat_get_userpass_input( + InteractionReadySeat iseat, prompts_t *p) { return iseat.seat->vt->get_userpass_input(iseat.seat, p); } static inline void seat_notify_session_started(Seat *seat) { seat->vt->notify_session_started(seat); } @@ -1334,20 +1406,20 @@ static inline char *seat_get_ttymode(Seat *seat, const char *mode) { return seat->vt->get_ttymode(seat, mode); } static inline void seat_set_busy_status(Seat *seat, BusyStatus status) { seat->vt->set_busy_status(seat, status); } -static inline int seat_confirm_ssh_host_key( +static inline SeatPromptResult seat_confirm_ssh_host_key( InteractionReadySeat iseat, const char *h, int p, const char *ktyp, char *kstr, const char *kdsp, char **fps, bool mis, - void (*cb)(void *ctx, int result), void *ctx) + void (*cb)(void *ctx, SeatPromptResult result), void *ctx) { return iseat.seat->vt->confirm_ssh_host_key( iseat.seat, h, p, ktyp, kstr, kdsp, fps, mis, cb, ctx); } -static inline int seat_confirm_weak_crypto_primitive( +static inline SeatPromptResult seat_confirm_weak_crypto_primitive( InteractionReadySeat iseat, const char *atyp, const char *aname, - void (*cb)(void *ctx, int result), void *ctx) + void (*cb)(void *ctx, SeatPromptResult result), void *ctx) { return iseat.seat->vt->confirm_weak_crypto_primitive( iseat.seat, atyp, aname, cb, ctx); } -static inline int seat_confirm_weak_cached_hostkey( +static inline SeatPromptResult seat_confirm_weak_cached_hostkey( InteractionReadySeat iseat, const char *aname, const char *better, - void (*cb)(void *ctx, int result), void *ctx) + void (*cb)(void *ctx, SeatPromptResult result), void *ctx) { return iseat.seat->vt->confirm_weak_cached_hostkey( iseat.seat, aname, better, cb, ctx); } static inline bool seat_is_utf8(Seat *seat) @@ -1412,7 +1484,7 @@ bool nullseat_eof(Seat *seat); void nullseat_sent(Seat *seat, size_t bufsize); size_t nullseat_banner(Seat *seat, const void *data, size_t len); size_t nullseat_banner_to_stderr(Seat *seat, const void *data, size_t len); -int nullseat_get_userpass_input(Seat *seat, prompts_t *p); +SeatPromptResult 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); @@ -1420,16 +1492,16 @@ void nullseat_connection_fatal(Seat *seat, const char *message); void nullseat_update_specials_menu(Seat *seat); char *nullseat_get_ttymode(Seat *seat, const char *mode); void nullseat_set_busy_status(Seat *seat, BusyStatus status); -int nullseat_confirm_ssh_host_key( +SeatPromptResult nullseat_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx); -int nullseat_confirm_weak_crypto_primitive( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); +SeatPromptResult nullseat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx); -int nullseat_confirm_weak_cached_hostkey( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); +SeatPromptResult nullseat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx); + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); bool nullseat_is_never_utf8(Seat *seat); bool nullseat_is_always_utf8(Seat *seat); void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing); @@ -1455,16 +1527,16 @@ bool nullseat_get_cursor_position(Seat *seat, int *x, int *y); */ void console_connection_fatal(Seat *seat, const char *message); -int console_confirm_ssh_host_key( +SeatPromptResult console_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx); -int console_confirm_weak_crypto_primitive( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); +SeatPromptResult console_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx); -int console_confirm_weak_cached_hostkey( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); +SeatPromptResult console_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx); + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); StripCtrlChars *console_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic); void console_set_trust_status(Seat *seat, bool trusted); @@ -1474,7 +1546,7 @@ bool console_has_mixed_input_stream(Seat *seat); /* * Other centralised seat functions. */ -int filexfer_get_userpass_input(Seat *seat, prompts_t *p); +SeatPromptResult filexfer_get_userpass_input(Seat *seat, prompts_t *p); bool cmdline_seat_verbose(Seat *seat); /* @@ -2080,7 +2152,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); +SeatPromptResult 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); @@ -2424,7 +2496,7 @@ bool have_ssh_host_key(const char *host, int port, const char *keytype); * that aren't equivalents to things in windlg.c et al. */ extern bool console_batch_mode, console_antispoof_prompt; -int console_get_userpass_input(prompts_t *p); +SeatPromptResult console_get_userpass_input(prompts_t *p); bool is_interactive(void); void console_print_error_msg(const char *prefix, const char *msg); void console_print_error_msg_fmt_v( @@ -2457,7 +2529,7 @@ void printer_finish_job(printer_job *); int cmdline_process_param(const char *, char *, int, Conf *); void cmdline_run_saved(Conf *); void cmdline_cleanup(void); -int cmdline_get_passwd_input(prompts_t *p); +SeatPromptResult cmdline_get_passwd_input(prompts_t *p); bool cmdline_host_ok(Conf *); bool cmdline_verbose(void); bool cmdline_loaded_session(void); diff --git a/ssh.h b/ssh.h index 294afada..0c2edf33 100644 --- a/ssh.h +++ b/ssh.h @@ -423,6 +423,7 @@ void ssh_proto_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); void ssh_sw_abort(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); void ssh_user_close(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); +void ssh_spr_close(Ssh *ssh, SeatPromptResult spr, const char *context); /* Bit positions in the SSH-1 cipher protocol word */ #define SSH1_CIPHER_IDEA 1 @@ -1709,10 +1710,11 @@ unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset); void add_to_commasep(strbuf *buf, const char *data); bool get_commasep_word(ptrlen *list, ptrlen *word); -int verify_ssh_host_key( +SeatPromptResult verify_ssh_host_key( InteractionReadySeat iseat, Conf *conf, const char *host, int port, ssh_key *key, const char *keytype, char *keystr, const char *keydisp, - char **fingerprints, void (*callback)(void *ctx, int result), void *ctx); + char **fingerprints, void (*callback)(void *ctx, SeatPromptResult result), + void *ctx); typedef struct ssh_transient_hostkey_cache ssh_transient_hostkey_cache; ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void); diff --git a/ssh/common.c b/ssh/common.c index c7d98061..0c9f51e3 100644 --- a/ssh/common.c +++ b/ssh/common.c @@ -845,15 +845,13 @@ bool ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin) * host key is already known. If so, it returns success on its own * account; otherwise, it calls out to the Seat to give an interactive * prompt (the nature of which varies depending on the Seat itself). - * - * Return values are 0 for 'abort connection', 1 for 'ok, carry on', - * and negative for 'answer not received yet, wait for a callback'. */ -int verify_ssh_host_key( +SeatPromptResult verify_ssh_host_key( InteractionReadySeat iseat, Conf *conf, const char *host, int port, ssh_key *key, const char *keytype, char *keystr, const char *keydisp, - char **fingerprints, void (*callback)(void *ctx, int result), void *ctx) + char **fingerprints, void (*callback)(void *ctx, SeatPromptResult result), + void *ctx) { /* * First, check if the Conf includes a manual specification of the @@ -878,7 +876,7 @@ int verify_ssh_host_key( fingerprint = p ? p+1 : fingerprint; if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys, fingerprint)) - return 1; /* success */ + return SPR_OK; } } @@ -902,12 +900,12 @@ int verify_ssh_host_key( if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys, base64blob)) { sfree(base64blob); - return 1; /* success */ + return SPR_OK; } sfree(base64blob); } - return 0; + return SPR_SW_ABORT("Host key not in manually configured list"); } /* @@ -915,7 +913,7 @@ int verify_ssh_host_key( */ int storage_status = check_stored_host_key(host, port, keytype, keystr); if (storage_status == 0) /* matching key was found in the cache */ - return 1; /* success */ + return SPR_OK; /* * The key is either missing from the cache, or does not match. @@ -994,3 +992,22 @@ void ssh1_compute_session_id( put_data(hash, cookie, 8); ssh_hash_final(hash, session_id); } + +/* ---------------------------------------------------------------------- + * Wrapper function to handle the abort-connection modes of a + * SeatPromptResult without a lot of verbiage at every call site. + * + * Can become ssh_sw_abort or ssh_user_close, depending on the kind of + * negative SeatPromptResult. + */ +void ssh_spr_close(Ssh *ssh, SeatPromptResult spr, const char *context) +{ + if (spr.kind == SPRK_USER_ABORT) { + ssh_user_close(ssh, "User aborted at %s", context); + } else { + assert(spr.kind == SPRK_SW_ABORT); + char *err = spr_get_error_message(spr); + ssh_sw_abort(ssh, "%s", err); + sfree(err); + } +} diff --git a/ssh/connection1.c b/ssh/connection1.c index 47ffd3c7..0b5485f8 100644 --- a/ssh/connection1.c +++ b/ssh/connection1.c @@ -381,7 +381,7 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl) dupstr("Access granted. Press Return to begin session. "), false); s->antispoof_ret = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->antispoof_prompt); - while (s->antispoof_ret < 0) { + while (s->antispoof_ret.kind == SPRK_INCOMPLETE) { crReturnV; s->antispoof_ret = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->antispoof_prompt); diff --git a/ssh/connection1.h b/ssh/connection1.h index 7d136d60..ff370131 100644 --- a/ssh/connection1.h +++ b/ssh/connection1.h @@ -50,7 +50,7 @@ struct ssh1_connection_state { bool sent_exit_status; /* also for server mode */ prompts_t *antispoof_prompt; - int antispoof_ret; + SeatPromptResult antispoof_ret; const SshServerConfig *ssc; diff --git a/ssh/connection2.c b/ssh/connection2.c index 86bd315a..fb24b7c6 100644 --- a/ssh/connection2.c +++ b/ssh/connection2.c @@ -994,7 +994,7 @@ static void ssh2_connection_process_queue(PacketProtocolLayer *ppl) dupstr("Access granted. Press Return to begin session. "), false); s->antispoof_ret = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->antispoof_prompt); - while (s->antispoof_ret < 0) { + while (s->antispoof_ret.kind == SPRK_INCOMPLETE) { crReturnV; s->antispoof_ret = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->antispoof_prompt); diff --git a/ssh/connection2.h b/ssh/connection2.h index 7971632c..54c3ebf9 100644 --- a/ssh/connection2.h +++ b/ssh/connection2.h @@ -37,7 +37,7 @@ struct ssh2_connection_state { bool portfwdmgr_configured; prompts_t *antispoof_prompt; - int antispoof_ret; + SeatPromptResult antispoof_ret; const SftpServerVtable *sftpserver_vt; const SshServerConfig *ssc; diff --git a/ssh/kex2-client.c b/ssh/kex2-client.c index 0f81ef15..9a8f75e2 100644 --- a/ssh/kex2-client.c +++ b/ssh/kex2-client.c @@ -853,7 +853,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) ppl_logevent("Host key fingerprint is:"); ppl_logevent("%s", fingerprints[fptype_default]); - s->dlgret = verify_ssh_host_key( + s->spr = verify_ssh_host_key( ppl_get_iseat(&s->ppl), s->conf, s->savedhost, s->savedport, s->hkey, ssh_key_cache_id(s->hkey), s->keystr, keydisp, fingerprints, ssh2_transport_dialog_callback, s); @@ -862,13 +862,12 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) sfree(keydisp); } #ifdef FUZZING - s->dlgret = 1; + s->spr = SPR_OK; #endif - crMaybeWaitUntilV(s->dlgret >= 0); - if (s->dlgret == 0) { - ssh_user_close(s->ppl.ssh, - "User aborted at host key verification"); + crMaybeWaitUntilV(s->spr.kind != SPRK_INCOMPLETE); + if (spr_is_abort(s->spr)) { *aborted = true; + ssh_spr_close(s->ppl.ssh, s->spr, "host key verification"); return; } diff --git a/ssh/login1.c b/ssh/login1.c index 62c7b866..7b345c78 100644 --- a/ssh/login1.c +++ b/ssh/login1.c @@ -47,7 +47,7 @@ struct ssh1_login_state { char *publickey_comment; bool privatekey_available, privatekey_encrypted; prompts_t *cur_prompt; - int userpass_ret; + SeatPromptResult spr; char c; int pwpkt_type; void *agent_response_to_free; @@ -58,7 +58,6 @@ struct ssh1_login_state { size_t agent_key_index, agent_key_limit; bool authed; RSAKey key; - int dlgret; Filename *keyfile; RSAKey servkey, hostkey; @@ -70,7 +69,7 @@ struct ssh1_login_state { static void ssh1_login_free(PacketProtocolLayer *); static void ssh1_login_process_queue(PacketProtocolLayer *); -static void ssh1_login_dialog_callback(void *, int); +static void ssh1_login_dialog_callback(void *, SeatPromptResult); static void ssh1_login_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg); static void ssh1_login_reconfigure(PacketProtocolLayer *ppl, Conf *conf); @@ -242,7 +241,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) char *keydisp = ssh1_pubkey_str(&s->hostkey); char **fingerprints = rsa_ssh1_fake_all_fingerprints(&s->hostkey); - s->dlgret = verify_ssh_host_key( + s->spr = verify_ssh_host_key( ppl_get_iseat(&s->ppl), s->conf, s->savedhost, s->savedport, NULL, "rsa", keystr, keydisp, fingerprints, ssh1_login_dialog_callback, s); @@ -253,13 +252,12 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) } #ifdef FUZZING - s->dlgret = 1; + s->spr = SPR_OK; #endif - crMaybeWaitUntilV(s->dlgret >= 0); + crMaybeWaitUntilV(s->spr.kind != SPRK_INCOMPLETE); - if (s->dlgret == 0) { - ssh_user_close(s->ppl.ssh, - "User aborted at host key verification"); + if (spr_is_abort(s->spr)) { + ssh_spr_close(s->ppl.ssh, s->spr, "host key verification"); return; } @@ -324,12 +322,12 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) /* Warn about chosen cipher if necessary. */ if (warn) { - s->dlgret = seat_confirm_weak_crypto_primitive( + s->spr = seat_confirm_weak_crypto_primitive( ppl_get_iseat(&s->ppl), "cipher", cipher_string, ssh1_login_dialog_callback, s); - crMaybeWaitUntilV(s->dlgret >= 0); - if (s->dlgret == 0) { - ssh_user_close(s->ppl.ssh, "User aborted at cipher warning"); + crMaybeWaitUntilV(s->spr.kind != SPRK_INCOMPLETE); + if (spr_is_abort(s->spr)) { + ssh_spr_close(s->ppl.ssh, s->spr, "cipher warning"); return; } } @@ -391,18 +389,18 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) 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->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); - while (s->userpass_ret < 0) { + while (s->spr.kind == SPRK_INCOMPLETE) { crReturnV; - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); } - if (!s->userpass_ret) { + if (spr_is_abort(s->spr)) { /* * Failed to get a username. Terminate. */ - ssh_user_close(s->ppl.ssh, "No username provided"); + ssh_spr_close(s->ppl.ssh, s->spr, "username prompt"); return; } s->username = prompt_get_result(s->cur_prompt->prompts[0]); @@ -688,17 +686,16 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%s\": ", s->publickey_comment), false); - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); - while (s->userpass_ret < 0) { + while (s->spr.kind == SPRK_INCOMPLETE) { crReturnV; - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); } - if (!s->userpass_ret) { + if (spr_is_abort(s->spr)) { /* Failed to get a passphrase. Terminate. */ - ssh_user_close(s->ppl.ssh, - "User aborted at passphrase prompt"); + ssh_spr_close(s->ppl.ssh, s->spr, "passphrase prompt"); return; } passphrase = prompt_get_result(s->cur_prompt->prompts[0]); @@ -943,20 +940,20 @@ 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->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); - while (s->userpass_ret < 0) { + while (s->spr.kind == SPRK_INCOMPLETE) { crReturnV; - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); } - if (!s->userpass_ret) { + if (spr_is_abort(s->spr)) { /* * Failed to get a password (for example * because one was supplied on the command line * which has already failed to work). Terminate. */ - ssh_user_close(s->ppl.ssh, "User aborted at password prompt"); + ssh_spr_close(s->ppl.ssh, s->spr, "password prompt"); return; } @@ -1141,10 +1138,10 @@ static void ssh1_login_setup_tis_scc(struct ssh1_login_state *s) s->tis_scc_initialised = true; } -static void ssh1_login_dialog_callback(void *loginv, int ret) +static void ssh1_login_dialog_callback(void *loginv, SeatPromptResult spr) { struct ssh1_login_state *s = (struct ssh1_login_state *)loginv; - s->dlgret = ret; + s->spr = spr; ssh_ppl_process_queue(&s->ppl); } diff --git a/ssh/server.c b/ssh/server.c index 347aa14b..f69bdd83 100644 --- a/ssh/server.c +++ b/ssh/server.c @@ -97,12 +97,14 @@ void mainchan_terminal_size(mainchan *mc, int width, int height) {} /* Seat functions to ensure we don't get choosy about crypto - as the * server, it's not up to us to give user warnings */ -static int server_confirm_weak_crypto_primitive( +static SeatPromptResult server_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) { return 1; } -static int server_confirm_weak_cached_hostkey( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) +{ return SPR_OK; } +static SeatPromptResult server_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) { return 1; } + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) +{ return SPR_OK; } static const SeatVtable server_seat_vt = { .output = nullseat_output, diff --git a/ssh/transport2.c b/ssh/transport2.c index 0ba937f3..b4261562 100644 --- a/ssh/transport2.c +++ b/ssh/transport2.c @@ -97,7 +97,7 @@ static void ssh2_transport_gss_update(struct ssh2_transport_state *s, static bool ssh2_transport_timer_update(struct ssh2_transport_state *s, unsigned long rekey_time); -static int ssh2_transport_confirm_weak_crypto_primitive( +static SeatPromptResult ssh2_transport_confirm_weak_crypto_primitive( struct ssh2_transport_state *s, const char *type, const char *name, const void *alg); @@ -1265,11 +1265,11 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) } if (s->warn_kex) { - s->dlgret = ssh2_transport_confirm_weak_crypto_primitive( + s->spr = ssh2_transport_confirm_weak_crypto_primitive( s, "key-exchange algorithm", s->kex_alg->name, s->kex_alg); - crMaybeWaitUntilV(s->dlgret >= 0); - if (s->dlgret == 0) { - ssh_user_close(s->ppl.ssh, "User aborted at kex warning"); + crMaybeWaitUntilV(s->spr.kind != SPRK_INCOMPLETE); + if (spr_is_abort(s->spr)) { + ssh_spr_close(s->ppl.ssh, s->spr, "kex warning"); return; } } @@ -1312,42 +1312,42 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) if (betteralgs) { /* Use the special warning prompt that lets us provide * a list of better algorithms */ - s->dlgret = seat_confirm_weak_cached_hostkey( + s->spr = seat_confirm_weak_cached_hostkey( ppl_get_iseat(&s->ppl), s->hostkey_alg->ssh_id, betteralgs, ssh2_transport_dialog_callback, s); sfree(betteralgs); } else { /* If none exist, use the more general 'weak crypto' * warning prompt */ - s->dlgret = ssh2_transport_confirm_weak_crypto_primitive( + s->spr = ssh2_transport_confirm_weak_crypto_primitive( s, "host key type", s->hostkey_alg->ssh_id, s->hostkey_alg); } - crMaybeWaitUntilV(s->dlgret >= 0); - if (s->dlgret == 0) { - ssh_user_close(s->ppl.ssh, "User aborted at host key warning"); + crMaybeWaitUntilV(s->spr.kind != SPRK_INCOMPLETE); + if (spr_is_abort(s->spr)) { + ssh_spr_close(s->ppl.ssh, s->spr, "host key warning"); return; } } if (s->warn_cscipher) { - s->dlgret = ssh2_transport_confirm_weak_crypto_primitive( + s->spr = ssh2_transport_confirm_weak_crypto_primitive( s, "client-to-server cipher", s->out.cipher->ssh2_id, s->out.cipher); - crMaybeWaitUntilV(s->dlgret >= 0); - if (s->dlgret == 0) { - ssh_user_close(s->ppl.ssh, "User aborted at cipher warning"); + crMaybeWaitUntilV(s->spr.kind != SPRK_INCOMPLETE); + if (spr_is_abort(s->spr)) { + ssh_spr_close(s->ppl.ssh, s->spr, "cipher warning"); return; } } if (s->warn_sccipher) { - s->dlgret = ssh2_transport_confirm_weak_crypto_primitive( + s->spr = ssh2_transport_confirm_weak_crypto_primitive( s, "server-to-client cipher", s->in.cipher->ssh2_id, s->in.cipher); - crMaybeWaitUntilV(s->dlgret >= 0); - if (s->dlgret == 0) { - ssh_user_close(s->ppl.ssh, "User aborted at cipher warning"); + crMaybeWaitUntilV(s->spr.kind != SPRK_INCOMPLETE); + if (spr_is_abort(s->spr)) { + ssh_spr_close(s->ppl.ssh, s->spr, "cipher warning"); return; } } @@ -1815,10 +1815,10 @@ static bool ssh2_transport_timer_update(struct ssh2_transport_state *s, return false; } -void ssh2_transport_dialog_callback(void *vctx, int ret) +void ssh2_transport_dialog_callback(void *vctx, SeatPromptResult spr) { struct ssh2_transport_state *s = (struct ssh2_transport_state *)vctx; - s->dlgret = ret; + s->spr = spr; ssh_ppl_process_queue(&s->ppl); } @@ -2139,12 +2139,12 @@ static int weak_algorithm_compare(void *av, void *bv) * tree234 s->weak_algorithms_consented_to to ensure we ask at most * once about any given crypto primitive. */ -static int ssh2_transport_confirm_weak_crypto_primitive( +static SeatPromptResult ssh2_transport_confirm_weak_crypto_primitive( struct ssh2_transport_state *s, const char *type, const char *name, const void *alg) { if (find234(s->weak_algorithms_consented_to, (void *)alg, NULL)) - return 1; + return SPR_OK; add234(s->weak_algorithms_consented_to, (void *)alg); return seat_confirm_weak_crypto_primitive( diff --git a/ssh/transport2.h b/ssh/transport2.h index 46cd67f9..5f0df80e 100644 --- a/ssh/transport2.h +++ b/ssh/transport2.h @@ -186,7 +186,7 @@ struct ssh2_transport_state { bool warned_about_no_gss_transient_hostkey; bool got_session_id; bool can_send_ext_info, post_newkeys_ext_info; - int dlgret; + SeatPromptResult spr; bool guessok; bool ignorepkt; struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST]; @@ -231,7 +231,7 @@ struct ssh2_transport_state { /* Helpers shared between transport and kex */ PktIn *ssh2_transport_pop(struct ssh2_transport_state *s); -void ssh2_transport_dialog_callback(void *, int); +void ssh2_transport_dialog_callback(void *, SeatPromptResult); /* Provided by transport for use in kex */ void ssh2transport_finalise_exhash(struct ssh2_transport_state *s); diff --git a/ssh/userauth2-client.c b/ssh/userauth2-client.c index b6ed5704..ba97502f 100644 --- a/ssh/userauth2-client.c +++ b/ssh/userauth2-client.c @@ -45,7 +45,7 @@ struct ssh2_userauth_state { AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET } type; bool need_pw, can_pubkey, can_passwd, can_keyb_inter; - int userpass_ret; + SeatPromptResult spr; bool tried_pubkey_config, done_agent; struct ssh_connection_shared_gss_state *shgss; #ifndef NO_GSSAPI @@ -442,21 +442,21 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) 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->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); - while (s->userpass_ret < 0) { + while (s->spr.kind == SPRK_INCOMPLETE) { crReturnV; - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); } - if (!s->userpass_ret) { + if (spr_is_abort(s->spr)) { /* * seat_get_userpass_input() failed to get a username. * Terminate. */ free_prompts(s->cur_prompt); s->cur_prompt = NULL; - ssh_user_close(s->ppl.ssh, "No username provided"); + ssh_spr_close(s->ppl.ssh, s->spr, "username prompt"); return; } sfree(s->locally_allocated_username); /* for change_username */ @@ -912,22 +912,22 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) dupprintf("Passphrase for key \"%s\": ", s->publickey_comment), false); - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); - while (s->userpass_ret < 0) { + while (s->spr.kind == SPRK_INCOMPLETE) { crReturnV; - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); } - if (!s->userpass_ret) { + if (spr_is_abort(s->spr)) { /* Failed to get a passphrase. Terminate. */ free_prompts(s->cur_prompt); s->cur_prompt = NULL; ssh_bpp_queue_disconnect( s->ppl.bpp, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); - ssh_user_close(s->ppl.ssh, "User aborted at " - "passphrase prompt"); + ssh_spr_close(s->ppl.ssh, s->spr, + "passphrase prompt"); return; } passphrase = @@ -1391,14 +1391,14 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * Our prompts_t is fully constructed now. Get the * user's response(s). */ - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); - while (s->userpass_ret < 0) { + while (s->spr.kind == SPRK_INCOMPLETE) { crReturnV; - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); } - if (!s->userpass_ret) { + if (spr_is_abort(s->spr)) { /* * Failed to get responses. Terminate. */ @@ -1407,8 +1407,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) ssh_bpp_queue_disconnect( s->ppl.bpp, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); - ssh_user_close(s->ppl.ssh, "User aborted during " - "keyboard-interactive authentication"); + ssh_spr_close(s->ppl.ssh, s->spr, "keyboard-" + "interactive authentication prompt"); return; } @@ -1473,14 +1473,14 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) s->username, s->hostname), false); - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); - while (s->userpass_ret < 0) { + while (s->spr.kind == SPRK_INCOMPLETE) { crReturnV; - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); } - if (!s->userpass_ret) { + if (spr_is_abort(s->spr)) { /* * Failed to get responses. Terminate. */ @@ -1489,8 +1489,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) ssh_bpp_queue_disconnect( s->ppl.bpp, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); - ssh_user_close(s->ppl.ssh, "User aborted during password " - "authentication"); + ssh_spr_close(s->ppl.ssh, s->spr, "password prompt"); return; } /* @@ -1585,14 +1584,14 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * password twice. */ while (!got_new) { - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); - while (s->userpass_ret < 0) { + while (s->spr.kind == SPRK_INCOMPLETE) { crReturnV; - s->userpass_ret = seat_get_userpass_input( + s->spr = seat_get_userpass_input( ppl_get_iseat(&s->ppl), s->cur_prompt); } - if (!s->userpass_ret) { + if (spr_is_abort(s->spr)) { /* * Failed to get responses. Terminate. */ @@ -1604,8 +1603,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) ssh_bpp_queue_disconnect( s->ppl.bpp, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); - ssh_user_close(s->ppl.ssh, "User aborted during " - "password changing"); + ssh_spr_close(s->ppl.ssh, s->spr, + "password-change prompt"); return; } diff --git a/stubs/nocmdline.c b/stubs/nocmdline.c index e4c6d08f..9efd9ef2 100644 --- a/stubs/nocmdline.c +++ b/stubs/nocmdline.c @@ -15,9 +15,9 @@ * handling, then there is no such option, so that function always * returns failure. */ -int cmdline_get_passwd_input(prompts_t *p) +SeatPromptResult cmdline_get_passwd_input(prompts_t *p) { - return -1; + return SPR_INCOMPLETE; } /* diff --git a/stubs/noterm.c b/stubs/noterm.c index b5329aec..c2e534b2 100644 --- a/stubs/noterm.c +++ b/stubs/noterm.c @@ -10,7 +10,7 @@ void term_nopaste(Terminal *term) { } -int term_get_userpass_input(Terminal *term, prompts_t *p) +SeatPromptResult term_get_userpass_input(Terminal *term, prompts_t *p) { - return 0; + return SPR_SW_ABORT("No terminal to send interactive prompts to"); } diff --git a/terminal/terminal.c b/terminal/terminal.c index 6efd9cfe..b5d01813 100644 --- a/terminal/terminal.c +++ b/terminal/terminal.c @@ -7672,30 +7672,32 @@ static inline void term_write(Terminal *term, ptrlen data) * 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) +static inline SeatPromptResult signal_prompts_t(Terminal *term, prompts_t *p, + SeatPromptResult spr) { 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; + p->spr = spr; + return spr; } /* * Process some terminal data in the course of username/password * input. */ -int term_get_userpass_input(Terminal *term, prompts_t *p) +SeatPromptResult 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); + return signal_prompts_t(term, p, SPR_SW_ABORT( + "Terminal not prepared for interactive prompts")); } - if (p->idata >= 0) { + if (p->spr.kind != SPRK_INCOMPLETE) { /* We've already finished these prompts, so return the same * result again */ - return p->idata; + return p->spr; } struct term_userpass_state *s = (struct term_userpass_state *)p->data; @@ -7705,7 +7707,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p) * First call. Set some stuff up. */ p->data = s = snew(struct term_userpass_state); - p->idata = -1; + p->spr = SPR_INCOMPLETE; s->curr_prompt = 0; s->done_prompt = false; /* We only print the `name' caption if we have to... */ @@ -7795,7 +7797,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p) term_write(term, PTRLEN_LITERAL("\r\n")); sfree(s); p->data = NULL; - return signal_prompts_t(term, p, 0); /* user abort */ + return signal_prompts_t(term, p, SPR_USER_ABORT); default: /* * This simplistic check for printability is disabled @@ -7816,11 +7818,11 @@ int term_get_userpass_input(Terminal *term, prompts_t *p) if (s->curr_prompt < p->n_prompts) { ldisc_enable_prompt_callback(term->ldisc, p); - return -1; /* more data required */ + return SPR_INCOMPLETE; } else { sfree(s); p->data = NULL; - return signal_prompts_t(term, p, +1); /* all done */ + return signal_prompts_t(term, p, SPR_OK); } } diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 9bdd2522..342ca14f 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -12,6 +12,7 @@ add_sources_from_current_dir(utils utils/keysym_to_unicode.c utils/make_dir_and_check_ours.c utils/make_dir_path.c + utils/make_spr_sw_abort_errno.c utils/nonblock.c utils/open_for_write_would_lose_data.c utils/pgp_fingerprints.c diff --git a/unix/console.c b/unix/console.c index 4131abdc..434854bd 100644 --- a/unix/console.c +++ b/unix/console.c @@ -102,10 +102,10 @@ static int block_and_read(int fd, void *buf, size_t len) return ret; } -int console_confirm_ssh_host_key( +SeatPromptResult console_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { char line[32]; struct termios cf; @@ -134,7 +134,7 @@ int console_confirm_ssh_host_key( if (console_batch_mode) { fputs(console_abandoned_msg, stderr); postmsg(&cf); - return 0; + return SPR_SW_ABORT("Cannot confirm a host key in batch mode"); } fputs(intro, stderr); @@ -173,17 +173,17 @@ int console_confirm_ssh_host_key( if (line[0] == 'y' || line[0] == 'Y') store_host_key(host, port, keytype, keystr); postmsg(&cf); - return 1; + return SPR_OK; } else { fputs(console_abandoned_msg, stderr); postmsg(&cf); - return 0; + return SPR_USER_ABORT; } } -int console_confirm_weak_crypto_primitive( +SeatPromptResult console_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { char line[32]; struct termios cf; @@ -194,7 +194,8 @@ int console_confirm_weak_crypto_primitive( if (console_batch_mode) { fputs(console_abandoned_msg, stderr); postmsg(&cf); - return 0; + return SPR_SW_ABORT("Cannot confirm a weak crypto primitive " + "in batch mode"); } fputs(console_continue_prompt, stderr); @@ -214,17 +215,17 @@ int console_confirm_weak_crypto_primitive( if (line[0] == 'y' || line[0] == 'Y') { postmsg(&cf); - return 1; + return SPR_OK; } else { fputs(console_abandoned_msg, stderr); postmsg(&cf); - return 0; + return SPR_USER_ABORT; } } -int console_confirm_weak_cached_hostkey( +SeatPromptResult console_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { char line[32]; struct termios cf; @@ -235,7 +236,8 @@ int console_confirm_weak_cached_hostkey( if (console_batch_mode) { fputs(console_abandoned_msg, stderr); postmsg(&cf); - return 0; + return SPR_SW_ABORT("Cannot confirm a weak cached host key " + "in batch mode"); } fputs(console_continue_prompt, stderr); @@ -255,11 +257,11 @@ int console_confirm_weak_cached_hostkey( if (line[0] == 'y' || line[0] == 'Y') { postmsg(&cf); - return 1; + return SPR_OK; } else { fputs(console_abandoned_msg, stderr); postmsg(&cf); - return 0; + return SPR_USER_ABORT; } } @@ -443,7 +445,7 @@ static void console_write(FILE *outfp, ptrlen data) fflush(outfp); } -int console_get_userpass_input(prompts_t *p) +SeatPromptResult console_get_userpass_input(prompts_t *p) { size_t curr_prompt; FILE *outfp = NULL; @@ -459,7 +461,8 @@ int console_get_userpass_input(prompts_t *p) } if (p->n_prompts && console_batch_mode) - return 0; + return SPR_SW_ABORT("Cannot answer interactive prompts " + "in batch mode"); console_open(&outfp, &infd); @@ -498,14 +501,26 @@ int console_get_userpass_input(prompts_t *p) console_write(outfp, ptrlen_from_asciz(pr->prompt)); bool failed = false; + SeatPromptResult spr; while (1) { size_t toread = 65536; size_t prev_result_len = pr->result->len; void *ptr = strbuf_append(pr->result, toread); int ret = read(infd, ptr, toread); - if (ret <= 0) { + if (ret == 0) { + /* Regard EOF on the terminal as a deliberate user-abort */ failed = true; + spr = SPR_USER_ABORT; + break; + } + + if (ret < 0) { + /* Any other failure to read from the terminal is treated as + * an unexpected error and reported to the user. */ + failed = true; + spr = make_spr_sw_abort_errno( + "Error reading from terminal", errno); break; } @@ -521,13 +536,13 @@ int console_get_userpass_input(prompts_t *p) if (failed) { console_close(outfp, infd); - return 0; /* failure due to read error */ + return spr; } } console_close(outfp, infd); - return 1; /* success */ + return SPR_OK; } bool is_interactive(void) diff --git a/unix/dialog.c b/unix/dialog.c index 84a5762f..8f86d116 100644 --- a/unix/dialog.c +++ b/unix/dialog.c @@ -3454,7 +3454,7 @@ struct confirm_ssh_host_key_dialog_ctx { char *keytype; char *keystr; char *more_info; - void (*callback)(void *callback_ctx, int result); + void (*callback)(void *callback_ctx, SeatPromptResult result); void *callback_ctx; Seat *seat; @@ -3468,7 +3468,7 @@ static void confirm_ssh_host_key_result_callback(void *vctx, int result) (struct confirm_ssh_host_key_dialog_ctx *)vctx; if (result >= 0) { - int logical_result; + SeatPromptResult logical_result; /* * Convert the dialog-box return value (one of three @@ -3478,11 +3478,11 @@ static void confirm_ssh_host_key_result_callback(void *vctx, int result) */ if (result == 2) { store_host_key(ctx->host, ctx->port, ctx->keytype, ctx->keystr); - logical_result = 1; /* continue with connection */ + logical_result = SPR_OK; } else if (result == 1) { - logical_result = 1; /* continue with connection */ + logical_result = SPR_OK; } else { - logical_result = 0; /* do not continue with connection */ + logical_result = SPR_USER_ABORT; } ctx->callback(ctx->callback_ctx, logical_result); @@ -3539,10 +3539,10 @@ static void more_info_button_clicked(GtkButton *button, gpointer vctx) &buttons_ok, more_info_closed, ctx); } -int gtk_seat_confirm_ssh_host_key( +SeatPromptResult gtk_seat_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { static const char absenttxt[] = "The host key is not cached for this server:\n\n" @@ -3641,25 +3641,28 @@ int gtk_seat_confirm_ssh_host_key( sfree(text); - return -1; /* dialog still in progress */ + return SPR_INCOMPLETE; /* dialog still in progress */ } -struct simple_prompt_result_ctx { - void (*callback)(void *callback_ctx, int result); +struct simple_prompt_result_spr_ctx { + void (*callback)(void *callback_ctx, SeatPromptResult spr); void *callback_ctx; Seat *seat; enum DialogSlot dialog_slot; }; -static void simple_prompt_result_callback(void *vctx, int result) +static void simple_prompt_result_spr_callback(void *vctx, int result) { - struct simple_prompt_result_ctx *ctx = - (struct simple_prompt_result_ctx *)vctx; + struct simple_prompt_result_spr_ctx *ctx = + (struct simple_prompt_result_spr_ctx *)vctx; unregister_dialog(ctx->seat, ctx->dialog_slot); - if (result >= 0) - ctx->callback(ctx->callback_ctx, result); + if (result == 0) + ctx->callback(ctx->callback_ctx, SPR_USER_ABORT); + else if (result > 0) + ctx->callback(ctx->callback_ctx, SPR_OK); + /* if <0, we're cleaning up for some other reason */ /* * Clean up this context structure, whether or not a result was @@ -3672,9 +3675,9 @@ static void simple_prompt_result_callback(void *vctx, int result) * Ask whether the selected algorithm is acceptable (since it was * below the configured 'warn' threshold). */ -int gtk_seat_confirm_weak_crypto_primitive( +SeatPromptResult gtk_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { static const char msg[] = "The first %s supported by the server is " @@ -3682,12 +3685,12 @@ int gtk_seat_confirm_weak_crypto_primitive( "Continue with connection?"; char *text; - struct simple_prompt_result_ctx *result_ctx; + struct simple_prompt_result_spr_ctx *result_ctx; GtkWidget *mainwin, *msgbox; text = dupprintf(msg, algtype, algname); - result_ctx = snew(struct simple_prompt_result_ctx); + result_ctx = snew(struct simple_prompt_result_spr_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; result_ctx->seat = seat; @@ -3697,17 +3700,17 @@ int gtk_seat_confirm_weak_crypto_primitive( msgbox = create_message_box( mainwin, "PuTTY Security Alert", text, string_width("Reasonably long line of text as a width template"), - false, &buttons_yn, simple_prompt_result_callback, result_ctx); + false, &buttons_yn, simple_prompt_result_spr_callback, result_ctx); register_dialog(seat, result_ctx->dialog_slot, msgbox); sfree(text); - return -1; /* dialog still in progress */ + return SPR_INCOMPLETE; } -int gtk_seat_confirm_weak_cached_hostkey( +SeatPromptResult gtk_seat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { static const char msg[] = "The first host key type we have stored for this server\n" @@ -3718,12 +3721,12 @@ int gtk_seat_confirm_weak_cached_hostkey( "Continue with connection?"; char *text; - struct simple_prompt_result_ctx *result_ctx; + struct simple_prompt_result_spr_ctx *result_ctx; GtkWidget *mainwin, *msgbox; text = dupprintf(msg, algname, betteralgs); - result_ctx = snew(struct simple_prompt_result_ctx); + result_ctx = snew(struct simple_prompt_result_spr_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; result_ctx->seat = seat; @@ -3734,12 +3737,12 @@ int gtk_seat_confirm_weak_cached_hostkey( mainwin, "PuTTY Security Alert", text, string_width("is ecdsa-nistp521, which is below the configured" " warning threshold."), - false, &buttons_yn, simple_prompt_result_callback, result_ctx); + false, &buttons_yn, simple_prompt_result_spr_callback, result_ctx); register_dialog(seat, result_ctx->dialog_slot, msgbox); sfree(text); - return -1; /* dialog still in progress */ + return SPR_INCOMPLETE; } void old_keyfile_warning(void) @@ -4149,6 +4152,30 @@ void logevent_dlg(eventlog_stuff *es, const char *string) } } +struct simple_prompt_result_int_ctx { + void (*callback)(void *callback_ctx, int result); + void *callback_ctx; + Seat *seat; + enum DialogSlot dialog_slot; +}; + +static void simple_prompt_result_int_callback(void *vctx, int result) +{ + struct simple_prompt_result_int_ctx *ctx = + (struct simple_prompt_result_int_ctx *)vctx; + + unregister_dialog(ctx->seat, ctx->dialog_slot); + + if (result >= 0) + ctx->callback(ctx->callback_ctx, result); + + /* + * Clean up this context structure, whether or not a result was + * ever actually delivered from the dialog box. + */ + sfree(ctx); +} + int gtkdlg_askappend(Seat *seat, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { @@ -4168,13 +4195,13 @@ int gtkdlg_askappend(Seat *seat, Filename *filename, char *message; char *mbtitle; - struct simple_prompt_result_ctx *result_ctx; + struct simple_prompt_result_int_ctx *result_ctx; GtkWidget *mainwin, *msgbox; message = dupprintf(msgtemplate, FILENAME_MAX, filename->path); mbtitle = dupprintf("%s Log to File", appname); - result_ctx = snew(struct simple_prompt_result_ctx); + result_ctx = snew(struct simple_prompt_result_int_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; result_ctx->seat = seat; @@ -4184,7 +4211,7 @@ int gtkdlg_askappend(Seat *seat, Filename *filename, msgbox = create_message_box( mainwin, mbtitle, message, string_width("LINE OF TEXT SUITABLE FOR THE ASKAPPEND WIDTH"), - false, &buttons_append, simple_prompt_result_callback, result_ctx); + false, &buttons_append, simple_prompt_result_int_callback, result_ctx); register_dialog(seat, result_ctx->dialog_slot, msgbox); sfree(message); diff --git a/unix/pageant.c b/unix/pageant.c index f8a766db..df124dc6 100644 --- a/unix/pageant.c +++ b/unix/pageant.c @@ -437,19 +437,23 @@ static FingerprintType key_list_fptype = SSH_FPTYPE_DEFAULT; static char *askpass_tty(const char *prompt) { - int ret; prompts_t *p = new_prompts(); p->to_server = false; p->from_server = false; p->name = dupstr("Pageant passphrase prompt"); add_prompt(p, dupcat(prompt, ": "), false); - ret = console_get_userpass_input(p); - assert(ret >= 0); + SeatPromptResult spr = console_get_userpass_input(p); + assert(spr.kind != SPRK_INCOMPLETE); - if (!ret) { - perror("pageant: unable to read passphrase"); + if (spr.kind == SPRK_USER_ABORT) { free_prompts(p); return NULL; + } else if (spr.kind == SPRK_SW_ABORT) { + free_prompts(p); + char *err = spr_get_error_message(spr); + fprintf(stderr, "pageant: unable to read passphrase: %s", err); + sfree(err); + return NULL; } else { char *passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); diff --git a/unix/platform.h b/unix/platform.h index 86996b3d..58cb4fbf 100644 --- a/unix/platform.h +++ b/unix/platform.h @@ -217,16 +217,16 @@ void showeventlog(eventlog_stuff *estuff, void *parentwin); void logevent_dlg(eventlog_stuff *estuff, const char *string); int gtkdlg_askappend(Seat *seat, Filename *filename, void (*callback)(void *ctx, int result), void *ctx); -int gtk_seat_confirm_ssh_host_key( +SeatPromptResult gtk_seat_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx); -int gtk_seat_confirm_weak_crypto_primitive( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); +SeatPromptResult gtk_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx); -int gtk_seat_confirm_weak_cached_hostkey( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); +SeatPromptResult gtk_seat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx); + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); #ifdef MAY_REFER_TO_GTK_IN_HEADERS struct message_box_button { const char *title; @@ -464,4 +464,6 @@ bool cliloop_always_continue(void *ctx, bool, bool); /* network.c: network error reporting helper taking an OS error code */ void plug_closing_errno(Plug *plug, int error); +SeatPromptResult make_spr_sw_abort_errno(const char *prefix, int errno_value); + #endif /* PUTTY_UNIX_PLATFORM_H */ diff --git a/unix/plink.c b/unix/plink.c index 385fe993..ca7fcad8 100644 --- a/unix/plink.c +++ b/unix/plink.c @@ -371,13 +371,13 @@ 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) +static SeatPromptResult plink_get_userpass_input(Seat *seat, prompts_t *p) { - int ret; - ret = cmdline_get_passwd_input(p); - if (ret == -1) - ret = console_get_userpass_input(p); - return ret; + SeatPromptResult spr; + spr = cmdline_get_passwd_input(p); + if (spr.kind == SPRK_INCOMPLETE) + spr = console_get_userpass_input(p); + return spr; } static bool plink_seat_interactive(Seat *seat) diff --git a/unix/sftp.c b/unix/sftp.c index 331cbc70..92f3f8ed 100644 --- a/unix/sftp.c +++ b/unix/sftp.c @@ -63,13 +63,13 @@ Filename *platform_default_filename(const char *name) return filename_from_str(""); } -int filexfer_get_userpass_input(Seat *seat, prompts_t *p) +SeatPromptResult filexfer_get_userpass_input(Seat *seat, prompts_t *p) { - int ret; - ret = cmdline_get_passwd_input(p); - if (ret == -1) - ret = console_get_userpass_input(p); - return ret; + SeatPromptResult spr; + spr = cmdline_get_passwd_input(p); + if (spr.kind == SPRK_INCOMPLETE) + spr = console_get_userpass_input(p); + return spr; } /* diff --git a/unix/utils/make_spr_sw_abort_errno.c b/unix/utils/make_spr_sw_abort_errno.c new file mode 100644 index 00000000..57f58c2f --- /dev/null +++ b/unix/utils/make_spr_sw_abort_errno.c @@ -0,0 +1,22 @@ +/* + * Constructor function for a SeatPromptResult of the 'software abort' + * category, whose error message includes the translation of an OS + * error code. + */ + +#include "putty.h" + +static void spr_errno_errfn(SeatPromptResult spr, BinarySink *bs) +{ + put_fmt(bs, "%s: %s", spr.errdata_lit, strerror(spr.errdata_u)); +} + +SeatPromptResult make_spr_sw_abort_errno(const char *prefix, int errno_value) +{ + SeatPromptResult spr; + spr.kind = SPRK_SW_ABORT; + spr.errfn = spr_errno_errfn; + spr.errdata_lit = prefix; + spr.errdata_u = errno_value; + return spr; +} diff --git a/unix/window.c b/unix/window.c index dba32c27..93281429 100644 --- a/unix/window.c +++ b/unix/window.c @@ -339,14 +339,14 @@ 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) +static SeatPromptResult 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); - return ret; + SeatPromptResult spr; + spr = cmdline_get_passwd_input(p); + if (spr.kind == SPRK_INCOMPLETE) + spr = term_get_userpass_input(inst->term, p); + return spr; } static bool gtk_seat_is_utf8(Seat *seat) diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 86f48952..4a6a1fe8 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -26,6 +26,7 @@ add_sources_from_current_dir(utils host_strduptrim.c host_strrchr.c log_proxy_stderr.c + make_spr_sw_abort_static.c marshal.c memory.c memxor.c @@ -42,6 +43,7 @@ add_sources_from_current_dir(utils sk_free_peer_info.c smemclr.c smemeq.c + spr_get_error_message.c ssh2_pick_fingerprint.c sshutils.c strbuf.c diff --git a/utils/make_spr_sw_abort_static.c b/utils/make_spr_sw_abort_static.c new file mode 100644 index 00000000..f9eac59f --- /dev/null +++ b/utils/make_spr_sw_abort_static.c @@ -0,0 +1,21 @@ +/* + * Constructor function for a SeatPromptResult of the 'software abort' + * category, whose error message is in the simplest possible form of a + * static string constant. + */ + +#include "putty.h" + +static void spr_static_errfn(SeatPromptResult spr, BinarySink *bs) +{ + put_dataz(bs, spr.errdata_lit); +} + +SeatPromptResult make_spr_sw_abort_static(const char *str) +{ + SeatPromptResult spr; + spr.kind = SPRK_SW_ABORT; + spr.errfn = spr_static_errfn; + spr.errdata_lit = str; + return spr; +} diff --git a/utils/nullseat.c b/utils/nullseat.c index 43a338fb..ba09839e 100644 --- a/utils/nullseat.c +++ b/utils/nullseat.c @@ -11,7 +11,8 @@ void nullseat_sent(Seat *seat, size_t bufsize) {} size_t nullseat_banner(Seat *seat, const void *data, size_t len) {return 0;} size_t nullseat_banner_to_stderr(Seat *seat, const void *data, size_t len) { return seat_output(seat, SEAT_OUTPUT_STDERR, data, len); } -int nullseat_get_userpass_input(Seat *seat, prompts_t *p) { return 0; } +SeatPromptResult nullseat_get_userpass_input(Seat *seat, prompts_t *p) +{ return SPR_SW_ABORT("this seat can't handle interactive prompts"); } void nullseat_notify_session_started(Seat *seat) {} void nullseat_notify_remote_exit(Seat *seat) {} void nullseat_notify_remote_disconnect(Seat *seat) {} @@ -19,16 +20,19 @@ void nullseat_connection_fatal(Seat *seat, const char *message) {} void nullseat_update_specials_menu(Seat *seat) {} char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; } void nullseat_set_busy_status(Seat *seat, BusyStatus status) {} -int nullseat_confirm_ssh_host_key( +SeatPromptResult nullseat_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx) { return 0; } -int nullseat_confirm_weak_crypto_primitive( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) +{ return SPR_SW_ABORT("this seat can't handle interactive prompts"); } +SeatPromptResult nullseat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) { return 0; } -int nullseat_confirm_weak_cached_hostkey( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) +{ return SPR_SW_ABORT("this seat can't handle interactive prompts"); } +SeatPromptResult nullseat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) { return 0; } + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) +{ return SPR_SW_ABORT("this seat can't handle interactive prompts"); } bool nullseat_is_never_utf8(Seat *seat) { return false; } bool nullseat_is_always_utf8(Seat *seat) { return true; } void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing) {} diff --git a/utils/prompts.c b/utils/prompts.c index e01dd01c..d0823334 100644 --- a/utils/prompts.c +++ b/utils/prompts.c @@ -11,7 +11,7 @@ prompts_t *new_prompts(void) p->prompts = NULL; p->n_prompts = p->prompts_size = 0; p->data = NULL; - p->idata = -1; + p->spr = SPR_INCOMPLETE; p->to_server = true; /* to be on the safe side */ p->name = p->instruction = NULL; p->name_reqd = p->instr_reqd = false; diff --git a/utils/spr_get_error_message.c b/utils/spr_get_error_message.c new file mode 100644 index 00000000..b9f81a47 --- /dev/null +++ b/utils/spr_get_error_message.c @@ -0,0 +1,13 @@ +/* + * Construct the error message from a SeatPromptResult, and return it + * in a dynamically allocated string. + */ + +#include "putty.h" + +char *spr_get_error_message(SeatPromptResult spr) +{ + strbuf *sb = strbuf_new(); + spr.errfn(spr, BinarySink_UPCAST(sb)); + return strbuf_to_str(sb); +} diff --git a/utils/tempseat.c b/utils/tempseat.c index 5854a5dc..785c00c2 100644 --- a/utils/tempseat.c +++ b/utils/tempseat.c @@ -218,7 +218,7 @@ static bool tempseat_has_mixed_input_stream(Seat *seat) * for the network connection. */ -static int tempseat_get_userpass_input(Seat *seat, prompts_t *p) +static SeatPromptResult tempseat_get_userpass_input(Seat *seat, prompts_t *p) { /* * Interactive prompts of this nature are a thing that a backend @@ -235,25 +235,25 @@ static size_t tempseat_banner(Seat *seat, const void *data, size_t len) unreachable("banner should never be called on TempSeat"); } -static int tempseat_confirm_ssh_host_key( +static SeatPromptResult tempseat_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { unreachable("confirm_ssh_host_key should never be called on TempSeat"); } -static int tempseat_confirm_weak_crypto_primitive( +static SeatPromptResult tempseat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { unreachable("confirm_weak_crypto_primitive " "should never be called on TempSeat"); } -static int tempseat_confirm_weak_cached_hostkey( +static SeatPromptResult tempseat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { unreachable("confirm_weak_cached_hostkey " "should never be called on TempSeat"); diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 7338f4c2..0a999e51 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -16,6 +16,7 @@ add_sources_from_current_dir(utils utils/load_system32_dll.c utils/ltime.c utils/makedlgitemborderless.c + utils/make_spr_sw_abort_winerror.c utils/message_box.c utils/minefield.c utils/open_for_write_would_lose_data.c diff --git a/windows/console.c b/windows/console.c index 9fa9d7f0..8cb27c5a 100644 --- a/windows/console.c +++ b/windows/console.c @@ -32,10 +32,10 @@ void console_print_error_msg(const char *prefix, const char *msg) fflush(stderr); } -int console_confirm_ssh_host_key( +SeatPromptResult console_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { HANDLE hin; DWORD savemode, i; @@ -64,7 +64,7 @@ int console_confirm_ssh_host_key( if (console_batch_mode) { fputs(console_abandoned_msg, stderr); - return 0; + return SPR_SW_ABORT("Cannot confirm a host key in batch mode"); } fputs(intro, stderr); @@ -102,16 +102,16 @@ int console_confirm_ssh_host_key( line[0] != 'q' && line[0] != 'Q') { if (line[0] == 'y' || line[0] == 'Y') store_host_key(host, port, keytype, keystr); - return 1; + return SPR_OK; } else { fputs(console_abandoned_msg, stderr); - return 0; + return SPR_USER_ABORT; } } -int console_confirm_weak_crypto_primitive( +SeatPromptResult console_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { HANDLE hin; DWORD savemode, i; @@ -122,7 +122,8 @@ int console_confirm_weak_crypto_primitive( if (console_batch_mode) { fputs(console_abandoned_msg, stderr); - return 0; + return SPR_SW_ABORT("Cannot confirm a weak crypto primitive " + "in batch mode"); } fputs(console_continue_prompt, stderr); @@ -136,16 +137,16 @@ int console_confirm_weak_crypto_primitive( SetConsoleMode(hin, savemode); if (line[0] == 'y' || line[0] == 'Y') { - return 1; + return SPR_OK; } else { fputs(console_abandoned_msg, stderr); - return 0; + return SPR_USER_ABORT; } } -int console_confirm_weak_cached_hostkey( +SeatPromptResult console_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { HANDLE hin; DWORD savemode, i; @@ -156,7 +157,8 @@ int console_confirm_weak_cached_hostkey( if (console_batch_mode) { fputs(console_abandoned_msg, stderr); - return 0; + return SPR_SW_ABORT("Cannot confirm a weak cached host key " + "in batch mode"); } fputs(console_continue_prompt, stderr); @@ -170,10 +172,10 @@ int console_confirm_weak_cached_hostkey( SetConsoleMode(hin, savemode); if (line[0] == 'y' || line[0] == 'Y') { - return 1; + return SPR_OK; } else { fputs(console_abandoned_msg, stderr); - return 0; + return SPR_USER_ABORT; } } @@ -343,7 +345,7 @@ static void console_write(HANDLE hout, ptrlen data) WriteFile(hout, data.ptr, data.len, &dummy, NULL); } -int console_get_userpass_input(prompts_t *p) +SeatPromptResult console_get_userpass_input(prompts_t *p) { HANDLE hin = INVALID_HANDLE_VALUE, hout = INVALID_HANDLE_VALUE; size_t curr_prompt; @@ -365,7 +367,8 @@ int console_get_userpass_input(prompts_t *p) */ if (p->n_prompts) { if (console_batch_mode) - return 0; + return SPR_SW_ABORT("Cannot answer interactive prompts " + "in batch mode"); hin = GetStdHandle(STD_INPUT_HANDLE); if (hin == INVALID_HANDLE_VALUE) { fprintf(stderr, "Cannot get standard input handle\n"); @@ -418,6 +421,7 @@ int console_get_userpass_input(prompts_t *p) console_write(hout, ptrlen_from_asciz(pr->prompt)); bool failed = false; + SeatPromptResult spr; while (1) { /* * Amount of data to try to read from the console in one @@ -440,8 +444,17 @@ int console_get_userpass_input(prompts_t *p) void *ptr = strbuf_append(pr->result, toread); DWORD ret = 0; - if (!ReadFile(hin, ptr, toread, &ret, NULL) || ret == 0) { + if (!ReadFile(hin, ptr, toread, &ret, NULL)) { + /* An OS error when reading from the console is treated as an + * unexpected error and reported to the user. */ failed = true; + spr = make_spr_sw_abort_winerror( + "Error reading from console", GetLastError()); + break; + } else if (ret == 0) { + /* Regard EOF on the terminal as a deliberate user-abort */ + failed = true; + spr = SPR_USER_ABORT; break; } @@ -457,10 +470,9 @@ int console_get_userpass_input(prompts_t *p) if (!pr->echo) console_write(hout, PTRLEN_LITERAL("\r\n")); - if (failed) { - return 0; /* failure due to read error */ - } + if (failed) + return spr; } - return 1; /* success */ + return SPR_OK; } diff --git a/windows/dialog.c b/windows/dialog.c index b790f0f4..66f26128 100644 --- a/windows/dialog.c +++ b/windows/dialog.c @@ -976,10 +976,10 @@ static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg, return 0; } -int win_seat_confirm_ssh_host_key( +SeatPromptResult win_seat_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *vctx) + void (*callback)(void *ctx, SeatPromptResult result), void *vctx) { WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat); @@ -1008,21 +1008,21 @@ int win_seat_confirm_ssh_host_key( assert(mbret==IDC_HK_ACCEPT || mbret==IDC_HK_ONCE || mbret==IDCANCEL); if (mbret == IDC_HK_ACCEPT) { store_host_key(host, port, keytype, keystr); - return 1; + return SPR_OK; } else if (mbret == IDC_HK_ONCE) { - return 1; + return SPR_OK; } - return 0; /* abandon the connection */ + return SPR_USER_ABORT; } /* * Ask whether the selected algorithm is acceptable (since it was * below the configured 'warn' threshold). */ -int win_seat_confirm_weak_crypto_primitive( +SeatPromptResult win_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { static const char mbtitle[] = "%s Security Alert"; static const char msg[] = @@ -1041,14 +1041,14 @@ int win_seat_confirm_weak_crypto_primitive( sfree(message); sfree(title); if (mbret == IDYES) - return 1; + return SPR_OK; else - return 0; + return SPR_USER_ABORT; } -int win_seat_confirm_weak_cached_hostkey( +SeatPromptResult win_seat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) + void (*callback)(void *ctx, SeatPromptResult result), void *ctx) { static const char mbtitle[] = "%s Security Alert"; static const char msg[] = @@ -1069,9 +1069,9 @@ int win_seat_confirm_weak_cached_hostkey( sfree(message); sfree(title); if (mbret == IDYES) - return 1; + return SPR_OK; else - return 0; + return SPR_USER_ABORT; } /* diff --git a/windows/platform.h b/windows/platform.h index 660bf590..78ebf188 100644 --- a/windows/platform.h +++ b/windows/platform.h @@ -216,16 +216,16 @@ int has_embedded_chm(void); /* 1 = yes, 0 = no, -1 = N/A */ * GUI seat methods in windlg.c, so that the vtable definition in * window.c can refer to them. */ -int win_seat_confirm_ssh_host_key( +SeatPromptResult win_seat_confirm_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, bool mismatch, - void (*callback)(void *ctx, int result), void *ctx); -int win_seat_confirm_weak_crypto_primitive( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); +SeatPromptResult win_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx); -int win_seat_confirm_weak_cached_hostkey( + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); +SeatPromptResult win_seat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx); + void (*callback)(void *ctx, SeatPromptResult result), void *ctx); /* * Windows-specific clipboard helper function shared with windlg.c, @@ -734,4 +734,6 @@ bool handle_special_filemapping_cmdline(char *cmdline, Conf *conf); void plug_closing_system_error(Plug *plug, DWORD error); void plug_closing_winsock_error(Plug *plug, DWORD error); +SeatPromptResult make_spr_sw_abort_winerror(const char *prefix, DWORD error); + #endif /* PUTTY_WINDOWS_PLATFORM_H */ diff --git a/windows/plink.c b/windows/plink.c index 037387e9..af95c2b9 100644 --- a/windows/plink.c +++ b/windows/plink.c @@ -65,13 +65,13 @@ 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) +static SeatPromptResult plink_get_userpass_input(Seat *seat, prompts_t *p) { - int ret; - ret = cmdline_get_passwd_input(p); - if (ret == -1) - ret = console_get_userpass_input(p); - return ret; + SeatPromptResult spr; + spr = cmdline_get_passwd_input(p); + if (spr.kind == SPRK_INCOMPLETE) + spr = console_get_userpass_input(p); + return spr; } static bool plink_seat_interactive(Seat *seat) diff --git a/windows/sftp.c b/windows/sftp.c index b2e90faa..bc16f5d1 100644 --- a/windows/sftp.c +++ b/windows/sftp.c @@ -12,13 +12,13 @@ #include "ssh.h" #include "security-api.h" -int filexfer_get_userpass_input(Seat *seat, prompts_t *p) +SeatPromptResult filexfer_get_userpass_input(Seat *seat, prompts_t *p) { - int ret; - ret = cmdline_get_passwd_input(p); - if (ret == -1) - ret = console_get_userpass_input(p); - return ret; + SeatPromptResult spr; + spr = cmdline_get_passwd_input(p); + if (spr.kind == SPRK_INCOMPLETE) + spr = console_get_userpass_input(p); + return spr; } void platform_get_x11_auth(struct X11Display *display, Conf *conf) diff --git a/windows/utils/make_spr_sw_abort_winerror.c b/windows/utils/make_spr_sw_abort_winerror.c new file mode 100644 index 00000000..b05ef61f --- /dev/null +++ b/windows/utils/make_spr_sw_abort_winerror.c @@ -0,0 +1,22 @@ +/* + * Constructor function for a SeatPromptResult of the 'software abort' + * category, whose error message includes the translation of an OS + * error code. + */ + +#include "putty.h" + +static void spr_winerror_errfn(SeatPromptResult spr, BinarySink *bs) +{ + put_fmt(bs, "%s: %s", spr.errdata_lit, win_strerror(spr.errdata_u)); +} + +SeatPromptResult make_spr_sw_abort_winerror(const char *prefix, DWORD error) +{ + SeatPromptResult spr; + spr.kind = SPRK_SW_ABORT; + spr.errfn = spr_winerror_errfn; + spr.errdata_lit = prefix; + spr.errdata_u = error; + return spr; +} diff --git a/windows/window.c b/windows/window.c index b1a884dd..4d61a709 100644 --- a/windows/window.c +++ b/windows/window.c @@ -320,7 +320,7 @@ static StripCtrlChars *win_seat_stripctrl_new( static size_t win_seat_output( Seat *seat, SeatOutputType type, const void *, size_t); static bool win_seat_eof(Seat *seat); -static int win_seat_get_userpass_input(Seat *seat, prompts_t *p); +static SeatPromptResult 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); @@ -5787,13 +5787,13 @@ 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) +static SeatPromptResult 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); - return ret; + SeatPromptResult spr; + spr = cmdline_get_passwd_input(p); + if (spr.kind == SPRK_INCOMPLETE) + spr = term_get_userpass_input(term, p); + return spr; } static void win_seat_set_trust_status(Seat *seat, bool trusted)