Patch from Alejandro Sedeno, somewhat modified by me, which
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 19 May 2010 18:22:17 +0000 (18:22 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 19 May 2010 18:22:17 +0000 (18:22 +0000)
reorganises the GSSAPI support so that it handles alternative
implementations of the GSS-API. In particular, this means PuTTY can
now talk to MIT Kerberos for Windows instead of being limited to
SSPI. I don't know for sure whether further tweaking will be needed
(to the UI, most likely, or to automatic selection of credentials),
but testing reports suggest it's now at least worth committing to
trunk to get it more widely tested.

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

21 files changed:
Recipe
config.c
mac/macstuff.h
mkfiles.pl
nogss.c [new file with mode: 0644]
pgssapi.c [new file with mode: 0644]
pgssapi.h [new file with mode: 0644]
putty.h
settings.c
ssh.c
ssh.h
sshgss.h
sshgssc.c [new file with mode: 0644]
sshgssc.h [new file with mode: 0644]
sshnogss.c [new file with mode: 0644]
unix/configure.ac
unix/unix.h
unix/uxgss.c
unix/uxstore.c
windows/wingss.c
windows/winstuff.h

diff --git a/Recipe b/Recipe
index 3702191..3c4b6e3 100644 (file)
--- a/Recipe
+++ b/Recipe
 #      Disables PuTTY's ability to use GSSAPI functions for
 #      authentication and key exchange.
 #
+#  - COMPAT=/DSTATIC_GSSAPI
+#      Causes PuTTY to try to link statically against the GSSAPI
+#      library instead of the default of doing it at run time.
+#
 #  - COMPAT=/DMSVC4 (Windows only)
 #  - RCFL=/DMSVC4
 #      Makes a couple of minor changes so that PuTTY compiles using
@@ -261,9 +265,10 @@ NONSSH   = telnet raw rlogin ldisc pinger
 SSH      = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
          + sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
          + sshaes sshsh256 sshsh512 sshbn wildcard pinger ssharcf
+         + sshgssc pgssapi
 WINSSH   = SSH winnoise winpgntc wingss
 UXSSH    = SSH uxnoise uxagentc uxgss
-MACSSH   = SSH macnoise
+MACSSH   = SSH macnoise sshnogss
 
 # SFTP implementation (pscp, psftp).
 SFTP     = sftp int64 logging
@@ -304,7 +309,7 @@ U_BE_NOSSH = be_nos_s uxser nocproxy
 # X/GTK Unix app, [U] for command-line Unix app, [M] for Macintosh app.
 
 putty    : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res LIBS
-puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res LIBS
+puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss LIBS
 plink    : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC
          + winx11 plink.res LIBS
 pscp     : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC
@@ -323,11 +328,13 @@ puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
 
 pterm    : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
          + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg
+        + nogss
 putty    : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_ALL uxstore
          + uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11 xpmputty
          + xpmpucfg
 puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_NOSSH
         + uxstore uxsignal CHARSET uxputty NONSSH UXMISC xpmputty xpmpucfg
+        + nogss
 
 plink    : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal
          + ux_x11
@@ -345,7 +352,7 @@ PuTTY    : [M] terminal wcwidth ldiscucs logging BE_ALL mac macdlg macevlog
          + stricmp vsnprint dialog config macctrls minibidi
 PuTTYtel : [M] terminal wcwidth ldiscucs logging BE_NOSSH mac macdlg
         + macevlog macterm macucs mac_res.rsrc testback NONSSH MACMISC
-        + CHARSET stricmp vsnprint dialog config macctrls minibidi
+        + CHARSET stricmp vsnprint dialog config macctrls minibidi nogss
 PuTTYgen : [M] macpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand macnoise sshsha macstore misc sshrsa sshdss macmisc sshpubk
          + sshaes sshsh256 sshsh512 import macpgen.rsrc macpgkey macabout
index bc48e27..d689ede 100644 (file)
--- a/config.c
+++ b/config.c
@@ -236,6 +236,33 @@ static void cipherlist_handler(union control *ctrl, void *dlg,
     }
 }
 
