소스 검색

Merge branch 'thirdparty_dev' into dev

# Conflicts:
#	source/putty/misc.c
#	source/putty/putty.h
#	source/putty/ssh2userauth.c
#	source/putty/sshcommon.c

Source commit: 9895bb625ad4a6cb555e9a574cdd463d22194e9d
Martin Prikryl 6 년 전
부모
커밋
e8e16a5ccc

+ 2 - 2
source/putty/be_misc.c

@@ -8,7 +8,7 @@
 #include "putty.h"
 #include "network.h"
 
-void backend_socket_log(Frontend *frontend, LogContext *logctx,
+void backend_socket_log(Seat *seat, LogContext *logctx,
                         int type, SockAddr *addr, int port,
                         const char *error_msg, int error_code, Conf *conf,
                         int session_started)
@@ -43,7 +43,7 @@ void backend_socket_log(Frontend *frontend, LogContext *logctx,
             if (log_to_term == AUTO)
                 log_to_term = session_started ? FORCE_OFF : FORCE_ON;
             if (log_to_term == FORCE_ON)
-                from_backend(frontend, TRUE, msg, len);
+                seat_stderr(seat, msg, len);
 
             msg[len-2] = '\0';         /* remove the \r\n again */
         }

+ 3 - 0
source/putty/defs.h

@@ -53,6 +53,9 @@ typedef struct LogContext LogContext;
 typedef struct LogPolicy LogPolicy;
 typedef struct LogPolicyVtable LogPolicyVtable;
 
+typedef struct Seat Seat;
+typedef struct SeatVtable SeatVtable;
+
 typedef struct Frontend Frontend;
 
 typedef struct Ssh Ssh;

+ 41 - 2
source/putty/misc.c

@@ -216,12 +216,24 @@ char *host_strduptrim(const char *s)
     return dupstr(s);
 }
 
-prompts_t *new_prompts(Frontend *frontend)
+void seat_connection_fatal(Seat *seat, const char *fmt, ...)
+{
+    va_list ap;
+    char *msg;
+
+    va_start(ap, fmt);
+    msg = dupvprintf(fmt, ap);
+    va_end(ap);
+
+    seat->vt->connection_fatal(seat, msg);
+    sfree(msg);                        /* if we return */
+}
+
+prompts_t *new_prompts(void)
 {
     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;
@@ -1371,3 +1383,30 @@ const char * get_putty_version()
 }
 
 #endif
+int nullseat_output(
+    Seat *seat, int is_stderr, const void *data, int len) { return 0; }
+int nullseat_eof(Seat *seat) { return TRUE; }
+int nullseat_get_userpass_input(
+    Seat *seat, prompts_t *p, bufchain *input) { return 0; }
+void nullseat_notify_remote_exit(Seat *seat) {}
+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_verify_ssh_host_key(
+    Seat *seat, const char *host, int port,
+    const char *keytype, char *keystr, char *key_fingerprint,
+    void (*callback)(void *ctx, int result), void *ctx) { return 0; }
+int 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(
+    Seat *seat, const char *algname, const char *betteralgs,
+    void (*callback)(void *ctx, int result), void *ctx) { return 0; }
+int nullseat_is_never_utf8(Seat *seat) { return FALSE; }
+int nullseat_is_always_utf8(Seat *seat) { return TRUE; }
+void nullseat_echoedit_update(Seat *seat, int echoing, int editing) {}
+const char *nullseat_get_x_display(Seat *seat) { return NULL; }
+int nullseat_get_windowid(Seat *seat, long *id_out) { return FALSE; }
+int nullseat_get_char_cell_size(
+    Seat *seat, int *width, int *height) { return FALSE; }

+ 1 - 1
source/putty/network.h

@@ -245,7 +245,7 @@ extern Plug *const nullplug;
 /*
  * Exports from be_misc.c.
  */
