windows-nt/Source/XPSP1/NT/ds/security/protocols/kerberos/kernel/krnlapi.cxx
2020-09-26 16:20:57 +08:00

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 ;
}