mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-07-01 03:22:48 -05:00
Initial commit of GSSAPI Kerberos support.
[originally from svn r8138]
This commit is contained in:
319
windows/wingss.c
Normal file
319
windows/wingss.c
Normal file
@ -0,0 +1,319 @@
|
||||
#ifndef NO_GSSAPI
|
||||
|
||||
#include <windows.h>
|
||||
#define SECURITY_WIN32
|
||||
#include <security.h>
|
||||
#include "sshgss.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;
|
||||
|
||||
typedef struct winSsh_gss_ctx {
|
||||
unsigned long maj_stat;
|
||||
unsigned long min_stat;
|
||||
CredHandle cred_handle;
|
||||
CtxtHandle context;
|
||||
PCtxtHandle context_handle;
|
||||
TimeStamp expiry;
|
||||
} 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Ssh_gss_stat ssh_gss_indicate_mech(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)
|
||||
{
|
||||
char *pStr;
|
||||
|
||||
/* Check hostname */
|
||||
if (host == NULL) return SSH_GSS_FAILURE;
|
||||
|
||||
/* copy it into form host/FQDN */
|
||||
pStr = dupcat("host/", host, NULL);
|
||||
|
||||
*srv_name = (Ssh_gss_name) pStr;
|
||||
|
||||
return SSH_GSS_OK;
|
||||
}
|
||||
|
||||
Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx)
|
||||
{
|
||||
winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
|
||||
|
||||
/* prepare our "wrapper" structure */
|
||||
winctx->maj_stat = winctx->min_stat = SEC_E_OK;
|
||||
winctx->context_handle = NULL;
|
||||
|
||||
/* Specifying no principal name here means use the credentials of
|
||||
the current logged-in user */
|
||||
|
||||
winctx->maj_stat = p_AcquireCredentialsHandleA(NULL,
|
||||
"Kerberos",
|
||||
SECPKG_CRED_OUTBOUND,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&winctx->cred_handle,
|
||||
&winctx->expiry);
|
||||
|
||||
if (winctx->maj_stat != SEC_E_OK) return SSH_GSS_FAILURE;
|
||||
|
||||
*ctx = (Ssh_gss_ctx) winctx;
|
||||
return SSH_GSS_OK;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
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};
|
||||
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|
|
||||
ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY;
|
||||
unsigned long ret_flags=0;
|
||||
|
||||
/* check if we have to delegate ... */
|
||||
if (to_deleg) flags |= ISC_REQ_DELEGATE;
|
||||
winctx->maj_stat = p_InitializeSecurityContextA(&winctx->cred_handle,
|
||||
winctx->context_handle,
|
||||
(char*) srv_name,
|
||||
flags,
|
||||
0, /* reserved */
|
||||
SECURITY_NATIVE_DREP,
|
||||
&input_desc,
|
||||
0, /* reserved */
|
||||
&winctx->context,
|
||||
&output_desc,
|
||||
&ret_flags,
|
||||
&winctx->expiry);
|
||||
|
||||
/* prepare for the next round */
|
||||
winctx->context_handle = &winctx->context;
|
||||
send_tok->data = (char*) wsend_tok.pvBuffer;
|
||||
send_tok->len = wsend_tok.cbBuffer;
|
||||
|
||||
/* check & return our status */
|
||||
if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE;
|
||||
if (winctx->maj_stat==SEC_I_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
|
||||
|
||||
return SSH_GSS_FAILURE;
|
||||
}
|
||||
|
||||
Ssh_gss_stat ssh_gss_free_tok(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;
|
||||
|
||||
return SSH_GSS_OK;
|
||||
}
|
||||
|
||||
Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
|
||||
{
|
||||
winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;
|
||||
|
||||
/* check input */
|
||||
if (winctx == NULL) return SSH_GSS_FAILURE;
|
||||
|
||||
/* free Windows data */
|
||||
p_FreeCredentialsHandle(&winctx->cred_handle);
|
||||
p_DeleteSecurityContext(&winctx->context);
|
||||
|
||||
/* delete our "wrapper" structure */
|
||||
sfree(winctx);
|
||||
*ctx = (Ssh_gss_ctx) NULL;
|
||||
|
||||
return SSH_GSS_OK;
|
||||
}
|
||||
|
||||
|
||||
Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name)
|
||||
{
|
||||
char *pStr= (char *) *srv_name;
|
||||
|
||||
if (pStr == NULL) return SSH_GSS_FAILURE;
|
||||
sfree(pStr);
|
||||
*srv_name = (Ssh_gss_name) NULL;
|
||||
|
||||
return SSH_GSS_OK;
|
||||
}
|
||||
|
||||
Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
|
||||
{
|
||||
winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;
|
||||
char *msg;
|
||||
|
||||
if (winctx == NULL) return SSH_GSS_FAILURE;
|
||||
|
||||
/* decode the error code */
|
||||
switch (winctx->maj_stat) {
|
||||
case SEC_E_OK: msg="SSPI status OK"; break;
|
||||
case SEC_E_INVALID_HANDLE: msg="The handle passed to the function"
|
||||
" is invalid.";
|
||||
break;
|
||||
case SEC_E_TARGET_UNKNOWN: msg="The target was not recognized."; break;
|
||||
case SEC_E_LOGON_DENIED: msg="The logon failed."; break;
|
||||
case SEC_E_INTERNAL_ERROR: msg="The Local Security Authority cannot"
|
||||
" be contacted.";
|
||||
break;
|
||||
case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the"
|
||||
" security package.";
|
||||
break;
|
||||
case SEC_E_NO_AUTHENTICATING_AUTHORITY:
|
||||
msg="No authority could be contacted for authentication."
|
||||
"The domain name of the authenticating party could be wrong,"
|
||||
" the domain could be unreachable, or there might have been"
|
||||
" a trust relationship failure.";
|
||||
break;
|
||||
case SEC_E_INSUFFICIENT_MEMORY:
|
||||
msg="One or more of the SecBufferDesc structures passed as"
|
||||
" an OUT parameter has a buffer that is too small.";
|
||||
break;
|
||||
case SEC_E_INVALID_TOKEN:
|
||||
msg="The error is due to a malformed input token, such as a"
|
||||
" token corrupted in transit, a token"
|
||||
" of incorrect size, or a token passed into the wrong"
|
||||
" security package. Passing a token to"
|
||||
" the wrong package can happen if client and server did not"
|
||||
" negotiate the proper security package.";
|
||||
break;
|
||||
default:
|
||||
msg = "Internal SSPI error";
|
||||
break;
|
||||
}
|
||||
|
||||
buf->data = dupstr(msg);
|
||||
buf->len = strlen(buf->data);
|
||||
|
||||
return SSH_GSS_OK;
|
||||
}
|
||||
|
||||
Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
|
||||
Ssh_gss_buf *hash)
|
||||
{
|
||||
winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;
|
||||
SecPkgContext_Sizes ContextSizes;
|
||||
SecBufferDesc InputBufferDescriptor;
|
||||
SecBuffer InputSecurityToken[2];
|
||||
|
||||
if (winctx == NULL) return SSH_GSS_FAILURE;
|
||||
|
||||
winctx->maj_stat = 0;
|
||||
|
||||
memset(&ContextSizes, 0, sizeof(ContextSizes));
|
||||
|
||||
winctx->maj_stat = p_QueryContextAttributesA(&winctx->context,
|
||||
SECPKG_ATTR_SIZES,
|
||||
&ContextSizes);
|
||||
|
||||
if (winctx->maj_stat != SEC_E_OK ||
|
||||
ContextSizes.cbMaxSignature == 0)
|
||||
return winctx->maj_stat;
|
||||
|
||||
InputBufferDescriptor.cBuffers = 2;
|
||||
InputBufferDescriptor.pBuffers = InputSecurityToken;
|
||||
InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
|
||||
InputSecurityToken[0].BufferType = SECBUFFER_DATA;
|
||||
InputSecurityToken[0].cbBuffer = buf->len;
|
||||
InputSecurityToken[0].pvBuffer = buf->data;
|
||||
InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;
|
||||
InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature;
|
||||
InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char);
|
||||
|
||||
winctx->maj_stat = p_MakeSignature(&winctx->context,
|
||||
0,
|
||||
&InputBufferDescriptor,
|
||||
0);
|
||||
|
||||
if (winctx->maj_stat == SEC_E_OK) {
|
||||
hash->len = InputSecurityToken[1].cbBuffer;
|
||||
hash->data = InputSecurityToken[1].pvBuffer;
|
||||
}
|
||||
|
||||
return winctx->maj_stat;
|
||||
}
|
||||
|
||||
Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash)
|
||||
{
|
||||
sfree(hash->data);
|
||||
return SSH_GSS_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Dummy function so this source file defines something if NO_GSSAPI
|
||||
is defined. */
|
||||
|
||||
int ssh_gss_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user