-void backend_socket_log(Frontend *frontend, LogContext *logctx,
+void backend_socket_log(Seat *seat, LogContext *logctx,
                         int type, SockAddr *addr, int port,
                         const char *error_msg, int error_code, Conf *conf,
                         int session_started);

+ 310 - 67
source/putty/putty.h

@@ -479,7 +479,7 @@ struct Backend {
     const BackendVtable *vt;
 };
 struct BackendVtable {
-    const char *(*init) (Frontend *frontend, Backend **backend_out,
+    const char *(*init) (Seat *seat, Backend **backend_out,
                          LogContext *logctx, Conf *conf,
                          const char *host, int port,
                          char **realhost, int nodelay, int keepalive);
@@ -515,8 +515,8 @@ struct BackendVtable {
     int default_port;
 };
 
-#define backend_init(vt, fe, out, logctx, conf, host, port, rhost, nd, ka) \
-    ((vt)->init(fe, out, logctx, conf, host, port, rhost, nd, ka))
+#define backend_init(vt, seat, out, logctx, conf, host, port, rhost, nd, ka) \
+    ((vt)->init(seat, out, logctx, conf, host, port, rhost, nd, ka))
 #define backend_free(be) ((be)->vt->free(be))
 #define backend_reconfig(be, conf) ((be)->vt->reconfig(be, conf))
 #define backend_send(be, buf, len) ((be)->vt->send(be, buf, len))
@@ -635,11 +635,10 @@ typedef struct {
     size_t n_prompts;   /* May be zero (in which case display the foregoing,
                          * if any, and return success) */
     prompt_t **prompts;
-    Frontend *frontend;
     void *data;		/* slot for housekeeping data, managed by
-			 * get_userpass_input(); initially NULL */
+			 * seat_get_userpass_input(); initially NULL */
 } prompts_t;
-prompts_t *new_prompts(Frontend *frontend);
+prompts_t *new_prompts();
 void add_prompt(prompts_t *p, char *promptstr, int echo);
 void prompt_set_result(prompt_t *pr, const char *newstr);
 void prompt_ensure_result_size(prompt_t *pr, int len);
@@ -697,6 +696,306 @@ typedef struct truecolour {
 enum { ALL_CLIPBOARDS(CLIP_ID) N_CLIPBOARDS };
 #undef CLIP_ID
 
+/* Hint from backend to frontend about time-consuming operations, used
+ * by seat_set_busy_status. Initial state is assumed to be
+ * BUSY_NOT. */
+typedef enum BusyStatus {
+    BUSY_NOT,	    /* Not busy, all user interaction OK */
+    BUSY_WAITING,   /* Waiting for something; local event loops still
+		       running so some local interaction (e.g. menus)
+		       OK, but network stuff is suspended */
+    BUSY_CPU	    /* Locally busy (e.g. crypto); user interaction
+                     * suspended */
+} BusyStatus;
+
+/*
+ * Data type 'Seat', which is an API intended to contain essentially
+ * everything that a back end might need to talk to its client for:
+ * session output, password prompts, SSH warnings about host keys and
+ * weak cryptography, notifications of events like the remote process
+ * exiting or the GUI specials menu needing an update.
+ */
+struct Seat {
+    const struct SeatVtable *vt;
+};
+struct SeatVtable {
+    /*
+     * Provide output from the remote session. 'is_stderr' indicates
+     * that the output should be sent to a separate error message
+     * channel, if the seat has one. But combining both channels into
+     * one is OK too; that's what terminal-window based seats do.
+     *
+     * The return value is the current size of the output backlog.
+     */
+    int (*output)(Seat *seat, int is_stderr, const void *data, int len);
+
+    /*
+     * Called when the back end wants to indicate that EOF has arrived
+     * on the server-to-client stream. Returns FALSE to indicate that
+     * we intend to keep the session open in the other direction, or
+     * TRUE to indicate that if they're closing so are we.
+     */
+    int (*eof)(Seat *seat);
+
+    /*
+     * Try to get answers from a set of interactive login prompts. The
+     * prompts are provided in 'p'; the bufchain 'input' holds the
+     * data currently outstanding in the session's normal standard-
+     * input channel. Seats may implement this function by consuming
+     * data from 'input' (e.g. password prompts in GUI PuTTY,
+     * displayed in the same terminal as the subsequent session), or
+     * by doing something entirely different (e.g. directly
+     * interacting with standard I/O, or putting up a dialog box).
+     *
+     * 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
+     * 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
+     * remember your private key passphrase then perhaps you'd rather
+     * fall back to password auth rather than aborting the whole
+     * session.)
+     *
+     * (Also FIXME: currently, backends' only response to the 'try
+     * again later' is to try again when more input data becomes
+     * available, because they assume that a seat is returning that
+     * value because it's consuming keyboard input. But a seat that
+     * handled this function by putting up a dialog box might want to
+     * put it up non-modally, and therefore would want to proactively
+     * notify the backend to retry once the dialog went away. So if I
+     * ever do want to move password prompts into a dialog box, I'll
+     * want a backend method for sending that notification.)
+     */
+    int (*get_userpass_input)(Seat *seat, prompts_t *p, bufchain *input);
+
+    /*
+     * Notify the seat that the process running at the other end of
+     * the connection has finished.
+     */
+    void (*notify_remote_exit)(Seat *seat);
+
+    /*
+     * Notify the seat that the connection has suffered a fatal error.
+     */
+    void (*connection_fatal)(Seat *seat, const char *message);
+
+    /*
+     * Notify the seat that the list of special commands available
+     * from backend_get_specials() has changed, so that it might want
+     * to call that function to repopulate its menu.
+     *
+     * Seats are not expected to call backend_get_specials()
+     * proactively; they may start by assuming that the backend
+     * provides no special commands at all, so if the backend does
+     * provide any, then it should use this notification at startup
+     * time. Of course it can also invoke it later if the set of
+     * special commands changes.
+     *
+     * It does not need to invoke it at session shutdown.
+     */
+    void (*update_specials_menu)(Seat *seat);
+
+    /*
+     * Get the seat's preferred value for an SSH terminal mode
+     * setting. Returning NULL indicates no preference (i.e. the SSH
+     * connection will not attempt to set the mode at all).
+     *
+     * The returned value is dynamically allocated, and the caller
+     * should free it.
+     */
+    char *(*get_ttymode)(Seat *seat, const char *mode);
+
+    /*
+     * Tell the seat whether the backend is currently doing anything
+     * CPU-intensive (typically a cryptographic key exchange). See
+     * BusyStatus enumeration above.
+     */
+    void (*set_busy_status)(Seat *seat, BusyStatus status);
+
+    /*
+     * Ask the seat whether a given SSH host key should be accepted.
+     * This may return immediately after checking saved configuration
+     * or command-line options, or it may have to present a prompt to
+     * the user and return asynchronously later.
+     *
+     * Return values:
+     *
+     *  - +1 means `key was OK' (either already known or the user just
+     *    approved it) `so continue with the connection'
+     *
+     *  - 0 means `key was not OK, abandon the connection'
+     *
+     *  - -1 means `I've initiated enquiries, please wait to be called
+     *    back via the provided function with a result that's either 0
+     *    or +1'.
+     */
+    int (*verify_ssh_host_key)(
+        Seat *seat, const char *host, int port,
+        const char *keytype, char *keystr, char *key_fingerprint,
+        void (*callback)(void *ctx, int result), void *ctx);
+
+    /*
+     * Check with the seat whether it's OK to use a cryptographic
+     * primitive from below the 'warn below this line' threshold in
+     * the input Conf. Return values are the same as
+     * verify_ssh_host_key above.
+     */
+    int (*confirm_weak_crypto_primitive)(
+        Seat *seat, const char *algtype, const char *algname,
+        void (*callback)(void *ctx, int result), void *ctx);
+
+    /*
+     * Variant form of confirm_weak_crypto_primitive, which prints a
+     * slightly different message but otherwise has the same
+     * semantics.
+     *
+     * This form is used in the case where we're using a host key
+     * below the warning threshold because that's the best one we have
+     * cached, but at least one host key algorithm *above* the
+     * threshold is available that we don't have cached. 'betteralgs'
+     * lists the better algorithm(s).
+     */
+    int (*confirm_weak_cached_hostkey)(
+        Seat *seat, const char *algname, const char *betteralgs,
+        void (*callback)(void *ctx, int result), void *ctx);
+
+    /*
+     * Indicates whether the seat is expecting to interact with the
+     * user in the UTF-8 character set. (Affects e.g. visual erase
+     * handling in local line editing.)
+     */
+    int (*is_utf8)(Seat *seat);
+
+    /*
+     * Notify the seat that the back end, and/or the ldisc between
+     * them, have changed their idea of whether they currently want
+     * local echo and/or local line editing enabled.
+     */
+    void (*echoedit_update)(Seat *seat, int echoing, int editing);
+
+    /*
+     * Return the local X display string relevant to a seat, or NULL
+     * if there isn't one or if the concept is meaningless.
+     */
+    const char *(*get_x_display)(Seat *seat);
+
+    /*
+     * Return the X11 id of the X terminal window relevant to a seat,
+     * by returning TRUE and filling in the output pointer. Return
+     * FALSE if there isn't one or if the concept is meaningless.
+     */
+    int (*get_windowid)(Seat *seat, long *id_out);
+
+    /*
+     * Return the pixel size of a terminal character cell. If the
+     * concept is meaningless or the information is unavailable,
+     * return FALSE; otherwise fill in the output pointers and return
+     * TRUE.
+     */
+    int (*get_char_cell_size)(Seat *seat, int *width, int *height);
+};
+
+#define seat_output(seat, is_stderr, data, len) \
+    ((seat)->vt->output(seat, is_stderr, data, len))
+#define seat_eof(seat) \
+    ((seat)->vt->eof(seat))
+#define seat_get_userpass_input(seat, p, input) \
+    ((seat)->vt->get_userpass_input(seat, p, input))
+#define seat_notify_remote_exit(seat) \
+    ((seat)->vt->notify_remote_exit(seat))
+#define seat_update_specials_menu(seat) \
+    ((seat)->vt->update_specials_menu(seat))
+#define seat_get_ttymode(seat, mode) \
+    ((seat)->vt->get_ttymode(seat, mode))
+#define seat_set_busy_status(seat, status) \
+    ((seat)->vt->set_busy_status(seat, status))
+#define seat_verify_ssh_host_key(seat, h, p, typ, str, fp, cb, ctx) \
+    ((seat)->vt->verify_ssh_host_key(seat, h, p, typ, str, fp, cb, ctx))
+#define seat_confirm_weak_crypto_primitive(seat, typ, alg, cb, ctx) \
+    ((seat)->vt->confirm_weak_crypto_primitive(seat, typ, alg, cb, ctx))
+#define seat_confirm_weak_cached_hostkey(seat, alg, better, cb, ctx) \
+    ((seat)->vt->confirm_weak_cached_hostkey(seat, alg, better, cb, ctx))
+#define seat_is_utf8(seat) \
+    ((seat)->vt->is_utf8(seat))
+#define seat_echoedit_update(seat, echoing, editing) \
+    ((seat)->vt->echoedit_update(seat, echoing, editing))
+#define seat_get_x_display(seat) \
+    ((seat)->vt->get_x_display(seat))
+#define seat_get_windowid(seat, out) \
+    ((seat)->vt->get_windowid(seat, out))
+#define seat_get_char_cell_size(seat, width, height) \
+    ((seat)->vt->get_char_cell_size(seat, width, height))
+
+/* Unlike the seat's actual method, the public entry point
+ * seat_connection_fatal is a wrapper function with a printf-like API,
+ * defined in misc.c. */
+void seat_connection_fatal(Seat *seat, const char *fmt, ...);
+
+/* Handy aliases for seat_output which set is_stderr to a fixed value. */
+#define seat_stdout(seat, data, len) \
+    seat_output(seat, FALSE, data, len)
+#define seat_stderr(seat, data, len) \
+    seat_output(seat, TRUE, data, len)
+
+/*
+ * Stub methods for seat implementations that want to use the obvious
+ * null handling for a given method.
+ *
+ * These are generally obvious, except for is_utf8, where you might
+ * plausibly want to return either fixed answer 'no' or 'yes'.
+ */
+int nullseat_output(Seat *seat, int is_stderr, const void *data, int len);
+int nullseat_eof(Seat *seat);
+int nullseat_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input);
+void nullseat_notify_remote_exit(Seat *seat);
+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_verify_ssh_host_key(
+    Seat *seat, const char *host, int port,
+    const char *keytype, char *keystr, char *key_fingerprint,
+    void (*callback)(void *ctx, int result), void *ctx);
+int 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(
+    Seat *seat, const char *algname, const char *betteralgs,
+    void (*callback)(void *ctx, int result), void *ctx);
+int nullseat_is_never_utf8(Seat *seat);
+int nullseat_is_always_utf8(Seat *seat);
+void nullseat_echoedit_update(Seat *seat, int echoing, int editing);
+const char *nullseat_get_x_display(Seat *seat);
+int nullseat_get_windowid(Seat *seat, long *id_out);
+int nullseat_get_char_cell_size(Seat *seat, int *width, int *height);
+
+/*
+ * Seat functions provided by the platform's console-application
+ * support module (wincons.c, uxcons.c).
+ */
+
+void console_connection_fatal(Seat *seat, const char *message);
+int console_verify_ssh_host_key(
+    Seat *seat, const char *host, int port,
+    const char *keytype, char *keystr, char *key_fingerprint,
+    void (*callback)(void *ctx, int result), void *ctx);
+int 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(
+    Seat *seat, const char *algname, const char *betteralgs,
+    void (*callback)(void *ctx, int result), void *ctx);
+
+/*
+ * Other centralised seat functions.
+ */
+int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input);
+
 /*
  * Exports from the front end.
  */
@@ -721,7 +1020,6 @@ void write_clip(Frontend *frontend, int clipboard, wchar_t *, int *,
                 truecolour *, int, int);
 void optimised_move(Frontend *frontend, int, int, int);
 void set_raw_mouse_mode(Frontend *frontend, int);
-void connection_fatal(Frontend *frontend, const char *, ...);
 void nonfatal(const char *, ...);
 void modalfatalbox(const char *, ...);
 #ifdef macintosh
@@ -730,28 +1028,6 @@ void modalfatalbox(const char *, ...);
 void do_beep(Frontend *frontend, int);
 void sys_cursor(Frontend *frontend, int x, int y);
 void frontend_request_paste(Frontend *frontend, int clipboard);
-void frontend_echoedit_update(Frontend *frontend, int echo, int edit);
-/* It's the backend's responsibility to invoke this at the start of a
- * connection, if necessary; it can also invoke it later if the set of
- * special commands changes. It does not need to invoke it at session
- * shutdown. */
-void update_specials_menu(Frontend *frontend);
-int from_backend(Frontend *frontend, int is_stderr, const void *data, int len);
-/* Called when the back end wants to indicate that EOF has arrived on
- * the server-to-client stream. Returns FALSE to indicate that we
- * intend to keep the session open in the other direction, or TRUE to
- * indicate that if they're closing so are we. */
-int from_backend_eof(Frontend *frontend);
-void notify_remote_exit(Frontend *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(Frontend *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 a fuller bufchain'
- */
-int get_userpass_input(prompts_t *p, bufchain *input);
 #define OPTIMISE_IS_SCROLL 1
 
 void set_iconic(Frontend *frontend, int iconic);
@@ -763,16 +1039,6 @@ int is_iconic(Frontend *frontend);
 void get_window_pos(Frontend *frontend, int *x, int *y);
 void get_window_pixels(Frontend *frontend, int *x, int *y);
 char *get_window_title(Frontend *frontend, int icon);
-/* Hint from backend to frontend about time-consuming operations.
- * Initial state is assumed to be BUSY_NOT. */
-enum {
-    BUSY_NOT,	    /* Not busy, all user interaction OK */
-    BUSY_WAITING,   /* Waiting for something; local event loops still running
-		       so some local interaction (e.g. menus) OK, but network
-		       stuff is suspended */
-    BUSY_CPU	    /* Locally busy (e.g. crypto); user interaction suspended */
-};
-void set_busy_status(Frontend *frontend, int status);
 int frontend_is_utf8(Frontend *frontend);
 
 void cleanup_exit(int);
@@ -1280,7 +1546,7 @@ extern const struct BackendVtable ssh_backend;
 /*
  * Exports from ldisc.c.
  */
-Ldisc *ldisc_create(Conf *, Terminal *, Backend *, Frontend *);
+Ldisc *ldisc_create(Conf *, Terminal *, Backend *, Seat *);
 void ldisc_configure(Ldisc *, Conf *);
 void ldisc_free(Ldisc *);
 void ldisc_send(Ldisc *, const void *buf, int len, int interactive);
@@ -1409,21 +1675,6 @@ int wc_unescape(char *output, const char *wildcard);
  * Exports from frontend (windlg.c etc)
  */
 void pgp_fingerprints(void);
-/*
- * verify_ssh_host_key() can return one of three values:
- *
- *  - +1 means `key was OK' (either already known or the user just
- *    approved it) `so continue with the connection'
- *
- *  - 0 means `key was not OK, abandon the connection'
- *
- *  - -1 means `I've initiated enquiries, please wait to be called
- *    back via the provided function with a result that's either 0
- *    or +1'.
- */
-int verify_ssh_host_key(Frontend *frontend, char *host, int port,
-                        const char *keytype, char *keystr, char *fingerprint,
-                        void (*callback)(void *ctx, int result), void *ctx);
 /*
  * have_ssh_host_key() just returns true if a key of that type is
  * already cached and false otherwise.
@@ -1433,18 +1684,6 @@ int have_ssh_host_key(void *frontend, const char *host, int port, const char *ke
 #else
 int have_ssh_host_key(const char *host, int port, const char *keytype);
 #endif
-/*
- * askalg and askhk have the same set of return values as
- * verify_ssh_host_key.
- *
- * (askhk is used in the case where we're using a host key below the
- * warning threshold because that's all we have cached, but at least
- * one acceptable algorithm is available that we don't have cached.)
- */
-int askalg(Frontend *frontend, const char *algtype, const char *algname,
-	   void (*callback)(void *ctx, int result), void *ctx);
-int askhk(Frontend *frontend, const char *algname, const char *betteralgs,
-          void (*callback)(void *ctx, int result), void *ctx);
 
 #ifdef MPEXT
 void display_banner(Frontend *frontend, const char* banner, int size);
@@ -1456,6 +1695,10 @@ void display_banner(Frontend *frontend, const char* banner, int size);
 extern int console_batch_mode;
 int console_get_userpass_input(prompts_t *p);
 int is_interactive(void);
+void console_print_error_msg(const char *prefix, const char *msg);
+void console_print_error_msg_fmt_v(
+    const char *prefix, const char *fmt, va_list ap);
+void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...);
 
 /*
  * Exports from printing.c.

+ 16 - 16
source/putty/ssh.c

@@ -31,7 +31,7 @@
 
 struct Ssh {
     Socket *s;
-    Frontend *frontend;
+    Seat *seat;
     Conf *conf;
 
     struct ssh_version_receiver version_receiver;
@@ -140,7 +140,7 @@ static void ssh_connect_ppl(Ssh *ssh, PacketProtocolLayer *ppl)
 {
     ppl->bpp = ssh->bpp;
     ppl->user_input = &ssh->user_input;
-    ppl->frontend = ssh->frontend;
+    ppl->seat = ssh->seat;
     ppl->ssh = ssh;
     ppl->remote_bugs = ssh->remote_bugs;
 }
@@ -284,7 +284,7 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
     ssh->base_layer->selfptr = &ssh->base_layer;
     ssh_ppl_setup_queues(ssh->base_layer, &ssh->bpp->in_pq, &ssh->bpp->out_pq);
 
-    update_specials_menu(ssh->frontend);
+    seat_update_specials_menu(ssh->seat);
     ssh->pinger = pinger_new(ssh->conf, &ssh->backend);
 
     queue_idempotent_callback(&ssh->bpp->ic_in_raw);
@@ -410,7 +410,7 @@ void ssh_remote_error(Ssh *ssh, const char *fmt, ...)
         ssh_shutdown(ssh);
 
         logevent(ssh->logctx, msg);
-        connection_fatal(ssh->frontend, "%s", msg);
+        seat_connection_fatal(ssh->seat, "%s", msg);
         sfree(msg);
     }
 }
@@ -430,7 +430,7 @@ void ssh_remote_eof(Ssh *ssh, const char *fmt, ...)
 
         logevent(ssh->logctx, msg);
         sfree(msg);
-        notify_remote_exit(ssh->frontend);
+        seat_notify_remote_exit(ssh->seat);
     } else {
         /* This is responding to EOF after we've already seen some
          * other reason for terminating the session. */
@@ -450,7 +450,7 @@ void ssh_proto_error(Ssh *ssh, const char *fmt, ...)
         ssh_initiate_connection_close(ssh);
 
         logevent(ssh->logctx, msg);
-        connection_fatal(ssh->frontend, "%s", msg);
+        seat_connection_fatal(ssh->seat, "%s", msg);
         sfree(msg);
     }
 }
@@ -465,10 +465,10 @@ void ssh_sw_abort(Ssh *ssh, const char *fmt, ...)
         ssh_initiate_connection_close(ssh);
 
         logevent(ssh->logctx, msg);
-        connection_fatal(ssh->frontend, "%s", msg);
+        seat_connection_fatal(ssh->seat, "%s", msg);
         sfree(msg);
 
-        notify_remote_exit(ssh->frontend);
+        seat_notify_remote_exit(ssh->seat);
     }
 }
 
