1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00

Cleanups of the GSSAPI support. On Windows, standard GSS libraries

are now loaded from standard locations (system32 for SSPI, the
registry-stored MIT KfW install location for KfW) rather than using
the risky default DLL search path; I've therefore also added an
option to manually specify a GSS DLL we haven't heard of (which
should in principle Just Work provided it supports proper GSS-API as
specified in the RFC). The same option exists on Unix too, because
it seemed like too useful an idea to reserve to Windows. In
addition, GSSAPI is now documented, and also (unfortunately) its GUI
configuration has been moved out into a sub-subpanel on the grounds
that it was too big to fit in Auth.

[originally from svn r9003]
This commit is contained in:
Simon Tatham 2010-09-25 07:16:56 +00:00
parent 010f8c42c5
commit 406e62f77b
12 changed files with 350 additions and 66 deletions

View File

@ -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
}

View File

@ -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.

View File

@ -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 */

View File

@ -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,

28
ssh.c
View File

@ -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();

View File

@ -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 */

View File

@ -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 */

View File

@ -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.

View File

@ -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 <gssapi/gssapi.h>
/* 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 */

View File

@ -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", &regkey)
== 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,

View File

@ -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"

View File

@ -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