+#ifndef NO_GSSAPI
+static void gsslist_handler(union control *ctrl, void *dlg,
+                           void *data, int event)
+{
+    Config *cfg = (Config *)data;
+    if (event == EVENT_REFRESH) {
+       int i;
+
+       dlg_update_start(ctrl, dlg);
+       dlg_listbox_clear(ctrl, dlg);
+       for (i = 0; i < ngsslibs; i++) {
+           int id = cfg->ssh_gsslist[i];
+           assert(id >= 0 && id < ngsslibs);
+           dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
+       }
+       dlg_update_done(ctrl, dlg);
+
+    } else if (event == EVENT_VALCHANGE) {
+       int i;
+
+       /* Update array to match the list box. */
+       for (i=0; i < ngsslibs; i++)
+           cfg->ssh_gsslist[i] = dlg_listbox_getid(ctrl, dlg, i);
+    }
+}
+#endif
+
 static void kexlist_handler(union control *ctrl, void *dlg,
                            void *data, int event)
 {
@@ -2089,7 +2116,7 @@ void setup_config_box(struct controlbox *b, int midsession,
            ctrl_checkbox(s, "Allow agent forwarding", 'f',
                          HELPCTX(ssh_auth_agentfwd),
                          dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
-           ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", 'u',
+           ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
                          HELPCTX(ssh_auth_changeuser),
                          dlg_stdcheckbox_handler,
                          I(offsetof(Config,change_username)));
@@ -2103,6 +2130,18 @@ void setup_config_box(struct controlbox *b, int midsession,
                         FILTER_KEY_FILES, FALSE, "Select private key file",
                         HELPCTX(ssh_auth_privkey),
                         dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
+
+#ifndef NO_GSSAPI
+           /*
+            * GSSAPI library selection.
+            */
+           if (ngsslibs > 1) {
+               c = ctrl_draglist(s, "Preference order for GSSAPI libraries:", NO_SHORTCUT,
+                                 HELPCTX(no_help),
+                                 gsslist_handler, P(NULL));
+               c->listbox.height = ngsslibs;
+           }
+#endif
        }
 
        if (!midsession) {
index 3f49cd0..796515b 100644 (file)
@@ -44,6 +44,9 @@ struct FontSpec {
 #define BYTE UInt8
 #define DWORD UInt32
 
+typedef UInt32 uint32;
+#define PUTTY_UINT32_DEFINED
+
 #define OPTIMISE_SCROLL
 
 /*
index 8155c90..4c58e4c 100755 (executable)
@@ -944,9 +944,14 @@ if (defined $makefiles{'gtk'}) {
     "XLDFLAGS = \$(LDFLAGS) \$(shell \$(GTK_CONFIG) --libs)\n".
     "ULDFLAGS = \$(LDFLAGS)\n".
     "ifeq (,\$(findstring NO_GSSAPI,\$(COMPAT)))\n".
-    "CFLAGS+= \$(shell \$(KRB5CONFIG) --cflags gssapi)\n".
+    "ifeq (,\$(findstring STATIC_GSSAPI,\$(COMPAT)))\n".
+    "XLDFLAGS+= -ldl\n".
+    "ULDFLAGS+= -ldl\n".
+    "else\n".
+    "CFLAGS+= -DNO_LIBDL \$(shell \$(KRB5CONFIG) --cflags gssapi)\n".
     "XLDFLAGS+= \$(shell \$(KRB5CONFIG) --libs gssapi)\n".
-    "ULDFLAGS = \$(shell \$(KRB5CONFIG) --libs gssapi)\n".
+    "ULDFLAGS+= \$(shell \$(KRB5CONFIG) --libs gssapi)\n".
+    "endif\n".
     "endif\n".
     "INSTALL=install\n".
     "INSTALL_PROGRAM=\$(INSTALL)\n".
@@ -1006,8 +1011,6 @@ if (defined $makefiles{'unix'}) {
     "# You can define this path to point at your tools if you need to\n".
     "# TOOLPATH = /opt/gcc/bin\n".
     "CC = \$(TOOLPATH)cc\n".
-    "# If necessary set the path to krb5-config here\n".
-    "KRB5CONFIG=krb5-config\n".
     "\n".
     "-include Makefile.local\n".
     "\n".
@@ -1017,10 +1020,6 @@ if (defined $makefiles{'unix'}) {
               (join " ", map {"-I$dirpfx$_"} @srcdirs)).
                 " -D _FILE_OFFSET_BITS=64\n".
     "ULDFLAGS = \$(LDFLAGS)\n".
-    "ifeq (,\$(findstring NO_GSSAPI,\$(COMPAT)))\n".
-    "CFLAGS+= \$(shell \$(KRB5CONFIG) --cflags gssapi)\n".
-    "ULDFLAGS = \$(shell \$(KRB5CONFIG) --libs gssapi)\n".
-    "endif\n".
     "INSTALL=install\n".
     "INSTALL_PROGRAM=\$(INSTALL)\n".
     "INSTALL_DATA=\$(INSTALL)\n".
diff --git a/nogss.c b/nogss.c
new file mode 100644 (file)
index 0000000..57a1b37
--- /dev/null
+++ b/nogss.c
@@ -0,0 +1,10 @@
+/*
+ * Stub definitions of the GSSAPI library list, for Unix pterm and
+ * any other application that needs the symbols defined but has no
+ * use for them.
+ */
+
+const int ngsslibs = 0;
+const char *const gsslibnames[1] = { "dummy" };
+const char *const gsslibkeywords[1] = { "dummy" };
+
diff --git a/pgssapi.c b/pgssapi.c
new file mode 100644 (file)
index 0000000..5d9ffeb
--- /dev/null
+++ b/pgssapi.c
@@ -0,0 +1,105 @@
+/* This file actually defines the GSSAPI function pointers for
+ * functions we plan to import from a GSSAPI library.
+ */
+#include "putty.h"
+
+#ifndef NO_GSSAPI
+
+#include "pgssapi.h"
+
+#ifndef NO_LIBDL
+
+/* Reserved static storage for GSS_oids.  Comments are quotes from RFC 2744. */
+static const gss_OID_desc oids[] = {
+    /* The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"},
+    /* corresponding to an object-identifier value of
+     * {iso(1) member-body(2) United States(840) mit(113554)
+     * infosys(1) gssapi(2) generic(1) user_name(1)}.  The constant
+     * GSS_C_NT_USER_NAME should be initialized to point
+     * to that gss_OID_desc.
+
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"},
+    /* corresponding to an object-identifier value of
+     * {iso(1) member-body(2) United States(840) mit(113554)
+     * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
+     * The constant GSS_C_NT_MACHINE_UID_NAME should be
+     * initialized to point to that gss_OID_desc.
+
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"},
+    /* corresponding to an object-identifier value of
+     * {iso(1) member-body(2) United States(840) mit(113554)
+     * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
+     * The constant GSS_C_NT_STRING_UID_NAME should be
+     * initialized to point to that gss_OID_desc.
+     *
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
+    /* corresponding to an object-identifier value of
+     * {iso(1) org(3) dod(6) internet(1) security(5)
+     * nametypes(6) gss-host-based-services(2)).  The constant
+     * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
+     * to that gss_OID_desc.  This is a deprecated OID value, and
+     * implementations wishing to support hostbased-service names
+     * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
+     * defined below, to identify such names;
+     * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
+     * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
+     * parameter, but should not be emitted by GSS-API
+     * implementations
+     *
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+     {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"},
+    /* corresponding to an object-identifier value of {iso(1)
+     * member-body(2) Unites States(840) mit(113554) infosys(1)
+     * gssapi(2) generic(1) service_name(4)}.  The constant
+     * GSS_C_NT_HOSTBASED_SERVICE should be initialized
+     * to point to that gss_OID_desc.
+     *
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {6, (void *)"\x2b\x06\01\x05\x06\x03"},
+    /* corresponding to an object identifier value of
+     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+     * 6(nametypes), 3(gss-anonymous-name)}.  The constant
+     * and GSS_C_NT_ANONYMOUS should be initialized to point
+     * to that gss_OID_desc.
+     *
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
+     /* corresponding to an object-identifier value of
+     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+     * 6(nametypes), 4(gss-api-exported-name)}.  The constant
+     * GSS_C_NT_EXPORT_NAME should be initialized to point
+     * to that gss_OID_desc.
+     */
+};
+
+/* Here are the constants which point to the static structure above.
+ *
+ * Constants of the form GSS_C_NT_* are specified by rfc 2744.
+ */
+const_gss_OID GSS_C_NT_USER_NAME           = oids+0;
+const_gss_OID GSS_C_NT_MACHINE_UID_NAME    = oids+1;
+const_gss_OID GSS_C_NT_STRING_UID_NAME     = oids+2;
+const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = oids+3;
+const_gss_OID GSS_C_NT_HOSTBASED_SERVICE   = oids+4;
+const_gss_OID GSS_C_NT_ANONYMOUS           = oids+5;
+const_gss_OID GSS_C_NT_EXPORT_NAME         = oids+6;
+
+#endif /* NO_LIBDL */
+
+static gss_OID_desc gss_mech_krb5_desc =
+{ 9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
+/* iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) krb5(2)*/
+const gss_OID GSS_MECH_KRB5 = &gss_mech_krb5_desc;
+
+#endif /* NO_GSSAPI */
diff --git a/pgssapi.h b/pgssapi.h
new file mode 100644 (file)
index 0000000..f6370c2
--- /dev/null
+++ b/pgssapi.h
@@ -0,0 +1,296 @@
+#ifndef PUTTY_PGSSAPI_H
+#define PUTTY_PGSSAPI_H
+
+#include "putty.h"
+
+#ifndef NO_GSSAPI
+
+/*
+ * On Unix, if we're statically linking against GSSAPI, we leave the
+ * declaration of all this lot to the official header. If we're
+ * dynamically linking, we declare it ourselves, because that avoids
+ * us needing the official header at compile time.
+ *
+ * However, we still need the function pointer types, because even
+ * with statically linked GSSAPI we use the ssh_gss_library wrapper.
+ */
+#ifdef STATIC_GSSAPI
+#include <gssapi/gssapi.h>
+typedef gss_OID const_gss_OID;        /* for our prototypes below */
+#else /* STATIC_GSSAPI */
+
+/*******************************************************************************
+ *  GSSAPI Definitions, taken from RFC 2744
+ ******************************************************************************/
+
+/* GSSAPI Type Definitions */
+typedef uint32 OM_uint32;
+
+typedef struct gss_OID_desc_struct {
+    OM_uint32 length;
+    void *elements;
+} gss_OID_desc;
+typedef const gss_OID_desc *const_gss_OID;
+typedef gss_OID_desc *gss_OID;
+
+typedef struct gss_OID_set_desc_struct  {
+    size_t  count;
+    gss_OID elements;
+} gss_OID_set_desc;
+typedef const gss_OID_set_desc *const_gss_OID_set;
+typedef gss_OID_set_desc *gss_OID_set;
+
+typedef struct gss_buffer_desc_struct {
+    size_t length;
+    void *value;
+} gss_buffer_desc, *gss_buffer_t;
+
+typedef struct gss_channel_bindings_struct {
+    OM_uint32 initiator_addrtype;
+    gss_buffer_desc initiator_address;
+    OM_uint32 acceptor_addrtype;
+    gss_buffer_desc acceptor_address;
+    gss_buffer_desc application_data;
+} *gss_channel_bindings_t;
+
+typedef void * gss_ctx_id_t;
+typedef void * gss_name_t;
+typedef void * gss_cred_id_t;
+
+typedef OM_uint32 gss_qop_t;
+
+/* Flag bits for context-level services. */
+
+#define GSS_C_DELEG_FLAG      1
+#define GSS_C_MUTUAL_FLAG     2
+#define GSS_C_REPLAY_FLAG     4
+#define GSS_C_SEQUENCE_FLAG   8
+#define GSS_C_CONF_FLAG       16
+#define GSS_C_INTEG_FLAG      32
+#define GSS_C_ANON_FLAG       64
+#define GSS_C_PROT_READY_FLAG 128
+#define GSS_C_TRANS_FLAG      256
+
+/* Credential usage options */
+#define GSS_C_BOTH     0
+#define GSS_C_INITIATE 1
+#define GSS_C_ACCEPT   2
+
+/* Status code types for gss_display_status */
+#define GSS_C_GSS_CODE  1
+#define GSS_C_MECH_CODE 2
+
+/* The constant definitions for channel-bindings address families */
+#define GSS_C_AF_UNSPEC     0
+#define GSS_C_AF_LOCAL      1
+#define GSS_C_AF_INET       2
+#define GSS_C_AF_IMPLINK    3
+#define GSS_C_AF_PUP        4
+#define GSS_C_AF_CHAOS      5
+#define GSS_C_AF_NS         6
+#define GSS_C_AF_NBS        7
+#define GSS_C_AF_ECMA       8
+#define GSS_C_AF_DATAKIT    9
+#define GSS_C_AF_CCITT      10
+#define GSS_C_AF_SNA        11
+#define GSS_C_AF_DECnet     12
+#define GSS_C_AF_DLI        13
+#define GSS_C_AF_LAT        14
+#define GSS_C_AF_HYLINK     15
+#define GSS_C_AF_APPLETALK  16
+#define GSS_C_AF_BSC        17
+#define GSS_C_AF_DSS        18
+#define GSS_C_AF_OSI        19
+#define GSS_C_AF_X25        21
+
+#define GSS_C_AF_NULLADDR   255
+
+/* Various Null values */
+#define GSS_C_NO_NAME ((gss_name_t) 0)
+#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
+#define GSS_C_NO_OID ((gss_OID) 0)
+#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
+#define GSS_C_EMPTY_BUFFER {0, NULL}
+
+/* Major status codes */
+#define GSS_S_COMPLETE 0
+
+/* Some "helper" definitions to make the status code macros obvious. */
+#define GSS_C_CALLING_ERROR_OFFSET 24
+#define GSS_C_ROUTINE_ERROR_OFFSET 16
+
+#define GSS_C_SUPPLEMENTARY_OFFSET 0
+#define GSS_C_CALLING_ERROR_MASK 0377ul
+#define GSS_C_ROUTINE_ERROR_MASK 0377ul
+#define GSS_C_SUPPLEMENTARY_MASK 0177777ul
+
+/*
+ * The macros that test status codes for error conditions.
+ * Note that the GSS_ERROR() macro has changed slightly from
+ * the V1 GSS-API so that it now evaluates its argument
+ * only once.
+ */
+#define GSS_CALLING_ERROR(x)                                            \
+    (x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
+#define GSS_ROUTINE_ERROR(x)                                            \
+    (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
+#define GSS_SUPPLEMENTARY_INFO(x)                                       \
+    (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
+#define GSS_ERROR(x)                                                    \
+    (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) |    \
+          (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
+
+/* Now the actual status code definitions */
+
+/* Calling errors: */
+#define GSS_S_CALL_INACCESSIBLE_READ            \
+    (1ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_INACCESSIBLE_WRITE           \
+    (2ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_BAD_STRUCTURE                \
+    (3ul << GSS_C_CALLING_ERROR_OFFSET)
+
+/* Routine errors: */
+#define GSS_S_BAD_MECH             (1ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAME             (2ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAMETYPE         (3ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_BINDINGS         (4ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_STATUS           (5ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_SIG              (6ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_MIC GSS_S_BAD_SIG
+#define GSS_S_NO_CRED              (7ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CONTEXT           (8ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_TOKEN      (9ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_CREDENTIAL (10ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CREDENTIALS_EXPIRED  (11ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CONTEXT_EXPIRED      (12ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_FAILURE              (13ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_QOP              (14ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAUTHORIZED         (15ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAVAILABLE          (16ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DUPLICATE_ELEMENT    (17ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NAME_NOT_MN          (18ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+
+/* Supplementary info bits: */
+#define GSS_S_CONTINUE_NEEDED                                           \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
+#define GSS_S_DUPLICATE_TOKEN                                           \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
+#define GSS_S_OLD_TOKEN                                                 \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
+#define GSS_S_UNSEQ_TOKEN                                               \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
+#define GSS_S_GAP_TOKEN                                                 \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
+
+extern const_gss_OID GSS_C_NT_USER_NAME;
+extern const_gss_OID GSS_C_NT_MACHINE_UID_NAME;
+extern const_gss_OID GSS_C_NT_STRING_UID_NAME;
+extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
+extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE;
+extern const_gss_OID GSS_C_NT_ANONYMOUS;
+extern const_gss_OID GSS_C_NT_EXPORT_NAME;
+
+#endif /* STATIC_GSSAPI */
+
+extern const gss_OID GSS_MECH_KRB5;
+
+/* GSSAPI functions we use.
+ * TODO: Replace with all GSSAPI functions from RFC?
+ */
+
+/* Calling convention, just in case we need one. */
+#ifndef GSS_CC
+#define GSS_CC
+#endif /*GSS_CC*/
+
+typedef OM_uint32 (GSS_CC *t_gss_release_cred)
+            (OM_uint32                    * /*minor_status*/,
+             gss_cred_id_t                * /*cred_handle*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_init_sec_context)
+            (OM_uint32                    * /*minor_status*/,
+             const gss_cred_id_t            /*initiator_cred_handle*/,
+             gss_ctx_id_t                 * /*context_handle*/,
+             const gss_name_t               /*target_name*/,
+             const gss_OID                  /*mech_type*/,
+             OM_uint32                      /*req_flags*/,
+             OM_uint32                      /*time_req*/,
+             const gss_channel_bindings_t   /*input_chan_bindings*/,
+             const gss_buffer_t             /*input_token*/,
+             gss_OID                      * /*actual_mech_type*/,
+             gss_buffer_t                   /*output_token*/,
+             OM_uint32                    * /*ret_flags*/,
+             OM_uint32                    * /*time_rec*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_delete_sec_context)
+            (OM_uint32                    * /*minor_status*/,
+             gss_ctx_id_t                 * /*context_handle*/,
+             gss_buffer_t                   /*output_token*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_get_mic)
+            (OM_uint32                    * /*minor_status*/,
+             const gss_ctx_id_t             /*context_handle*/,
+             gss_qop_t                      /*qop_req*/,
+             const gss_buffer_t             /*message_buffer*/,
+             gss_buffer_t                   /*msg_token*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_display_status)
+            (OM_uint32                   * /*minor_status*/,
+             OM_uint32                     /*status_value*/,
+             int                           /*status_type*/,
+             const gss_OID                 /*mech_type*/,
+             OM_uint32                   * /*message_context*/,
+             gss_buffer_t                  /*status_string*/);
+
+
+typedef OM_uint32 (GSS_CC *t_gss_import_name)
+            (OM_uint32                   * /*minor_status*/,
+             const gss_buffer_t            /*input_name_buffer*/,
+             const_gss_OID                 /*input_name_type*/,
+             gss_name_t                  * /*output_name*/);
+
+
+typedef OM_uint32 (GSS_CC *t_gss_release_name)
+            (OM_uint32                   * /*minor_status*/,
+             gss_name_t                  * /*name*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_release_buffer)
+            (OM_uint32                   * /*minor_status*/,
+             gss_buffer_t                  /*buffer*/);
+
+struct gssapi_functions {
+    t_gss_delete_sec_context delete_sec_context;
+    t_gss_display_status display_status;
+    t_gss_get_mic get_mic;
+    t_gss_import_name import_name;
+    t_gss_init_sec_context init_sec_context;
+    t_gss_release_buffer release_buffer;
+    t_gss_release_cred release_cred;
+    t_gss_release_name release_name;
+};
+
+#endif /* NO_GSSAPI */
+
+#endif /* PUTTY_PGSSAPI_H */
diff --git a/putty.h b/putty.h
index ca232bc..0c97562 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -348,6 +348,19 @@ enum {
     SER_FLOW_NONE, SER_FLOW_XONXOFF, SER_FLOW_RTSCTS, SER_FLOW_DSRDTR
 };
 
+/*
+ * Tables of string <-> enum value mappings used in settings.c.
+ * Defined here so that backends can export their GSS library tables
+ * to the cross-platform settings code.
+ */
+struct keyval { char *s; int v; };
+
+#ifndef NO_GSSAPI
+extern const int ngsslibs;
+extern const char *const gsslibnames[];/* for displaying in configuration */
+extern const struct keyval gsslibkeywords[];   /* for storing by settings.c */
+#endif
+
 extern const char *const ttymodes[];
 
 enum {
@@ -461,6 +474,7 @@ struct config_tag {
     int try_ki_auth;
     int try_gssapi_auth;               /* attempt gssapi auth */
     int gssapifwd;                     /* forward tgt via gss */
+    int ssh_gsslist[4];                       /* preference order for local GSS libs */
     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 38c4188..3d51f51 100644 (file)
@@ -8,11 +8,6 @@
 #include "putty.h"
 #include "storage.h"
 
-/*
- * Tables of string <-> enum value mappings
- */
-struct keyval { char *s; int v; };
-
 /* The cipher order given here is the default order. */
 static const struct keyval ciphernames[] = {
     { "aes",       CIPHER_AES },
@@ -356,6 +351,8 @@ 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);
+    wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs,
+          cfg->ssh_gsslist);
     write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell);
     write_setting_i(sesskey, "SshProt", cfg->sshprot);
     write_setting_s(sesskey, "LogHost", cfg->loghost);
@@ -645,6 +642,8 @@ 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);
+    gprefs(sesskey, "GSSList", "\0",
+          gsslibkeywords, ngsslibs, cfg->ssh_gsslist);
     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 029c78a..0616eff 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -13,6 +13,7 @@
 #include "tree234.h"
 #include "ssh.h"
 #ifndef NO_GSSAPI
+#include "sshgssc.h"
 #include "sshgss.h"
 #endif
 
@@ -7209,6 +7210,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        int num_env, env_left, env_ok;
        struct Packet *pktout;
 #ifndef NO_GSSAPI
+       struct ssh_gss_library *gsslib;
        Ssh_gss_ctx gss_ctx;
        Ssh_gss_buf gss_buf;
        Ssh_gss_buf gss_rcvtok, gss_sndtok;
@@ -7584,9 +7586,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                s->can_keyb_inter = ssh->cfg.try_ki_auth &&
                    in_commasep_string("keyboard-interactive", methods, methlen);
 #ifndef NO_GSSAPI              
+               ssh_gss_init();
                s->can_gssapi = ssh->cfg.try_gssapi_auth &&
-                 in_commasep_string("gssapi-with-mic", methods, methlen) &&
-                 ssh_gss_init();
+                   in_commasep_string("gssapi-with-mic", methods, methlen) &&
+                   n_ssh_gss_libraries > 0;
 #endif
            }
 
@@ -7929,6 +7932,35 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                s->gotit = TRUE;
                ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;
 
+               /*
+                * Pick the highest GSS library on the preference
+                * list.
+                */
+               {
+                   int i, j;
+                   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];
+                               goto got_gsslib;   /* double break */
+                           }
+                   }
+                   got_gsslib:
+                   /*
+                    * We always expect to have found something in
+                    * the above loop: we only came here if there
+                    * was at least one viable GSS library, and the
+                    * preference list should always mention
+                    * everything and only change the order.
+                    */
+                   assert(s->gsslib);
+               }
+
+               if (s->gsslib->gsslogmsg)
+                   logevent(s->gsslib->gsslogmsg);
+
                /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
                ssh2_pkt_addstring(s->pktout, s->username);
@@ -7936,7 +7968,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                ssh2_pkt_addstring(s->pktout, "gssapi-with-mic");
 
                /* add mechanism info */
-               ssh_gss_indicate_mech(&s->gss_buf);
+               s->gsslib->indicate_mech(s->gsslib, &s->gss_buf);
 
                /* number of GSSAPI mechanisms */
                ssh2_pkt_adduint32(s->pktout,1);
@@ -7972,8 +8004,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                }
 
                /* now start running */
-               s->gss_stat = ssh_gss_import_name(ssh->fullhostname,
-                                                 &s->gss_srv_name);
+               s->gss_stat = s->gsslib->import_name(s->gsslib,
+                                                    ssh->fullhostname,
+                                                    &s->gss_srv_name);
                if (s->gss_stat != SSH_GSS_OK) {
                    if (s->gss_stat == SSH_GSS_BAD_HOST_NAME)
                        logevent("GSSAPI import name failed - Bad service name");
@@ -7983,11 +8016,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                }
 
                /* fetch TGT into GSS engine */
-               s->gss_stat = ssh_gss_acquire_cred(&s->gss_ctx);
+               s->gss_stat = s->gsslib->acquire_cred(s->gsslib, &s->gss_ctx);
 
                if (s->gss_stat != SSH_GSS_OK) {
                    logevent("GSSAPI authentication failed to get credentials");
-                   ssh_gss_release_name(&s->gss_srv_name);
+                   s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
                    continue;
                }
 
@@ -7997,17 +8030,20 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 
                /* now enter the loop */
                do {
-                   s->gss_stat = ssh_gss_init_sec_context(&s->gss_ctx,
-                                                          s->gss_srv_name,
-                                                          ssh->cfg.gssapifwd,
-                                                          &s->gss_rcvtok,
-                                                          &s->gss_sndtok);
+                   s->gss_stat = s->gsslib->init_sec_context
+                       (s->gsslib,
+                        &s->gss_ctx,
+                        s->gss_srv_name,
+                        ssh->cfg.gssapifwd,
+                        &s->gss_rcvtok,
+                        &s->gss_sndtok);
 
                    if (s->gss_stat!=SSH_GSS_S_COMPLETE &&
                        s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) {
                        logevent("GSSAPI authentication initialisation failed");
 
-                       if (ssh_gss_display_status(s->gss_ctx,&s->gss_buf) == SSH_GSS_OK) {
+                       if (s->gsslib->display_status(s->gsslib, s->gss_ctx,
+                                                     &s->gss_buf) == SSH_GSS_OK) {
                            logevent(s->gss_buf.value);
                            sfree(s->gss_buf.value);
                        }
@@ -8024,7 +8060,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                        ssh_pkt_addstring_start(s->pktout);
                        ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.value,s->gss_sndtok.length);
                        ssh2_pkt_send(ssh, s->pktout);
-                       ssh_gss_free_tok(&s->gss_sndtok);
+                       s->gsslib->free_tok(s->gsslib, &s->gss_sndtok);
                    }
 
                    if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {
@@ -8041,8 +8077,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
 
                if (s->gss_stat != SSH_GSS_OK) {
-                   ssh_gss_release_name(&s->gss_srv_name);
-                   ssh_gss_release_cred(&s->gss_ctx);
+                   s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+                   s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
                    continue;
                }
                logevent("GSSAPI authentication loop finished OK");
@@ -8061,17 +8097,17 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                s->gss_buf.value = (char *)s->pktout->data + micoffset;
                s->gss_buf.length = s->pktout->length - micoffset;
 
-               ssh_gss_get_mic(s->gss_ctx, &s->gss_buf, &mic);
+               s->gsslib->get_mic(s->gsslib, s->gss_ctx, &s->gss_buf, &mic);
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC);
                ssh_pkt_addstring_start(s->pktout);
                ssh_pkt_addstring_data(s->pktout, mic.value, mic.length);
                ssh2_pkt_send(ssh, s->pktout);
-               ssh_gss_free_mic(&mic);
+               s->gsslib->free_mic(s->gsslib, &mic);
 
                s->gotit = FALSE;
 
-               ssh_gss_release_name(&s->gss_srv_name);
-               ssh_gss_release_cred(&s->gss_ctx);
+               s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+               s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
                continue;
 #endif
            } else if (s->can_keyb_inter && !s->kbd_inter_refused) {
diff --git a/ssh.h b/ssh.h
index d17920c..3d02ad7 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -71,8 +71,12 @@ unsigned char *rsa_public_blob(struct RSAKey *key, int *len);
 int rsa_public_blob_len(void *data, int maxlen);
 void freersakey(struct RSAKey *key);
 
-typedef unsigned int word32;
+#ifndef PUTTY_UINT32_DEFINED
+/* This makes assumptions about the int type. */
 typedef unsigned int uint32;
+#define PUTTY_UINT32_DEFINED
+#endif
+typedef uint32 word32;
 
 unsigned long crc32_compute(const void *s, size_t len);
 unsigned long crc32_update(unsigned long crc_input, const void *s, size_t len);
index 2115cb1..d12c70f 100644 (file)
--- a/sshgss.h
+++ b/sshgss.h
@@ -1,4 +1,9 @@
-#include "puttyps.h"
+#ifndef PUTTY_SSHGSS_H
+#define PUTTY_SSHGSS_H
+#include "putty.h"
+#include "pgssapi.h"
+
+#ifndef NO_GSSAPI
 
 #define SSH2_GSS_OIDTYPE 0x06
 typedef void *Ssh_gss_ctx;
@@ -18,46 +23,54 @@ typedef enum Ssh_gss_stat {
     (*buf).value = NULL;                               \
 } while (0)
 
-/* Functions, provided by either wingss.c or uxgss.c */
+typedef gss_buffer_desc Ssh_gss_buf;
+typedef gss_name_t Ssh_gss_name;
+
+/* Functions, provided by either wingss.c or sshgssc.c */
+
+struct ssh_gss_library;
 
 /*
- * Do startup-time initialisation for using GSSAPI. (On Windows,
- * for instance, this dynamically loads the GSSAPI DLL and
- * retrieves some function pointers.)
- *
- * Return value is 1 on success, or 0 if initialisation failed.
+ * Do startup-time initialisation for using GSSAPI. This should
+ * correctly initialise the array of struct ssh_gss_library declared
+ * below.
  *
- * May be called multiple times (since the most convenient place
- * to call it _from_ is the ssh.c setup code), and will harmlessly
+ * 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.
  */
-int ssh_gss_init(void);
+void ssh_gss_init(void);
 
 /*
  * Fills in buf with a string describing the GSSAPI mechanism in
  * use. buf->data is not dynamically allocated.
  */
-Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *buf);
+typedef Ssh_gss_stat (*t_ssh_gss_indicate_mech)(struct ssh_gss_library *lib,
+                                               Ssh_gss_buf *buf);
 
 /*
  * Converts a name such as a hostname into a GSSAPI internal form,
  * which is placed in "out". The result should be freed by
  * ssh_gss_release_name().
  */
-Ssh_gss_stat ssh_gss_import_name(char *in, Ssh_gss_name *out);
+typedef Ssh_gss_stat (*t_ssh_gss_import_name)(struct ssh_gss_library *lib,
+                                             char *in, Ssh_gss_name *out);
 
 /*
  * Frees the contents of an Ssh_gss_name structure filled in by
  * ssh_gss_import_name().
  */
-Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *name);
+typedef Ssh_gss_stat (*t_ssh_gss_release_name)(struct ssh_gss_library *lib,
+                                              Ssh_gss_name *name);
 
 /*
  * The main GSSAPI security context setup function. The "out"
  * parameter will need to be freed by ssh_gss_free_tok.
  */
-Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate,
-                                     Ssh_gss_buf *in, Ssh_gss_buf *out);
+typedef Ssh_gss_stat (*t_ssh_gss_init_sec_context)
+    (struct ssh_gss_library *lib,
+     Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate,
+     Ssh_gss_buf *in, Ssh_gss_buf *out);
 
 /*
  * Frees the contents of an Ssh_gss_buf filled in by
@@ -66,26 +79,30 @@ Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx, Ssh_gss_name name, int d
  * different free function) or something filled in by any other
  * way.
  */
-Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *);
+typedef Ssh_gss_stat (*t_ssh_gss_free_tok)(struct ssh_gss_library *lib,
+                                          Ssh_gss_buf *);
 
 /*
  * Acquires the credentials to perform authentication in the first
  * place. Needs to be freed by ssh_gss_release_cred().
  */
-Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *);
+typedef Ssh_gss_stat (*t_ssh_gss_acquire_cred)(struct ssh_gss_library *lib,
+                                              Ssh_gss_ctx *);
 
 /*
  * Frees the contents of an Ssh_gss_ctx filled in by
  * ssh_gss_acquire_cred().
  */
-Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *);
+typedef Ssh_gss_stat (*t_ssh_gss_release_cred)(struct ssh_gss_library *lib,
+                                              Ssh_gss_ctx *);
 
 /*
  * Gets a MIC for some input data. "out" needs to be freed by
  * ssh_gss_free_mic().
  */
-Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *in,
-                            Ssh_gss_buf *out);
+typedef Ssh_gss_stat (*t_ssh_gss_get_mic)(struct ssh_gss_library *lib,
+                                         Ssh_gss_ctx ctx, Ssh_gss_buf *in,
+                 Ssh_gss_buf *out);
 
 /*
  * Frees the contents of an Ssh_gss_buf filled in by
@@ -94,7 +111,8 @@ Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *in,
  * different free function) or something filled in by any other
  * way.
  */
-Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *);
+typedef Ssh_gss_stat (*t_ssh_gss_free_mic)(struct ssh_gss_library *lib,
+                                          Ssh_gss_buf *);
 
 /*
  * Return an error message after authentication failed. The
@@ -103,4 +121,56 @@ Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *);
  * containing one more character which is a trailing NUL.
  * buf->data should be manually freed by the caller. 
  */
-Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx, Ssh_gss_buf *buf);
+typedef Ssh_gss_stat (*t_ssh_gss_display_status)(struct ssh_gss_library *lib,
+                                                Ssh_gss_ctx, Ssh_gss_buf *buf);
+
+struct ssh_gss_library {
+    /*
+     * Identifying number in the enumeration used by the
+     * configuration code to specify a preference order.
+     */
+    int id;
+
+    /*
+     * Filled in at initialisation time, if there's anything
+     * interesting to say about how GSSAPI was initialised (e.g.
+     * which of a number of alternative libraries was used).
+     */
+    const char *gsslogmsg;
+
+    /*
+     * Function pointers implementing the SSH wrapper layer on top
+     * of GSSAPI. (Defined in sshgssc, typically, though Windows
+     * provides an alternative layer to sit on top of the annoyingly
+     * different SSPI.)
+     */
+    t_ssh_gss_indicate_mech indicate_mech;
+    t_ssh_gss_import_name import_name;
+    t_ssh_gss_release_name release_name;
+    t_ssh_gss_init_sec_context init_sec_context;
+    t_ssh_gss_free_tok free_tok;
+    t_ssh_gss_acquire_cred acquire_cred;
+    t_ssh_gss_release_cred release_cred;
+    t_ssh_gss_get_mic get_mic;
+    t_ssh_gss_free_mic free_mic;
+    t_ssh_gss_display_status display_status;
+
+    /*
+     * Additional data for the wrapper layers.
+     */
+    union {
+       struct gssapi_functions gssapi;
+       /*
+        * The SSPI wrappers don't need to store their Windows API
+        * function pointers in this structure, because there can't
+        * be more than one set of them available.
+        */
+    } u;
+};
+
+extern struct ssh_gss_library ssh_gss_libraries[];
+extern int n_ssh_gss_libraries;
+
+#endif /* NO_GSSAPI */
+
+#endif /*PUTTY_SSHGSS_H*/
diff --git a/sshgssc.c b/sshgssc.c
new file mode 100644 (file)
index 0000000..4590ed7
--- /dev/null
+++ b/sshgssc.c
@@ -0,0 +1,209 @@
+#include "putty.h"
+
+#include <string.h>
+#include "sshgssc.h"
+#include "misc.h"
+
+#ifndef NO_GSSAPI
+
+static Ssh_gss_stat ssh_gssapi_indicate_mech(struct ssh_gss_library *lib,
+                                            Ssh_gss_buf *mech)
+{
+    /* Copy constant into mech */
+    mech->length  = GSS_MECH_KRB5->length;
+    mech->value = GSS_MECH_KRB5->elements;
+    return SSH_GSS_OK;
+}
+
+static Ssh_gss_stat ssh_gssapi_import_name(struct ssh_gss_library *lib,
+                                          char *host,
+                                          Ssh_gss_name *srv_name)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    OM_uint32 min_stat,maj_stat;
+    gss_buffer_desc host_buf;
+    char *pStr;
+
+    pStr = dupcat("host@", host, NULL);
+
+    host_buf.value = pStr;
+    host_buf.length = strlen(pStr);
+
+    maj_stat = gss->import_name(&min_stat, &host_buf,
+                               GSS_C_NT_HOSTBASED_SERVICE, srv_name);
+    /* Release buffer */
+    sfree(pStr);
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+    return SSH_GSS_FAILURE;
+}
+
+static Ssh_gss_stat ssh_gssapi_acquire_cred(struct ssh_gss_library *lib,
+                                           Ssh_gss_ctx *ctx)
+{
+    gssapi_ssh_gss_ctx *gssctx = snew(gssapi_ssh_gss_ctx);
+
+    gssctx->maj_stat =  gssctx->min_stat = GSS_S_COMPLETE;
+    gssctx->ctx = GSS_C_NO_CONTEXT;
+    *ctx = (Ssh_gss_ctx) gssctx;
+
+    return SSH_GSS_OK;
+}
+
+static Ssh_gss_stat ssh_gssapi_init_sec_context(struct ssh_gss_library *lib,
+                                               Ssh_gss_ctx *ctx,
+                                               Ssh_gss_name srv_name,
+                                               int to_deleg,
+                                               Ssh_gss_buf *recv_tok,
+                                               Ssh_gss_buf *send_tok)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx*) *ctx;
+    OM_uint32 ret_flags;
+
+    if (to_deleg) to_deleg = GSS_C_DELEG_FLAG;
+    gssctx->maj_stat = gss->init_sec_context(&gssctx->min_stat,
+                                            GSS_C_NO_CREDENTIAL,
+                                            &gssctx->ctx,
+                                            srv_name,
+                                            (gss_OID) GSS_MECH_KRB5,
+                                            GSS_C_MUTUAL_FLAG |
+                                            GSS_C_INTEG_FLAG | to_deleg,
+                                            0,
+                                            GSS_C_NO_CHANNEL_BINDINGS,
+                                            recv_tok,
+                                            NULL,   /* ignore mech type */
+                                            send_tok,
+                                            &ret_flags,
+                                            NULL);  /* ignore time_rec */
+
+    if (gssctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE;
+    if (gssctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
+    return SSH_GSS_FAILURE;
+}
+
+static Ssh_gss_stat ssh_gssapi_display_status(struct ssh_gss_library *lib,
+                                             Ssh_gss_ctx ctx,
+                                             Ssh_gss_buf *buf)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;
+    OM_uint32 lmin,lmax;
+    OM_uint32 ccc;
+    gss_buffer_desc msg_maj=GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc msg_min=GSS_C_EMPTY_BUFFER;
+
+    /* Return empty buffer in case of failure */
+    SSH_GSS_CLEAR_BUF(buf);
+
+    /* get first mesg from GSS */
+    ccc=0;
+    lmax=gss->display_status(&lmin,gssctx->maj_stat,GSS_C_GSS_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_maj);
+
+    if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE;
+
+    /* get first mesg from Kerberos */
+    ccc=0;
+    lmax=gss->display_status(&lmin,gssctx->min_stat,GSS_C_MECH_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_min);
+
+    if (lmax != GSS_S_COMPLETE) {
+        gss->release_buffer(&lmin, &msg_maj);
+        return SSH_GSS_FAILURE;
+    }
+
+    /* copy data into buffer */
+    buf->length = msg_maj.length + msg_min.length + 1;
+    buf->value = snewn(buf->length + 1, char);
+
+    /* copy mem */
+    memcpy((char *)buf->value, msg_maj.value, msg_maj.length);
+    ((char *)buf->value)[msg_maj.length] = ' ';
+    memcpy((char *)buf->value + msg_maj.length + 1, msg_min.value, msg_min.length);
+    ((char *)buf->value)[buf->length] = 0;
+    /* free mem & exit */
+    gss->release_buffer(&lmin, &msg_maj);
+    gss->release_buffer(&lmin, &msg_min);
+    return SSH_GSS_OK;
+}
+
+static Ssh_gss_stat ssh_gssapi_free_tok(struct ssh_gss_library *lib,
+                                       Ssh_gss_buf *send_tok)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    OM_uint32 min_stat,maj_stat;
+    maj_stat = gss->release_buffer(&min_stat, send_tok);
+
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+    return SSH_GSS_FAILURE;
+}
+
+static Ssh_gss_stat ssh_gssapi_release_cred(struct ssh_gss_library *lib,
+                                           Ssh_gss_ctx *ctx)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) *ctx;
+    OM_uint32 min_stat;
+    OM_uint32 maj_stat=GSS_S_COMPLETE;
+
+    if (gssctx == NULL) return SSH_GSS_FAILURE;
+    if (gssctx->ctx != GSS_C_NO_CONTEXT)
+        maj_stat = gss->delete_sec_context(&min_stat,&gssctx->ctx,GSS_C_NO_BUFFER);
+    sfree(gssctx);
+
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+    return SSH_GSS_FAILURE;
+}
+
+
+static Ssh_gss_stat ssh_gssapi_release_name(struct ssh_gss_library *lib,
+                                           Ssh_gss_name *srv_name)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    OM_uint32 min_stat,maj_stat;
+    maj_stat = gss->release_name(&min_stat, srv_name);
+
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+    return SSH_GSS_FAILURE;
+}
+
+static Ssh_gss_stat ssh_gssapi_get_mic(struct ssh_gss_library *lib,
+                                      Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
+                                      Ssh_gss_buf *hash)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;
+    if (gssctx == NULL) return SSH_GSS_FAILURE;
+    return gss->get_mic(&(gssctx->min_stat), gssctx->ctx, 0, buf, hash);
+}
+
+static Ssh_gss_stat ssh_gssapi_free_mic(struct ssh_gss_library *lib,
+                                       Ssh_gss_buf *hash)
+{
+    /* On Unix this is the same freeing process as ssh_gssapi_free_tok. */
+    return ssh_gssapi_free_tok(lib, hash);
+}
+
+void ssh_gssapi_bind_fns(struct ssh_gss_library *lib)
+{
+    lib->indicate_mech = ssh_gssapi_indicate_mech;
+    lib->import_name = ssh_gssapi_import_name;
+    lib->release_name = ssh_gssapi_release_name;
+    lib->init_sec_context = ssh_gssapi_init_sec_context;
+    lib->free_tok = ssh_gssapi_free_tok;
+    lib->acquire_cred = ssh_gssapi_acquire_cred;
+    lib->release_cred = ssh_gssapi_release_cred;
+    lib->get_mic = ssh_gssapi_get_mic;
+    lib->free_mic = ssh_gssapi_free_mic;
+    lib->display_status = ssh_gssapi_display_status;
+}
+
+#else
+
+/* Dummy function so this source file defines something if NO_GSSAPI
+   is defined. */
+
+int ssh_gssapi_init(void)
+{
+    return 0;
+}
+
+#endif
diff --git a/sshgssc.h b/sshgssc.h
new file mode 100644 (file)
index 0000000..c98ee86
--- /dev/null
+++ b/sshgssc.h
@@ -0,0 +1,23 @@
+#ifndef PUTTY_SSHGSSC_H
+#define PUTTY_SSHGSSC_H
+#include "putty.h"
+#ifndef NO_GSSAPI
+
+#include "pgssapi.h"
+#include "sshgss.h"
+
+typedef struct gssapi_ssh_gss_ctx {
+    OM_uint32 maj_stat;
+    OM_uint32 min_stat;
+    gss_ctx_id_t ctx;
+} gssapi_ssh_gss_ctx;
+
+void ssh_gssapi_bind_fns(struct ssh_gss_library *lib);
+
+#else
+
+int ssh_gssapi_init(void);
+
+#endif /*NO_GSSAPI*/
+
+#endif /*PUTTY_SSHGSSC_H*/
diff --git a/sshnogss.c b/sshnogss.c
new file mode 100644 (file)
index 0000000..715d8df
--- /dev/null
@@ -0,0 +1,10 @@
+#include "putty.h"
+#ifndef NO_GSSAPI
+
+/* For platforms not supporting GSSAPI */
+
+void ssh_gss_init(void)
+{
+}
+
+#endif /* NO_GSSAPI */
index d84d61e..fd881ad 100644 (file)
@@ -17,18 +17,22 @@ else
 fi
 AC_SUBST(PUTTYCFLAGS)
 
