diff --git a/Recipe b/Recipe index 3702191a..3c4b6e3d 100644 --- a/Recipe +++ b/Recipe @@ -100,6 +100,10 @@ # 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 diff --git a/config.c b/config.c index bc48e271..d689ede9 100644 --- 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) { diff --git a/mac/macstuff.h b/mac/macstuff.h index 3f49cd03..796515b4 100644 --- a/mac/macstuff.h +++ b/mac/macstuff.h @@ -44,6 +44,9 @@ struct FontSpec { #define BYTE UInt8 #define DWORD UInt32 +typedef UInt32 uint32; +#define PUTTY_UINT32_DEFINED + #define OPTIMISE_SCROLL /* diff --git a/mkfiles.pl b/mkfiles.pl index 8155c901..4c58e4c2 100755 --- a/mkfiles.pl +++ b/mkfiles.pl @@ -944,9 +944,14 @@ sub manpages { "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 @@ sub manpages { "# 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 @@ sub manpages { (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 index 00000000..57a1b372 --- /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 index 00000000..5d9ffeb4 --- /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 index 00000000..f6370c2d --- /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 +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 ca232bc2..0c97562b 100644 --- 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 */ diff --git a/settings.c b/settings.c index 38c41881..3d51f51f 100644 --- a/settings.c +++ b/settings.c @@ -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 029c78a7..0616effd 100644 --- 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 d17920c4..3d02ad7d 100644 --- 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); diff --git a/sshgss.h b/sshgss.h index 2115cb12..d12c70f8 100644 --- 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 index 00000000..4590ed7b --- /dev/null +++ b/sshgssc.c @@ -0,0 +1,209 @@ +#include "putty.h" + +#include +#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 index 00000000..c98ee86f --- /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 index 00000000..715d8df2 --- /dev/null +++ b/sshnogss.c @@ -0,0 +1,10 @@ +#include "putty.h" +#ifndef NO_GSSAPI + +/* For platforms not supporting GSSAPI */ + +void ssh_gss_init(void) +{ +} + +#endif /* NO_GSSAPI */ diff --git a/unix/configure.ac b/unix/configure.ac index d84d61e1..fd881ad3 100644 --- a/unix/configure.ac +++ b/unix/configure.ac @@ -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 #include ]) -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 ]) diff --git a/unix/unix.h b/unix/unix.h index 0e61faab..9de4e4a9 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -6,6 +6,10 @@ #endif #include /* for FILENAME_MAX */ +#include /* C99 int types */ +#ifndef NO_LIBDL +#include /* 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 -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 * diff --git a/unix/uxgss.c b/unix/uxgss.c index 7bc6dca4..b8fb0b07 100644 --- a/unix/uxgss.c +++ b/unix/uxgss.c @@ -1,197 +1,118 @@ #include "putty.h" - #ifndef NO_GSSAPI - -#include -#include +#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 -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 */ diff --git a/unix/uxstore.c b/unix/uxstore.c index 2476e4ed..e7b9c158 100644 --- a/unix/uxstore.c +++ b/unix/uxstore.c @@ -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; diff --git a/windows/wingss.c b/windows/wingss.c index 6e97d2e3..5f45c98b 100644 --- a/windows/wingss.c +++ b/windows/wingss.c @@ -4,9 +4,28 @@ #include +#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 diff --git a/windows/winstuff.h b/windows/winstuff.h index 5e95a9df..99bda104 100644 --- a/windows/winstuff.h +++ b/windows/winstuff.h @@ -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 /*