Browse Source

PuTTY 0.76

Source commit: 8ab62f28c137131b224e5a7b60889c25d7c16507
Martin Prikryl 4 years ago
parent
commit
2514e9cd8f

+ 43 - 0
source/putty/doc/config.but

@@ -2623,6 +2623,49 @@ interact with them.)
 This option only affects SSH-2 connections. SSH-1 connections always
 require an authentication step.
 
+\S{config-ssh-notrivialauth} \q{Disconnect if authentication succeeds
+trivially}
+
+This option causes PuTTY to abandon an SSH session and disconnect from
+the server, if the server accepted authentication without ever having
+asked for any kind of password or signature or token.
+
+This might be used as a security measure. There are some forms of
+attack against an SSH client user which work by terminating the SSH
+authentication stage early, and then doing something in the main part
+of the SSH session which \e{looks} like part of the authentication,
+but isn't really.
+
+For example, instead of demanding a signature from your public key,
+for which PuTTY would ask for your key's passphrase, a compromised or
+malicious server might allow you to log in with no signature or
+password at all, and then print a message that \e{imitates} PuTTY's
+request for your passphrase, in the hope that you would type it in.
+(In fact, the passphrase for your public key should not be sent to any
+server.)
+
+PuTTY's main defence against attacks of this type is the \q{trust
+sigil} system: messages in the PuTTY window that are truly originated
+by PuTTY itself are shown next to a small copy of the PuTTY icon,
+which the server cannot fake when it tries to imitate the same message
+using terminal output.
+
+However, if you think you might be at risk of this kind of thing
+anyway (if you don't watch closely for the trust sigils, or if you
+think you're at extra risk of one of your servers being malicious),
+then you could enable this option as an extra defence. Then, if the
+server tries any of these attacks involving letting you through the
+authentication stage, PuTTY will disconnect from the server before it
+can send a follow-up fake prompt or other type of attack.
+
+On the other hand, some servers \e{legitimately} let you through the
+SSH authentication phase trivially, either because they are genuinely
+public, or because the important authentication step happens during
+the terminal session. (An example might be an SSH server that connects
+you directly to the terminal login prompt of a legacy mainframe.) So
+enabling this option might cause some kinds of session to stop
+working. It's up to you.
+
 \S{config-ssh-tryagent} \q{Attempt authentication using Pageant}
 
 If this option is enabled, then PuTTY will look for Pageant (the SSH

+ 9 - 0
source/putty/doc/man-plink.but

@@ -203,6 +203,15 @@ which of the agent's keys to use. }
 \dd Allow use of an authentication agent. (This option is only necessary
 to override a setting in a saved session.)
 
+\dt \cw{\-no\-trivial\-auth}
+
+\dd Disconnect from any SSH server which accepts authentication without
+ever having asked for any kind of password or signature or token. (You
+might want to enable this for a server you always expect to challenge
+you, for instance to ensure you don't accidentally type your key file's
+passphrase into a compromised server spoofing Plink's passphrase
+prompt.)
+
 \dt \cw{\-noshare}
 
 \dd Don't test and try to share an existing connection, always make

+ 8 - 0
source/putty/doc/man-pscp.but

@@ -155,6 +155,14 @@ which of the agent's keys to use. }
 \dd Allow use of an authentication agent. (This option is only necessary
 to override a setting in a saved session.)
 
+\dt \cw{\-no\-trivial\-auth}
+
+\dd Disconnect from any SSH server which accepts authentication without
+ever having asked for any kind of password or signature or token. (You
+might want to enable this for a server you always expect to challenge
+you, for instance to ensure you don't accidentally type your key file's
+passphrase into a compromised server spoofing PSCP's passphrase prompt.)
+
 \dt \cw{\-hostkey} \e{key}
 
 \dd Specify an acceptable host public key. This option may be specified

+ 9 - 0
source/putty/doc/man-psftp.but

@@ -143,6 +143,15 @@ which of the agent's keys to use. }
 \dd Allow use of an authentication agent. (This option is only necessary
 to override a setting in a saved session.)
 
+\dt \cw{\-no\-trivial\-auth}
+
+\dd Disconnect from any SSH server which accepts authentication without
+ever having asked for any kind of password or signature or token. (You
+might want to enable this for a server you always expect to challenge
+you, for instance to ensure you don't accidentally type your key file's
+passphrase into a compromised server spoofing PSFTP's passphrase
+prompt.)
+
 \dt \cw{\-hostkey} \e{key}
 
 \dd Specify an acceptable host public key. This option may be specified

+ 9 - 0
source/putty/doc/man-putty.but

@@ -287,6 +287,15 @@ which of the agent's keys to use. }
 \dd Allow use of an authentication agent. (This option is only necessary
 to override a setting in a saved session.)
 
+\dt \cw{\-no\-trivial\-auth}
+
+\dd Disconnect from any SSH server which accepts authentication without
+ever having asked for any kind of password or signature or token. (You
+might want to enable this for a server you always expect to challenge
+you, for instance to ensure you don't accidentally type your key file's
+passphrase into a compromised server spoofing PuTTY's passphrase
+prompt.)
+
 \dt \cw{\-hostkey} \e{key}
 
 \dd Specify an acceptable host public key. This option may be specified

+ 3 - 1
source/putty/doc/plink.but

@@ -41,7 +41,7 @@ use Plink:
 
 \c C:\>plink
 \c Plink: command-line connection utility
-\c Release 0.75
+\c Release 0.76
 \c Usage: plink [options] [user@]host [command]
 \c        ("host" can also be a PuTTY saved session name)
 \c Options:
@@ -77,6 +77,8 @@ use Plink:
 \c   -i key    private key file for user authentication
 \c   -noagent  disable use of Pageant
 \c   -agent    enable use of Pageant
+\c   -no-trivial-auth
+\c             disconnect if SSH authentication succeeds trivially
 \c   -noshare  disable use of connection sharing
 \c   -share    enable use of connection sharing
 \c   -hostkey keyid

+ 3 - 1
source/putty/doc/pscp.but

@@ -39,7 +39,7 @@ use PSCP:
 
 \c C:\>pscp
 \c PuTTY Secure Copy client
-\c Release 0.75
+\c Release 0.76
 \c Usage: pscp [options] [user@]host:source target
 \c        pscp [options] source [source...] [user@]host:target
 \c        pscp [options] -ls [user@]host:filespec
@@ -62,6 +62,8 @@ use PSCP:
 \c   -i key    private key file for user authentication
 \c   -noagent  disable use of Pageant
 \c   -agent    enable use of Pageant
+\c   -no-trivial-auth
+\c             disconnect if SSH authentication succeeds trivially
 \c   -hostkey keyid
 \c             manually specify a host key (may be repeated)
 \c   -batch    disable all interactive prompts

+ 9 - 0
source/putty/doc/using.but

@@ -1014,6 +1014,15 @@ This option is equivalent to the \q{Private key file for
 authentication} box in the Auth panel of the PuTTY configuration box
 (see \k{config-ssh-privkey}).
 
+\S2{using-cmdline-no-trivial-auth} \i\c{-no-trivial-auth}: disconnect
+if SSH authentication succeeds trivially
+
+This option causes PuTTY to abandon an SSH session if the server
+accepts authentication without ever having asked for any kind of
+password or signature or token.
+
+See \k{config-ssh-notrivialauth} for why you might want this.
+
 \S2{using-cmdline-loghost} \i\c{-loghost}: specify a \i{logical host
 name}
 

+ 12 - 5
source/putty/putty.h

@@ -1299,8 +1299,14 @@ struct TermWinVtable {
 
     /* Query the front end for any OS-local overrides to the default
      * colours stored in Conf. The front end should set any it cares
-     * about by calling term_palette_override. */
-    void (*palette_get_overrides)(TermWin *);
+     * about by calling term_palette_override.
+     *
+     * The Terminal object is passed in as a parameter, because this
+     * can be called as a callback from term_init(). So the TermWin
+     * itself won't yet have been told where to find its Terminal
+     * object, because that doesn't happen until term_init
+     * returns. */
+    void (*palette_get_overrides)(TermWin *, Terminal *);
 };
 
 static inline bool win_setup_draw_ctx(TermWin *win)
@@ -1354,8 +1360,8 @@ static inline void win_set_zorder(TermWin *win, bool top)
 static inline void win_palette_set(
     TermWin *win, unsigned start, unsigned ncolours, const rgb *colours)
 { win->vt->palette_set(win, start, ncolours, colours); }
-static inline void win_palette_get_overrides(TermWin *win)
-{ win->vt->palette_get_overrides(win); }
+static inline void win_palette_get_overrides(TermWin *win, Terminal *term)
+{ win->vt->palette_get_overrides(win, term); }
 
 /*
  * Global functions not specific to a connection instance.
@@ -1422,6 +1428,7 @@ NORETURN void cleanup_exit(int);
     X(INT, NONE, sshprot) \
     X(BOOL, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \
     X(BOOL, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \
+    X(BOOL, NONE, ssh_no_trivial_userauth) /* disable trivial types of auth */ \
     X(BOOL, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \
     X(BOOL, NONE, try_tis_auth) \
     X(BOOL, NONE, try_ki_auth) \
@@ -1784,7 +1791,7 @@ void term_keyinputw(Terminal *, const wchar_t * widebuf, int len);
 void term_get_cursor_position(Terminal *term, int *x, int *y);
 void term_setup_window_titles(Terminal *term, const char *title_hostname);
 void term_notify_minimised(Terminal *term, bool minimised);
-void term_notify_palette_overrides_changed(Terminal *term);
+void term_notify_palette_changed(Terminal *term);
 void term_notify_window_pos(Terminal *term, int x, int y);
 void term_notify_window_size_pixels(Terminal *term, int x, int y);
 void term_palette_override(Terminal *term, unsigned osc4_index, rgb rgb);

+ 2 - 0
source/putty/settings.c

@@ -609,6 +609,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
 #endif
     write_setting_s(sesskey, "RekeyBytes", conf_get_str(conf, CONF_ssh_rekey_data));
     write_setting_b(sesskey, "SshNoAuth", conf_get_bool(conf, CONF_ssh_no_userauth));
+    write_setting_b(sesskey, "SshNoTrivialAuth", conf_get_bool(conf, CONF_ssh_no_trivial_userauth));
     write_setting_b(sesskey, "SshBanner", conf_get_bool(conf, CONF_ssh_show_banner));
     write_setting_b(sesskey, "AuthTIS", conf_get_bool(conf, CONF_try_tis_auth));
     write_setting_b(sesskey, "AuthKI", conf_get_bool(conf, CONF_try_ki_auth));
@@ -1025,6 +1026,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
     gpps(sesskey, "LogHost", "", conf, CONF_loghost);
     gppb(sesskey, "SSH2DES", false, conf, CONF_ssh2_des_cbc);
     gppb(sesskey, "SshNoAuth", false, conf, CONF_ssh_no_userauth);
+    gppb(sesskey, "SshNoTrivialAuth", false, conf, CONF_ssh_no_trivial_userauth);
     gppb(sesskey, "SshBanner", true, conf, CONF_ssh_show_banner);
     gppb(sesskey, "AuthTIS", false, conf, CONF_try_tis_auth);
     gppb(sesskey, "AuthKI", true, conf, CONF_try_ki_auth);

+ 3 - 1
source/putty/ssh.c

@@ -254,7 +254,9 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
                     connection_layer, ssh->savedhost, ssh->fullhostname,
                     conf_get_filename(ssh->conf, CONF_keyfile),
                     conf_get_bool(ssh->conf, CONF_ssh_show_banner),
-                    conf_get_bool(ssh->conf, CONF_tryagent), username,
+                    conf_get_bool(ssh->conf, CONF_tryagent),
+                    conf_get_bool(ssh->conf, CONF_ssh_no_trivial_userauth),
+                    username,
                     conf_get_bool(ssh->conf, CONF_change_username),
                     conf_get_bool(ssh->conf, CONF_try_ki_auth),
 #ifndef NO_GSSAPI

+ 13 - 1
source/putty/ssh1login.c

@@ -27,7 +27,7 @@ struct ssh1_login_state {
 
     char *savedhost;
     int savedport;
-    bool try_agent_auth;
+    bool try_agent_auth, is_trivial_auth;
 
     int remote_protoflags;
     int local_protoflags;
@@ -105,6 +105,8 @@ PacketProtocolLayer *ssh1_login_new(
     s->savedhost = dupstr(host);
     s->savedport = port;
     s->successor_layer = successor_layer;
+    s->is_trivial_auth = true;
+
     return &s->ppl;
 }
 
@@ -645,6 +647,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                                 s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE);
                             put_data(pkt, ret + 5, 16);
                             pq_push(s->ppl.out_pq, pkt);
+                            s->is_trivial_auth = false;
                             crMaybeWaitUntilV(
                                 (pktin = ssh1_login_pop(s))
                                 != NULL);
@@ -814,6 +817,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                         s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE);
                     put_data(pkt, buffer, 16);
                     pq_push(s->ppl.out_pq, pkt);
+                    s->is_trivial_auth = false;
 
                     mp_free(challenge);
                     mp_free(response);
@@ -1105,6 +1109,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
             put_stringz(pkt, prompt_get_result_ref(s->cur_prompt->prompts[0]));
             pq_push(s->ppl.out_pq, pkt);
         }
+        s->is_trivial_auth = false;
         ppl_logevent("Sent password");
         free_prompts(s->cur_prompt);
         s->cur_prompt = NULL;
@@ -1121,6 +1126,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
         }
     }
 