-AC_ARG_WITH(gssapi,
-[  --without-gssapi        disable GSS-API support])
+AC_ARG_WITH([gssapi],
+  [AS_HELP_STRING([--without-gssapi],
+                  [disable GSSAPI support])],
+  [],
+  [with_gssapi=yes])
+
+WITH_GSSAPI=
+AS_IF([test "x$with_gssapi" != xno],
+  [AC_DEFINE([WITH_GSSAPI], [1], [Define if building with GSSAPI support.])])
 
 AC_CHECK_HEADERS([utmpx.h sys/select.h],,,[
 #include <sys/types.h>
 #include <utmp.h>])
-if test "$with_gssapi" != "no"; then
-  AC_CHECK_HEADERS([gssapi/gssapi.h])
-fi
 
 # Look for both GTK 1 and GTK 2.
-AM_PATH_GTK([1.2.0], [gtk=1], [gtk=none])
+AM_PATH_GTK([1.2.0], [gtk=1], [gtk=none])
 AM_PATH_GTK_2_0([2.0.0], [gtk=2], [])
 if test "$gtk" = "none"; then
   all_targets="all-cli"
@@ -47,9 +51,17 @@ fi
 AC_SUBST([all_targets])
 
 AC_SEARCH_LIBS([socket], [xnet])
-if test "$with_gssapi" != "no"; then
-  AC_SEARCH_LIBS([gss_init_sec_context], [gssapi gssapi_krb5 gss])
-fi
+
+AS_IF([test "x$with_gssapi" != xno],
+  [AC_SEARCH_LIBS(
+    [dlopen],[dl],
+    [],
+    [AC_DEFINE([NO_LIBDL], [1], [Define if we could not find libdl.])
+     AC_CHECK_HEADERS([gssapi/gssapi.h])
+     AC_SEARCH_LIBS(
+       [gss_init_sec_context],[gssapi gssapi_krb5 gss],
+       [],
+       [AC_DEFINE([NO_GSSAPI_LIB], [1], [Define if we could not find a gssapi library])])])])
 
 AC_CHECK_LIB(X11, XOpenDisplay)
 
@@ -84,7 +96,12 @@ AH_BOTTOM([
 #ifndef HAVE_PANGO_FONT_MAP_LIST_FAMILIES
 # define PANGO_PRE_1POINT6
 #endif
-#ifndef HAVE_GSSAPI_GSSAPI_H
+#if !defined(WITH_GSSAPI)
 # define NO_GSSAPI
 #endif
+#if !defined(NO_GSSAPI) && defined(NO_LIBDL)
+# if !defined(HAVE_GSSAPI_GSSAPI_H) || defined(NO_GSSAPI_LIB)
+#  define NO_GSSAPI
+# endif
+#endif
 ])
index 0e61faa..9de4e4a 100644 (file)
@@ -6,6 +6,10 @@
 #endif
 
 #include <stdio.h>                    /* for FILENAME_MAX */
+#include <stdint.h>                   /* C99 int types */
+#ifndef NO_LIBDL
+#include <dlfcn.h>                    /* Dynamic library loading */
+#endif /*  NO_LIBDL */
 #include "charset.h"
 
 struct Filename {
@@ -24,6 +28,9 @@ typedef int OSSocket;
 
 extern Backend pty_backend;
 
+typedef uint32_t uint32; /* C99: uint32_t defined in stdint.h */
+#define PUTTY_UINT32_DEFINED
+
 /*
  * Under GTK, we send MA_CLICK _and_ MA_2CLK, or MA_CLICK _and_
  * MA_3CLK, when a button is pressed for the second or third time.
@@ -60,16 +67,6 @@ extern long tickcount_offset;
 #define WCHAR wchar_t
 #define BYTE unsigned char
 
-#ifndef NO_GSSAPI
-/*
- * GSS-API stuff
- */
-#include <gssapi/gssapi.h>
-typedef gss_buffer_desc Ssh_gss_buf;
-#define SSH_GSS_EMPTY_BUF GSS_C_EMPTY_BUFFER
-typedef gss_name_t Ssh_gss_name;
-#endif
-
 /*
  * Unix-specific global flag
  *
index 7bc6dca..b8fb0b0 100644 (file)
 #include "putty.h"
-
 #ifndef NO_GSSAPI
-
-#include <string.h>
-#include <gssapi/gssapi.h>
+#include "pgssapi.h"
 #include "sshgss.h"
-#include "misc.h"
-
-static gss_OID_desc putty_gss_mech_krb5_desc =
-    { 9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
-static gss_OID const putty_gss_mech_krb5 = &putty_gss_mech_krb5_desc;
-
-typedef struct uxSsh_gss_ctx {
-    OM_uint32 maj_stat;
-    OM_uint32 min_stat;
-    gss_ctx_id_t ctx;
-} uxSsh_gss_ctx;
-
-int ssh_gss_init(void)
-{
-    /* On Windows this tries to load the SSPI library functions.  On
-       Unix we assume we have GSSAPI at runtime if we were linked with
-       it at compile time */
-    return 1;
-}
-
-Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech)
+#include "sshgssc.h"
+
+/* 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;
+
+const int ngsslibs = 3;
+const char *const gsslibnames[3] = {
+    "libgssapi (Heimdal)",
+    "libgssapi_krb5 (MIT Kerberos)",
+    "libgss (Sun)",
+};
+const struct keyval gsslibkeywords[] = {
+    { "libgssapi", 0 },
+    { "libgssapi_krb5", 1 },
+    { "libgss", 2 },
+};
+
+#ifndef NO_LIBDL
+
+/*
+ * Run-time binding against a choice of GSSAPI implementations. We
+ * try loading several libraries, and produce an entry in
+ * ssh_gss_libraries[] for each one.
+ */
+
+static void gss_init(struct ssh_gss_library *lib, void *dlhandle,
+                    int id, const char *msg)
 {
-    /* Copy constant into mech */
-    mech->length  = putty_gss_mech_krb5->length;
-    mech->value = putty_gss_mech_krb5->elements;
+    lib->id = id;
+    lib->gsslogmsg = msg;
 
-    return SSH_GSS_OK;
-}
+#define BIND_GSS_FN(name) \
+    lib->u.gssapi.name = (t_gss_##name) dlsym(dlhandle, "gss_" #name)
 
-Ssh_gss_stat ssh_gss_import_name(char *host,
-                                 Ssh_gss_name *srv_name)
-{
-    OM_uint32 min_stat,maj_stat;
-    gss_buffer_desc host_buf;
-    char *pStr;
+    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);
 
-    pStr = dupcat("host@", host, NULL);
+#undef BIND_GSS_FN
 
-    host_buf.value = pStr;
-    host_buf.length = strlen(pStr);
-
-    maj_stat = gss_import_name(&min_stat, &host_buf,
-                              GSS_C_NT_HOSTBASED_SERVICE, srv_name);
-    /* Release buffer */
-    sfree(pStr);
-    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
-    return SSH_GSS_FAILURE;
+    ssh_gssapi_bind_fns(lib);
 }
 
-Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx)
+/* Dynamically load gssapi libs. */
+void ssh_gss_init(void)
 {
-    uxSsh_gss_ctx *uxctx = snew(uxSsh_gss_ctx);
+    void *gsslib;
 
-    uxctx->maj_stat =  uxctx->min_stat = GSS_S_COMPLETE;
-    uxctx->ctx = GSS_C_NO_CONTEXT;
-    *ctx = (Ssh_gss_ctx) uxctx;
+    if (initialised) return;
+    initialised = TRUE;
 
-    return SSH_GSS_OK;
-}
+    /* Heimdal's GSSAPI Library */
+    if ((gsslib = dlopen("libgssapi.so.2", RTLD_LAZY)) != NULL)
+       gss_init(&ssh_gss_libraries[n_ssh_gss_libraries++], gsslib,
+                0, "Using GSSAPI from libgssapi.so.2");
 
-Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx,
-                                      Ssh_gss_name srv_name,
-                                      int to_deleg,
-                                     Ssh_gss_buf *recv_tok,
-                                     Ssh_gss_buf *send_tok)
-{
-    uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx*) *ctx;
-    OM_uint32 ret_flags;
-
-    if (to_deleg) to_deleg = GSS_C_DELEG_FLAG;
-    uxctx->maj_stat = gss_init_sec_context(&uxctx->min_stat,
-                                          GSS_C_NO_CREDENTIAL,
-                                          &uxctx->ctx,
-                                          srv_name,
-                                          (gss_OID) putty_gss_mech_krb5,
-                                          GSS_C_MUTUAL_FLAG |
-                                          GSS_C_INTEG_FLAG | to_deleg,
-                                          0,
-                                          GSS_C_NO_CHANNEL_BINDINGS,
-                                          recv_tok,
-                                          NULL,   /* ignore mech type */
-                                          send_tok,
-                                          &ret_flags,
-                                          NULL);  /* ignore time_rec */
-  
-    if (uxctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE;
-    if (uxctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
-    return SSH_GSS_FAILURE;
-}
+    /* 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,
+                1, "Using GSSAPI from libgssapi_krb5.so.2");
 
-Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
-{
-    uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) ctx;
-    OM_uint32 lmin,lmax;
-    OM_uint32 ccc;
-    gss_buffer_desc msg_maj=GSS_C_EMPTY_BUFFER;
-    gss_buffer_desc msg_min=GSS_C_EMPTY_BUFFER;
-
-    /* Return empty buffer in case of failure */
-    SSH_GSS_CLEAR_BUF(buf);
-
-    /* get first mesg from GSS */
-    ccc=0;
-    lmax=gss_display_status(&lmin,uxctx->maj_stat,GSS_C_GSS_CODE,(gss_OID) putty_gss_mech_krb5,&ccc,&msg_maj);
-
-    if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE;
-
-    /* get first mesg from Kerberos */
-    ccc=0;
-    lmax=gss_display_status(&lmin,uxctx->min_stat,GSS_C_MECH_CODE,(gss_OID) putty_gss_mech_krb5,&ccc,&msg_min);
-
-    if (lmax != GSS_S_COMPLETE) {
-       gss_release_buffer(&lmin, &msg_maj);
-       return SSH_GSS_FAILURE;
-    }
-
-    /* copy data into buffer */
-    buf->length = msg_maj.length + msg_min.length + 1;
-    buf->value = snewn(buf->length + 1, char);
-  
-    /* copy mem */
-    memcpy((char *)buf->value, msg_maj.value, msg_maj.length);
-    ((char *)buf->value)[msg_maj.length] = ' ';
-    memcpy((char *)buf->value + msg_maj.length + 1, msg_min.value, msg_min.length);
-    ((char *)buf->value)[buf->length] = 0;
-    /* free mem & exit */
-    gss_release_buffer(&lmin, &msg_maj);
-    gss_release_buffer(&lmin, &msg_min);
-    return SSH_GSS_OK;
+    /* Sun's GSSAPI Library */
+    if ((gsslib = dlopen("libgss.so.1", RTLD_LAZY)) != NULL)
+       gss_init(&ssh_gss_libraries[n_ssh_gss_libraries++], gsslib,
+                2, "Using GSSAPI from libgss.so.1");
 }
 
-Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok)
-{
-    OM_uint32 min_stat,maj_stat;
-    maj_stat = gss_release_buffer(&min_stat, send_tok);
-  
-    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
-    return SSH_GSS_FAILURE;
-}
+#else /* NO_LIBDL */
 
-Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
-{
-    uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) *ctx;
-    OM_uint32 min_stat;
-    OM_uint32 maj_stat=GSS_S_COMPLETE;
-  
-    if (uxctx == NULL) return SSH_GSS_FAILURE;
-    if (uxctx->ctx != GSS_C_NO_CONTEXT)
-       maj_stat = gss_delete_sec_context(&min_stat,&uxctx->ctx,GSS_C_NO_BUFFER);
-    sfree(uxctx);
-  
-    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
-    return SSH_GSS_FAILURE;
-}
+/*
+ * Link-time binding against GSSAPI. Here we just construct a single
+ * library structure containing pointers to the functions we linked
+ * against.
+ */
 
+#include <gssapi/gssapi.h>
 
-Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name)
+/* Dynamically load gssapi libs. */
+void ssh_gss_init(void)
 {
-    OM_uint32 min_stat,maj_stat;
-    maj_stat = gss_release_name(&min_stat, srv_name);
-  
-    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
-    return SSH_GSS_FAILURE;
-}
+    if (initialised) return;
+    initialised = TRUE;
 
-Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
-                            Ssh_gss_buf *hash) 
-{
-    uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) ctx;
-    if (uxctx == NULL) return SSH_GSS_FAILURE;
-    return gss_get_mic(&(uxctx->min_stat), uxctx->ctx, 0, buf, hash);
-}
+    n_ssh_gss_libraries = 1;
+    ssh_gss_libraries[0].gsslogmsg = "Using statically linked GSSAPI";
 
-Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash)
-{
-    /* On Unix this is the same freeing process as ssh_gss_free_tok. */
-    return ssh_gss_free_tok(hash);
-}
+#define BIND_GSS_FN(name) \
+    ssh_gss_libraries[0].u.gssapi.name = (t_gss_##name) gss_##name
 
-#else
+    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);
 
-/* Dummy function so this source file defines something if NO_GSSAPI
-   is defined. */
+#undef BIND_GSS_FN
 
-int ssh_gss_init(void)
-{
-    return 1;
+    ssh_gssapi_bind_fns(&ssh_gss_libraries[0]);
 }
 
-#endif
+#endif /* NO_LIBDL */
+
+#endif /* NO_GSSAPI */
index 2476e4e..e7b9c15 100644 (file)
@@ -220,7 +220,7 @@ void close_settings_w(void *handle)
  * FIXME: the above comment is a bit out of date. Did it happen?
  */
 
