2467 lines
63 KiB
C++
2467 lines
63 KiB
C++
//+-----------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1992 - 1996
|
|
//
|
|
// File: userapi.cxx
|
|
//
|
|
// Contents: User-mode APIs to Kerberos package
|
|
//
|
|
//
|
|
// History: 17-April-1996 Created MikeSw
|
|
//
|
|
//------------------------------------------------------------------------
|
|
#include <kerbkrnl.h>
|
|
extern "C"
|
|
{
|
|
#include <cryptdll.h>
|
|
}
|
|
#include "krnlapi.h"
|
|
|
|
#define DONT_SUPPORT_OLD_TYPES_USER 1
|
|
|
|
//
|
|
// Make these extern "C" to allow them to be pageable.
|
|
//
|
|
|
|
extern "C"
|
|
{
|
|
KspInitPackageFn KerbInitKernelPackage;
|
|
KspDeleteContextFn KerbDeleteKernelContext;
|
|
KspInitContextFn KerbInitKernelContext;
|
|
KspMapHandleFn KerbMapKernelHandle;
|
|
KspMakeSignatureFn KerbMakeSignature;
|
|
KspVerifySignatureFn KerbVerifySignature;
|
|
KspSealMessageFn KerbSealMessage;
|
|
KspUnsealMessageFn KerbUnsealMessage;
|
|
KspGetTokenFn KerbGetContextToken;
|
|
KspQueryAttributesFn KerbQueryContextAttributes;
|
|
KspCompleteTokenFn KerbCompleteToken;
|
|
SpExportSecurityContextFn KerbExportContext;
|
|
SpImportSecurityContextFn KerbImportContext;
|
|
KspSetPagingModeFn KerbSetPageMode ;
|
|
|
|
NTSTATUS
|
|
KerbMakeSignatureToken(
|
|
IN PKERB_KERNEL_CONTEXT Context,
|
|
IN ULONG QualityOfProtection,
|
|
IN PSecBuffer SignatureBuffer,
|
|
IN ULONG TotalBufferSize,
|
|
IN BOOLEAN Encrypt,
|
|
IN ULONG SuppliedNonce,
|
|
OUT PKERB_GSS_SIGNATURE * OutputSignature,
|
|
OUT PULONG ChecksumType,
|
|
OUT PULONG EncryptionType,
|
|
OUT PULONG SequenceNumber
|
|
);
|
|
|
|
NTSTATUS
|
|
KerbVerifySignatureToken(
|
|
IN PKERB_KERNEL_CONTEXT Context,
|
|
IN PSecBuffer SignatureBuffer,
|
|
IN ULONG TotalBufferSize,
|
|
IN BOOLEAN Decrypt,
|
|
IN ULONG SuppliedNonce,
|
|
OUT PKERB_GSS_SIGNATURE * OutputSignature,
|
|
OUT PULONG QualityOfProtection,
|
|
OUT PULONG ChecksumType,
|
|
OUT PCRYPTO_SYSTEM * CryptSystem,
|
|
OUT PULONG SequenceNumber
|
|
);
|
|
|
|
NTSTATUS NTAPI
|
|
KerbInitDefaults();
|
|
|
|
}
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, KerbInitKernelPackage)
|
|
#pragma alloc_text(PAGE, KerbDeleteKernelContext)
|
|
#pragma alloc_text(PAGE, KerbInitKernelContext)
|
|
#pragma alloc_text(PAGE, KerbMapKernelHandle)
|
|
#pragma alloc_text(PAGEMSG, KerbMakeSignature)
|
|
#pragma alloc_text(PAGEMSG, KerbVerifySignature)
|
|
#pragma alloc_text(PAGEMSG, KerbSealMessage)
|
|
#pragma alloc_text(PAGEMSG, KerbUnsealMessage)
|
|
#pragma alloc_text(PAGEMSG, KerbGetContextToken)
|
|
#pragma alloc_text(PAGEMSG, KerbQueryContextAttributes)
|
|
#pragma alloc_text(PAGEMSG, KerbMakeSignatureToken)
|
|
#pragma alloc_text(PAGEMSG, KerbVerifySignatureToken)
|
|
#pragma alloc_text(PAGE, KerbCompleteToken)
|
|
#pragma alloc_text(PAGE, KerbExportContext)
|
|
#pragma alloc_text(PAGE, KerbImportContext)
|
|
#pragma alloc_text(PAGE, KerbInitDefaults)
|
|
#endif
|
|
|
|
SECPKG_KERNEL_FUNCTION_TABLE KerberosFunctionTable = {
|
|
KerbInitKernelPackage,
|
|
KerbDeleteKernelContext,
|
|
KerbInitKernelContext,
|
|
KerbMapKernelHandle,
|
|
KerbMakeSignature,
|
|
KerbVerifySignature,
|
|
KerbSealMessage,
|
|
KerbUnsealMessage,
|
|
KerbGetContextToken,
|
|
KerbQueryContextAttributes,
|
|
KerbCompleteToken,
|
|
KerbExportContext,
|
|
KerbImportContext,
|
|
KerbSetPageMode
|
|
|
|
};
|
|
POOL_TYPE KerbPoolType ;
|
|
|
|
#define MAYBE_PAGED_CODE() \
|
|
if ( KerbPoolType == PagedPool ) \
|
|
{ \
|
|
PAGED_CODE(); \
|
|
}
|
|
|
|
PVOID KerbPagedList ;
|
|
PVOID KerbNonPagedList ;
|
|
PVOID KerbActiveList ;
|
|
ERESOURCE KerbGlobalResource;
|
|
BOOLEAN KerbCryptInitialized;
|
|
ULONG KerbMaxTokenSize = KERBEROS_MAX_TOKEN;
|
|
|
|
extern "C"
|
|
{
|
|
int LibAttach(HANDLE, PVOID);
|
|
}
|
|
|
|
#define KerbWriteLockGlobals() \
|
|
{ \
|
|
DebugLog((DEB_TRACE_LOCKS,"Write locking Globals\n")); \
|
|
KeEnterCriticalRegion(); \
|
|
ExAcquireResourceExclusiveLite(&KerbGlobalResource,TRUE); \
|
|
}
|
|
#define KerbReadLockGlobals() \
|
|
{ \
|
|
DebugLog((DEB_TRACE_LOCKS,"Read locking Globals\n")); \
|
|
KeEnterCriticalRegion(); \
|
|
ExAcquireSharedWaitForExclusive(&KerbGlobalResource, TRUE); \
|
|
}
|
|
#define KerbUnlockGlobals() \
|
|
{ \
|
|
DebugLog((DEB_TRACE_LOCKS,"Unlocking Globals\n")); \
|
|
ExReleaseResourceLite(&KerbGlobalResource); \
|
|
KeLeaveCriticalRegion(); \
|
|
}
|
|
|
|
//
|
|
// Common GSS object IDs, taken from MIT kerberos distribution.
|
|
//
|
|
|
|
gss_OID_desc oids[] = {
|
|
{5, "\053\005\001\005\002"}, // original mech id
|
|
{9, "\052\206\110\206\367\022\001\002\002"}, // standard mech id
|
|
{10, "\052\206\110\206\367\022\001\002\002\001"}, // krb5_name type
|
|
{10, "\052\206\110\206\367\022\001\002\002\002"}, // krb5_principal type
|
|
{10, "\052\206\110\206\367\022\001\002\002\003"}, // user2user mech id
|
|
};
|
|
|
|
gss_OID_desc * gss_mech_krb5 = oids;
|
|
gss_OID_desc * gss_mech_krb5_new = oids+1;
|
|
gss_OID_desc * gss_mech_krb5_u2u = oids+4;
|
|
|
|
|
|
#define KERB_MAX_CHECKSUM_LENGTH 24
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbInitDefaults
|
|
//
|
|
// Synopsis: Opens registry key, and gets custom defaults
|
|
//
|
|
// Effects: Changes MaxTokenSize
|
|
//
|
|
// Arguments: None
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS on success
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS NTAPI
|
|
KerbInitDefaults()
|
|
{
|
|
UNICODE_STRING ParameterPath;
|
|
UNICODE_STRING MaxTokenValue;
|
|
OBJECT_ATTRIBUTES oa;
|
|
ULONG BytesRead;
|
|
NTSTATUS Status;
|
|
HANDLE hParamKey = INVALID_HANDLE_VALUE;
|
|
|
|
KEY_VALUE_PARTIAL_INFORMATION KeyPartialInformation;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlInitUnicodeString(&ParameterPath, KERB_PARAMETER_PATH);
|
|
RtlInitUnicodeString(&MaxTokenValue, KERB_PARAMETER_MAX_TOKEN_SIZE);
|
|
|
|
InitializeObjectAttributes(
|
|
&oa,
|
|
&ParameterPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = ZwOpenKey(
|
|
&hParamKey,
|
|
KEY_READ,
|
|
&oa
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_WARN, "KerbInitDefault:OpenKey failed:0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = ZwQueryValueKey(
|
|
hParamKey,
|
|
&MaxTokenValue,
|
|
KeyValuePartialInformation,
|
|
(PVOID)&KeyPartialInformation,
|
|
sizeof(KeyPartialInformation),
|
|
&BytesRead
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status) || KeyPartialInformation.Type != REG_DWORD)
|
|
{
|
|
DebugLog((DEB_WARN, "KerbInitDefault:QueryValueKey failed:0x%x\n",Status));
|
|
goto Cleanup;
|
|
} else {
|
|
PULONG Value = (PULONG) &KeyPartialInformation.Data;
|
|
KerbMaxTokenSize = *((PULONG)Value);
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (INVALID_HANDLE_VALUE != hParamKey)
|
|
{
|
|
ZwClose(hParamKey);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbInitKernelPackage
|
|
//
|
|
// Synopsis: Initialize an instance of the Kerberos package in the kernel
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Version - Version of the security dll loading the package
|
|
// FunctionTable - Contains helper routines for use by Kerberos
|
|
// UserFunctions - Receives a copy of Kerberos's user mode
|
|
// function table
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
KerbInitKernelPackage(
|
|
PSECPKG_KERNEL_FUNCTIONS FunctionTable
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PAGED_CODE();
|
|
|
|
KerbPoolType = PagedPool ;
|
|
|
|
KerbPagedList = KSecCreateContextList( KSecPaged );
|
|
|
|
if ( !KerbPagedList )
|
|
{
|
|
return STATUS_NO_MEMORY ;
|
|
}
|
|
|
|
KerbActiveList = KerbPagedList ;
|
|
|
|
Status = ExInitializeResourceLite (&KerbGlobalResource);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Get registry values, ignore failures
|
|
KerbInitDefaults();
|
|
|
|
return STATUS_SUCCESS ;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDeleteKernelContext
|
|
//
|
|
// Synopsis: Deletes a kernel mode context by unlinking it and then
|
|
// dereferencing it.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: ContextHandle - Kernel context handle of the context to delete
|
|
// LsaContextHandle - Receives LSA context handle of the context
|
|
// to delete
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS on success
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
KerbDeleteKernelContext(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
OUT PLSA_SEC_HANDLE LsaContextHandle
|
|
)
|
|
{
|
|
PKERB_KERNEL_CONTEXT Context = NULL;
|
|
NTSTATUS SaveStatus = STATUS_SUCCESS;
|
|
DebugLog((DEB_TRACE,"KerbDeleteUserModeContext called\n"));
|
|
|
|
|
|
Context = KerbReferenceContext(
|
|
ContextHandle,
|
|
TRUE // unlink it
|
|
);
|
|
if (Context == NULL)
|
|
{
|
|
DebugLog((DEB_WARN,"Failed to reference context 0x%x by lsa handle\n",
|
|
ContextHandle ));
|
|
*LsaContextHandle = ContextHandle;
|
|
return(STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
KerbReadLockContexts();
|
|
|
|
*LsaContextHandle = Context->LsaContextHandle;
|
|
|
|
if ((Context->ContextAttributes & KERB_CONTEXT_EXPORTED) != 0)
|
|
{
|
|
SaveStatus = SEC_I_NO_LSA_CONTEXT;
|
|
}
|
|
|
|
KerbUnlockContexts();
|
|
|
|
KerbDereferenceContext(
|
|
Context
|
|
);
|
|
|
|
return((SaveStatus == SEC_I_NO_LSA_CONTEXT) ?
|
|
SEC_I_NO_LSA_CONTEXT :
|
|
STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbInitKernelContext
|
|
//
|
|
// Synopsis: Creates a kernel-mode context from a packed LSA mode context
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: ContextHandle - Lsa mode context handle for the context
|
|
// PackedContext - A marshalled buffer containing the LSA
|
|
// mode context.
|
|
// NewContextHandle - Receives kernel mode context handle
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
KerbInitKernelContext(
|
|
IN LSA_SEC_HANDLE LsaContextHandle,
|
|
IN PSecBuffer PackedContext,
|
|
OUT PLSA_SEC_HANDLE NewContextHandle
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_KERNEL_CONTEXT Context = NULL;
|
|
|
|
PAGED_CODE();
|
|
DebugLog((DEB_TRACE,"KerbInitUserModeContex called\n"));
|
|
|
|
Status = KerbCreateKernelModeContext(
|
|
LsaContextHandle,
|
|
PackedContext,
|
|
&Context
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to create kernel mode context: 0x%x\n",
|
|
Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
*NewContextHandle = KerbGetContextHandle(Context);
|
|
|
|
Cleanup:
|
|
if (Context != NULL)
|
|
{
|
|
KerbDereferenceContext(Context);
|
|
}
|
|
if (PackedContext->pvBuffer != NULL)
|
|
{
|
|
KspKernelFunctions.FreeHeap(PackedContext->pvBuffer);
|
|
PackedContext->pvBuffer = NULL;
|
|
}
|
|
|
|
|
|
return(Status);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbMapKernelHandle
|
|
//
|
|
// Synopsis: Maps a kernel handle into an LSA handle
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: ContextHandle - Kernel context handle of the context to map
|
|
// LsaContextHandle - Receives LSA context handle of the context
|
|
// to map
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS on success
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
KerbMapKernelHandle(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
OUT PLSA_SEC_HANDLE LsaContextHandle
|
|
)
|
|
{
|
|
PKERB_KERNEL_CONTEXT Context = NULL;
|
|
DebugLog((DEB_TRACE,"KerbMapKernelhandle called\n"));
|
|
|
|
PAGED_CODE();
|
|
Context = KerbReferenceContext(
|
|
ContextHandle,
|
|
FALSE // don't it
|
|
);
|
|
if (Context == NULL)
|
|
{
|
|
DebugLog((DEB_WARN,"Failed to reference context 0x%x by lsa handle\n",
|
|
ContextHandle ));
|
|
*LsaContextHandle = ContextHandle;
|
|
}
|
|
else
|
|
{
|
|
*LsaContextHandle = Context->LsaContextHandle;
|
|
KerbDereferenceContext(
|
|
Context
|
|
);
|
|
//
|
|
// If the lsa context handle is zero, this is an imported context
|
|
// so there is no lsa context
|
|
//
|
|
|
|
if (*LsaContextHandle == 0)
|
|
{
|
|
return(SEC_E_UNSUPPORTED_FUNCTION);
|
|
}
|
|
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbRandomFill
|
|
//
|
|
// Synopsis: Generates random data in the buffer.
|
|
//
|
|
// Arguments: [pbBuffer] --
|
|
// [cbBuffer] --
|
|
//
|
|
// History: 5-20-93 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbRandomFill( PUCHAR pbBuffer,
|
|
ULONG cbBuffer)
|
|
{
|
|
if (!CDGenerateRandomBits(pbBuffer, cbBuffer))
|
|
{
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbMakeSignatureToken
|
|
//
|
|
// Synopsis: Makes the signature token for a signed or sealed message
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Context - Context to use for signing
|
|
// QualityOfProtection - flags indicating what kind of checksum
|
|
// to use
|
|
// SignatureBuffer - Buffer in which to place signature
|
|
// TotalBufferSize - Total size of all buffers to be signed
|
|
// Encrypt - if TRUE, then prepare a header for an encrypted buffer
|
|
// SuppliedNonce - Nonce supplied by caller, used for datagram
|
|
// ChecksumType - Receives the type of checksum to use
|
|
// EncryptionType - Receives the type of encryption to use
|
|
//
|
|
// Requires: The context must be write locked
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbMakeSignatureToken(
|
|
IN PKERB_KERNEL_CONTEXT Context,
|
|
IN ULONG QualityOfProtection,
|
|
IN PSecBuffer SignatureBuffer,
|
|
IN ULONG TotalBufferSize,
|
|
IN BOOLEAN Encrypt,
|
|
IN ULONG SuppliedNonce,
|
|
OUT PKERB_GSS_SIGNATURE * OutputSignature,
|
|
OUT PULONG ChecksumType,
|
|
OUT PULONG EncryptionType,
|
|
OUT PULONG SequenceNumber
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PKERB_GSS_SIGNATURE Signature;
|
|
PKERB_GSS_SEAL_SIGNATURE SealSignature;
|
|
ULONG MessageSize;
|
|
ULONG SignatureSize = 0;
|
|
PULONG Nonce;
|
|
gss_OID MechUsed;
|
|
BOOLEAN GssCompatible = TRUE;
|
|
|
|
//
|
|
// Make sure that cryptdll stuff is initialized.
|
|
//
|
|
|
|
MAYBE_PAGED_CODE();
|
|
|
|
if (!KerbCryptInitialized)
|
|
{
|
|
KerbWriteLockGlobals();
|
|
if ( !KerbCryptInitialized )
|
|
{
|
|
if (LibAttach(NULL, NULL))
|
|
{
|
|
KerbCryptInitialized = TRUE;
|
|
}
|
|
}
|
|
KerbUnlockGlobals();
|
|
}
|
|
|
|
//
|
|
// Compute the size of the header. For encryption headers, we need
|
|
// to round up the size of the data & add 8 bytes for a confounder.
|
|
//
|
|
|
|
if (QualityOfProtection == GSS_KRB5_INTEG_C_QOP_DEFAULT)
|
|
{
|
|
GssCompatible = FALSE;
|
|
}
|
|
|
|
//
|
|
// Since RPC doesn't carry around the size of the size of the
|
|
// signature bufer, we use it in the header. This break rfc1964 compat.
|
|
//
|
|
|
|
if (!Encrypt || !GssCompatible)
|
|
{
|
|
TotalBufferSize = 0;
|
|
}
|
|
|
|
if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0)
|
|
{
|
|
MechUsed = gss_mech_krb5_u2u;
|
|
}
|
|
else
|
|
{
|
|
MechUsed = gss_mech_krb5_new;
|
|
}
|
|
if (Encrypt)
|
|
{
|
|
//
|
|
// NOTE: according to rfc1964, buffers that are an even multiple of
|
|
// 8 bytes have 8 bytes of zeros appended. Because we cannot modify
|
|
// the input buffers, the caller will have to do this for us.
|
|
//
|
|
|
|
|
|
MessageSize = TotalBufferSize + sizeof(KERB_GSS_SEAL_SIGNATURE);
|
|
}
|
|
else
|
|
{
|
|
MessageSize = TotalBufferSize + sizeof(KERB_GSS_SIGNATURE);
|
|
}
|
|
|
|
SignatureSize = g_token_size(MechUsed, MessageSize) - TotalBufferSize;
|
|
|
|
|
|
//
|
|
// Make Dave happy (verify that the supplied signature buffer is large
|
|
// enough for a signature):
|
|
//
|
|
|
|
if (SignatureBuffer->cbBuffer < SignatureSize)
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// create the header with the GSS oid
|
|
//
|
|
|
|
Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer;
|
|
g_make_token_header(
|
|
MechUsed,
|
|
MessageSize,
|
|
(PUCHAR *) &Signature,
|
|
(Encrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG)
|
|
);
|
|
|
|
|
|
//
|
|
// Fill in the header information according to RFC1964
|
|
//
|
|
|
|
|
|
|
|
Signature->SignatureAlgorithm[1] = KERB_GSS_SIG_SECOND;
|
|
|
|
//
|
|
// If the keytype is an MS keytype, we need to use an MS encryption
|
|
// scheme.
|
|
//
|
|
|
|
if (!KERB_IS_DES_ENCRYPTION(Context->SessionKey.keytype))
|
|
{
|
|
|
|
#ifndef DONT_SUPPORT_OLD_TYPES_USER
|
|
if (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_OLD)
|
|
{
|
|
*ChecksumType = KERB_CHECKSUM_HMAC_MD5;
|
|
*EncryptionType = KERB_ETYPE_RC4_PLAIN_OLD;
|
|
Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC;
|
|
if (Encrypt)
|
|
{
|
|
Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND;
|
|
Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4_OLD;
|
|
|
|
}
|
|
}
|
|
else if (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_OLD_EXP)
|
|
{
|
|
*ChecksumType = KERB_CHECKSUM_HMAC_MD5;
|
|
*EncryptionType = KERB_ETYPE_RC4_PLAIN_OLD_EXP;
|
|
Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC;
|
|
if (Encrypt)
|
|
{
|
|
Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND;
|
|
Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4_OLD;
|
|
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_NT)
|
|
{
|
|
*ChecksumType = KERB_CHECKSUM_HMAC_MD5;
|
|
*EncryptionType = KERB_ETYPE_RC4_PLAIN;
|
|
Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC;
|
|
if (Encrypt)
|
|
{
|
|
Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND;
|
|
Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_NT_EXP);
|
|
*ChecksumType = KERB_CHECKSUM_HMAC_MD5;
|
|
*EncryptionType = KERB_ETYPE_RC4_PLAIN_EXP;
|
|
Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC;
|
|
if (Encrypt)
|
|
{
|
|
Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND;
|
|
Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4;
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Encrypt)
|
|
{
|
|
Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND;
|
|
Signature->SealAlgorithm[0] = KERB_GSS_SEAL_DES_CBC;
|
|
|
|
}
|
|
|
|
//
|
|
// Use the exportable version if necessasry
|
|
//
|
|
|
|
*EncryptionType = KERB_ETYPE_DES_PLAIN;
|
|
|
|
|
|
switch(QualityOfProtection)
|
|
{
|
|
case GSS_KRB5_INTEG_C_QOP_MD5:
|
|
Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_MD25;
|
|
*ChecksumType = KERB_CHECKSUM_MD25;
|
|
break;
|
|
case GSS_KRB5_INTEG_C_QOP_DEFAULT:
|
|
case GSS_KRB5_INTEG_C_QOP_DES_MD5:
|
|
Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_DES_MAC_MD5;
|
|
*ChecksumType = KERB_CHECKSUM_DES_MAC_MD5;
|
|
break;
|
|
case GSS_KRB5_INTEG_C_QOP_DES_MAC:
|
|
Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_DES_MAC;
|
|
*ChecksumType = KERB_CHECKSUM_DES_MAC;
|
|
break;
|
|
default:
|
|
DebugLog((DEB_ERROR,"Invalid quality of protection sent to MakeSignature: %d.\n",
|
|
QualityOfProtection ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Put in the filler - it is different for signing & sealing
|
|
//
|
|
|
|
if (Encrypt)
|
|
{
|
|
memset(Signature->SealFiller,0xff,2);
|
|
}
|
|
else
|
|
{
|
|
memset(Signature->SignFiller,0xff,4);
|
|
}
|
|
|
|
//
|
|
// Inbound contexts get a high dword of 0xffffffff, outbound gets
|
|
// 0x00000000.
|
|
//
|
|
|
|
if (Context->ContextAttributes & KERB_CONTEXT_INBOUND)
|
|
{
|
|
*(PULONG)(&Signature->SequenceNumber[4]) = 0xffffffff;
|
|
|
|
Nonce = &Context->ReceiveNonce;
|
|
|
|
}
|
|
else
|
|
{
|
|
ASSERT((Context->ContextAttributes & KERB_CONTEXT_OUTBOUND) != 0);
|
|
*(PULONG)(&Signature->SequenceNumber[4]) = 0x00000000;
|
|
Nonce = &Context->Nonce;
|
|
}
|
|
|
|
//
|
|
// If this is datagram, or integrity without replay & sequence detection,
|
|
// use the nonce from the caller
|
|
//
|
|
|
|
if (((Context->ContextFlags & ISC_RET_DATAGRAM) != 0) ||
|
|
((Context->ContextFlags & (ISC_RET_INTEGRITY | ISC_RET_SEQUENCE_DETECT | ISC_RET_REPLAY_DETECT)) == ISC_RET_INTEGRITY))
|
|
|
|
{
|
|
Nonce = &SuppliedNonce;
|
|
}
|
|
|
|
Signature->SequenceNumber[0] = (UCHAR) ((*Nonce & 0xff000000) >> 24);
|
|
Signature->SequenceNumber[1] = (UCHAR) ((*Nonce & 0x00ff0000) >> 16);
|
|
Signature->SequenceNumber[2] = (UCHAR) ((*Nonce & 0x0000ff00) >> 8);
|
|
Signature->SequenceNumber[3] = (UCHAR) (*Nonce & 0x000000ff);
|
|
|
|
(*Nonce)++;
|
|
|
|
|
|
*SequenceNumber = *(PULONG)Signature->SequenceNumber;
|
|
|
|
//
|
|
// If we are encrypting, add the confounder to the end of the signature
|
|
//
|
|
|
|
if (Encrypt)
|
|
{
|
|
SealSignature = (PKERB_GSS_SEAL_SIGNATURE) Signature;
|
|
KerbRandomFill(
|
|
SealSignature->Confounder,
|
|
KERB_GSS_SIG_CONFOUNDER_SIZE
|
|
);
|
|
}
|
|
//
|
|
// Set the size of the signature
|
|
//
|
|
|
|
SignatureBuffer->cbBuffer = SignatureSize;
|
|
*OutputSignature = Signature;
|
|
|
|
Cleanup:
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbVerifySignatureToken
|
|
//
|
|
// Synopsis: Verifies the header on a signed or sealed message
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Context - context to use for verification
|
|
// SignatureBuffer - Buffer containing signature
|
|
// TotalBufferSize - Size of all buffers signed/encrypted
|
|
// Decrypt - TRUE if we are unsealing
|
|
// SuppliedNonce - Nonce supplied by caller, used for datagram
|
|
// QualityOfProtection - returns GSS quality of protection flags
|
|
// ChecksumType - Type of checksum used in this signature
|
|
// EncryptionType - Type of encryption used in this signature
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbVerifySignatureToken(
|
|
IN PKERB_KERNEL_CONTEXT Context,
|
|
IN PSecBuffer SignatureBuffer,
|
|
IN ULONG TotalBufferSize,
|
|
IN BOOLEAN Decrypt,
|
|
IN ULONG SuppliedNonce,
|
|
OUT PKERB_GSS_SIGNATURE * OutputSignature,
|
|
OUT PULONG QualityOfProtection,
|
|
OUT PULONG ChecksumType,
|
|
OUT PCRYPTO_SYSTEM * CryptSystem,
|
|
OUT PULONG SequenceNumber
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG SignatureSize = 0;
|
|
UCHAR Nonce[8];
|
|
PCRYPT_STATE_BUFFER CryptBuffer = NULL;
|
|
ULONG OutputSize;
|
|
ULONG EncryptionType;
|
|
PCRYPTO_SYSTEM LocalCryptSystem = NULL ;
|
|
PKERB_GSS_SIGNATURE Signature;
|
|
PULONG ContextNonce;
|
|
gss_OID MechUsed;
|
|
|
|
//
|
|
// Make sure that cryptdll stuff is initialized.
|
|
//
|
|
|
|
MAYBE_PAGED_CODE();
|
|
|
|
if (!KerbCryptInitialized)
|
|
{
|
|
KerbWriteLockGlobals();
|
|
|
|
if ( !KerbCryptInitialized )
|
|
{
|
|
if (LibAttach(NULL, NULL))
|
|
{
|
|
KerbCryptInitialized = TRUE;
|
|
}
|
|
}
|
|
KerbUnlockGlobals();
|
|
}
|
|
|
|
//
|
|
// Since RPC doesn't carry around the size of the size of the
|
|
// signature bufer, we use it in the header. This break rfc1964 compat.
|
|
//
|
|
|
|
if (!Decrypt ||
|
|
((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0) ||
|
|
((Context->ContextFlags & ISC_RET_DATAGRAM) != 0))
|
|
{
|
|
TotalBufferSize = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Verify the signature header
|
|
//
|
|
|
|
if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0)
|
|
{
|
|
MechUsed = gss_mech_krb5_u2u;
|
|
}
|
|
else
|
|
{
|
|
MechUsed = gss_mech_krb5_new;
|
|
}
|
|
|
|
Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer;
|
|
if (!g_verify_token_header(
|
|
MechUsed,
|
|
(int *) &SignatureSize,
|
|
(PUCHAR *) &Signature,
|
|
(Decrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG),
|
|
SignatureBuffer->cbBuffer + TotalBufferSize))
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
}
|
|
|
|
//
|
|
// If that didn't work, try with the old mech. Need this is for DCE clients
|
|
// for whom we can't tell what mech they use.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status) && ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0))
|
|
{
|
|
Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer;
|
|
if (!g_verify_token_header(
|
|
gss_mech_krb5,
|
|
(int *) &SignatureSize,
|
|
(PUCHAR *) &Signature,
|
|
(Decrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG),
|
|
SignatureBuffer->cbBuffer + TotalBufferSize))
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// MS RPC clients don't send the size properly, so set the total size
|
|
// to zero and try again.
|
|
//
|
|
|
|
if (Decrypt && !NT_SUCCESS(Status))
|
|
{
|
|
TotalBufferSize = 0;
|
|
Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer;
|
|
if (!g_verify_token_header(
|
|
MechUsed,
|
|
(int *) &SignatureSize,
|
|
(PUCHAR *) &Signature,
|
|
(Decrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG),
|
|
SignatureBuffer->cbBuffer + TotalBufferSize))
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If that didn't work, try with the old mech. Need this is for DCE clients
|
|
// for whom we can't tell what mech they use.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status) && ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0))
|
|
{
|
|
Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer;
|
|
if (!g_verify_token_header(
|
|
gss_mech_krb5,
|
|
(int *) &SignatureSize,
|
|
(PUCHAR *) &Signature,
|
|
(Decrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG),
|
|
SignatureBuffer->cbBuffer + TotalBufferSize))
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Protection from bad Signature Size
|
|
//
|
|
|
|
if (SignatureSize == 0)
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Subtract the total buffer size from Signature size to get the real
|
|
// size of the signature.
|
|
//
|
|
|
|
SignatureSize -= TotalBufferSize;
|
|
|
|
//
|
|
// Make sure the signature is big enough. We can't enforce a strict
|
|
// size because RPC will transmit the maximum number of bytes instead
|
|
// of the actual number.
|
|
//
|
|
|
|
if ((Decrypt && (SignatureSize < sizeof(KERB_GSS_SEAL_SIGNATURE))) ||
|
|
(!Decrypt && (SignatureSize < sizeof(KERB_GSS_SIGNATURE))))
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Verify the sequence number
|
|
//
|
|
|
|
if (Signature->SignatureAlgorithm[1] != KERB_GSS_SIG_SECOND)
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
//
|
|
// Figure out the algorithm
|
|
//
|
|
switch(Context->SessionKey.keytype) {
|
|
case KERB_ETYPE_DES_CBC_MD5:
|
|
case KERB_ETYPE_DES_CBC_CRC:
|
|
EncryptionType = KERB_ETYPE_DES_PLAIN;
|
|
break;
|
|
case KERB_ETYPE_RC4_HMAC_OLD_EXP:
|
|
EncryptionType = KERB_ETYPE_RC4_PLAIN_OLD_EXP;
|
|
break;
|
|
case KERB_ETYPE_RC4_HMAC_OLD:
|
|
EncryptionType = KERB_ETYPE_RC4_PLAIN_OLD;
|
|
break;
|
|
case KERB_ETYPE_RC4_HMAC_NT_EXP:
|
|
EncryptionType = KERB_ETYPE_RC4_PLAIN_EXP;
|
|
break;
|
|
case KERB_ETYPE_RC4_HMAC_NT:
|
|
EncryptionType = KERB_ETYPE_RC4_PLAIN;
|
|
break;
|
|
default:
|
|
DebugLog((DEB_ERROR,"Unknown key type: %d\n",
|
|
Context->SessionKey.keytype ));
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// if the key is exportable, make sure to use the exportable plain
|
|
// version.
|
|
//
|
|
|
|
|
|
switch(Signature->SignatureAlgorithm[0]) {
|
|
case KERB_GSS_SIG_MD25:
|
|
*QualityOfProtection = GSS_KRB5_INTEG_C_QOP_MD5;
|
|
*ChecksumType = KERB_CHECKSUM_MD25;
|
|
break;
|
|
case KERB_GSS_SIG_DES_MAC_MD5:
|
|
*QualityOfProtection = GSS_KRB5_INTEG_C_QOP_DES_MD5;
|
|
*ChecksumType = KERB_CHECKSUM_DES_MAC_MD5;
|
|
break;
|
|
case KERB_GSS_SIG_DES_MAC:
|
|
*QualityOfProtection = GSS_KRB5_INTEG_C_QOP_DES_MAC;
|
|
*ChecksumType = KERB_CHECKSUM_DES_MAC;
|
|
break;
|
|
case KERB_GSS_SIG_HMAC:
|
|
*QualityOfProtection = GSS_KRB5_INTEG_C_QOP_DEFAULT;
|
|
*ChecksumType = KERB_CHECKSUM_HMAC_MD5;
|
|
break;
|
|
default:
|
|
DebugLog((DEB_ERROR,"Invalid signature type to VerifySignature: %d\n",
|
|
Signature->SignatureAlgorithm[0]));
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
if (Decrypt)
|
|
{
|
|
if (Signature->SealAlgorithm[1] != KERB_GSS_SIG_SECOND)
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Verify the seal algorithm
|
|
//
|
|
|
|
switch(EncryptionType) {
|
|
case KERB_ETYPE_DES_PLAIN:
|
|
if (Signature->SealAlgorithm[0] != KERB_GSS_SEAL_DES_CBC)
|
|
{
|
|
DebugLog((DEB_ERROR,"Trying to mix encryption types\n" ));
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
break;
|
|
case KERB_ETYPE_RC4_PLAIN_OLD_EXP:
|
|
case KERB_ETYPE_RC4_PLAIN_OLD:
|
|
if (Signature->SealAlgorithm[0] != KERB_GSS_SEAL_RC4_OLD)
|
|
{
|
|
DebugLog((DEB_ERROR,"Trying to mix encryption types\n"));
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
break;
|
|
case KERB_ETYPE_RC4_PLAIN_EXP:
|
|
case KERB_ETYPE_RC4_PLAIN:
|
|
if (Signature->SealAlgorithm[0] != KERB_GSS_SEAL_RC4)
|
|
{
|
|
DebugLog((DEB_ERROR,"Trying to mix encryption types\n"));
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
break;
|
|
default:
|
|
DebugLog((DEB_ERROR,"Invalid seal type to VerifySignature: %d, %d\n", Signature->SealAlgorithm[0], EncryptionType));
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check the filler
|
|
//
|
|
|
|
if ((Decrypt && (*(PUSHORT) Signature->SealFiller != 0xffff)) ||
|
|
(!Decrypt && (*(PULONG) Signature->SignFiller != 0xffffffff)))
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Verify the sequence number. To do this we need to decrypt it with
|
|
// the session key with the checksum as the IV.
|
|
//
|
|
|
|
|
|
Status = CDLocateCSystem(EncryptionType, &LocalCryptSystem);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x\n", EncryptionType,Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Now we need to Decrypt the sequence number, using the checksum as the
|
|
// IV
|
|
//
|
|
|
|
Status = LocalCryptSystem->Initialize(
|
|
Context->SessionKey.keyvalue.value,
|
|
Context->SessionKey.keyvalue.length,
|
|
0, // no flags
|
|
&CryptBuffer
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the initial vector
|
|
//
|
|
|
|
Status = LocalCryptSystem->Control(
|
|
CRYPT_CONTROL_SET_INIT_VECT,
|
|
CryptBuffer,
|
|
Signature->Checksum,
|
|
8
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Now encrypt the sequence number
|
|
//
|
|
|
|
OutputSize = 8;
|
|
|
|
Status = LocalCryptSystem->Decrypt(
|
|
CryptBuffer,
|
|
Signature->SequenceNumber,
|
|
8,
|
|
Signature->SequenceNumber,
|
|
&OutputSize
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// For datagram or integrity only, we use just the supplied nonce.
|
|
//
|
|
|
|
if (((Context->ContextFlags & ISC_RET_DATAGRAM) != 0) ||
|
|
((Context->ContextFlags & (ISC_RET_INTEGRITY | ISC_RET_SEQUENCE_DETECT | ISC_RET_REPLAY_DETECT)) == ISC_RET_INTEGRITY))
|
|
{
|
|
ContextNonce = &SuppliedNonce;
|
|
}
|
|
else
|
|
{
|
|
if ((Context->ContextAttributes & KERB_CONTEXT_OUTBOUND) != 0)
|
|
{
|
|
ContextNonce = &Context->ReceiveNonce;
|
|
}
|
|
else
|
|
{
|
|
ContextNonce = &Context->Nonce;
|
|
}
|
|
}
|
|
|
|
Nonce[0] = (UCHAR) ((*ContextNonce & 0xff000000) >> 24);
|
|
Nonce[1] = (UCHAR) ((*ContextNonce & 0x00ff0000) >> 16);
|
|
Nonce[2] = (UCHAR) ((*ContextNonce & 0x0000ff00) >> 8);
|
|
Nonce[3] = (UCHAR) (*ContextNonce & 0x000000ff);
|
|
|
|
*SequenceNumber = *(PULONG) Nonce;
|
|
|
|
if (!RtlEqualMemory(
|
|
Nonce,
|
|
Signature->SequenceNumber,
|
|
4))
|
|
{
|
|
Status = SEC_E_OUT_OF_SEQUENCE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
(*ContextNonce)++;
|
|
|
|
//
|
|
// Inbound contexts send a high dword of 0xffffffff, outbound gets
|
|
// 0x00000000.
|
|
//
|
|
|
|
if (Context->ContextAttributes & KERB_CONTEXT_OUTBOUND)
|
|
{
|
|
if (*(PULONG)(&Signature->SequenceNumber[4]) != 0xffffffff)
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT((Context->ContextAttributes & KERB_CONTEXT_INBOUND) != 0);
|
|
if (*(PULONG)(&Signature->SequenceNumber[4]) != 0)
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(CryptSystem))
|
|
{
|
|
*CryptSystem = LocalCryptSystem;
|
|
}
|
|
|
|
*OutputSignature = Signature;
|
|
|
|
Cleanup:
|
|
if ( ( CryptBuffer != NULL) &&
|
|
( LocalCryptSystem != NULL ) )
|
|
{
|
|
LocalCryptSystem->Discard(&CryptBuffer);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbMakeSignature
|
|
//
|
|
// Synopsis: Signs a message buffer by calculatinga checksum over all
|
|
// the non-read only data buffers and encrypting the checksum
|
|
// along with a nonce.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: ContextHandle - Handle of the context to use to sign the
|
|
// message.
|
|
// QualityOfProtection - Unused flags.
|
|
// MessageBuffers - Contains an array of buffers to sign and
|
|
// to store the signature.
|
|
// MessageSequenceNumber - Sequence number for this message,
|
|
// only used in datagram cases.
|
|
//
|
|
// Requires: STATUS_INVALID_HANDLE - the context could not be found or
|
|
// was not configured for message integrity.
|
|
// STATUS_INVALID_PARAMETER - the signature buffer could not
|
|
// be found.
|
|
// STATUS_BUFFER_TOO_SMALL - the signature buffer is too small
|
|
// to hold the signature
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes: Cluster folks need to run this at dpc level (non paged)
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
KerbMakeSignature(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
IN ULONG QualityOfProtection,
|
|
IN PSecBufferDesc MessageBuffers,
|
|
IN ULONG MessageSequenceNumber
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PKERB_KERNEL_CONTEXT Context = NULL;
|
|
PCHECKSUM_FUNCTION Check;
|
|
PCRYPTO_SYSTEM CryptSystem = NULL ;
|
|
PSecBuffer SignatureBuffer = NULL;
|
|
ULONG Index;
|
|
PCHECKSUM_BUFFER CheckBuffer = NULL;
|
|
PCRYPT_STATE_BUFFER CryptBuffer = NULL;
|
|
PKERB_GSS_SIGNATURE Signature;
|
|
UCHAR LocalChecksum[KERB_MAX_CHECKSUM_LENGTH];
|
|
BOOLEAN ContextsLocked = FALSE;
|
|
ULONG ChecksumType = 0;
|
|
ULONG EncryptType;
|
|
ULONG TotalBufferSize = 0;
|
|
ULONG OutputSize;
|
|
ULONG SequenceNumber;
|
|
|
|
|
|
MAYBE_PAGED_CODE();
|
|
DebugLog((DEB_TRACE,"KerbMakeSignature Called\n"));
|
|
|
|
Context = KerbReferenceContext(
|
|
ContextHandle,
|
|
FALSE // don't unlink
|
|
);
|
|
|
|
if (Context == NULL)
|
|
{
|
|
DebugLog((DEB_ERROR, "Invalid handle supplied for MakeSignature(0x%x)\n",
|
|
ContextHandle));
|
|
Status = STATUS_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Find the body and signature SecBuffers from pMessage
|
|
//
|
|
|
|
for (Index = 0; Index < MessageBuffers->cBuffers ; Index++ )
|
|
{
|
|
if (BUFFERTYPE(MessageBuffers->pBuffers[Index]) == SECBUFFER_TOKEN)
|
|
{
|
|
SignatureBuffer = &MessageBuffers->pBuffers[Index];
|
|
}
|
|
else if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) &&
|
|
(!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY)))
|
|
|
|
{
|
|
TotalBufferSize += MessageBuffers->pBuffers[Index].cbBuffer;
|
|
}
|
|
}
|
|
|
|
|
|
if (SignatureBuffer == NULL)
|
|
{
|
|
DebugLog((DEB_ERROR, "No signature buffer found\n"));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
KerbWriteLockContexts();
|
|
ContextsLocked = TRUE;
|
|
|
|
//
|
|
// Verify that the context was created with the integrity bit
|
|
//
|
|
|
|
if ((Context->ContextFlags & KERB_SIGN_FLAGS) == 0)
|
|
{
|
|
if (SignatureBuffer->cbBuffer < sizeof(KERB_NULL_SIGNATURE))
|
|
{
|
|
Status = SEC_E_BUFFER_TOO_SMALL;
|
|
goto Cleanup;
|
|
}
|
|
SignatureBuffer->cbBuffer = sizeof(KERB_NULL_SIGNATURE);
|
|
*(PKERB_NULL_SIGNATURE) SignatureBuffer->pvBuffer = 0;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
Status = KerbMakeSignatureToken(
|
|
Context,
|
|
QualityOfProtection,
|
|
SignatureBuffer,
|
|
TotalBufferSize,
|
|
FALSE, // don't encrypt
|
|
MessageSequenceNumber,
|
|
&Signature,
|
|
&ChecksumType,
|
|
&EncryptType,
|
|
&SequenceNumber
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Locate the checksum for the context, loading it if necessary from the
|
|
// the crypto support DLL
|
|
//
|
|
|
|
Status = CDLocateCheckSum(ChecksumType, &Check);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to load %d checksum: 0x%x.\n ",ChecksumType,Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(Check->CheckSumSize <= sizeof(LocalChecksum));
|
|
|
|
Status = CDLocateCSystem(EncryptType, &CryptSystem);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x.\n",EncryptType,Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Generate a check sum of the message, and store it into the signature
|
|
// buffer.
|
|
//
|
|
|
|
if (NULL != Check->InitializeEx2)
|
|
{
|
|
Status = Check->InitializeEx2(
|
|
Context->SessionKey.keyvalue.value,
|
|
(ULONG) Context->SessionKey.keyvalue.length,
|
|
NULL,
|
|
KERB_SAFE_SALT,
|
|
&CheckBuffer
|
|
);
|
|
}
|
|
else
|
|
{
|
|
Status = Check->InitializeEx(
|
|
Context->SessionKey.keyvalue.value,
|
|
(ULONG) Context->SessionKey.keyvalue.length,
|
|
KERB_SAFE_SALT,
|
|
&CheckBuffer
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
KerbUnlockContexts();
|
|
ContextsLocked = FALSE;
|
|
|
|
//
|
|
// Sum in 8 bytes of the signature
|
|
//
|
|
|
|
Check->Sum(
|
|
CheckBuffer,
|
|
8,
|
|
((PUCHAR) Signature) -2
|
|
);
|
|
|
|
for (Index = 0; Index < MessageBuffers->cBuffers; Index++ )
|
|
{
|
|
if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) &&
|
|
(!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY)) &&
|
|
(MessageBuffers->pBuffers[Index].cbBuffer != 0))
|
|
{
|
|
|
|
Check->Sum(
|
|
CheckBuffer,
|
|
MessageBuffers->pBuffers[Index].cbBuffer,
|
|
(PBYTE) MessageBuffers->pBuffers[Index].pvBuffer
|
|
);
|
|
}
|
|
}
|
|
|
|
(void) Check->Finalize(CheckBuffer, LocalChecksum);
|
|
|
|
|
|
Status = Check->Finish(&CheckBuffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Copy in the first 8 bytes of the checksum
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
Signature->Checksum,
|
|
LocalChecksum,
|
|
8
|
|
);
|
|
|
|
|
|
//
|
|
// Now we need to encrypt the sequence number, using the checksum as the
|
|
// IV
|
|
//
|
|
|
|
Status = CryptSystem->Initialize(
|
|
Context->SessionKey.keyvalue.value,
|
|
Context->SessionKey.keyvalue.length,
|
|
0, // no options
|
|
&CryptBuffer
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the initial vector
|
|
//
|
|
|
|
Status = CryptSystem->Control(
|
|
CRYPT_CONTROL_SET_INIT_VECT,
|
|
CryptBuffer,
|
|
LocalChecksum,
|
|
8
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Now encrypt the sequence number
|
|
//
|
|
|
|
Status = CryptSystem->Encrypt(
|
|
CryptBuffer,
|
|
Signature->SequenceNumber,
|
|
8,
|
|
Signature->SequenceNumber,
|
|
&OutputSize
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if ( ( CryptBuffer != NULL ) &&
|
|
( CryptSystem != NULL ) )
|
|
{
|
|
CryptSystem->Discard(&CryptBuffer);
|
|
}
|
|
|
|
if (ContextsLocked)
|
|
{
|
|
KerbUnlockContexts();
|
|
}
|
|
|
|
if (Context != NULL)
|
|
{
|
|
KerbDereferenceContext(Context);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbVerifySignature
|
|
//
|
|
// Synopsis: Verifies a signed message buffer by calculating a checksum over all
|
|
// the non-read only data buffers and encrypting the checksum
|
|
// along with a nonce.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: ContextHandle - Handle of the context to use to sign the
|
|
// message.
|
|
// MessageBuffers - Contains an array of signed buffers and
|
|
// a signature buffer.
|
|
// MessageSequenceNumber - Sequence number for this message,
|
|
// only used in datagram cases.
|
|
// QualityOfProtection - Unused flags.
|
|
//
|
|
// Requires: STATUS_INVALID_HANDLE - the context could not be found or
|
|
// was not configured for message integrity.
|
|
// STATUS_INVALID_PARAMETER - the signature buffer could not
|
|
// be found or was too small.
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes: Cluster folks need to run this at dpc level (non paged)
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
KerbVerifySignature(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
IN PSecBufferDesc MessageBuffers,
|
|
IN ULONG MessageSequenceNumber,
|
|
OUT PULONG QualityOfProtection
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PKERB_KERNEL_CONTEXT Context = NULL;
|
|
PCHECKSUM_FUNCTION Check;
|
|
PSecBuffer SignatureBuffer = NULL;
|
|
ULONG Index;
|
|
PCHECKSUM_BUFFER CheckBuffer = NULL;
|
|
PKERB_GSS_SIGNATURE Signature;
|
|
ULONG ChecksumType;
|
|
BOOLEAN ContextsLocked = FALSE;
|
|
UCHAR LocalChecksum[KERB_MAX_CHECKSUM_LENGTH];
|
|
ULONG Protection;
|
|
ULONG TotalBufferSize = 0;
|
|
ULONG SequenceNumber;
|
|
|
|
|
|
MAYBE_PAGED_CODE();
|
|
DebugLog((DEB_TRACE,"KerbVerifySignature Called\n"));
|
|
|
|
Context = KerbReferenceContext(
|
|
ContextHandle,
|
|
FALSE // don't unlink
|
|
);
|
|
|
|
if (Context == NULL)
|
|
{
|
|
DebugLog((DEB_ERROR, "Invalid handle supplied for VerifySignature(0x%x)\n",
|
|
ContextHandle));
|
|
Status = STATUS_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Find the body and signature SecBuffers from pMessage
|
|
//
|
|
|
|
for (Index = 0; Index < MessageBuffers->cBuffers ; Index++ )
|
|
{
|
|
if (BUFFERTYPE(MessageBuffers->pBuffers[Index]) == SECBUFFER_TOKEN)
|
|
{
|
|
SignatureBuffer = &MessageBuffers->pBuffers[Index];
|
|
}
|
|
else if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) &&
|
|
(!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY)))
|
|
|
|
{
|
|
TotalBufferSize += MessageBuffers->pBuffers[Index].cbBuffer;
|
|
}
|
|
}
|
|
|
|
|
|
if (SignatureBuffer == NULL)
|
|
{
|
|
DebugLog((DEB_ERROR, "No signature buffer found\n"));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
KerbWriteLockContexts();
|
|
ContextsLocked = TRUE;
|
|
|
|
//
|
|
// Also, verify that the context was created with the integrity bit
|
|
//
|
|
|
|
if ((Context->ContextFlags & KERB_SIGN_FLAGS) == 0)
|
|
{
|
|
PKERB_NULL_SIGNATURE NullSignature = (PKERB_NULL_SIGNATURE) SignatureBuffer->pvBuffer;
|
|
|
|
if (SignatureBuffer->cbBuffer >= sizeof(KERB_NULL_SIGNATURE) &&
|
|
(*NullSignature == 0))
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
}
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Verify the signature header
|
|
//
|
|
|
|
Status = KerbVerifySignatureToken(
|
|
Context,
|
|
SignatureBuffer,
|
|
TotalBufferSize,
|
|
FALSE, // don't decrypt
|
|
MessageSequenceNumber,
|
|
&Signature,
|
|
&Protection,
|
|
&ChecksumType,
|
|
NULL, // don't need crypt system
|
|
&SequenceNumber
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR, "Failed to verify signature token: 0x%x\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Now compute the checksum and verify it
|
|
//
|
|
|
|
Status = CDLocateCheckSum(ChecksumType, &Check);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to load MD5 checksum: 0x%x\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(Check->CheckSumSize <= sizeof(LocalChecksum));
|
|
|
|
//
|
|
// Generate a check sum of the message, and store it into the signature
|
|
// buffer.
|
|
//
|
|
|
|
//
|
|
// if available use the Ex2 version for keyed checksums where checksum
|
|
// must be passed in on verification
|
|
//
|
|
if (NULL != Check->InitializeEx2)
|
|
{
|
|
Status = Check->InitializeEx2(
|
|
Context->SessionKey.keyvalue.value,
|
|
(ULONG) Context->SessionKey.keyvalue.length,
|
|
Signature->Checksum,
|
|
KERB_SAFE_SALT,
|
|
&CheckBuffer
|
|
);
|
|
}
|
|
else
|
|
{
|
|
Status = Check->InitializeEx(
|
|
Context->SessionKey.keyvalue.value,
|
|
(ULONG) Context->SessionKey.keyvalue.length,
|
|
KERB_SAFE_SALT,
|
|
&CheckBuffer
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
KerbUnlockContexts();
|
|
ContextsLocked = FALSE;
|
|
|
|
//
|
|
// Sum in 8 bytes of the signature
|
|
//
|
|
|
|
Check->Sum(
|
|
CheckBuffer,
|
|
8,
|
|
((PUCHAR) Signature) -2
|
|
);
|
|
|
|
for (Index = 0; Index < MessageBuffers->cBuffers; Index++ )
|
|
{
|
|
if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) &&
|
|
(!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY)) &&
|
|
(MessageBuffers->pBuffers[Index].cbBuffer != 0))
|
|
{
|
|
|
|
Check->Sum(
|
|
CheckBuffer,
|
|
MessageBuffers->pBuffers[Index].cbBuffer,
|
|
(PBYTE) MessageBuffers->pBuffers[Index].pvBuffer
|
|
);
|
|
}
|
|
}
|
|
|
|
(void) Check->Finalize(CheckBuffer, LocalChecksum);
|
|
|
|
|
|
Status = Check->Finish(&CheckBuffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!RtlEqualMemory(
|
|
LocalChecksum,
|
|
Signature->Checksum,
|
|
8))
|
|
{
|
|
Status = SEC_E_MESSAGE_ALTERED;
|
|
goto Cleanup;
|
|
}
|
|
if (ARGUMENT_PRESENT(QualityOfProtection))
|
|
{
|
|
*QualityOfProtection = Protection;
|
|
}
|
|
Cleanup:
|
|
if (ContextsLocked)
|
|
{
|
|
KerbUnlockContexts();
|
|
}
|
|
if (Context != NULL)
|
|
{
|
|
KerbDereferenceContext(Context);
|
|
}
|
|
|
|
DebugLog((DEB_TRACE, "SpVerifySignature returned 0x%x\n", Status));
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
KerbSealMessage(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
IN ULONG QualityOfProtection,
|
|
IN PSecBufferDesc MessageBuffers,
|
|
IN ULONG MessageSequenceNumber
|
|
)
|
|
{
|
|
MAYBE_PAGED_CODE();
|
|
return(STATUS_NOT_SUPPORTED);
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
KerbUnsealMessage(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
IN PSecBufferDesc MessageBuffers,
|
|
IN ULONG MessageSequenceNumber,
|
|
OUT PULONG QualityOfProtection
|
|
)
|
|
{
|
|
MAYBE_PAGED_CODE();
|
|
return(STATUS_NOT_SUPPORTED);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbGetContextToken
|
|
//
|
|
// Synopsis: returns a pointer to the token for a server-side context
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
KerbGetContextToken(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
OUT OPTIONAL PHANDLE ImpersonationToken,
|
|
OUT OPTIONAL PACCESS_TOKEN * RawToken
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PKERB_KERNEL_CONTEXT Context = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
DebugLog((DEB_TRACE,"KerbGetContextToken Called\n"));
|
|
|
|
|
|
Context = KerbReferenceContext(
|
|
ContextHandle,
|
|
FALSE // don't unlink
|
|
);
|
|
|
|
if (Context == NULL)
|
|
{
|
|
DebugLog((DEB_ERROR, "Invalid handle supplied for GetContextToken(0x%x)\n",
|
|
ContextHandle));
|
|
return(STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
KerbReadLockContexts();
|
|
|
|
if (Context->TokenHandle == NULL)
|
|
{
|
|
Status = SEC_E_NO_IMPERSONATION;
|
|
KerbUnlockContexts();
|
|
goto Cleanup;
|
|
}
|
|
if (ARGUMENT_PRESENT(ImpersonationToken))
|
|
{
|
|
*ImpersonationToken = Context->TokenHandle;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(RawToken))
|
|
{
|
|
if (Context->TokenHandle != NULL)
|
|
{
|
|
if (Context->AccessToken == NULL)
|
|
{
|
|
Status = ObReferenceObjectByHandle(
|
|
Context->TokenHandle,
|
|
TOKEN_IMPERSONATE,
|
|
NULL,
|
|
ExGetPreviousMode(),
|
|
(PVOID *) &Context->AccessToken,
|
|
NULL // no handle information
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*RawToken = Context->AccessToken;
|
|
}
|
|
}
|
|
|
|
KerbUnlockContexts();
|
|
|
|
Cleanup:
|
|
|
|
if (Context != NULL)
|
|
{
|
|
//
|
|
// Note: once we dereference the context the handle we return
|
|
// may go away or be re-used. That is the price we have to pay
|
|
// to avoid duplicating it.
|
|
//
|
|
|
|
KerbDereferenceContext(Context);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbQueryContextAttributes
|
|
//
|
|
// Synopsis: Querys attributes of the specified context
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
KerbQueryContextAttributes(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
IN ULONG ContextAttribute,
|
|
IN OUT PVOID Buffer
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PKERB_KERNEL_CONTEXT Context = NULL;
|
|
PSecPkgContext_Sizes SizeInfo;
|
|
PSecPkgContext_Names NameInfo;
|
|
PSecPkgContext_Lifespan LifespanInfo;
|
|
PSecPkgContext_Flags FlagsInfo;
|
|
PSecPkgContext_SessionKey SessionKeyInfo;
|
|
PSecPkgContext_UserFlags UserFlagsInfo ;
|
|
PSecPkgContext_PackageInfo PackageInfo = NULL;
|
|
PSecPkgContext_TargetInformation TargetInformation = NULL;
|
|
UNICODE_STRING FullName;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugLog((DEB_TRACE,"SpQueryContextAttributes Called\n"));
|
|
|
|
|
|
|
|
Context = KerbReferenceContext(
|
|
ContextHandle,
|
|
FALSE // don't unlink
|
|
);
|
|
|
|
|
|
//
|
|
// allow PACKAGE_INFO or NEGOTIATION_INFO to be queried against
|
|
// incomplete contexts.
|
|
//
|
|
|
|
if( (Context == NULL) &&
|
|
(ContextAttribute != SECPKG_ATTR_PACKAGE_INFO) &&
|
|
(ContextAttribute != SECPKG_ATTR_NEGOTIATION_INFO)
|
|
) {
|
|
|
|
DebugLog((DEB_ERROR, "Invalid handle supplied for QueryContextAttributes(0x%x)\n",
|
|
ContextHandle));
|
|
return(STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
|
|
//
|
|
// Return the appropriate information
|
|
//
|
|
|
|
switch(ContextAttribute)
|
|
{
|
|
case SECPKG_ATTR_SIZES:
|
|
//
|
|
// The sizes returned are used by RPC to determine whether to call
|
|
// MakeSignature or SealMessage. The signature size should be zero
|
|
// if neither is to be called, and the block size and trailer size
|
|
// should be zero if SignMessage is not to be called.
|
|
//
|
|
|
|
SizeInfo = (PSecPkgContext_Sizes) Buffer;
|
|
SizeInfo->cbMaxToken = KerbMaxTokenSize;
|
|
// if ((Context->ContextFlags & (ISC_RET_CONFIDENTIALITY | ISC_RET_SEQUENCE_DETECT)) != 0)
|
|
// {
|
|
SizeInfo->cbMaxSignature = KERB_SIGNATURE_SIZE;
|
|
// }
|
|
// else
|
|
// {
|
|
// SizeInfo->cbMaxSignature = 0;
|
|
// }
|
|
if ((Context->ContextFlags & ISC_RET_CONFIDENTIALITY) != 0)
|
|
{
|
|
SizeInfo->cbBlockSize = 1;
|
|
SizeInfo->cbSecurityTrailer = KERB_SIGNATURE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
SizeInfo->cbBlockSize = 0;
|
|
SizeInfo->cbSecurityTrailer = 0;
|
|
}
|
|
break;
|
|
case SECPKG_ATTR_NAMES:
|
|
NameInfo = (PSecPkgContext_Names) Buffer;
|
|
|
|
NameInfo->sUserName = (LPWSTR) KspKernelFunctions.AllocateHeap(Context->FullName.Length + sizeof(WCHAR));
|
|
if (NameInfo->sUserName != NULL)
|
|
{
|
|
RtlCopyMemory(
|
|
NameInfo->sUserName,
|
|
Context->FullName.Buffer,
|
|
Context->FullName.Length
|
|
);
|
|
NameInfo->sUserName[Context->FullName.Length/sizeof(WCHAR)] = L'\0';
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
break;
|
|
case SECPKG_ATTR_TARGET_INFORMATION:
|
|
|
|
TargetInformation = (PSecPkgContext_TargetInformation) Buffer;
|
|
|
|
if (TargetInformation == NULL)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
TargetInformation->MarshalledTargetInfo = NULL;
|
|
|
|
if (Context->pbMarshalledTargetInfo == NULL)
|
|
{
|
|
Status = STATUS_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
TargetInformation->MarshalledTargetInfo = (PUCHAR) KspKernelFunctions.AllocateHeap(
|
|
Context->cbMarshalledTargetInfo
|
|
);
|
|
|
|
if (TargetInformation->MarshalledTargetInfo != NULL)
|
|
{
|
|
RtlCopyMemory(
|
|
TargetInformation->MarshalledTargetInfo,
|
|
Context->pbMarshalledTargetInfo,
|
|
Context->cbMarshalledTargetInfo
|
|
);
|
|
|
|
TargetInformation->MarshalledTargetInfoLength = Context->cbMarshalledTargetInfo;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
break;
|
|
|
|
case SECPKG_ATTR_LIFESPAN:
|
|
LifespanInfo = (PSecPkgContext_Lifespan) Buffer;
|
|
//
|
|
// BUG 454552: set start time properly.
|
|
//
|
|
LifespanInfo->tsStart.QuadPart = 0;
|
|
LifespanInfo->tsExpiry = Context->Lifetime;
|
|
break;
|
|
case SECPKG_ATTR_FLAGS:
|
|
FlagsInfo = (PSecPkgContext_Flags) Buffer;
|
|
|
|
FlagsInfo->Flags = Context->ContextFlags;
|
|
break;
|
|
case SECPKG_ATTR_SESSION_KEY:
|
|
SessionKeyInfo = (PSecPkgContext_SessionKey) Buffer;
|
|
SessionKeyInfo->SessionKeyLength = Context->SessionKey.keyvalue.length;
|
|
if (SessionKeyInfo->SessionKeyLength != 0)
|
|
{
|
|
SessionKeyInfo->SessionKey = (PUCHAR) KspKernelFunctions.AllocateHeap(SessionKeyInfo->SessionKeyLength);
|
|
if (SessionKeyInfo->SessionKey != NULL)
|
|
{
|
|
RtlCopyMemory(
|
|
SessionKeyInfo->SessionKey,
|
|
Context->SessionKey.keyvalue.value,
|
|
Context->SessionKey.keyvalue.length
|
|
);
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
SessionKeyInfo->SessionKey = (PUCHAR) KspKernelFunctions.AllocateHeap(1);
|
|
if (SessionKeyInfo->SessionKey != NULL)
|
|
{
|
|
*(PUCHAR) SessionKeyInfo->SessionKey = 0;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SECPKG_ATTR_USER_FLAGS:
|
|
UserFlagsInfo = (PSecPkgContext_UserFlags) Buffer ;
|
|
UserFlagsInfo->UserFlags = 0 ;
|
|
Status = STATUS_SUCCESS ;
|
|
break;
|
|
|
|
case SECPKG_ATTR_PACKAGE_INFO:
|
|
case SECPKG_ATTR_NEGOTIATION_INFO:
|
|
//
|
|
// Return the information about this package. This is useful for
|
|
// callers who used SPNEGO and don't know what package they got.
|
|
//
|
|
|
|
PackageInfo = (PSecPkgContext_PackageInfo) Buffer;
|
|
PackageInfo->PackageInfo = (PSecPkgInfo) KspKernelFunctions.AllocateHeap(
|
|
sizeof(SecPkgInfo) +
|
|
sizeof(KERBEROS_PACKAGE_NAME) +
|
|
sizeof(KERBEROS_PACKAGE_COMMENT)
|
|
);
|
|
if (PackageInfo->PackageInfo == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
PackageInfo->PackageInfo->Name = (LPTSTR) (PackageInfo->PackageInfo + 1);
|
|
PackageInfo->PackageInfo->Comment = (LPTSTR) (((PBYTE) PackageInfo->PackageInfo->Name) + sizeof(KERBEROS_PACKAGE_NAME));
|
|
wcscpy(
|
|
PackageInfo->PackageInfo->Name,
|
|
KERBEROS_PACKAGE_NAME
|
|
);
|
|
|
|
wcscpy(
|
|
PackageInfo->PackageInfo->Comment,
|
|
KERBEROS_PACKAGE_COMMENT
|
|
);
|
|
PackageInfo->PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION;
|
|
PackageInfo->PackageInfo->wRPCID = KERBEROS_RPCID;
|
|
PackageInfo->PackageInfo->fCapabilities = KERBEROS_CAPABILITIES;
|
|
PackageInfo->PackageInfo->cbMaxToken = KerbMaxTokenSize;
|
|
if ( ContextAttribute == SECPKG_ATTR_NEGOTIATION_INFO )
|
|
{
|
|
PSecPkgContext_NegotiationInfo NegInfo ;
|
|
|
|
NegInfo = (PSecPkgContext_NegotiationInfo) PackageInfo ;
|
|
if( Context != NULL ) {
|
|
NegInfo->NegotiationState = SECPKG_NEGOTIATION_COMPLETE ;
|
|
} else {
|
|
NegInfo->NegotiationState = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
if (Context != NULL)
|
|
{
|
|
KerbDereferenceContext(Context);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCompleteToken
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS NTAPI
|
|
KerbCompleteToken(
|
|
IN LSA_SEC_HANDLE ContextId,
|
|
IN PSecBufferDesc Token
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
return(SEC_E_UNSUPPORTED_FUNCTION);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbExportContext(
|
|
IN LSA_SEC_HANDLE Context,
|
|
IN ULONG Flags,
|
|
OUT PSecBuffer PackedContext,
|
|
IN OUT PHANDLE TokenHandle
|
|
)
|
|
{
|
|
return(SEC_E_UNSUPPORTED_FUNCTION);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbImportContext(
|
|
IN PSecBuffer PackedContext,
|
|
IN OPTIONAL HANDLE TokenHandle,
|
|
OUT PLSA_SEC_HANDLE ContextHandle
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_KERNEL_CONTEXT Context = NULL;
|
|
|
|
PAGED_CODE();
|
|
DebugLog((DEB_TRACE,"KerbInitUserModeContext called\n"));
|
|
|
|
Status = KerbCreateKernelModeContext(
|
|
0, // LsaContextHandle not present
|
|
PackedContext,
|
|
&Context
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to create kernel mode context: 0x%x\n",
|
|
Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!KerbCryptInitialized)
|
|
{
|
|
KerbWriteLockGlobals();
|
|
if ( !KerbCryptInitialized )
|
|
{
|
|
if (LibAttach(NULL, NULL))
|
|
{
|
|
KerbCryptInitialized = TRUE;
|
|
}
|
|
}
|
|
KerbUnlockGlobals();
|
|
}
|
|
|
|
KerbWriteLockContexts();
|
|
|
|
Context->TokenHandle = TokenHandle;
|
|
*ContextHandle = KerbGetContextHandle(Context);
|
|
Context->ContextAttributes |= KERB_CONTEXT_IMPORTED;
|
|
|
|
KerbUnlockContexts();
|
|
|
|
Cleanup:
|
|
if (Context != NULL)
|
|
{
|
|
KerbDereferenceContext(Context);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
KerbSetPageMode(
|
|
BOOLEAN Pagable
|
|
)
|
|
{
|
|
if ( Pagable )
|
|
{
|
|
KerbPoolType = PagedPool ;
|
|
KerbActiveList = KerbPagedList ;
|
|
}
|
|
else
|
|
{
|
|
if ( KerbNonPagedList == NULL )
|
|
{
|
|
KerbNonPagedList = KSecCreateContextList( KSecNonPaged );
|
|
if ( KerbNonPagedList == NULL )
|
|
{
|
|
return STATUS_NO_MEMORY ;
|
|
}
|
|
}
|
|
|
|
KerbActiveList = KerbNonPagedList ;
|
|
|
|
KerbPoolType = NonPagedPool ;
|
|
}
|
|
return STATUS_SUCCESS ;
|
|
|
|
|
|
}
|