Cleanups of the GSSAPI support. On Windows, standard GSS libraries
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 25 Sep 2010 07:16:56 +0000 (07:16 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 25 Sep 2010 07:16:56 +0000 (07:16 +0000)
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.

git-svn-id: svn://svn.tartarus.org/sgt/putty@9003 cda61777-01e9-0310-a592-d414129be87e

12 files changed:
config.c
doc/config.but
putty.h
settings.c
ssh.c
sshgss.h
sshnogss.c
unix/unix.h
unix/uxgss.c
windows/wingss.c
windows/winhelp.h
windows/winstuff.h

index d689ede..f6c1640 100644 (file)
--- 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,12 +2113,6 @@ 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),
@@ -2133,13 +2120,56 @@ void setup_config_box(struct controlbox *b, int midsession,
 
 #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
        }
index c36e818..62aaaa6 100644 (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.
diff --git a/putty.h b/putty.h
index 0c97562..9a55757 100644 (file)
--- 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 */
index 5514752..372a954 100644 (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,
diff --git a/ssh.c b/ssh.c
index 401706c..27a4fab 100644 (file)
--- 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();
index d12c70f..ffce099 100644 (file)
--- 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 */
 
index 715d8df..fd8915b 100644 (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 */
index 9de4e4a..74f4174 100644 (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.
index b8fb0b0..62d517a 100644 (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;
+}
+
+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);
 }
 
-#else /* NO_LIBDL */
+#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 */
index 90cd24e..9084c3c 100644 (file)
 
 /* 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,
index f26501d..7b6e0c5 100644 (file)
 #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"
index 2317748..201bf66 100644 (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