+    if (conf_get_bool(s->conf, CONF_ssh_no_trivial_userauth) &&
+        s->is_trivial_auth) {
+        ssh_proto_error(s->ppl.ssh, "Authentication was trivial! "
+                        "Abandoning session as specified in configuration.");
+        return;
+    }
+
     ppl_logevent("Authentication successful");
 
     if (conf_get_bool(s->conf, CONF_compression)) {

+ 16 - 4
source/putty/ssh2userauth.c

@@ -28,7 +28,7 @@ struct ssh2_userauth_state {
 
     PacketProtocolLayer *transport_layer, *successor_layer;
     Filename *keyfile;
-    bool show_banner, tryagent, change_username;
+    bool show_banner, tryagent, notrivialauth, change_username;
     char *hostname, *fullhostname;
     char *default_username;
     bool try_ki_auth, try_gssapi_auth, try_gssapi_kex_auth, gssapi_fwd;
@@ -82,6 +82,7 @@ struct ssh2_userauth_state {
     int len;
     PktOut *pktout;
     bool want_user_input;
+    bool is_trivial_auth;
 
     agent_pending_query *auth_agent_query;
     bufchain banner;
@@ -134,7 +135,7 @@ static const PacketProtocolLayerVtable ssh2_userauth_vtable = {
 PacketProtocolLayer *ssh2_userauth_new(
     PacketProtocolLayer *successor_layer,
     const char *hostname, const char *fullhostname,
-    Filename *keyfile, bool show_banner, bool tryagent,
+    Filename *keyfile, bool show_banner, bool tryagent, bool notrivialauth,
     const char *default_username, bool change_username,
     bool try_ki_auth, bool try_gssapi_auth, bool try_gssapi_kex_auth,
     bool gssapi_fwd, struct ssh_connection_shared_gss_state *shgss)
@@ -149,6 +150,7 @@ PacketProtocolLayer *ssh2_userauth_new(
     s->keyfile = filename_copy(keyfile);
     s->show_banner = show_banner;
     s->tryagent = tryagent;
+    s->notrivialauth = notrivialauth;
     s->default_username = dupstr(default_username);
     s->change_username = change_username;
     s->try_ki_auth = try_ki_auth;
@@ -157,6 +159,7 @@ PacketProtocolLayer *ssh2_userauth_new(
     s->gssapi_fwd = gssapi_fwd;
     s->shgss = shgss;
     s->last_methods_string = strbuf_new();
+    s->is_trivial_auth = true;
     bufchain_init(&s->banner);
     bufchain_sink_init(&s->banner_bs, &s->banner);
 
@@ -818,6 +821,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                                 sigblob);
                             pq_push(s->ppl.out_pq, s->pktout);
                             s->type = AUTH_TYPE_PUBLICKEY;
+                            s->is_trivial_auth = false;
                         } else {
                             ppl_logevent("Pageant refused signing request");
                             ppl_printf("Pageant failed to "
@@ -1038,6 +1042,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     ssh_key_free(key->key);
                     sfree(key->comment);
                     sfree(key);
+                    s->is_trivial_auth = false;
                 }
 
 #ifndef NO_GSSAPI
@@ -1169,6 +1174,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                      * no longer says CONTINUE_NEEDED
                      */
                     if (s->gss_sndtok.length != 0) {
+                        s->is_trivial_auth = false;
                         s->pktout =
                             ssh_bpp_new_pktout(
                                 s->ppl.bpp, SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
@@ -1288,7 +1294,6 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                  * Loop while the server continues to send INFO_REQUESTs.
                  */
                 while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
-
                     ptrlen name, inst;
                     strbuf *sb;
 
@@ -1308,6 +1313,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                      */
                     s->num_prompts = get_uint32(pktin);
                     for (uint32_t i = 0; i < s->num_prompts; i++) {
+                        s->is_trivial_auth = false;
                         ptrlen prompt = get_string(pktin);
                         bool echo = get_bool(pktin);
 
@@ -1472,7 +1478,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                 pq_push_front(s->ppl.in_pq, pktin);
 
             } else if (s->can_passwd) {
-
+                s->is_trivial_auth = false;
                 /*
                  * Plain old password authentication.
                  */
@@ -1731,6 +1737,12 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
     }
 
   userauth_success:
+    if (s->notrivialauth && s->is_trivial_auth) {
+        ssh_proto_error(s->ppl.ssh, "Authentication was trivial! "
+                        "Abandoning session as specified in configuration.");
+        return;
+    }
+
     /*
      * We've just received USERAUTH_SUCCESS, and we haven't sent
      * any packets since. Signal the transport layer to consider

+ 1 - 0
source/putty/sshaes.c

@@ -1580,6 +1580,7 @@ NI_ENC_DEC(256)
  */
 #define __ARM_NEON 1
 #define __ARM_FEATURE_CRYPTO 1
+#define __ARM_FEATURE_AES 1
 #define FUNC_ISA __attribute__ ((target("neon,crypto")))
 #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */
 

+ 1 - 1
source/putty/sshppl.h

@@ -116,7 +116,7 @@ PacketProtocolLayer *ssh2_transport_new(
 PacketProtocolLayer *ssh2_userauth_new(
     PacketProtocolLayer *successor_layer,
     const char *hostname, const char *fullhostname,
-    Filename *keyfile, bool show_banner, bool tryagent,
+    Filename *keyfile, bool show_banner, bool tryagent, bool notrivialauth,
     const char *default_username, bool change_username,
     bool try_ki_auth,
     bool try_gssapi_auth, bool try_gssapi_kex_auth,

+ 4 - 4
source/putty/sshpubk.c

@@ -480,7 +480,7 @@ static bool read_header(BinarySource *src, char *header)
 
     while (1) {
         c = get_byte(src);
-        if (c == '\n' || c == '\r' || c == EOF)
+        if (c == '\n' || c == '\r' || get_err(src))
             return false;              /* failure */
         if (c == ':') {
             c = get_byte(src);
@@ -503,10 +503,10 @@ static char *read_body(BinarySource *src)
 
     while (1) {
         int c = get_byte(src);
-        if (c == '\r' || c == '\n' || c == EOF) {
-            if (c != EOF) {
+        if (c == '\r' || c == '\n' || get_err(src)) {
+            if (!get_err(src)) {
                 c = get_byte(src);
-                if (c != '\r' && c != '\n')
+                if (c != '\r' && c != '\n' && !get_err(src))
                     src->pos--;
             }
             return strbuf_to_str(buf);

+ 1 - 0
source/putty/sshsh256.c

@@ -723,6 +723,7 @@ const ssh_hashalg ssh_sha256_hw = {
  */
 #define __ARM_NEON 1
 #define __ARM_FEATURE_CRYPTO 1
+#define __ARM_FEATURE_SHA2 1
 #define FUNC_ISA __attribute__ ((target("neon,crypto")))
 #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */
 

+ 1 - 0
source/putty/sshsha.c

@@ -690,6 +690,7 @@ const ssh_hashalg ssh_sha1_hw = {
  */
 #define __ARM_NEON 1
 #define __ARM_FEATURE_CRYPTO 1
+#define __ARM_FEATURE_SHA2 1
 #define FUNC_ISA __attribute__ ((target("neon,crypto")))
 #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */
 

+ 5 - 5
source/putty/version.h

@@ -1,6 +1,6 @@
 /* Generated by automated build script */
-#define RELEASE 0.75
-#define TEXTVER "Release 0.75"
-#define SSHVER "-Release-0.75"
-#define BINARY_VERSION 0,75,0,0
-#define SOURCE_COMMIT "c72200ff8851b0d95574b8a8a88a2780a243c66c"
+#define RELEASE 0.76
+#define TEXTVER "Release 0.76"
+#define SSHVER "-Release-0.76"
+#define BINARY_VERSION 0,76,0,0
+#define SOURCE_COMMIT "1fd7baa7344bb38d62a024e5dba3a720c67d05cf"

+ 44 - 1
source/putty/windows/wingss.c

@@ -95,6 +95,28 @@ const char *gsslogmsg = NULL;
 
 static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
 
+static tree234 *libraries_to_never_unload;
+static int library_to_never_unload_cmp(void *av, void *bv)
+{
+    uintptr_t a = (uintptr_t)av, b = (uintptr_t)bv;
+    return a < b ? -1 : a > b ? +1 : 0;
+}
+static void ensure_library_tree_exists(void)
+{
+    if (!libraries_to_never_unload)
+        libraries_to_never_unload = newtree234(library_to_never_unload_cmp);
+}
+static bool library_is_in_never_unload_tree(HMODULE module)
+{
+    ensure_library_tree_exists();
+    return find234(libraries_to_never_unload, module, NULL);
+}
+static void add_library_to_never_unload_tree(HMODULE module)
+{
+    ensure_library_tree_exists();
+    add234(libraries_to_never_unload, module);
+}
+
 struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
 {
     HMODULE module;
@@ -145,6 +167,23 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
                                         LOAD_LIBRARY_SEARCH_SYSTEM32 |
                                         LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
                                         LOAD_LIBRARY_SEARCH_USER_DIRS);
+
+                /*
+                 * The MIT Kerberos DLL suffers an internal segfault
+                 * for some reason if you unload and reload one within
+                 * the same process. So, make sure that after we load
+                 * this library, we never free it.
+                 *
+                 * Or rather: after we've loaded it once, if any
+                 * _further_ load returns the same module handle, we
+                 * immediately free it again (to prevent the Windows
+                 * API's internal reference count growing without
+                 * bound). But on the other hand we never free it in
+                 * ssh_gss_cleanup.
+                 */
+                if (library_is_in_never_unload_tree(module))
+                    FreeLibrary(module);
+                add_library_to_never_unload_tree(module);
             }
             sfree(buffer);
         }
@@ -280,7 +319,11 @@ void ssh_gss_cleanup(struct ssh_gss_liblist *list)
      * another SSH instance still using it.
      */
     for (i = 0; i < list->nlibraries; i++) {
-        FreeLibrary((HMODULE)list->libraries[i].handle);
+        if (list->libraries[i].id != 0) {
+            HMODULE module = (HMODULE)list->libraries[i].handle;
+            if (!library_is_in_never_unload_tree(module))
+                FreeLibrary(module);
+        }
         if (list->libraries[i].id == 2) {
             /* The 'custom' id involves a dynamically allocated message.
              * Note that we must cast away the 'const' to free it. */

+ 8 - 4
source/putty/windows/winhandl.c

@@ -451,8 +451,10 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata,
         handles_by_evtomain = newtree234(handle_cmp_evtomain);
     add234(handles_by_evtomain, h);
 
-    CreateThread(NULL, 0, handle_input_threadfunc,
-                 &h->u.i, 0, &in_threadid);
+    HANDLE hThread = CreateThread(NULL, 0, handle_input_threadfunc,
+                                  &h->u.i, 0, &in_threadid);
+    if (hThread)
+        CloseHandle(hThread);          /* we don't need the thread handle */
     h->u.i.busy = true;
 
     return h;
@@ -482,8 +484,10 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
         handles_by_evtomain = newtree234(handle_cmp_evtomain);
     add234(handles_by_evtomain, h);
 
-    CreateThread(NULL, 0, handle_output_threadfunc,
-                 &h->u.o, 0, &out_threadid);
+    HANDLE hThread = CreateThread(NULL, 0, handle_output_threadfunc,
+                                  &h->u.o, 0, &out_threadid);
+    if (hThread)
+        CloseHandle(hThread);          /* we don't need the thread handle */
 
     return h;
 }

+ 4 - 1
source/putty/windows/winpgntc.c

@@ -169,6 +169,7 @@ bool agent_exists(void)
 
 struct agent_pending_query {
     struct handle *handle;
+    HANDLE os_handle;
     strbuf *response;
     void (*callback)(void *, void *, int);
     void *callback_ctx;
@@ -266,7 +267,8 @@ static agent_pending_query *named_pipe_agent_query(
 
     pq = snew(agent_pending_query);
     pq->handle = handle_input_new(pipehandle, named_pipe_agent_gotdata, pq, 0);
-    pipehandle = NULL;  /* prevent it being closed below */
+    pq->os_handle = pipehandle;
+    pipehandle = INVALID_HANDLE_VALUE;  /* prevent it being closed below */
     pq->response = strbuf_new_nm();
     pq->callback = callback;
     pq->callback_ctx = callback_ctx;
@@ -290,6 +292,7 @@ static agent_pending_query *named_pipe_agent_query(
 void agent_cancel_query(agent_pending_query *pq)
 {
     handle_free(pq->handle);
+    CloseHandle(pq->os_handle);
     if (pq->response)
         strbuf_free(pq->response);
     sfree(pq);