X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/65d1432e946f534ce0b3216235798ffc882b6e4d..8d90b8b27bf086da6245030459ab3e5977313eb0:/windows/wingss.c diff --git a/windows/wingss.c b/windows/wingss.c index a16db031..237b3890 100644 --- a/windows/wingss.c +++ b/windows/wingss.c @@ -2,12 +2,28 @@ #include "putty.h" -#define SECURITY_WIN32 #include +#include "pgssapi.h" #include "sshgss.h" +#include "sshgssc.h" + #include "misc.h" +/* Windows code to set up the GSSAPI library list. */ + +const int ngsslibs = 3; +const char *const gsslibnames[3] = { + "MIT Kerberos GSSAPI32.DLL", + "Microsoft SSPI SECUR32.DLL", + "User-specified GSSAPI DLL", +}; +const struct keyvalwhere gsslibkeywords[] = { + { "gssapi32", 0, -1, -1 }, + { "sspi", 1, -1, -1 }, + { "custom", 2, -1, -1 }, +}; + DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, AcquireCredentialsHandleA, (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID, @@ -33,8 +49,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; @@ -47,33 +61,163 @@ 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); + +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) +{ + HMODULE module; + HKEY regkey; + struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); + char *path; + + list->libraries = snewn(3, struct ssh_gss_library); + list->nlibraries = 0; + + /* MIT Kerberos GSSAPI implementation */ + /* TODO: For 64-bit builds, check for gssapi64.dll */ + module = NULL; + if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", ®key) + == ERROR_SUCCESS) { + DWORD type, size; + LONG ret; + char *buffer; + + /* Find out the string length */ + ret = RegQueryValueEx(regkey, "InstallDir", NULL, &type, NULL, &size); + + if (ret == ERROR_SUCCESS && type == REG_SZ) { + buffer = snewn(size + 20, char); + ret = RegQueryValueEx(regkey, "InstallDir", NULL, + &type, buffer, &size); + if (ret == ERROR_SUCCESS && type == REG_SZ) { + strcat(buffer, "\\bin\\gssapi32.dll"); + module = LoadLibrary(buffer); + } + sfree(buffer); + } + RegCloseKey(regkey); + } + if (module) { + struct ssh_gss_library *lib = + &list->libraries[list->nlibraries++]; + + lib->id = 0; + lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL"; + lib->handle = (void *)module; + +#define BIND_GSS_FN(name) \ + lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name) + + 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 = load_system32_dll("secur32.dll"); + if (module) { + struct ssh_gss_library *lib = + &list->libraries[list->nlibraries++]; + + lib->id = 1; + lib->gsslogmsg = "Using SSPI from SECUR32.DLL"; + lib->handle = (void *)module; + + GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA); + GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA); + 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); + } + + /* + * Custom GSSAPI DLL. + */ + module = NULL; + path = conf_get_filename(conf, CONF_ssh_gss_custom)->path; + if (*path) { + module = LoadLibrary(path); + } + if (module) { + struct ssh_gss_library *lib = + &list->libraries[list->nlibraries++]; + + lib->id = 2; + lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified" + " library '%s'", path); + lib->handle = (void *)module; + +#define BIND_GSS_FN(name) \ + lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name) + + BIND_GSS_FN(delete_sec_context); + BIND_GSS_FN(display_status); + BIND_GSS_FN(get_mic); + BIND_GSS_FN(import_name); + BIND_GSS_FN(init_sec_context); + BIND_GSS_FN(release_buffer); + BIND_GSS_FN(release_cred); + BIND_GSS_FN(release_name); + +#undef BIND_GSS_FN + + ssh_gssapi_bind_fns(lib); + } + + + return list; +} + +void ssh_gss_cleanup(struct ssh_gss_liblist *list) { - 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; + int i; + + /* + * LoadLibrary and FreeLibrary are defined to employ reference + * counting in the case where the same library is repeatedly + * loaded, so even in a multiple-sessions-per-process context + * (not that we currently expect ever to have such a thing on + * Windows) it's safe to naively FreeLibrary everything here + * without worrying about destroying it under the feet of + * another SSH instance still using it. + */ + for (i = 0; i < list->nlibraries; i++) { + FreeLibrary((HMODULE)list->libraries[i].handle); + if (list->libraries[i].id == 2) { + /* The 'custom' id involves a dynamically allocated message. + * Note that we must cast away the 'const' to free it. */ + sfree((char *)list->libraries[i].gsslogmsg); + } } - return 0; + sfree(list->libraries); + sfree(list); } -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; @@ -88,7 +232,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)); @@ -117,11 +262,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}; @@ -159,7 +305,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; @@ -171,7 +318,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; @@ -190,7 +338,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; @@ -201,7 +350,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; @@ -251,8 +401,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; @@ -296,20 +447,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