@@ -491,7 +491,7 @@ void ssh_user_close(Ssh *ssh, const char *fmt, ...)
         logevent(ssh->logctx, msg);
         sfree(msg);
 
-        notify_remote_exit(ssh->frontend);
+        seat_notify_remote_exit(ssh->seat);
     }
 }
 
@@ -510,7 +510,7 @@ static void ssh_socket_log(Plug *plug, int type, SockAddr *addr, int port,
      */
 
     if (!ssh->attempting_connshare)
-        backend_socket_log(ssh->frontend, ssh->logctx, type, addr, port,
+        backend_socket_log(ssh->seat, ssh->logctx, type, addr, port,
                            error_msg, error_code, ssh->conf,
                            ssh->session_started);
 }
@@ -672,7 +672,7 @@ static const char *connect_to_host(Ssh *ssh, const char *host, int port,
              * behave in quite the usual way. */
             const char *msg =
                 "Reusing a shared connection to this server.\r\n";
-            from_backend(ssh->frontend, TRUE, msg, strlen(msg));
+            seat_stderr(ssh->seat, msg, strlen(msg));
         }
     } else {
         /*
@@ -696,7 +696,7 @@ static const char *connect_to_host(Ssh *ssh, const char *host, int port,
                                 &ssh->plug, ssh->conf);
         if ((err = sk_socket_error(ssh->s)) != NULL) {
             ssh->s = NULL;
-            notify_remote_exit(ssh->frontend);
+            seat_notify_remote_exit(ssh->seat);
             return err;
         }
     }
@@ -795,7 +795,7 @@ static void ssh_cache_conf_values(Ssh *ssh)
  *
  * Returns an error message, or NULL on success.
  */
-static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
+static const char *ssh_init(Seat *seat, Backend **backend_handle,
                             LogContext *logctx, Conf *conf,
                             const char *host, int port, char **realhost,
 			    int nodelay, int keepalive)
@@ -821,7 +821,7 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
     ssh->backend.vt = &ssh_backend;
     *backend_handle = &ssh->backend;
 
-    ssh->frontend = frontend;
+    ssh->seat = seat;
     ssh->cl_dummy.logctx = ssh->logctx = logctx;
 
     random_ref(); /* do this now - may be needed by sharing setup code */
@@ -1011,8 +1011,8 @@ static void ssh_special(Backend *be, SessionSpecialCode code, int arg)
 }
 
 /*
- * This is called when stdout/stderr (the entity to which
- * from_backend sends data) manages to clear some backlog.
+ * This is called when the seat's output channel manages to clear some
+ * backlog.
  */
 static void ssh_unthrottle(Backend *be, int bufsize)
 {

+ 1 - 1
source/putty/ssh.h

@@ -1424,7 +1424,7 @@ enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_REAL_ENUM) };
 
 /* Shared function that writes tty modes into a pty request */
 void write_ttymodes_to_packet_from_conf(
-    BinarySink *bs, Frontend *frontend, Conf *conf,
+    BinarySink *bs, Seat *seat, Conf *conf,
     int ssh_version, int ospeed, int ispeed);
 
 /* Shared system for allocating local SSH channel ids. Expects to be

+ 3 - 3
source/putty/ssh1connection.c

@@ -579,8 +579,8 @@ static int ssh1_connection_filter_queue(struct ssh1_connection_state *s)
           case SSH1_SMSG_STDERR_DATA:
             data = get_string(pktin);
             if (!get_err(pktin)) {
-                int bufsize = from_backend(
-                    s->ppl.frontend, pktin->type == SSH1_SMSG_STDERR_DATA,
+                int bufsize = seat_output(
+                    s->ppl.seat, pktin->type == SSH1_SMSG_STDERR_DATA,
                     data.ptr, data.len);
                 if (!s->stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
                     s->stdout_throttling = 1;
@@ -706,7 +706,7 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl)
 	put_uint32(pktout, 0); /* width in pixels */
 	put_uint32(pktout, 0); /* height in pixels */
         write_ttymodes_to_packet_from_conf(
-            BinarySink_UPCAST(pktout), s->ppl.frontend, s->conf,
+            BinarySink_UPCAST(pktout), s->ppl.seat, s->conf,
             1, s->ospeed, s->ispeed);
         pq_push(s->ppl.out_pq, pktout);
         crMaybeWaitUntilV((pktin = ssh1_connection_pop(s)) != NULL);

