diff --git a/config.c b/config.c index d689ede9..f6c1640a 100644 --- a/config.c +++ b/config.c @@ -2104,13 +2104,6 @@ void setup_config_box(struct controlbox *b, int midsession, dlg_stdcheckbox_handler, I(offsetof(Config,try_ki_auth))); -#ifndef NO_GSSAPI - ctrl_checkbox(s, "Attempt GSSAPI auth (SSH-2)", - NO_SHORTCUT, HELPCTX(no_help), - dlg_stdcheckbox_handler, - I(offsetof(Config,try_gssapi_auth))); -#endif - s = ctrl_getset(b, "Connection/SSH/Auth", "params", "Authentication parameters"); ctrl_checkbox(s, "Allow agent forwarding", 'f', @@ -2120,26 +2113,63 @@ void setup_config_box(struct controlbox *b, int midsession, HELPCTX(ssh_auth_changeuser), dlg_stdcheckbox_handler, I(offsetof(Config,change_username))); -#ifndef NO_GSSAPI - ctrl_checkbox(s, "Allow GSSAPI credential delegation in SSH-2", NO_SHORTCUT, - HELPCTX(no_help), - dlg_stdcheckbox_handler, - I(offsetof(Config,gssapifwd))); -#endif ctrl_filesel(s, "Private key file for authentication:", 'k', FILTER_KEY_FILES, FALSE, "Select private key file", HELPCTX(ssh_auth_privkey), dlg_stdfilesel_handler, I(offsetof(Config, keyfile))); #ifndef NO_GSSAPI + /* + * Connection/SSH/Auth/GSSAPI, which sadly won't fit on + * the main Auth panel. + */ + ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI", + "Options controlling GSSAPI authentication"); + s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL); + + ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)", + NO_SHORTCUT, HELPCTX(ssh_gssapi), + dlg_stdcheckbox_handler, + I(offsetof(Config,try_gssapi_auth))); + + ctrl_checkbox(s, "Allow GSSAPI credential delegation", NO_SHORTCUT, + HELPCTX(ssh_gssapi_delegation), + dlg_stdcheckbox_handler, + I(offsetof(Config,gssapifwd))); + /* * GSSAPI library selection. */ if (ngsslibs > 1) { c = ctrl_draglist(s, "Preference order for GSSAPI libraries:", NO_SHORTCUT, - HELPCTX(no_help), + HELPCTX(ssh_gssapi_libraries), gsslist_handler, P(NULL)); c->listbox.height = ngsslibs; + + /* + * I currently assume that if more than one GSS + * library option is available, then one of them is + * 'user-supplied' and so we should present the + * following file selector. This is at least half- + * reasonable, because if we're using statically + * linked GSSAPI then there will only be one option + * and no way to load from a user-supplied library, + * whereas if we're using dynamic libraries then + * there will almost certainly be some default + * option in addition to a user-supplied path. If + * anyone ever ports PuTTY to a system on which + * dynamic-library GSSAPI is available but there is + * absolutely no consensus on where to keep the + * libraries, there'll need to be a flag alongside + * ngsslibs to control whether the file selector is + * displayed. + */ + + ctrl_filesel(s, "User-supplied GSSAPI library path:", 'l', + FILTER_DYNLIB_FILES, FALSE, "Select library file", + HELPCTX(ssh_gssapi_libraries), + dlg_stdfilesel_handler, + I(offsetof(Config, ssh_gss_custom))); } #endif } diff --git a/doc/config.but b/doc/config.but index c36e8184..62aaaa66 100644 --- a/doc/config.but +++ b/doc/config.but @@ -2571,6 +2571,76 @@ If a key file is specified here, and \i{Pageant} is running (see that key, and ignore any other keys Pageant may have. If that fails, PuTTY will ask for a passphrase as normal. +\H{config-ssh-auth-gssapi} The GSSAPI panel + +\cfg{winhelp-topic}{ssh.auth.gssapi} + +The \q{GSSAPI} subpanel of the \q{Auth} panel controls the use of +GSSAPI authentication. This is a mechanism which delegates the +authentication exchange to a library elsewhere on the client +machine, which in principle can authenticate in many different ways +but in practice is usually used with the Kerberos single-sign-on +protocol. + +GSSAPI is only available in the SSH-2 protocol. + +The topmost control on the GSSAPI subpanel is the checkbox labelled +\q{Attempt GSSAPI authentication}. If this is disabled, GSSAPI will +not be attempted at all and the rest of this panel is unused. If it +is enabled, GSSAPI authentication will be attempted, and (typically) +if your client machine has valid Kerberos credentials loaded, then +PuTTY should be able to authenticate automatically to servers that +support Kerberos logins. + +\S{config-ssh-auth-gssapi-delegation} \q{Allow GSSAPI credential +delegation} + +\cfg{winhelp-topic}{ssh.auth.gssapi.delegation} + +GSSAPI credential delegation is a mechanism for passing on your +Kerberos (or other) identity to the session on the SSH server. If +you enable this option, then not only will PuTTY be able to log in +automatically to a server that accepts your Kerberos credentials, +but also you will be able to connect out from that server to other +Kerberos-supporting services and use the same credentials just as +automatically. + +(This option is the Kerberos analogue of SSH agent forwarding; see +\k{pageant-forward} for some information on that.) + +Note that, like SSH agent forwarding, there is a security +implication in the use of this option: the administrator of the +server you connect to, or anyone else who has cracked the +administrator account on that server, could fake your identity when +connecting to further Kerberos-supporting services. However, +Kerberos sites are typically run by a central authority, so the +administrator of one server is likely to already have access to the +other services too; so this would typically be less of a risk than +SSH agent forwarding. + +\S{config-ssh-auth-gssapi-libraries} Preference order for GSSAPI +libraries + +\cfg{winhelp-topic}{ssh.auth.gssapi.libraries} + +GSSAPI is a mechanism which allows more than one authentication +method to be accessed through the same interface. Therefore, more +than one authentication library may exist on your system which can +be accessed using GSSAPI. + +PuTTY contains native support for a few well-known such libraries, +and will look for all of them on your system and use whichever it +finds. If more than one exists on your system and you need to use a +specific one, you can adjust the order in which it will search using +this preference list control. + +One of the options in the preference list is to use a user-specified +GSSAPI library. If the library you want to use is not mentioned by +name in PuTTY's list of options, you can enter its full pathname in +the \q{User-supplied GSSAPI library path} field, and move the +\q{User-supplied GSSAPI library} option in the preference list to +make sure it is selected before anything else. + \H{config-ssh-tty} The TTY panel The TTY panel lets you configure the remote pseudo-terminal. diff --git a/putty.h b/putty.h index 0c97562b..9a557570 100644 --- a/putty.h +++ b/putty.h @@ -475,6 +475,7 @@ struct config_tag { int try_gssapi_auth; /* attempt gssapi auth */ int gssapifwd; /* forward tgt via gss */ int ssh_gsslist[4]; /* preference order for local GSS libs */ + Filename ssh_gss_custom; int ssh_subsys; /* run a subsystem rather than a command */ int ssh_subsys2; /* fallback to go with remote_cmd_ptr2 */ int ssh_no_shell; /* avoid running a shell */ diff --git a/settings.c b/settings.c index 55147526..372a954d 100644 --- a/settings.c +++ b/settings.c @@ -351,8 +351,11 @@ void save_open_settings(void *sesskey, Config *cfg) write_setting_i(sesskey, "AuthTIS", cfg->try_tis_auth); write_setting_i(sesskey, "AuthKI", cfg->try_ki_auth); write_setting_i(sesskey, "AuthGSSAPI", cfg->try_gssapi_auth); +#ifndef NO_GSSAPI wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, cfg->ssh_gsslist); + write_setting_filename(sesskey, "GSSCustom", cfg->ssh_gss_custom); +#endif write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell); write_setting_i(sesskey, "SshProt", cfg->sshprot); write_setting_s(sesskey, "LogHost", cfg->loghost); @@ -642,8 +645,11 @@ void load_open_settings(void *sesskey, Config *cfg) gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth); gppi(sesskey, "AuthKI", 1, &cfg->try_ki_auth); gppi(sesskey, "AuthGSSAPI", 1, &cfg->try_gssapi_auth); +#ifndef NO_GSSAPI gprefs(sesskey, "GSSLibs", "\0", gsslibkeywords, ngsslibs, cfg->ssh_gsslist); + gppfile(sesskey, "GSSCustom", &cfg->ssh_gss_custom); +#endif gppi(sesskey, "SshNoShell", 0, &cfg->ssh_no_shell); gppfile(sesskey, "PublicKeyFile", &cfg->keyfile); gpps(sesskey, "RemoteCommand", "", cfg->remote_cmd, diff --git a/ssh.c b/ssh.c index 401706c4..27a4fab6 100644 --- a/ssh.c +++ b/ssh.c @@ -941,6 +941,13 @@ struct ssh_tag { * Fully qualified host name, which we need if doing GSSAPI. */ char *fullhostname; + +#ifndef NO_GSSAPI + /* + * GSSAPI libraries for this session. + */ + struct ssh_gss_liblist *gsslibs; +#endif }; #define logevent(s) logevent(ssh->frontend, s) @@ -7645,11 +7652,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, in_commasep_string("password", methods, methlen); s->can_keyb_inter = ssh->cfg.try_ki_auth && in_commasep_string("keyboard-interactive", methods, methlen); -#ifndef NO_GSSAPI - ssh_gss_init(); +#ifndef NO_GSSAPI + if (!ssh->gsslibs) + ssh->gsslibs = ssh_gss_setup(&ssh->cfg); s->can_gssapi = ssh->cfg.try_gssapi_auth && in_commasep_string("gssapi-with-mic", methods, methlen) && - n_ssh_gss_libraries > 0; + ssh->gsslibs->nlibraries > 0; #endif } @@ -8001,9 +8009,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->gsslib = NULL; for (i = 0; i < ngsslibs; i++) { int want_id = ssh->cfg.ssh_gsslist[i]; - for (j = 0; j < n_ssh_gss_libraries; j++) - if (ssh_gss_libraries[j].id == want_id) { - s->gsslib = &ssh_gss_libraries[j]; + for (j = 0; j < ssh->gsslibs->nlibraries; j++) + if (ssh->gsslibs->libraries[j].id == want_id) { + s->gsslib = &ssh->gsslibs->libraries[j]; goto got_gsslib; /* double break */ } } @@ -9283,6 +9291,10 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data); ssh->kex_in_progress = FALSE; +#ifndef NO_GSSAPI + ssh->gsslibs = NULL; +#endif + p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive); if (p != NULL) return p; @@ -9379,6 +9391,10 @@ static void ssh_free(void *handle) if (ssh->pinger) pinger_free(ssh->pinger); bufchain_clear(&ssh->queued_incoming_data); +#ifndef NO_GSSAPI + if (ssh->gsslibs) + ssh_gss_cleanup(ssh->gsslibs); +#endif sfree(ssh); random_unref(); diff --git a/sshgss.h b/sshgss.h index d12c70f8..ffce0995 100644 --- a/sshgss.h +++ b/sshgss.h @@ -31,15 +31,24 @@ typedef gss_name_t Ssh_gss_name; struct ssh_gss_library; /* - * Do startup-time initialisation for using GSSAPI. This should - * correctly initialise the array of struct ssh_gss_library declared - * below. + * Prepare a collection of GSSAPI libraries for use in a single SSH + * connection. Returns a structure containing a list of libraries, + * with their ids (see struct ssh_gss_library below) filled in so + * that the client can go through them in the SSH user's preferred + * order. * - * Must be callable multiple times (since the most convenient place - * to call it _from_ is the ssh.c setup code), and should harmlessly - * return success if already initialised. + * Must always return non-NULL. (Even if no libraries are available, + * it must return an empty structure.) + * + * The free function cleans up the structure, and its associated + * libraries (if any). */ -void ssh_gss_init(void); +struct ssh_gss_liblist { + struct ssh_gss_library *libraries; + int nlibraries; +}; +struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg); +void ssh_gss_cleanup(struct ssh_gss_liblist *list); /* * Fills in buf with a string describing the GSSAPI mechanism in @@ -166,10 +175,13 @@ struct ssh_gss_library { * be more than one set of them available. */ } u; -}; -extern struct ssh_gss_library ssh_gss_libraries[]; -extern int n_ssh_gss_libraries; + /* + * Wrapper layers will often also need to store a library handle + * of some sort for cleanup time. + */ + void *handle; +}; #endif /* NO_GSSAPI */ diff --git a/sshnogss.c b/sshnogss.c index 715d8df2..fd8915ba 100644 --- a/sshnogss.c +++ b/sshnogss.c @@ -3,8 +3,17 @@ /* For platforms not supporting GSSAPI */ -void ssh_gss_init(void) +struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) { + struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist *); + list->libraries = NULL; + list->nlibraries = 0; + return list; +} + +void ssh_gss_cleanup(struct ssh_gss_liblist *list) +{ + sfree(list); } #endif /* NO_GSSAPI */ diff --git a/unix/unix.h b/unix/unix.h index 9de4e4a9..74f41746 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -42,6 +42,7 @@ typedef uint32_t uint32; /* C99: uint32_t defined in stdint.h */ */ #define HELPCTX(x) P(NULL) #define FILTER_KEY_FILES NULL /* FIXME */ +#define FILTER_DYNLIB_FILES NULL /* FIXME */ /* * Under X, selection data must not be NUL-terminated. diff --git a/unix/uxgss.c b/unix/uxgss.c index b8fb0b07..62d517ab 100644 --- a/unix/uxgss.c +++ b/unix/uxgss.c @@ -6,24 +6,22 @@ /* Unix code to set up the GSSAPI library list. */ -struct ssh_gss_library ssh_gss_libraries[3]; -int n_ssh_gss_libraries = 0; -static int initialised = FALSE; +#if !defined NO_LIBDL && !defined NO_GSSAPI -const int ngsslibs = 3; -const char *const gsslibnames[3] = { +const int ngsslibs = 4; +const char *const gsslibnames[4] = { "libgssapi (Heimdal)", "libgssapi_krb5 (MIT Kerberos)", "libgss (Sun)", + "User-specified GSSAPI library", }; const struct keyval gsslibkeywords[] = { { "libgssapi", 0 }, { "libgssapi_krb5", 1 }, { "libgss", 2 }, + { "custom", 3 }, }; -#ifndef NO_LIBDL - /* * Run-time binding against a choice of GSSAPI implementations. We * try loading several libraries, and produce an entry in @@ -35,6 +33,7 @@ static void gss_init(struct ssh_gss_library *lib, void *dlhandle, { lib->id = id; lib->gsslogmsg = msg; + lib->handle = dlhandle; #define BIND_GSS_FN(name) \ lib->u.gssapi.name = (t_gss_##name) dlsym(dlhandle, "gss_" #name) @@ -54,30 +53,72 @@ static void gss_init(struct ssh_gss_library *lib, void *dlhandle, } /* Dynamically load gssapi libs. */ -void ssh_gss_init(void) +struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) { void *gsslib; + struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); - if (initialised) return; - initialised = TRUE; + list->libraries = snewn(4, struct ssh_gss_library); + list->nlibraries = 0; /* Heimdal's GSSAPI Library */ if ((gsslib = dlopen("libgssapi.so.2", RTLD_LAZY)) != NULL) - gss_init(&ssh_gss_libraries[n_ssh_gss_libraries++], gsslib, + gss_init(&list->libraries[list->nlibraries++], gsslib, 0, "Using GSSAPI from libgssapi.so.2"); /* MIT Kerberos's GSSAPI Library */ if ((gsslib = dlopen("libgssapi_krb5.so.2", RTLD_LAZY)) != NULL) - gss_init(&ssh_gss_libraries[n_ssh_gss_libraries++], gsslib, + gss_init(&list->libraries[list->nlibraries++], gsslib, 1, "Using GSSAPI from libgssapi_krb5.so.2"); /* Sun's GSSAPI Library */ if ((gsslib = dlopen("libgss.so.1", RTLD_LAZY)) != NULL) - gss_init(&ssh_gss_libraries[n_ssh_gss_libraries++], gsslib, + gss_init(&list->libraries[list->nlibraries++], gsslib, 2, "Using GSSAPI from libgss.so.1"); + + /* User-specified GSSAPI library */ + if (cfg->ssh_gss_custom.path[0] && + (gsslib = dlopen(cfg->ssh_gss_custom.path, RTLD_LAZY)) != NULL) + gss_init(&list->libraries[list->nlibraries++], gsslib, + 3, dupprintf("Using GSSAPI from user-specified" + " library '%s'", cfg->ssh_gss_custom.path)); + + return list; } -#else /* NO_LIBDL */ +void ssh_gss_cleanup(struct ssh_gss_liblist *list) +{ + int i; + + /* + * dlopen and dlclose are defined to employ reference counting + * in the case where the same library is repeatedly dlopened, so + * even in a multiple-sessions-per-process context it's safe to + * naively dlclose everything here without worrying about + * destroying it under the feet of another SSH instance still + * using it. + */ + for (i = 0; i < list->nlibraries; i++) { + dlclose(list->libraries[i].handle); + if (list->libraries[i].id == 3) { + /* The 'custom' id involves a dynamically allocated message. + * Note that we must cast away the 'const' to free it. */ + sfree((char *)list->libraries[i].gsslogmsg); + } + } + sfree(list->libraries); + sfree(list); +} + +#elif !defined NO_GSSAPI + +const int ngsslibs = 1; +const char *const gsslibnames[1] = { + "static", +}; +const struct keyval gsslibkeywords[] = { + { "static", 0 }, +}; /* * Link-time binding against GSSAPI. Here we just construct a single @@ -88,16 +129,17 @@ void ssh_gss_init(void) #include /* Dynamically load gssapi libs. */ -void ssh_gss_init(void) +struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) { - if (initialised) return; - initialised = TRUE; + struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); - n_ssh_gss_libraries = 1; - ssh_gss_libraries[0].gsslogmsg = "Using statically linked GSSAPI"; + list->libraries = snew(struct ssh_gss_library); + list->nlibraries = 1; + + list->libraries[0].gsslogmsg = "Using statically linked GSSAPI"; #define BIND_GSS_FN(name) \ - ssh_gss_libraries[0].u.gssapi.name = (t_gss_##name) gss_##name + list->libraries[0].u.gssapi.name = (t_gss_##name) gss_##name BIND_GSS_FN(delete_sec_context); BIND_GSS_FN(display_status); @@ -110,7 +152,15 @@ void ssh_gss_init(void) #undef BIND_GSS_FN - ssh_gssapi_bind_fns(&ssh_gss_libraries[0]); + ssh_gssapi_bind_fns(&list->libraries[0]); + + return list; +} + +void ssh_gss_cleanup(struct ssh_gss_liblist *list) +{ + sfree(list->libraries); + sfree(list); } #endif /* NO_LIBDL */ diff --git a/windows/wingss.c b/windows/wingss.c index 90cd24ee..9084c3cf 100644 --- a/windows/wingss.c +++ b/windows/wingss.c @@ -12,18 +12,16 @@ /* Windows code to set up the GSSAPI library list. */ -struct ssh_gss_library ssh_gss_libraries[2]; -int n_ssh_gss_libraries = 0; -static int initialised = FALSE; - -const int ngsslibs = 2; -const char *const gsslibnames[2] = { - "GSSAPI32.DLL (MIT Kerberos)", - "SECUR32.DLL (Microsoft SSPI)", +const int ngsslibs = 3; +const char *const gsslibnames[3] = { + "MIT Kerberos GSSAPI32.DLL", + "Microsoft SSPI SECUR32.DLL", + "User-specified GSSAPI DLL", }; const struct keyval gsslibkeywords[] = { { "gssapi32", 0 }, { "sspi", 1 }, + { "custom", 2 }, }; DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, @@ -67,22 +65,46 @@ const char *gsslogmsg = NULL; static void ssh_sspi_bind_fns(struct ssh_gss_library *lib); -void ssh_gss_init(void) +struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg) { HMODULE module; + HKEY regkey; + struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); - if (initialised) return; - initialised = TRUE; + list->libraries = snewn(3, struct ssh_gss_library); + list->nlibraries = 0; /* MIT Kerberos GSSAPI implementation */ /* TODO: For 64-bit builds, check for gssapi64.dll */ - module = LoadLibrary("gssapi32.dll"); + module = NULL; + if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", ®key) + == ERROR_SUCCESS) { + DWORD type, size; + LONG ret; + char *buffer; + + /* Find out the string length */ + ret = RegQueryValueEx(regkey, "InstallDir", NULL, &type, NULL, &size); + + if (ret == ERROR_SUCCESS && type == REG_SZ) { + buffer = snewn(size + 20, char); + ret = RegQueryValueEx(regkey, "InstallDir", NULL, + &type, buffer, &size); + if (ret == ERROR_SUCCESS && type == REG_SZ) { + strcat(buffer, "\\bin\\gssapi32.dll"); + module = LoadLibrary(buffer); + } + sfree(buffer); + } + RegCloseKey(regkey); + } if (module) { struct ssh_gss_library *lib = - &ssh_gss_libraries[n_ssh_gss_libraries++]; + &list->libraries[list->nlibraries++]; lib->id = 0; lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL"; + lib->handle = (void *)module; #define BIND_GSS_FN(name) \ lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name) @@ -105,10 +127,11 @@ void ssh_gss_init(void) module = load_system32_dll("secur32.dll"); if (module) { struct ssh_gss_library *lib = - &ssh_gss_libraries[n_ssh_gss_libraries++]; + &list->libraries[list->nlibraries++]; lib->id = 1; lib->gsslogmsg = "Using SSPI from SECUR32.DLL"; + lib->handle = (void *)module; GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA); GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA); @@ -120,6 +143,67 @@ void ssh_gss_init(void) ssh_sspi_bind_fns(lib); } + + /* + * Custom GSSAPI DLL. + */ + module = NULL; + if (cfg->ssh_gss_custom.path[0]) { + module = LoadLibrary(cfg->ssh_gss_custom.path); + } + if (module) { + struct ssh_gss_library *lib = + &list->libraries[list->nlibraries++]; + + lib->id = 2; + lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified" + " library '%s'", cfg->ssh_gss_custom.path); + lib->handle = (void *)module; + +#define BIND_GSS_FN(name) \ + lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name) + + BIND_GSS_FN(delete_sec_context); + BIND_GSS_FN(display_status); + BIND_GSS_FN(get_mic); + BIND_GSS_FN(import_name); + BIND_GSS_FN(init_sec_context); + BIND_GSS_FN(release_buffer); + BIND_GSS_FN(release_cred); + BIND_GSS_FN(release_name); + +#undef BIND_GSS_FN + + ssh_gssapi_bind_fns(lib); + } + + + return list; +} + +void ssh_gss_cleanup(struct ssh_gss_liblist *list) +{ + int i; + + /* + * LoadLibrary and FreeLibrary are defined to employ reference + * counting in the case where the same library is repeatedly + * loaded, so even in a multiple-sessions-per-process context + * (not that we currently expect ever to have such a thing on + * Windows) it's safe to naively FreeLibrary everything here + * without worrying about destroying it under the feet of + * another SSH instance still using it. + */ + for (i = 0; i < list->nlibraries; i++) { + FreeLibrary((HMODULE)list->libraries[i].handle); + 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. */ + sfree((char *)list->libraries[i].gsslogmsg); + } + } + sfree(list->libraries); + sfree(list); } static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib, diff --git a/windows/winhelp.h b/windows/winhelp.h index f26501d1..7b6e0c5b 100644 --- a/windows/winhelp.h +++ b/windows/winhelp.h @@ -108,6 +108,9 @@ #define WINHELP_CTX_ssh_auth_pageant "ssh.auth.pageant:config-ssh-tryagent" #define WINHELP_CTX_ssh_auth_tis "ssh.auth.tis:config-ssh-tis" #define WINHELP_CTX_ssh_auth_ki "ssh.auth.ki:config-ssh-ki" +#define WINHELP_CTX_ssh_gssapi "ssh.auth.gssapi:config-ssh-auth-gssapi" +#define WINHELP_CTX_ssh_gssapi_delegation "ssh.auth.gssapi.delegation:config-ssh-auth-gssapi-delegation" +#define WINHELP_CTX_ssh_gssapi_libraries "ssh.auth.gssapi.libraries:config-ssh-auth-gssapi-libraries" #define WINHELP_CTX_selection_buttons "selection.buttons:config-mouse" #define WINHELP_CTX_selection_shiftdrag "selection.shiftdrag:config-mouseshift" #define WINHELP_CTX_selection_rect "selection.rect:config-rectselect" diff --git a/windows/winstuff.h b/windows/winstuff.h index 23177485..201bf66a 100644 --- a/windows/winstuff.h +++ b/windows/winstuff.h @@ -222,6 +222,8 @@ GLOBAL void *logctx; "All Files (*.*)\0*\0\0\0") #define FILTER_WAVE_FILES ("Wave Files (*.wav)\0*.WAV\0" \ "All Files (*.*)\0*\0\0\0") +#define FILTER_DYNLIB_FILES ("Dynamic Library Files (*.dll)\0*.dll\0" \ + "All Files (*.*)\0*\0\0\0") /* * On some versions of Windows, it has been known for WM_TIMER to