-struct keyval {
+struct skeyval {
     const char *key;
     const char *value;
 };
@@ -229,15 +229,15 @@ static tree234 *xrmtree = NULL;
 
 int keycmp(void *av, void *bv)
 {
-    struct keyval *a = (struct keyval *)av;
-    struct keyval *b = (struct keyval *)bv;
+    struct skeyval *a = (struct skeyval *)av;
+    struct skeyval *b = (struct skeyval *)bv;
     return strcmp(a->key, b->key);
 }
 
 void provide_xrm_string(char *string)
 {
     char *p, *q, *key;
-    struct keyval *xrms, *ret;
+    struct skeyval *xrms, *ret;
 
     p = q = strchr(string, ':');
     if (!q) {
@@ -248,7 +248,7 @@ void provide_xrm_string(char *string)
     q++;
     while (p > string && p[-1] != '.' && p[-1] != '*')
        p--;
-    xrms = snew(struct keyval);
+    xrms = snew(struct skeyval);
     key = snewn(q-p, char);
     memcpy(key, p, q-p);
     key[q-p-1] = '\0';
@@ -270,7 +270,7 @@ void provide_xrm_string(char *string)
 
 const char *get_setting(const char *key)
 {
-    struct keyval tmp, *ret;
+    struct skeyval tmp, *ret;
     tmp.key = key;
     if (xrmtree) {
        ret = find234(xrmtree, &tmp, NULL);
@@ -297,14 +297,14 @@ void *open_settings_r(const char *sessionname)
 
     while ( (line = fgetline(fp)) ) {
         char *value = strchr(line, '=');
-        struct keyval *kv;
+        struct skeyval *kv;
 
         if (!value)
             continue;
         *value++ = '\0';
         value[strcspn(value, "\r\n")] = '\0';   /* trim trailing NL */
 
-        kv = snew(struct keyval);
+        kv = snew(struct skeyval);
         kv->key = dupstr(line);
         kv->value = dupstr(value);
         add234(ret, kv);
@@ -321,7 +321,7 @@ char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)
 {
     tree234 *tree = (tree234 *)handle;
     const char *val;
-    struct keyval tmp, *kv;
+    struct skeyval tmp, *kv;
 
     tmp.key = key;
     if (tree != NULL &&
@@ -344,7 +344,7 @@ int read_setting_i(void *handle, const char *key, int defvalue)
 {
     tree234 *tree = (tree234 *)handle;
     const char *val;
-    struct keyval tmp, *kv;
+    struct skeyval tmp, *kv;
 
     tmp.key = key;
     if (tree != NULL &&
@@ -416,7 +416,7 @@ void write_setting_filename(void *handle, const char *name, Filename result)
 void close_settings_r(void *handle)
 {
     tree234 *tree = (tree234 *)handle;
-    struct keyval *kv;
+    struct skeyval *kv;
 
     if (!tree)
         return;
index 6e97d2e..5f45c98 100644 (file)
@@ -4,9 +4,28 @@
 
 #include <security.h>
 
+#include "pgssapi.h"
 #include "sshgss.h"
+#include "sshgssc.h"
+
 #include "misc.h"
 
+/* 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)",
+    "SSPI.DLL (Microsoft SSPI)",
+};
+const struct keyval gsslibkeywords[] = {
+    { "gssapi32", 0 },
+    { "sspi", 1 },
+};
+
 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
                      AcquireCredentialsHandleA,
                      (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
@@ -32,8 +51,6 @@ DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
                      MakeSignature,
                      (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
 
-static HMODULE security_module = NULL;
-
 typedef struct winSsh_gss_ctx {
     unsigned long maj_stat;
     unsigned long min_stat;
@@ -46,33 +63,75 @@ typedef struct winSsh_gss_ctx {
 
 const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
 
-int ssh_gss_init(void)
+const char *gsslogmsg = NULL;
+
+static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
+
+void ssh_gss_init(void)
 {
-    if (security_module)
-       return 1;                      /* already initialised */
-
-    security_module = LoadLibrary("secur32.dll");
-    if (security_module) {
-       GET_WINDOWS_FUNCTION(security_module, AcquireCredentialsHandleA);
-       GET_WINDOWS_FUNCTION(security_module, InitializeSecurityContextA);
-       GET_WINDOWS_FUNCTION(security_module, FreeContextBuffer);
-       GET_WINDOWS_FUNCTION(security_module, FreeCredentialsHandle);
-       GET_WINDOWS_FUNCTION(security_module, DeleteSecurityContext);
-       GET_WINDOWS_FUNCTION(security_module, QueryContextAttributesA);
-       GET_WINDOWS_FUNCTION(security_module, MakeSignature);
-       return 1;
+    HMODULE module;
+
+    if (initialised) return;
+    initialised = TRUE;
+
+    /* MIT Kerberos GSSAPI implementation */
+    /* TODO: For 64-bit builds, check for gssapi64.dll */
+    module = LoadLibrary("gssapi32.dll");
+    if (module) {
+       struct ssh_gss_library *lib =
+           &ssh_gss_libraries[n_ssh_gss_libraries++];
+
+       lib->id = 0;
+       lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL";
+
+#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);
+    }
+
+    /* Microsoft SSPI Implementation */
+    module = LoadLibrary("secur32.dll");
+    if (module) {
+       struct ssh_gss_library *lib =
+           &ssh_gss_libraries[n_ssh_gss_libraries++];
+
+       lib->id = 1;
+       lib->gsslogmsg = "Using SSPI from SECUR32.DLL";
+
+       GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA);
+       GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA);
+       GET_WINDOWS_FUNCTION(module, FreeContextBuffer);
+       GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle);
+       GET_WINDOWS_FUNCTION(module, DeleteSecurityContext);
+       GET_WINDOWS_FUNCTION(module, QueryContextAttributesA);
+       GET_WINDOWS_FUNCTION(module, MakeSignature);
+
+       ssh_sspi_bind_fns(lib);
     }
-    return 0;
 }
 
-Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech)
+static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib,
+                                          Ssh_gss_buf *mech)
 {
     *mech = gss_mech_krb5;
     return SSH_GSS_OK;
 }
 
 
-Ssh_gss_stat ssh_gss_import_name(char *host, Ssh_gss_name *srv_name)
+static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
+                                        char *host, Ssh_gss_name *srv_name)
 {
     char *pStr;
 
@@ -87,7 +146,8 @@ Ssh_gss_stat ssh_gss_import_name(char *host, Ssh_gss_name *srv_name)
     return SSH_GSS_OK;
 }
 
-Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx)
+static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
+                                         Ssh_gss_ctx *ctx)
 {
     winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
     memset(winctx, 0, sizeof(winSsh_gss_ctx));
@@ -116,11 +176,12 @@ Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx)
 }
 
 
-Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx,
-                                     Ssh_gss_name srv_name,
-                                     int to_deleg,
-                                     Ssh_gss_buf *recv_tok,
-                                     Ssh_gss_buf *send_tok)
+static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
+                                             Ssh_gss_ctx *ctx,
+                                             Ssh_gss_name srv_name,
+                                             int to_deleg,
+                                             Ssh_gss_buf *recv_tok,
+                                             Ssh_gss_buf *send_tok)
 {
     winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;
     SecBuffer wsend_tok = {send_tok->length,SECBUFFER_TOKEN,send_tok->value};
@@ -158,7 +219,8 @@ Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx,
     return SSH_GSS_FAILURE;
 }
 
-Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok)
+static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib,
+                                     Ssh_gss_buf *send_tok)
 {
     /* check input */
     if (send_tok == NULL) return SSH_GSS_FAILURE;
@@ -170,7 +232,8 @@ Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok)
     return SSH_GSS_OK;
 }
 
-Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
+static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib,
+                                         Ssh_gss_ctx *ctx)
 {
     winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;
 
@@ -189,7 +252,8 @@ Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
 }
 
 
-Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name)
+static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib,
+                                         Ssh_gss_name *srv_name)
 {
     char *pStr= (char *) *srv_name;
 
@@ -200,7 +264,8 @@ Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name)
     return SSH_GSS_OK;
 }
 
-Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
+static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib,
+                                           Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
 {
     winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;
     char *msg;
@@ -250,8 +315,9 @@ Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
     return SSH_GSS_OK;
 }
 
-Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
-                            Ssh_gss_buf *hash)
+static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib,
+                                    Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
+                                    Ssh_gss_buf *hash)
 {
     winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;
     SecPkgContext_Sizes ContextSizes;
@@ -295,20 +361,34 @@ Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
     return winctx->maj_stat;
 }
 
-Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash)
+static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib,
+                                     Ssh_gss_buf *hash)
 {
     sfree(hash->value);
     return SSH_GSS_OK;
 }
 
+static void ssh_sspi_bind_fns(struct ssh_gss_library *lib)
+{
+    lib->indicate_mech = ssh_sspi_indicate_mech;
+    lib->import_name = ssh_sspi_import_name;
+    lib->release_name = ssh_sspi_release_name;
+    lib->init_sec_context = ssh_sspi_init_sec_context;
+    lib->free_tok = ssh_sspi_free_tok;
+    lib->acquire_cred = ssh_sspi_acquire_cred;
+    lib->release_cred = ssh_sspi_release_cred;
+    lib->get_mic = ssh_sspi_get_mic;
+    lib->free_mic = ssh_sspi_free_mic;
+    lib->display_status = ssh_sspi_display_status;
+}
+
 #else
 
 /* Dummy function so this source file defines something if NO_GSSAPI
    is defined. */
 
-int ssh_gss_init(void)
+void ssh_gss_init(void)
 {
-    return 0;
 }
 
 #endif
index 5e95a9d..99bda10 100644 (file)
@@ -138,17 +138,23 @@ typedef struct terminal_tag Terminal;
 
 typedef HDC Context;
 
+typedef unsigned int uint32; /* int is 32-bits on Win32 and Win64. */
+#define PUTTY_UINT32_DEFINED
+
 #ifndef NO_GSSAPI
 /*
  * GSS-API stuff
  */
+#define GSS_CC CALLBACK
+/*
 typedef struct Ssh_gss_buf {
-    int length;
+    size_t length;
     char *value;
 } Ssh_gss_buf;
 
 #define SSH_GSS_EMPTY_BUF (Ssh_gss_buf) {0,NULL}
 typedef void *Ssh_gss_name;
+*/
 #endif
 
 /*