X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/94fe9122886861d39bbb5bcfb9f7fe5eb2c46427..5fd70d0768f86274b22e73f3cd8f59fec0325e36:/windows/wingss.c diff --git a/windows/wingss.c b/windows/wingss.c index 5cf7e0f9..237b3890 100644 --- a/windows/wingss.c +++ b/windows/wingss.c @@ -1,44 +1,53 @@ #ifndef NO_GSSAPI -#include -#define SECURITY_WIN32 +#include "putty.h" + #include + +#include "pgssapi.h" #include "sshgss.h" +#include "sshgssc.h" + #include "misc.h" -#define NOTHING -#define DECL_SSPI_FUNCTION(linkage, rettype, name, params) \ - typedef rettype (WINAPI *t_##name) params; \ - linkage t_##name p_##name -#define GET_SSPI_FUNCTION(module, name) \ - p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL - -DECL_SSPI_FUNCTION(static, SECURITY_STATUS, - AcquireCredentialsHandleA, - (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID, - PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp)); -DECL_SSPI_FUNCTION(static, SECURITY_STATUS, - InitializeSecurityContextA, - (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG, - ULONG, PSecBufferDesc, ULONG, PCtxtHandle, - PSecBufferDesc, PULONG, PTimeStamp)); -DECL_SSPI_FUNCTION(static, SECURITY_STATUS, - FreeContextBuffer, - (PVOID)); -DECL_SSPI_FUNCTION(static, SECURITY_STATUS, - FreeCredentialsHandle, - (PCredHandle)); -DECL_SSPI_FUNCTION(static, SECURITY_STATUS, - DeleteSecurityContext, - (PCtxtHandle)); -DECL_SSPI_FUNCTION(static, SECURITY_STATUS, - QueryContextAttributesA, - (PCtxtHandle, ULONG, PVOID)); -DECL_SSPI_FUNCTION(static, SECURITY_STATUS, - MakeSignature, - (PCtxtHandle, ULONG, PSecBufferDesc, ULONG)); - -static HMODULE security_module = NULL; +/* 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, + PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp)); +DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, + InitializeSecurityContextA, + (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG, + ULONG, PSecBufferDesc, ULONG, PCtxtHandle, + PSecBufferDesc, PULONG, PTimeStamp)); +DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, + FreeContextBuffer, + (PVOID)); +DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, + FreeCredentialsHandle, + (PCredHandle)); +DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, + DeleteSecurityContext, + (PCtxtHandle)); +DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, + QueryContextAttributesA, + (PCtxtHandle, ULONG, PVOID)); +DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, + MakeSignature, + (PCtxtHandle, ULONG, PSecBufferDesc, ULONG)); typedef struct winSsh_gss_ctx { unsigned long maj_stat; @@ -52,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) { - if (security_module) - return 1; /* already initialised */ - - security_module = LoadLibrary("secur32.dll"); - if (security_module) { - GET_SSPI_FUNCTION(security_module, AcquireCredentialsHandleA); - GET_SSPI_FUNCTION(security_module, InitializeSecurityContextA); - GET_SSPI_FUNCTION(security_module, FreeContextBuffer); - GET_SSPI_FUNCTION(security_module, FreeCredentialsHandle); - GET_SSPI_FUNCTION(security_module, DeleteSecurityContext); - GET_SSPI_FUNCTION(security_module, QueryContextAttributesA); - GET_SSPI_FUNCTION(security_module, MakeSignature); - return 1; + 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); } - return 0; + + /* 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; } -Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech) +void ssh_gss_cleanup(struct ssh_gss_liblist *list) +{ + int i; + + /* + * LoadLibrary and FreeLibrary are defined to employ reference + * counting in the case where the same library is repeatedly + * loaded, so even in a multiple-sessions-per-process context + * (not that we currently expect ever to have such a thing on + * Windows) it's safe to naively FreeLibrary everything here + * without worrying about destroying it under the feet of + * another SSH instance still using it. + */ + for (i = 0; i < list->nlibraries; i++) { + FreeLibrary((HMODULE)list->libraries[i].handle); + if (list->libraries[i].id == 2) { + /* The 'custom' id involves a dynamically allocated message. + * Note that we must cast away the 'const' to free it. */ + sfree((char *)list->libraries[i].gsslogmsg); + } + } + sfree(list->libraries); + sfree(list); +} + +static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib, + 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; @@ -93,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)); @@ -122,15 +262,16 @@ 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->len,SECBUFFER_TOKEN,send_tok->data}; - SecBuffer wrecv_tok = {recv_tok->len,SECBUFFER_TOKEN,recv_tok->data}; + SecBuffer wsend_tok = {send_tok->length,SECBUFFER_TOKEN,send_tok->value}; + SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value}; SecBufferDesc output_desc={SECBUFFER_VERSION,1,&wsend_tok}; SecBufferDesc input_desc ={SECBUFFER_VERSION,1,&wrecv_tok}; unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT| @@ -154,8 +295,8 @@ Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx, /* prepare for the next round */ winctx->context_handle = &winctx->context; - send_tok->data = (char*) wsend_tok.pvBuffer; - send_tok->len = wsend_tok.cbBuffer; + send_tok->value = wsend_tok.pvBuffer; + send_tok->length = wsend_tok.cbBuffer; /* check & return our status */ if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE; @@ -164,19 +305,21 @@ 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; /* free Windows buffer */ - p_FreeContextBuffer(send_tok->data); - send_tok->len = 0; send_tok->data = NULL; + p_FreeContextBuffer(send_tok->value); + SSH_GSS_CLEAR_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; @@ -195,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; @@ -206,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; @@ -250,14 +395,15 @@ Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf) break; } - buf->data = dupstr(msg); - buf->len = strlen(buf->data); + buf->value = dupstr(msg); + buf->length = strlen(buf->value); 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; @@ -282,8 +428,8 @@ Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf, InputBufferDescriptor.pBuffers = InputSecurityToken; InputBufferDescriptor.ulVersion = SECBUFFER_VERSION; InputSecurityToken[0].BufferType = SECBUFFER_DATA; - InputSecurityToken[0].cbBuffer = buf->len; - InputSecurityToken[0].pvBuffer = buf->data; + InputSecurityToken[0].cbBuffer = buf->length; + InputSecurityToken[0].pvBuffer = buf->value; InputSecurityToken[1].BufferType = SECBUFFER_TOKEN; InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature; InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char); @@ -294,27 +440,41 @@ Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf, 0); if (winctx->maj_stat == SEC_E_OK) { - hash->len = InputSecurityToken[1].cbBuffer; - hash->data = InputSecurityToken[1].pvBuffer; + hash->length = InputSecurityToken[1].cbBuffer; + hash->value = InputSecurityToken[1].pvBuffer; } 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->data); + 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