+ 20 - 16
source/putty/ssh1login.c

@@ -279,8 +279,8 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                             "configured list");
             return;
         } else if (s->dlgret < 0) { /* none configured; use standard handling */
-            s->dlgret = verify_ssh_host_key(
-                s->ppl.frontend, s->savedhost, s->savedport,
+            s->dlgret = seat_verify_ssh_host_key(
+                s->ppl.seat, s->savedhost, s->savedport,
                 "rsa", keystr, fingerprint, ssh1_login_dialog_callback, s);
             sfree(keystr);
 #ifdef FUZZING
@@ -359,8 +359,9 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
 
         /* Warn about chosen cipher if necessary. */
         if (warn) {
-            s->dlgret = askalg(s->ppl.frontend, "cipher", cipher_string,
-                               ssh1_login_dialog_callback, s);
+            s->dlgret = seat_confirm_weak_crypto_primitive(
+                s->ppl.seat, "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");
@@ -434,16 +435,17 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
     ppl_logevent(("Successfully started encryption"));
 
     if ((s->username = get_remote_username(s->conf)) == NULL) {
-        s->cur_prompt = new_prompts(s->ppl.frontend);
+        s->cur_prompt = new_prompts();
         s->cur_prompt->to_server = TRUE;
         s->cur_prompt->name = dupstr("SSH login name");
         add_prompt(s->cur_prompt, dupstr("login as: "), TRUE);
-        s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
+        s->userpass_ret = seat_get_userpass_input(
+            s->ppl.seat, s->cur_prompt, NULL);
         while (1) {
             while (s->userpass_ret < 0 &&
                    bufchain_size(s->ppl.user_input) > 0)
-                s->userpass_ret = get_userpass_input(
-                    s->cur_prompt, s->ppl.user_input);
+                s->userpass_ret = seat_get_userpass_input(
+                    s->ppl.seat, s->cur_prompt, s->ppl.user_input);
 
             if (s->userpass_ret >= 0)
                 break;
@@ -691,18 +693,19 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                         ppl_printf(("No passphrase required.\r\n"));
                     passphrase = NULL;
                 } else {
-                    s->cur_prompt = new_prompts(s->ppl.frontend);
+                    s->cur_prompt = new_prompts(s->ppl.seat);
                     s->cur_prompt->to_server = FALSE;
                     s->cur_prompt->name = dupstr("SSH key passphrase");
                     add_prompt(s->cur_prompt,
                                dupprintf("Passphrase for key \"%.100s\": ",
                                          s->publickey_comment), FALSE);
-                    s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
+                    s->userpass_ret = seat_get_userpass_input(
+                        s->ppl.seat, s->cur_prompt, NULL);
                     while (1) {
                         while (s->userpass_ret < 0 &&
                                bufchain_size(s->ppl.user_input) > 0)
-                            s->userpass_ret = get_userpass_input(
-                                s->cur_prompt, s->ppl.user_input);
+                            s->userpass_ret = seat_get_userpass_input(
+                                s->ppl.seat, s->cur_prompt, s->ppl.user_input);
 
                         if (s->userpass_ret >= 0)
                             break;
@@ -830,7 +833,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
         /*
          * Otherwise, try various forms of password-like authentication.
          */
-        s->cur_prompt = new_prompts(s->ppl.frontend);
+        s->cur_prompt = new_prompts(s->ppl.seat);
 
         if (conf_get_int(s->conf, CONF_try_tis_auth) &&
             (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
@@ -950,12 +953,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
          * or CryptoCard exchange if we're doing TIS or CryptoCard
          * authentication.
          */
-        s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
+        s->userpass_ret = seat_get_userpass_input(
+            s->ppl.seat, s->cur_prompt, NULL);
         while (1) {
             while (s->userpass_ret < 0 &&
                    bufchain_size(s->ppl.user_input) > 0)
-                s->userpass_ret = get_userpass_input(
-                    s->cur_prompt, s->ppl.user_input);
+                s->userpass_ret = seat_get_userpass_input(
+                    s->ppl.seat, s->cur_prompt, s->ppl.user_input);
 
             if (s->userpass_ret >= 0)
                 break;

+ 9 - 10
source/putty/ssh2connection.c

@@ -1691,7 +1691,7 @@ static void ssh2_setup_pty(struct ssh2_channel *c, PktIn *pktin, void *ctx)
     {
         strbuf *modebuf = strbuf_new();
         write_ttymodes_to_packet_from_conf(
-            BinarySink_UPCAST(modebuf), cs->ppl.frontend, cs->conf,
+            BinarySink_UPCAST(modebuf), cs->ppl.seat, cs->conf,
             2, s->ospeed, s->ispeed);
         put_stringsb(pktout, modebuf);
     }
@@ -2231,7 +2231,7 @@ static void mainchan_open_confirmation(Channel *chan)
     struct ssh2_connection_state *s = mc->connlayer;
     PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
 
-    update_specials_menu(s->ppl.frontend);
+    seat_update_specials_menu(s->ppl.seat);
     ppl_logevent(("Opened main channel"));
 }
 
@@ -2255,7 +2255,7 @@ static int mainchan_send(Channel *chan, int is_stderr,
     pinitassert(chan->vt == &mainchan_channelvt);
     mainchan *mc = container_of(chan, mainchan, chan);
     struct ssh2_connection_state *s = mc->connlayer;
-    return from_backend(s->ppl.frontend, is_stderr, data, length);
+    return seat_output(s->ppl.seat, is_stderr, data, length);
 }
 
 static void mainchan_send_eof(Channel *chan)
@@ -2265,14 +2265,13 @@ static void mainchan_send_eof(Channel *chan)
     struct ssh2_connection_state *s = mc->connlayer;
     PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
 
-    if (!s->mainchan_eof_sent &&
-        (from_backend_eof(s->ppl.frontend) || s->got_pty)) {
+    if (!s->mainchan_eof_sent && (seat_eof(s->ppl.seat) || s->got_pty)) {
         /*
-         * Either from_backend_eof told us that the front end wants us
-         * to close the outgoing side of the connection as soon as we
-         * see EOF from the far end, or else we've unilaterally
-         * decided to do that because we've allocated a remote pty and
-         * hence EOF isn't a particularly meaningful concept.
+         * Either seat_eof told us that the front end wants us to
+         * close the outgoing side of the connection as soon as we see
+         * EOF from the far end, or else we've unilaterally decided to
+         * do that because we've allocated a remote pty and hence EOF
+         * isn't a particularly meaningful concept.
          */
         sshfwd_write_eof(mc->sc);
         ppl_logevent(("Sent EOF message"));

+ 32 - 31
source/putty/ssh2transport.c

@@ -1146,9 +1146,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
                    BinarySource_UPCAST(pktin)->len + 1);
 
         if (s->warn_kex) {
-            s->dlgret = askalg(s->ppl.frontend, "key-exchange algorithm",
-                               s->kex_alg->name,
-                               ssh2_transport_dialog_callback, s);
+            s->dlgret = seat_confirm_weak_crypto_primitive(
+                s->ppl.seat, "key-exchange algorithm", s->kex_alg->name,
+                ssh2_transport_dialog_callback, s);
             crMaybeWaitUntilV(s->dlgret >= 0);
             if (s->dlgret == 0) {
                 ssh_user_close(s->ppl.ssh, "User aborted at kex warning");
@@ -1163,9 +1163,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
             /*
              * Change warning box wording depending on why we chose a
              * warning-level host key algorithm. If it's because
-             * that's all we have *cached*, use the askhk mechanism,
-             * and list the host keys we could usefully cross-certify.
-             * Otherwise, use askalg for the standard wording.
+             * that's all we have *cached*, list the host keys we
+             * could usefully cross-certify. Otherwise, use the same
+             * standard wording as any other weak crypto primitive.
              */
             betteralgs = NULL;
             for (j = 0; j < s->n_uncert_hostkeys; j++) {
@@ -1194,14 +1194,18 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
                 }
             }
             if (betteralgs) {
-                s->dlgret = askhk(
-                    s->ppl.frontend, s->hostkey_alg->ssh_id, betteralgs,
+                /* Use the special warning prompt that lets us provide
+                 * a list of better algorithms */
+                s->dlgret = seat_confirm_weak_cached_hostkey(
+                    s->ppl.seat, s->hostkey_alg->ssh_id, betteralgs,
                     ssh2_transport_dialog_callback, s);
                 sfree(betteralgs);
             } else {
-                s->dlgret = askalg(s->ppl.frontend, "host key type",
-                                   s->hostkey_alg->ssh_id,
-                                   ssh2_transport_dialog_callback, s);
+                /* If none exist, use the more general 'weak crypto'
+                 * warning prompt */
+                s->dlgret = seat_confirm_weak_crypto_primitive(
+                    s->ppl.seat, "host key type", s->hostkey_alg->ssh_id,
+                    ssh2_transport_dialog_callback, s);
             }
             crMaybeWaitUntilV(s->dlgret >= 0);
             if (s->dlgret == 0) {
@@ -1211,10 +1215,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
         }
 
         if (s->warn_cscipher) {
-            s->dlgret = askalg(s->ppl.frontend,
-                               "client-to-server cipher",
-                               s->out.cipher->name,
-                               ssh2_transport_dialog_callback, s);
+            s->dlgret = seat_confirm_weak_crypto_primitive(
+                s->ppl.seat, "client-to-server cipher", s->out.cipher->name,
+                ssh2_transport_dialog_callback, s);
             crMaybeWaitUntilV(s->dlgret >= 0);
             if (s->dlgret == 0) {
                 ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
@@ -1223,10 +1226,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
         }
 
         if (s->warn_sccipher) {
-            s->dlgret = askalg(s->ppl.frontend,
-                               "server-to-client cipher",
-                               s->in.cipher->name,
-                               ssh2_transport_dialog_callback, s);
+            s->dlgret = seat_confirm_weak_crypto_primitive(
+                s->ppl.seat, "server-to-client cipher", s->in.cipher->name,
+                ssh2_transport_dialog_callback, s);
             crMaybeWaitUntilV(s->dlgret >= 0);
             if (s->dlgret == 0) {
                 ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
@@ -1319,13 +1321,13 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
         /*
          * Now generate and send e for Diffie-Hellman.
          */
-        set_busy_status(s->ppl.frontend, BUSY_CPU); /* this can take a while */
+        seat_set_busy_status(s->ppl.seat, BUSY_CPU);
         s->e = dh_create_e(s->dh_ctx, s->nbits * 2);
         pktout = ssh_bpp_new_pktout(s->ppl.bpp, s->kex_init_value);
         put_mp_ssh2(pktout, s->e);
         pq_push(s->ppl.out_pq, pktout);
 
-        set_busy_status(s->ppl.frontend, BUSY_WAITING); /* wait for server */
+        seat_set_busy_status(s->ppl.seat, BUSY_WAITING);
         crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
         if (pktin->type != s->kex_reply_value) {
             ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
@@ -1336,7 +1338,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
                                           pktin->type));
             return;
         }
-        set_busy_status(s->ppl.frontend, BUSY_CPU); /* cogitate */
+        seat_set_busy_status(s->ppl.seat, BUSY_CPU);
         s->hostkeydata = get_string(pktin);
         s->hkey = ssh_key_new_pub(s->hostkey_alg, s->hostkeydata);
         s->f = get_mp_ssh2(pktin);
@@ -1359,7 +1361,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
 
         /* We assume everything from now on will be quick, and it might
          * involve user interaction. */
-        set_busy_status(s->ppl.frontend, BUSY_NOT);
+        seat_set_busy_status(s->ppl.seat, BUSY_NOT);
 
         put_stringpl(s->exhash, s->hostkeydata);
         if (dh_is_gex(s->kex_alg)) {
@@ -1515,7 +1517,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
         ppl_logevent(("Doing GSSAPI (with Kerberos V5) Diffie-Hellman key "
                       "exchange with hash %s", s->kex_alg->hash->text_name));
         /* Now generate e for Diffie-Hellman. */
-        set_busy_status(s->ppl.frontend, BUSY_CPU); /* this can take a while */
+        seat_set_busy_status(s->ppl.seat, BUSY_CPU);
         s->e = dh_create_e(s->dh_ctx, s->nbits * 2);
 
         if (s->shgss->lib->gsslogmsg)
@@ -1674,7 +1676,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
 
         /* We assume everything from now on will be quick, and it might
          * involve user interaction. */
-        set_busy_status(s->ppl.frontend, BUSY_NOT);
+        seat_set_busy_status(s->ppl.seat, BUSY_NOT);
 
         if (!s->hkey)
             put_stringz(s->exhash, "");
@@ -2031,11 +2033,10 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
                              "configured list");
                 return;
             } else if (s->dlgret < 0) { /* none configured; use standard handling */
-                s->dlgret = verify_ssh_host_key(s->ppl.frontend,
-                                                s->savedhost, s->savedport,
-                                                ssh_key_cache_id(s->hkey),
-                                                s->keystr, s->fingerprint,
-                                                ssh2_transport_dialog_callback, s);
+                s->dlgret = seat_verify_ssh_host_key(
+                    s->ppl.seat, s->savedhost, s->savedport,
+                    ssh_key_cache_id(s->hkey), s->keystr, s->fingerprint,
+                    ssh2_transport_dialog_callback, s);
 #ifdef FUZZING
                 s->dlgret = 1;
 #endif
@@ -2215,7 +2216,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
      * Update the specials menu to list the remaining uncertified host
      * keys.
      */
-    update_specials_menu(s->ppl.frontend);
+    seat_update_specials_menu(s->ppl.seat);
 
     /*
      * Key exchange is over. Loop straight back round if we have a

+ 30 - 25
source/putty/ssh2userauth.c

@@ -368,16 +368,17 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
              * it again.
              */
         } else if ((s->username = s->default_username) == NULL) {
-            s->cur_prompt = new_prompts(s->ppl.frontend);
+            s->cur_prompt = new_prompts();
             s->cur_prompt->to_server = TRUE;
             s->cur_prompt->name = dupstr("SSH login name");
             add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); 
-            s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
+            s->userpass_ret = seat_get_userpass_input(
+                s->ppl.seat, s->cur_prompt, NULL);
             while (1) {
                 while (s->userpass_ret < 0 &&
                        bufchain_size(s->ppl.user_input) > 0)
-                    s->userpass_ret = get_userpass_input(
-                        s->cur_prompt, s->ppl.user_input);
+                    s->userpass_ret = seat_get_userpass_input(
+                        s->ppl.seat, s->cur_prompt, s->ppl.user_input);
 
                 if (s->userpass_ret >= 0)
                     break;
@@ -388,7 +389,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
             }
             if (!s->userpass_ret) {
                 /*
-                 * get_userpass_input() failed to get a username.
+                 * seat_get_userpass_input() failed to get a username.
                  * Terminate.
                  */
                 free_prompts(s->cur_prompt);
@@ -459,7 +460,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                  *
                  * The banner data has been sanitised already by this
                  * point, so we can safely pass it straight to
-                 * from_backend.
+                 * seat_stderr.
                  */
                 if (bufchain_size(&s->banner) &&
                     (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) {
@@ -468,7 +469,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                         int len;
                         bufchain_prefix(&s->banner, &data, &len);
                         display_banner(s->ppl.frontend, &s->banner, len); // WINSCP
-                        from_backend(s->ppl.frontend, TRUE, data, len);
+                        seat_stderr(s->ppl.seat, data, len);
                         bufchain_consume(&s->banner, len);
                     }
                 }
@@ -776,20 +777,21 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                         /*
                          * Get a passphrase from the user.
                          */
-                        s->cur_prompt = new_prompts(s->ppl.frontend);
+                        s->cur_prompt = new_prompts();
                         s->cur_prompt->to_server = FALSE;
                         s->cur_prompt->name = dupstr("SSH key passphrase");
                         add_prompt(s->cur_prompt,
                                    dupprintf("Passphrase for key \"%.100s\": ",
                                              s->publickey_comment),
                                    FALSE);
-                        s->userpass_ret = get_userpass_input(
-                            s->cur_prompt, NULL);
+                        s->userpass_ret = seat_get_userpass_input(
+                            s->ppl.seat, s->cur_prompt, NULL);
                         while (1) {
                             while (s->userpass_ret < 0 &&
                                    bufchain_size(s->ppl.user_input) > 0)
-                                s->userpass_ret = get_userpass_input(
-                                    s->cur_prompt, s->ppl.user_input);
+                                s->userpass_ret = seat_get_userpass_input(
+                                    s->ppl.seat, s->cur_prompt,
+                                    s->ppl.user_input);
 
                             if (s->userpass_ret >= 0)
                                 break;
@@ -1106,7 +1108,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     name = get_string(pktin);
                     inst = get_string(pktin);
                     get_string(pktin); /* skip language tag */
-                    s->cur_prompt = new_prompts(s->ppl.frontend);
+                    s->cur_prompt = new_prompts();
                     s->cur_prompt->to_server = TRUE;
 
                     /*
@@ -1161,12 +1163,13 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                      * Display any instructions, and get the user's
                      * response(s).
                      */
-                    s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
+                    s->userpass_ret = seat_get_userpass_input(
+                        s->ppl.seat, s->cur_prompt, NULL);
                     while (1) {
                         while (s->userpass_ret < 0 &&
                                bufchain_size(s->ppl.user_input) > 0)
-                            s->userpass_ret = get_userpass_input(
-                                s->cur_prompt, s->ppl.user_input);
+                            s->userpass_ret = seat_get_userpass_input(
+                                s->ppl.seat, s->cur_prompt, s->ppl.user_input);
 
                         if (s->userpass_ret >= 0)
                             break;
@@ -1240,19 +1243,20 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                 {
                 // no indentation to ease merges
                 // /WINSCP
-                s->cur_prompt = new_prompts(s->ppl.frontend);
+                s->cur_prompt = new_prompts();
                 s->cur_prompt->to_server = TRUE;
                 s->cur_prompt->name = dupstr("SSH password");
                 add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
                                                     s->username, s->hostname),
                            FALSE);
 
-                s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
+                s->userpass_ret = seat_get_userpass_input(
+                    s->ppl.seat, s->cur_prompt, NULL);
                 while (1) {
                     while (s->userpass_ret < 0 &&
                            bufchain_size(s->ppl.user_input) > 0)
-                        s->userpass_ret = get_userpass_input(
-                            s->cur_prompt, s->ppl.user_input);
+                        s->userpass_ret = seat_get_userpass_input(
+                            s->ppl.seat, s->cur_prompt, s->ppl.user_input);
 
                     if (s->userpass_ret >= 0)
                         break;
@@ -1338,7 +1342,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
 
                     prompt = get_string(pktin);
 
-                    s->cur_prompt = new_prompts(s->ppl.frontend);
+                    s->cur_prompt = new_prompts();
                     s->cur_prompt->to_server = TRUE;
                     s->cur_prompt->name = dupstr("New SSH password");
                     s->cur_prompt->instruction = mkstr(prompt);
@@ -1368,13 +1372,14 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                      * password twice.
                      */
                     while (!got_new) {
-                        s->userpass_ret = get_userpass_input(
-                            s->cur_prompt, NULL);
+                        s->userpass_ret = seat_get_userpass_input(
+                            s->ppl.seat, s->cur_prompt, NULL);
                         while (1) {
                             while (s->userpass_ret < 0 &&
                                    bufchain_size(s->ppl.user_input) > 0)
-                                s->userpass_ret = get_userpass_input(
-                                    s->cur_prompt, s->ppl.user_input);
+                                s->userpass_ret = seat_get_userpass_input(
+                                    s->ppl.seat, s->cur_prompt,
+                                    s->ppl.user_input);
 
                             if (s->userpass_ret >= 0)
                                 break;

+ 4 - 4
source/putty/sshcommon.c

@@ -371,7 +371,7 @@ int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof)
  */
 
 void write_ttymodes_to_packet_from_conf(
-    BinarySink *bs, Frontend *frontend, Conf *conf,
+    BinarySink *bs, Seat *seat, Conf *conf,
     int ssh_version, int ospeed, int ispeed)
 {
     int i;
@@ -472,7 +472,7 @@ void write_ttymodes_to_packet_from_conf(
          *    mode.
          */
         if (sval[0] == 'A') {
-            sval = to_free = get_ttymode(frontend, mode->mode);
+            sval = to_free = seat_get_ttymode(seat, mode->mode);
         } else if (sval[0] == 'V') {
             sval++;                    /* skip the 'V' */
         } else {
@@ -670,7 +670,7 @@ void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new)
     ssh_ppl_setup_queues(new, old->in_pq, old->out_pq);
     new->selfptr = old->selfptr;
     new->user_input = old->user_input;
-    new->frontend = old->frontend;
+    new->seat = old->seat;
     new->ssh = old->ssh;
 
     *new->selfptr = new;
@@ -716,7 +716,7 @@ void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text)
     /* Messages sent via this function are from the SSH layer, not
      * from the server-side process, so they always have the stderr
      * flag set. */
-    from_backend(ppl->frontend, -1, text, strlen(text)); // WINSCP
+    seat_stderr(ppl->seat, text, strlen(text));
     sfree(text);
 }
 

+ 1 - 1
source/putty/sshppl.h

@@ -56,7 +56,7 @@ struct PacketProtocolLayer {
 
     /* Logging and error-reporting facilities. */
     LogContext *logctx;
-    void *frontend;               /* for dialog boxes etc */
+    Seat *seat;             /* for dialog boxes, session output etc */
     Ssh *ssh;   /* for session termination + assorted connection-layer ops */
 
     /* Known bugs in the remote implementation. */

+ 15 - 1
source/putty/windows/winstuff.h

@@ -248,11 +248,25 @@ void quit_help(HWND hwnd);
 /*
  * The terminal and logging context are notionally local to the
  * Windows front end, but they must be shared between window.c and
- * windlg.c. Likewise the saved-sessions list.
+ * windlg.c. Likewise the Seat structure for the Windows GUI.
  */
 GLOBAL Terminal *term;
 GLOBAL LogContext *logctx;
 
+/*
+ * GUI seat methods in windlg.c.
+ */
+int win_seat_verify_ssh_host_key(
+    Seat *seat, const char *host, int port,
+    const char *keytype, char *keystr, char *key_fingerprint,
+    void (*callback)(void *ctx, int result), void *ctx);
+int 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(
+    Seat *seat, const char *algname, const char *betteralgs,
+    void (*callback)(void *ctx, int result), void *ctx);
+
 /*
  * Windows-specific clipboard helper function shared with windlg.c,
  * which takes the data string in the system code page instead of