3004 lines
83 KiB
C++
3004 lines
83 KiB
C++
//+-----------------------------------------------------------------------
|
||
//
|
||
// Microsoft Windows
|
||
//
|
||
// Copyright (c) Microsoft Corporation 1992 - 1999
|
||
//
|
||
// File: krnlapi.cxx
|
||
//
|
||
// Contents: Kernel-mode APIs to the NTLM package
|
||
//
|
||
//
|
||
// History: 07-Sep-1996 Created ChandanS
|
||
//
|
||
//------------------------------------------------------------------------
|
||
|
||
#include <ntlmkrnl.h>
|
||
extern "C"
|
||
{
|
||
#include <cryptdll.h>
|
||
#include <zwapi.h>
|
||
}
|
||
|
||
#include "crc32.h" // How to use crc32
|
||
|
||
extern "C"
|
||
{
|
||
#include <rc4.h> // How to use RC4 routine
|
||
#include <md5.h>
|
||
#include <hmac.h>
|
||
}
|
||
|
||
#define DEFENSIVE_HANDLES
|
||
|
||
|
||
// Context Signatures
|
||
|
||
#define NTLM_CONTEXT_SIGNATURE 'MLTN'
|
||
#define NTLM_CONTEXT_DELETED_SIGNATURE 'XXXX'
|
||
|
||
// Keep this is sync with NTLM_KERNEL_CONTEXT defined in
|
||
// security\msv_sspi\userapi.cxx
|
||
|
||
typedef struct _NTLM_KERNEL_CONTEXT{
|
||
KSEC_LIST_ENTRY List;
|
||
ULONG_PTR LsaContext;
|
||
ULONG NegotiateFlags;
|
||
HANDLE ClientTokenHandle;
|
||
PACCESS_TOKEN AccessToken;
|
||
|
||
PULONG pSendNonce; // ptr to nonce to use for send
|
||
PULONG pRecvNonce; // ptr to nonce to use for receive
|
||
struct RC4_KEYSTRUCT * pSealRc4Sched; // ptr to key sched used for Seal
|
||
struct RC4_KEYSTRUCT * pUnsealRc4Sched; // ptr to key sched used to Unseal
|
||
|
||
ULONG SendNonce;
|
||
ULONG RecvNonce;
|
||
LPWSTR ContextNames;
|
||
PUCHAR pbMarshalledTargetInfo;
|
||
ULONG cbMarshalledTargetInfo;
|
||
UCHAR SessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
|
||
ULONG ContextSignature;
|
||
ULONG References ;
|
||
TimeStamp PasswordExpiry;
|
||
ULONG UserFlags;
|
||
UCHAR SignSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
|
||
UCHAR VerifySessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
|
||
UCHAR SealSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
|
||
UCHAR UnsealSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
|
||
|
||
ULONG64 Pad1; // pad keystructs to 64.
|
||
struct RC4_KEYSTRUCT SealRc4Sched; // key struct used for Seal
|
||
ULONG64 Pad2; // pad keystructs to 64.
|
||
struct RC4_KEYSTRUCT UnsealRc4Sched; // key struct used to Unseal
|
||
} NTLM_KERNEL_CONTEXT, * PNTLM_KERNEL_CONTEXT;
|
||
|
||
typedef struct _NTLM_PACKED_CONTEXT {
|
||
ULONG Tag ;
|
||
ULONG NegotiateFlags ;
|
||
ULONG ClientTokenHandle ;
|
||
ULONG SendNonce ;
|
||
ULONG RecvNonce ;
|
||
UCHAR SessionKey[ MSV1_0_USER_SESSION_KEY_LENGTH ];
|
||
ULONG ContextSignature ;
|
||
TimeStamp PasswordExpiry ;
|
||
ULONG UserFlags ;
|
||
ULONG ContextNames ;
|
||
ULONG ContextNameLength ;
|
||
ULONG MarshalledTargetInfo; // offset
|
||
ULONG MarshalledTargetInfoLength;
|
||
UCHAR SignSessionKey[ MSV1_0_USER_SESSION_KEY_LENGTH ];
|
||
UCHAR VerifySessionKey[ MSV1_0_USER_SESSION_KEY_LENGTH ];
|
||
UCHAR SealSessionKey[ MSV1_0_USER_SESSION_KEY_LENGTH ];
|
||
UCHAR UnsealSessionKey[ MSV1_0_USER_SESSION_KEY_LENGTH ];
|
||
struct RC4_KEYSTRUCT SealRc4Sched;
|
||
struct RC4_KEYSTRUCT UnsealRc4Sched;
|
||
} NTLM_PACKED_CONTEXT, * PNTLM_PACKED_CONTEXT ;
|
||
|
||
|
||
#define NTLM_PACKED_CONTEXT_MAP 0
|
||
|
||
#define CSSEALMAGIC "session key to client-to-server sealing key magic constant"
|
||
#define SCSEALMAGIC "session key to server-to-client sealing key magic constant"
|
||
#define CSSIGNMAGIC "session key to client-to-server signing key magic constant"
|
||
#define SCSIGNMAGIC "session key to server-to-client signing key magic constant"
|
||
|
||
typedef enum _eSignSealOp {
|
||
eSign, // MakeSignature is calling
|
||
eVerify, // VerifySignature is calling
|
||
eSeal, // SealMessage is calling
|
||
eUnseal // UnsealMessage is calling
|
||
} eSignSealOp;
|
||
|
||
|
||
//
|
||
// Make these extern "C" to allow them to be pageable.
|
||
//
|
||
|
||
extern "C"
|
||
{
|
||
KspInitPackageFn NtLmInitKernelPackage;
|
||
KspDeleteContextFn NtLmDeleteKernelContext;
|
||
KspInitContextFn NtLmInitKernelContext;
|
||
KspMapHandleFn NtLmMapKernelHandle;
|
||
KspMakeSignatureFn NtLmMakeSignature;
|
||
KspVerifySignatureFn NtLmVerifySignature;
|
||
KspSealMessageFn NtLmSealMessage;
|
||
KspUnsealMessageFn NtLmUnsealMessage;
|
||
KspGetTokenFn NtLmGetContextToken;
|
||
KspQueryAttributesFn NtLmQueryContextAttributes;
|
||
KspCompleteTokenFn NtLmCompleteToken;
|
||
SpExportSecurityContextFn NtLmExportSecurityContext;
|
||
SpImportSecurityContextFn NtLmImportSecurityContext;
|
||
KspSetPagingModeFn NtlmSetPagingMode ;
|
||
|
||
//
|
||
// Local prototypes:
|
||
//
|
||
|
||
NTSTATUS
|
||
NtLmCreateKernelModeContext(
|
||
IN ULONG ContextHandle,
|
||
IN PSecBuffer MarshalledContext,
|
||
OUT PNTLM_KERNEL_CONTEXT * NewContext
|
||
);
|
||
|
||
NTSTATUS
|
||
NtLmMakePackedContext(
|
||
IN PNTLM_KERNEL_CONTEXT Context,
|
||
OUT PBOOLEAN MappedContext,
|
||
OUT PSecBuffer ContextData,
|
||
IN ULONG Flags
|
||
);
|
||
|
||
NTSTATUS
|
||
NtlmFreeKernelContext (
|
||
PNTLM_KERNEL_CONTEXT KernelContext
|
||
);
|
||
|
||
#define NtlmReferenceContext( Context, Remove ) \
|
||
KSecReferenceListEntry( (PKSEC_LIST_ENTRY) Context, \
|
||
NTLM_CONTEXT_SIGNATURE, \
|
||
Remove )
|
||
|
||
VOID
|
||
NtlmDerefContext(
|
||
PNTLM_KERNEL_CONTEXT Context
|
||
);
|
||
|
||
void
|
||
SspGenCheckSum(
|
||
IN PSecBuffer pMessage,
|
||
OUT PNTLMSSP_MESSAGE_SIGNATURE pSig
|
||
);
|
||
|
||
VOID
|
||
SspEncryptBuffer(
|
||
IN PNTLM_KERNEL_CONTEXT pContext,
|
||
IN struct RC4_KEYSTRUCT * pRc4Key,
|
||
IN ULONG BufferSize,
|
||
IN OUT PVOID Buffer
|
||
);
|
||
|
||
VOID
|
||
SspRc4Key(
|
||
IN ULONG NegotiateFlags,
|
||
OUT struct RC4_KEYSTRUCT *pRc4Key,
|
||
IN PUCHAR pSessionKey
|
||
);
|
||
|
||
SECURITY_STATUS
|
||
SspSignSealHelper(
|
||
IN PNTLM_KERNEL_CONTEXT pContext,
|
||
IN eSignSealOp Op,
|
||
IN OUT PSecBufferDesc pMessage,
|
||
IN ULONG MessageSeqNo,
|
||
OUT PNTLMSSP_MESSAGE_SIGNATURE pSig,
|
||
OUT PNTLMSSP_MESSAGE_SIGNATURE * ppSig
|
||
);
|
||
|
||
} // extern "C"
|
||
|
||
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtLmInitKernelPackage)
|
||
#pragma alloc_text(PAGE, NtLmDeleteKernelContext)
|
||
#pragma alloc_text(PAGE, NtLmInitKernelContext)
|
||
#pragma alloc_text(PAGE, NtLmMapKernelHandle)
|
||
#pragma alloc_text(PAGEMSG, NtLmMakeSignature)
|
||
#pragma alloc_text(PAGEMSG, NtLmVerifySignature)
|
||
#pragma alloc_text(PAGEMSG, NtLmSealMessage)
|
||
#pragma alloc_text(PAGEMSG, NtLmUnsealMessage)
|
||
#pragma alloc_text(PAGEMSG, NtLmGetContextToken)
|
||
#pragma alloc_text(PAGEMSG, NtLmQueryContextAttributes)
|
||
#pragma alloc_text(PAGEMSG, NtlmDerefContext )
|
||
#pragma alloc_text(PAGE, NtLmCompleteToken)
|
||
#pragma alloc_text(PAGE, NtLmExportSecurityContext)
|
||
#pragma alloc_text(PAGE, NtLmImportSecurityContext)
|
||
#pragma alloc_text(PAGEMSG, NtlmFreeKernelContext )
|
||
|
||
#pragma alloc_text(PAGE, NtLmCreateKernelModeContext )
|
||
#pragma alloc_text(PAGE, NtLmMakePackedContext )
|
||
|
||
#pragma alloc_text(PAGEMSG, SspGenCheckSum)
|
||
#pragma alloc_text(PAGEMSG, SspEncryptBuffer)
|
||
#pragma alloc_text(PAGE, SspRc4Key)
|
||
#pragma alloc_text(PAGEMSG, SspSignSealHelper)
|
||
|
||
#endif
|
||
|
||
SECPKG_KERNEL_FUNCTION_TABLE NtLmFunctionTable = {
|
||
NtLmInitKernelPackage,
|
||
NtLmDeleteKernelContext,
|
||
NtLmInitKernelContext,
|
||
NtLmMapKernelHandle,
|
||
NtLmMakeSignature,
|
||
NtLmVerifySignature,
|
||
NtLmSealMessage,
|
||
NtLmUnsealMessage,
|
||
NtLmGetContextToken,
|
||
NtLmQueryContextAttributes,
|
||
NtLmCompleteToken,
|
||
NtLmExportSecurityContext,
|
||
NtLmImportSecurityContext,
|
||
NtlmSetPagingMode
|
||
};
|
||
|
||
PSECPKG_KERNEL_FUNCTIONS LsaKernelFunctions;
|
||
POOL_TYPE NtlmPoolType ;
|
||
PVOID NtlmPagedList ;
|
||
PVOID NtlmNonPagedList ;
|
||
PVOID NtlmActiveList ;
|
||
|
||
#define MAYBE_PAGED_CODE() \
|
||
if ( NtlmPoolType == PagedPool ) \
|
||
{ \
|
||
PAGED_CODE(); \
|
||
}
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: FreeKernelContext
|
||
//
|
||
// Synopsis: frees alloced pointers in this context and
|
||
// then frees the context
|
||
//
|
||
// Arguments: KernelContext - the unlinked kernel context
|
||
//
|
||
// Returns: STATUS_SUCCESS on success
|
||
//
|
||
// Notes:
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
NTSTATUS
|
||
NtlmFreeKernelContext (
|
||
PNTLM_KERNEL_CONTEXT KernelContext
|
||
)
|
||
{
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
MAYBE_PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering FreeKernelContext\n" ));
|
||
|
||
if (KernelContext->ContextNames != NULL)
|
||
{
|
||
NtLmFree (KernelContext->ContextNames);
|
||
}
|
||
|
||
if (KernelContext->ClientTokenHandle != NULL)
|
||
{
|
||
NTSTATUS IgnoreStatus;
|
||
|
||
#ifdef DEFENSIVE_HANDLES
|
||
OBJECT_HANDLE_FLAG_INFORMATION HandleInfo ;
|
||
|
||
HandleInfo.Inherit = FALSE ;
|
||
HandleInfo.ProtectFromClose = FALSE ;
|
||
|
||
IgnoreStatus = ZwSetInformationObject(
|
||
KernelContext->ClientTokenHandle,
|
||
ObjectHandleFlagInformation,
|
||
&HandleInfo,
|
||
sizeof(HandleInfo) );
|
||
|
||
ASSERT( NT_SUCCESS( IgnoreStatus ) );
|
||
#endif
|
||
IgnoreStatus = NtClose( KernelContext->ClientTokenHandle);
|
||
ASSERT (NT_SUCCESS (IgnoreStatus));
|
||
}
|
||
|
||
if (KernelContext->AccessToken != NULL)
|
||
{
|
||
ObDereferenceObject (KernelContext->AccessToken);
|
||
}
|
||
|
||
DebugLog(( DEB_TRACE, "Deleting Context 0x%lx\n", KernelContext));
|
||
|
||
NtLmFree (KernelContext);
|
||
|
||
DebugLog(( DEB_TRACE, "Leaving FreeKernelContext: 0x%lx\n", Status ));
|
||
|
||
return Status;
|
||
}
|
||
|
||
//+---------------------------------------------------------------------------
|
||
//
|
||
// Function: NtlmDerefContext
|
||
//
|
||
// Synopsis: Dereference a kernel context
|
||
//
|
||
// Arguments: [Context] --
|
||
//
|
||
// History: 7-07-98 RichardW Created
|
||
//
|
||
// Notes:
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
NtlmDerefContext(
|
||
PNTLM_KERNEL_CONTEXT Context
|
||
)
|
||
{
|
||
BOOLEAN Delete ;
|
||
|
||
MAYBE_PAGED_CODE();
|
||
|
||
KSecDereferenceListEntry(
|
||
&Context->List,
|
||
&Delete );
|
||
|
||
if ( Delete )
|
||
{
|
||
NtlmFreeKernelContext( Context );
|
||
}
|
||
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmInitKernelPackage
|
||
//
|
||
// Synopsis: Initialize an instance of the NtLm package in
|
||
// a client's (kernel) address space
|
||
//
|
||
// Arguments: None
|
||
//
|
||
// Returns: STATUS_SUCCESS or
|
||
// returns from ExInitializeResource
|
||
//
|
||
// Notes: we do what was done in SpInstanceInit()
|
||
// from security\msv_sspi\userapi.cxx
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmInitKernelPackage(
|
||
IN PSECPKG_KERNEL_FUNCTIONS KernelFunctions
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmInitKernelPackage\n" ));
|
||
|
||
LsaKernelFunctions = KernelFunctions;
|
||
//
|
||
// Set up Context list support:
|
||
//
|
||
|
||
NtlmPoolType = PagedPool ;
|
||
NtlmPagedList = LsaKernelFunctions->CreateContextList( KSecPaged );
|
||
|
||
if ( !NtlmPagedList )
|
||
{
|
||
return STATUS_NO_MEMORY ;
|
||
}
|
||
|
||
NtlmActiveList = NtlmPagedList ;
|
||
|
||
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmInitKernelPackage 0x%lx\n", Status ));
|
||
return(Status);
|
||
}
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmDeleteKernelContext
|
||
//
|
||
// Synopsis: Deletes a kernel mode context by unlinking it and then
|
||
// dereferencing it.
|
||
//
|
||
// Effects:
|
||
//
|
||
// Arguments: KernelContextHandle - Kernel context handle of the context to delete
|
||
// LsaContextHandle - The Lsa mode handle
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns: STATUS_SUCCESS on success, STATUS_INVALID_HANDLE if the
|
||
// context can't be located
|
||
//
|
||
// Notes:
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmDeleteKernelContext(
|
||
IN ULONG_PTR KernelContextHandle,
|
||
OUT PULONG_PTR LsaContextHandle
|
||
)
|
||
{
|
||
|
||
PNTLM_KERNEL_CONTEXT pContext = NULL;
|
||
NTSTATUS Status = STATUS_SUCCESS, SaveStatus = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmDeleteKernelContext\n" ));
|
||
|
||
|
||
Status = NtlmReferenceContext( KernelContextHandle, TRUE );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
pContext = (PNTLM_KERNEL_CONTEXT) KernelContextHandle ;
|
||
|
||
}
|
||
else
|
||
{
|
||
*LsaContextHandle = KernelContextHandle;
|
||
DebugLog(( DEB_ERROR,
|
||
"Bad kernel context 0x%lx\n", KernelContextHandle));
|
||
goto CleanUp;
|
||
|
||
}
|
||
|
||
*LsaContextHandle = pContext->LsaContext;
|
||
if ((pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_EXPORTED_CONTEXT) != 0)
|
||
{
|
||
// Ignore all other errors and pass back
|
||
SaveStatus = SEC_I_NO_LSA_CONTEXT;
|
||
}
|
||
|
||
|
||
CleanUp:
|
||
|
||
|
||
if (pContext != NULL)
|
||
{
|
||
NtlmDerefContext( pContext );
|
||
|
||
}
|
||
|
||
if (SaveStatus == SEC_I_NO_LSA_CONTEXT)
|
||
{
|
||
Status = SaveStatus;
|
||
}
|
||
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmDeleteKernelContext 0x%lx\n", Status ));
|
||
return(Status);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmInitKernelContext
|
||
//
|
||
// Synopsis: Creates a kernel-mode context from a packed LSA mode context
|
||
//
|
||
// Arguments: LsaContextHandle - Lsa mode context handle for the context
|
||
// PackedContext - A marshalled buffer containing the LSA
|
||
// mode context.
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES
|
||
//
|
||
// Notes:
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmInitKernelContext(
|
||
IN ULONG_PTR LsaContextHandle,
|
||
IN PSecBuffer PackedContext,
|
||
OUT PULONG_PTR NewContextHandle
|
||
)
|
||
{
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PNTLM_KERNEL_CONTEXT pContext = NULL;
|
||
|
||
PNTLM_PACKED_CONTEXT pTmpContext = (PNTLM_PACKED_CONTEXT) PackedContext->pvBuffer;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmInitKernelContext\n" ));
|
||
|
||
*NewContextHandle = NULL;
|
||
|
||
if (PackedContext->cbBuffer < sizeof(NTLM_PACKED_CONTEXT))
|
||
{
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
DebugLog(( DEB_ERROR,
|
||
"Bad size of Packed context 0x%lx\n", PackedContext->cbBuffer));
|
||
goto Cleanup;
|
||
}
|
||
|
||
pContext = (PNTLM_KERNEL_CONTEXT) NtLmAllocate(
|
||
sizeof(NTLM_KERNEL_CONTEXT) +
|
||
pTmpContext->MarshalledTargetInfoLength
|
||
);
|
||
|
||
if (!pContext)
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
DebugLog(( DEB_ERROR, "Allocation error for pContext\n"));
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
pContext,
|
||
sizeof(NTLM_KERNEL_CONTEXT)
|
||
);
|
||
|
||
KsecInitializeListEntry( &pContext->List, NTLM_CONTEXT_SIGNATURE );
|
||
|
||
// Copy contents of PackedContext->pvBuffer to pContext
|
||
|
||
pContext->ClientTokenHandle = (HANDLE) ULongToPtr(pTmpContext->ClientTokenHandle);
|
||
#ifdef DEFENSIVE_HANDLES
|
||
if ( pContext->ClientTokenHandle )
|
||
{
|
||
OBJECT_HANDLE_FLAG_INFORMATION HandleInfo ;
|
||
POBJECT_TYPE_INFORMATION TypeInfo ;
|
||
NTSTATUS IgnoreStatus = STATUS_SUCCESS ;
|
||
UNICODE_STRING TokenTypeName ;
|
||
|
||
TypeInfo = (POBJECT_TYPE_INFORMATION) ExAllocatePoolWithTag( PagedPool, sizeof( OBJECT_TYPE_INFORMATION ) + 16, NTLM_KRNL_POOL_TAG );
|
||
|
||
if ( TypeInfo )
|
||
{
|
||
IgnoreStatus = ZwQueryObject(pContext->ClientTokenHandle,
|
||
ObjectTypeInformation,
|
||
TypeInfo,
|
||
(sizeof( OBJECT_TYPE_INFORMATION ) + 16 ),
|
||
NULL );
|
||
|
||
if ( NT_SUCCESS( IgnoreStatus ) )
|
||
{
|
||
RtlInitUnicodeString( &TokenTypeName, L"Token" );
|
||
|
||
if ( !RtlEqualUnicodeString( &TypeInfo->TypeName, &TokenTypeName, FALSE ))
|
||
{
|
||
IgnoreStatus = STATUS_INVALID_HANDLE ;
|
||
}
|
||
|
||
}
|
||
else if ( IgnoreStatus != STATUS_INVALID_HANDLE )
|
||
{
|
||
IgnoreStatus = STATUS_SUCCESS ;
|
||
}
|
||
|
||
ExFreePool( TypeInfo );
|
||
}
|
||
|
||
if ( !NT_SUCCESS( IgnoreStatus ) )
|
||
{
|
||
ASSERT( NT_SUCCESS( IgnoreStatus ) );
|
||
Status = IgnoreStatus ;
|
||
DebugLog(( DEB_ERROR, "Bad token handle from LSA: %p\n", pContext->ClientTokenHandle ));
|
||
goto Cleanup;
|
||
|
||
}
|
||
|
||
HandleInfo.Inherit = FALSE ;
|
||
HandleInfo.ProtectFromClose = TRUE ;
|
||
|
||
IgnoreStatus = ZwSetInformationObject(
|
||
pContext->ClientTokenHandle,
|
||
ObjectHandleFlagInformation,
|
||
&HandleInfo,
|
||
sizeof( HandleInfo ) );
|
||
|
||
}
|
||
#endif
|
||
pContext->LsaContext = LsaContextHandle;
|
||
pContext->NegotiateFlags = pTmpContext->NegotiateFlags;
|
||
|
||
//
|
||
// keep all 128 bits here, so signing can be strong even if encrypt can't be
|
||
//
|
||
|
||
RtlCopyMemory( pContext->SessionKey,
|
||
pTmpContext->SessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
|
||
//
|
||
// if doing full duplex as part of NTLM2, generate different sign
|
||
// and seal keys for each direction
|
||
// all we do is MD5 the base session key with a different magic constant
|
||
//
|
||
|
||
if ( pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2 ) {
|
||
MD5_CTX Md5Context;
|
||
ULONG KeyLen;
|
||
|
||
ASSERT(MD5DIGESTLEN == MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
|
||
if( pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_128 )
|
||
KeyLen = 16;
|
||
else if( pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_56 )
|
||
KeyLen = 7;
|
||
else
|
||
KeyLen = 5;
|
||
|
||
// DebugLog(( SSP_SESSION_KEYS, "NTLMv2 session key size: %lu\n", KeyLen));
|
||
|
||
//
|
||
// make client to server encryption key
|
||
//
|
||
|
||
MD5Init(&Md5Context);
|
||
MD5Update(&Md5Context, pContext->SessionKey, KeyLen);
|
||
MD5Update(&Md5Context, (unsigned char*)CSSEALMAGIC, sizeof(CSSEALMAGIC));
|
||
MD5Final(&Md5Context);
|
||
|
||
//
|
||
// if TokenHandle == NULL, this is the client side
|
||
// put key in the right place: for client it's seal, for server it's unseal
|
||
//
|
||
|
||
if (pContext->ClientTokenHandle == NULL)
|
||
RtlCopyMemory(pContext->SealSessionKey, Md5Context.digest, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
else
|
||
RtlCopyMemory(pContext->UnsealSessionKey, Md5Context.digest, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
|
||
//
|
||
// make server to client encryption key
|
||
//
|
||
|
||
MD5Init(&Md5Context);
|
||
MD5Update(&Md5Context, pContext->SessionKey, KeyLen);
|
||
MD5Update(&Md5Context, (unsigned char*)SCSEALMAGIC, sizeof(SCSEALMAGIC));
|
||
MD5Final(&Md5Context);
|
||
ASSERT(MD5DIGESTLEN == MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
if (pContext->ClientTokenHandle == NULL)
|
||
RtlCopyMemory(pContext->UnsealSessionKey, Md5Context.digest, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
else
|
||
RtlCopyMemory(pContext->SealSessionKey, Md5Context.digest, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
|
||
//
|
||
// make client to server signing key -- always 128 bits!
|
||
//
|
||
|
||
MD5Init(&Md5Context);
|
||
MD5Update(&Md5Context, pContext->SessionKey, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
MD5Update(&Md5Context, (unsigned char*)CSSIGNMAGIC, sizeof(CSSIGNMAGIC));
|
||
MD5Final(&Md5Context);
|
||
if (pContext->ClientTokenHandle == NULL)
|
||
RtlCopyMemory(pContext->SignSessionKey, Md5Context.digest, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
else
|
||
RtlCopyMemory(pContext->VerifySessionKey, Md5Context.digest, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
|
||
//
|
||
// make server to client signing key
|
||
//
|
||
|
||
MD5Init(&Md5Context);
|
||
MD5Update(&Md5Context, pContext->SessionKey, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
MD5Update(&Md5Context, (unsigned char*)SCSIGNMAGIC, sizeof(SCSIGNMAGIC));
|
||
MD5Final(&Md5Context);
|
||
if (pContext->ClientTokenHandle == NULL)
|
||
RtlCopyMemory(pContext->VerifySessionKey, Md5Context.digest, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
else
|
||
RtlCopyMemory(pContext->SignSessionKey, Md5Context.digest, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
|
||
//
|
||
// set pointers to different key schedule and nonce for each direction
|
||
// key schedule will be filled in later...
|
||
//
|
||
|
||
pContext->pSealRc4Sched = &pContext->SealRc4Sched;
|
||
pContext->pUnsealRc4Sched = &pContext->UnsealRc4Sched;
|
||
pContext->pSendNonce = &pContext->SendNonce;
|
||
pContext->pRecvNonce = &pContext->RecvNonce;
|
||
} else {
|
||
|
||
//
|
||
// just copy session key to all four keys
|
||
// leave them 128 bits -- they get cut to 40 bits later
|
||
//
|
||
|
||
RtlCopyMemory( pContext->SealSessionKey,
|
||
pContext->SessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
RtlCopyMemory( pContext->UnsealSessionKey,
|
||
pContext->SessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
RtlCopyMemory( pContext->SignSessionKey,
|
||
pContext->SessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
RtlCopyMemory( pContext->VerifySessionKey,
|
||
pContext->SessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
|
||
//
|
||
// set pointers to share a key schedule and nonce for each direction
|
||
// (OK because half duplex!)
|
||
//
|
||
|
||
pContext->pSealRc4Sched = &pContext->SealRc4Sched;
|
||
pContext->pUnsealRc4Sched = &pContext->SealRc4Sched;
|
||
pContext->pSendNonce = &pContext->SendNonce;
|
||
pContext->pRecvNonce = &pContext->SendNonce;
|
||
}
|
||
|
||
|
||
|
||
if ( pTmpContext->ContextNameLength == 0 )
|
||
{
|
||
//There's no string after the NTLM_KERNEL_CONTEXT struct
|
||
pContext->ContextNames = NULL;
|
||
}
|
||
else
|
||
{
|
||
pContext->ContextNames = (LPWSTR) NtLmAllocate( pTmpContext->ContextNameLength );
|
||
|
||
if (!pContext->ContextNames)
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory(pContext->ContextNames,
|
||
(PUCHAR) pTmpContext + pTmpContext->ContextNames,
|
||
pTmpContext->ContextNameLength );
|
||
|
||
}
|
||
|
||
if( pTmpContext->MarshalledTargetInfo )
|
||
{
|
||
pContext->pbMarshalledTargetInfo = (PUCHAR)(pContext+1);
|
||
pContext->cbMarshalledTargetInfo = pTmpContext->MarshalledTargetInfoLength;
|
||
|
||
RtlCopyMemory( pContext->pbMarshalledTargetInfo,
|
||
(PUCHAR) pTmpContext + pTmpContext->MarshalledTargetInfo,
|
||
pTmpContext->MarshalledTargetInfoLength );
|
||
}
|
||
|
||
|
||
pContext->SendNonce = pTmpContext->SendNonce;
|
||
pContext->RecvNonce = pTmpContext->RecvNonce;
|
||
|
||
SspRc4Key(pContext->NegotiateFlags, &pContext->SealRc4Sched, pContext->SealSessionKey);
|
||
SspRc4Key(pContext->NegotiateFlags, &pContext->UnsealRc4Sched, pContext->UnsealSessionKey);
|
||
|
||
|
||
pContext->PasswordExpiry = pTmpContext->PasswordExpiry;
|
||
pContext->UserFlags = pTmpContext->UserFlags;
|
||
|
||
KSecInsertListEntry(
|
||
NtlmActiveList,
|
||
&pContext->List );
|
||
|
||
NtlmDerefContext( pContext );
|
||
|
||
*NewContextHandle = (ULONG_PTR) pContext;
|
||
|
||
Cleanup:
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
if (pContext != NULL)
|
||
{
|
||
NtlmFreeKernelContext( pContext );
|
||
}
|
||
}
|
||
|
||
if (PackedContext->pvBuffer != NULL)
|
||
{
|
||
LsaKernelFunctions->FreeHeap(PackedContext->pvBuffer);
|
||
PackedContext->pvBuffer = NULL;
|
||
}
|
||
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmInitKernelContext 0x%lx\n", Status ));
|
||
return(Status);
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmMapKernelHandle
|
||
//
|
||
// Synopsis: Maps a kernel handle into an LSA handle
|
||
//
|
||
// Arguments: KernelContextHandle - Kernel context handle of the context to map
|
||
// LsaContextHandle - Receives LSA context handle of the context
|
||
// to map
|
||
//
|
||
// Returns: STATUS_SUCCESS on success
|
||
//
|
||
// Notes:
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmMapKernelHandle(
|
||
IN ULONG_PTR KernelContextHandle,
|
||
OUT PULONG_PTR LsaContextHandle
|
||
)
|
||
{
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PNTLM_KERNEL_CONTEXT Context = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugLog((DEB_TRACE,"Entering NtLmMapKernelhandle\n"));
|
||
|
||
Status = NtlmReferenceContext( KernelContextHandle, FALSE );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
Context = (PNTLM_KERNEL_CONTEXT) KernelContextHandle ;
|
||
|
||
*LsaContextHandle = Context->LsaContext ;
|
||
|
||
NtlmDerefContext( Context );
|
||
|
||
}
|
||
else
|
||
{
|
||
DebugLog(( DEB_WARN, "Invalid context handle - %x\n",
|
||
KernelContextHandle ));
|
||
*LsaContextHandle = KernelContextHandle ;
|
||
}
|
||
|
||
DebugLog((DEB_TRACE,"Leaving NtLmMapKernelhandle 0x%lx\n", Status));
|
||
|
||
return (Status);
|
||
}
|
||
|
||
|
||
//
|
||
// Bogus add-shift check sum
|
||
//
|
||
|
||
void
|
||
SspGenCheckSum(
|
||
IN PSecBuffer pMessage,
|
||
OUT PNTLMSSP_MESSAGE_SIGNATURE pSig
|
||
)
|
||
|
||
/*++
|
||
|
||
RoutineDescription:
|
||
|
||
Generate a crc-32 checksum for a buffer
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
Notes: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
|
||
routine SspGenCheckSum. It's possible that
|
||
bugs got copied too
|
||
|
||
--*/
|
||
|
||
{
|
||
MAYBE_PAGED_CODE();
|
||
|
||
Crc32(pSig->CheckSum,pMessage->cbBuffer,pMessage->pvBuffer,&pSig->CheckSum);
|
||
}
|
||
|
||
|
||
VOID
|
||
SspEncryptBuffer(
|
||
IN PNTLM_KERNEL_CONTEXT pContext,
|
||
IN struct RC4_KEYSTRUCT * pRc4Key,
|
||
IN ULONG BufferSize,
|
||
IN OUT PVOID Buffer
|
||
)
|
||
|
||
/*++
|
||
|
||
RoutineDescription:
|
||
|
||
Encrypts a buffer with the RC4 key in the context. If the context
|
||
is for a datagram session, then the key is copied before being used
|
||
to encrypt the buffer.
|
||
|
||
Arguments:
|
||
|
||
pContext - Context containing the key to encrypt the data
|
||
|
||
BufferSize - Length of buffer in bytes
|
||
|
||
Buffer - Buffer to encrypt.
|
||
Notes: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
|
||
routine SspEncryptBuffer. It's possible that
|
||
bugs got copied too
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
{
|
||
MAYBE_PAGED_CODE();
|
||
|
||
struct RC4_KEYSTRUCT TemporaryKey;
|
||
struct RC4_KEYSTRUCT * EncryptionKey = pRc4Key;
|
||
|
||
if (BufferSize == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
//
|
||
// For datagram (application supplied sequence numbers) before NTLM2
|
||
// we used to copy the key before encrypting so we don't
|
||
// have a changing key; but that reused the key stream. Now we only
|
||
// do that when backwards compatibility is explicitly called for.
|
||
//
|
||
|
||
if (((pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) != 0) &&
|
||
((pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) == 0) ) {
|
||
|
||
RtlCopyMemory(
|
||
&TemporaryKey,
|
||
EncryptionKey,
|
||
sizeof(TemporaryKey)
|
||
);
|
||
EncryptionKey = &TemporaryKey;
|
||
|
||
}
|
||
|
||
rc4(
|
||
EncryptionKey,
|
||
BufferSize,
|
||
(PUCHAR) Buffer
|
||
);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SspRc4Key(
|
||
IN ULONG NegotiateFlags,
|
||
OUT struct RC4_KEYSTRUCT *pRc4Key,
|
||
IN PUCHAR pSessionKey
|
||
)
|
||
/*++
|
||
|
||
RoutineDescription:
|
||
|
||
Create an RC4 key schedule, making sure key length is OK for export
|
||
|
||
Arguments:
|
||
|
||
NegotiateFlags negotiate feature flags; NTLM2 bit is only one looked at
|
||
pRc4Key pointer to RC4 key schedule structure; filled in by this routine
|
||
pSessionKey pointer to session key -- must be full 16 bytes
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// For NTLM2, effective length was already cut down
|
||
//
|
||
|
||
if ((NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) != 0) {
|
||
|
||
rc4_key(pRc4Key, MSV1_0_USER_SESSION_KEY_LENGTH, pSessionKey);
|
||
|
||
} else if( NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY ) {
|
||
UCHAR Key[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
|
||
ULONG KeyLen;
|
||
|
||
ASSERT(MSV1_0_LANMAN_SESSION_KEY_LENGTH == 8);
|
||
|
||
// prior to Win2k, negotiated key strength had no bearing on
|
||
// key size. So, to allow proper interop to NT4, we don't
|
||
// worry about 128bit. 56bit and 40bit are the only supported options.
|
||
// 56bit is enabled because this was introduced in Win2k, and
|
||
// Win2k -> Win2k interops correctly.
|
||
//
|
||
#if 0
|
||
if( NegotiateFlags & NTLMSSP_NEGOTIATE_128 ) {
|
||
KeyLen = 8;
|
||
|
||
} else
|
||
#endif
|
||
if( NegotiateFlags & NTLMSSP_NEGOTIATE_56 ) {
|
||
KeyLen = 7;
|
||
|
||
//
|
||
// Put a well-known salt at the end of the key to
|
||
// limit the changing part to 56 bits.
|
||
//
|
||
|
||
Key[7] = 0xa0;
|
||
} else {
|
||
KeyLen = 5;
|
||
|
||
//
|
||
// Put a well-known salt at the end of the key to
|
||
// limit the changing part to 40 bits.
|
||
//
|
||
|
||
Key[5] = 0xe5;
|
||
Key[6] = 0x38;
|
||
Key[7] = 0xb0;
|
||
}
|
||
|
||
/// DebugLog(( SSP_SESSION_KEYS, "Non NTLMv2 session key size: %lu\n", KeyLen));
|
||
|
||
RtlCopyMemory(Key,pSessionKey,KeyLen);
|
||
|
||
rc4_key(pRc4Key, MSV1_0_LANMAN_SESSION_KEY_LENGTH, Key);
|
||
} else {
|
||
rc4_key(pRc4Key, MSV1_0_USER_SESSION_KEY_LENGTH, pSessionKey);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
SECURITY_STATUS
|
||
SspSignSealHelper(
|
||
IN PNTLM_KERNEL_CONTEXT pContext,
|
||
IN eSignSealOp Op,
|
||
IN OUT PSecBufferDesc pMessage,
|
||
IN ULONG MessageSeqNo,
|
||
OUT PNTLMSSP_MESSAGE_SIGNATURE pSig,
|
||
OUT PNTLMSSP_MESSAGE_SIGNATURE * ppSig
|
||
)
|
||
/*++
|
||
|
||
RoutineDescription:
|
||
|
||
Handle signing a message
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
HMACMD5_CTX HMACMD5Context;
|
||
UCHAR TempSig[MD5DIGESTLEN];
|
||
NTLMSSP_MESSAGE_SIGNATURE Sig;
|
||
int Signature;
|
||
ULONG i;
|
||
PUCHAR pKey; // ptr to key to use for encryption
|
||
PUCHAR pSignKey; // ptr to key to use for signing
|
||
PULONG pNonce; // ptr to nonce to use
|
||
struct RC4_KEYSTRUCT * pRc4Sched; // ptr to key schedule to use
|
||
|
||
NTLMSSP_MESSAGE_SIGNATURE AlignedSig;
|
||
|
||
|
||
MAYBE_PAGED_CODE();
|
||
|
||
|
||
|
||
Signature = -1;
|
||
for (i = 0; i < pMessage->cBuffers; i++)
|
||
{
|
||
if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
|
||
{
|
||
Signature = i;
|
||
break;
|
||
}
|
||
}
|
||
if (Signature == -1)
|
||
{
|
||
return(SEC_E_INVALID_TOKEN);
|
||
}
|
||
|
||
if (pMessage->pBuffers[Signature].cbBuffer < NTLMSSP_MESSAGE_SIGNATURE_SIZE)
|
||
{
|
||
return(SEC_E_INVALID_TOKEN);
|
||
}
|
||
|
||
*ppSig = (NTLMSSP_MESSAGE_SIGNATURE*)pMessage->pBuffers[Signature].pvBuffer;
|
||
|
||
RtlCopyMemory( &AlignedSig, *ppSig, sizeof(AlignedSig) );
|
||
|
||
//
|
||
// If sequence detect wasn't requested, put on an empty
|
||
// security token . Don't do the check if Seal/Unseal is called.
|
||
//
|
||
|
||
if (!(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) &&
|
||
(Op == eSign || Op == eVerify))
|
||
{
|
||
RtlZeroMemory(pSig,NTLMSSP_MESSAGE_SIGNATURE_SIZE);
|
||
pSig->Version = NTLM_SIGN_VERSION;
|
||
return(SEC_E_OK);
|
||
}
|
||
|
||
// figure out which key, key schedule, and nonce to use
|
||
// depends on the op. SspAddLocalContext set up so that code on client
|
||
// and server just (un)seals with (un)seal key or key schedule, etc.
|
||
// and also sets pointers to share sending/receiving key schedule/nonce
|
||
// when in half duplex mode. Hence, this code gets to act as if it were
|
||
// always in full duplex mode.
|
||
switch (Op) {
|
||
case eSeal:
|
||
pSignKey = pContext->SignSessionKey; // if NTLM2
|
||
pKey = pContext->SealSessionKey;
|
||
pRc4Sched = pContext->pSealRc4Sched;
|
||
pNonce = pContext->pSendNonce;
|
||
break;
|
||
case eUnseal:
|
||
pSignKey = pContext->VerifySessionKey; // if NTLM2
|
||
pKey = pContext->UnsealSessionKey;
|
||
pRc4Sched = pContext->pUnsealRc4Sched;
|
||
pNonce = pContext->pRecvNonce;
|
||
break;
|
||
case eSign:
|
||
pSignKey = pContext->SignSessionKey; // if NTLM2
|
||
pKey = pContext->SealSessionKey; // might be used to encrypt the signature
|
||
pRc4Sched = pContext->pSealRc4Sched;
|
||
pNonce = pContext->pSendNonce;
|
||
break;
|
||
case eVerify:
|
||
pSignKey = pContext->VerifySessionKey; // if NTLM2
|
||
pKey = pContext->UnsealSessionKey; // might be used to decrypt the signature
|
||
pRc4Sched = pContext->pUnsealRc4Sched;
|
||
pNonce = pContext->pRecvNonce;
|
||
break;
|
||
default:
|
||
ASSERT(FALSE);
|
||
return(STATUS_INVALID_LEVEL);
|
||
}
|
||
|
||
//
|
||
// Either we can supply the sequence number, or
|
||
// the application can supply the message sequence number.
|
||
//
|
||
|
||
Sig.Version = NTLM_SIGN_VERSION;
|
||
|
||
// if we're doing the new NTLM2 version:
|
||
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||
|
||
if ((pContext->NegotiateFlags & NTLMSSP_APP_SEQ) == 0)
|
||
{
|
||
Sig.Nonce = *pNonce; // use our sequence number
|
||
(*pNonce) += 1;
|
||
}
|
||
else {
|
||
|
||
if (Op == eSeal || Op == eSign || MessageSeqNo != 0)
|
||
Sig.Nonce = MessageSeqNo;
|
||
else
|
||
Sig.Nonce = AlignedSig.Nonce;
|
||
|
||
// if using RC4, must rekey for each packet
|
||
// RC4 is used for seal, unseal; and for encrypting the HMAC hash if
|
||
// key exchange was negotiated (we use just HMAC if no key exchange,
|
||
// so that a good signing option exists with no RC4 encryption needed)
|
||
|
||
if (Op == eSeal || Op == eUnseal || pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
|
||
{
|
||
MD5_CTX Md5ContextReKey;
|
||
|
||
MD5Init(&Md5ContextReKey);
|
||
MD5Update(&Md5ContextReKey, pKey, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
MD5Update(&Md5ContextReKey, (unsigned char*)&Sig.Nonce, sizeof(Sig.Nonce));
|
||
MD5Final(&Md5ContextReKey);
|
||
ASSERT(MD5DIGESTLEN == MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
SspRc4Key(pContext->NegotiateFlags, pRc4Sched, Md5ContextReKey.digest);
|
||
}
|
||
}
|
||
|
||
//
|
||
// using HMAC hash, init it with the key
|
||
//
|
||
|
||
HMACMD5Init(&HMACMD5Context, pSignKey, MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
|
||
//
|
||
// include the message sequence number
|
||
//
|
||
|
||
HMACMD5Update(&HMACMD5Context, (unsigned char*)&Sig.Nonce, sizeof(Sig.Nonce));
|
||
|
||
for (i = 0; i < pMessage->cBuffers ; i++ )
|
||
{
|
||
if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
|
||
(pMessage->pBuffers[i].cbBuffer != 0))
|
||
{
|
||
if ((pMessage->pBuffers[i].BufferType & (SECBUFFER_READONLY | SECBUFFER_READONLY_WITH_CHECKSUM))
|
||
== (SECBUFFER_READONLY | SECBUFFER_READONLY_WITH_CHECKSUM))
|
||
{
|
||
//
|
||
// FESTER: return INVALID_TOKEN because of data buffers
|
||
//
|
||
|
||
return SEC_E_INVALID_TOKEN;
|
||
}
|
||
|
||
// decrypt (before checksum...) if it's not READ_ONLY
|
||
if ( (Op == eUnseal)
|
||
&& !(pMessage->pBuffers[i].BufferType & (SECBUFFER_READONLY | SECBUFFER_READONLY_WITH_CHECKSUM) )
|
||
)
|
||
{
|
||
SspEncryptBuffer(
|
||
pContext,
|
||
pRc4Sched,
|
||
pMessage->pBuffers[i].cbBuffer,
|
||
pMessage->pBuffers[i].pvBuffer
|
||
);
|
||
}
|
||
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
(unsigned char*)pMessage->pBuffers[i].pvBuffer,
|
||
pMessage->pBuffers[i].cbBuffer);
|
||
|
||
//
|
||
// Encrypt if its not READ_ONLY
|
||
//
|
||
|
||
if ( (Op == eSeal)
|
||
&& !(pMessage->pBuffers[i].BufferType & (SECBUFFER_READONLY | SECBUFFER_READONLY_WITH_CHECKSUM) )
|
||
)
|
||
{
|
||
SspEncryptBuffer(
|
||
pContext,
|
||
pRc4Sched,
|
||
pMessage->pBuffers[i].cbBuffer,
|
||
pMessage->pBuffers[i].pvBuffer
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
HMACMD5Final(&HMACMD5Context, TempSig);
|
||
|
||
//
|
||
// use RandomPad and Checksum fields for 8 bytes of MD5 hash
|
||
//
|
||
|
||
RtlCopyMemory(&Sig.RandomPad, TempSig, 8);
|
||
|
||
//
|
||
// if we're using crypto for KEY_EXCH, may as well use it for signing too...
|
||
//
|
||
|
||
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
|
||
SspEncryptBuffer(
|
||
pContext,
|
||
pRc4Sched,
|
||
8,
|
||
&Sig.RandomPad
|
||
);
|
||
}
|
||
//
|
||
// pre-NTLM2 methods
|
||
//
|
||
else {
|
||
|
||
//
|
||
// required by CRC-32 algorithm
|
||
//
|
||
Sig.CheckSum = 0xffffffff;
|
||
|
||
for (i = 0; i < pMessage->cBuffers ; i++ )
|
||
{
|
||
if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA)
|
||
&& (pMessage->pBuffers[i].cbBuffer != 0))
|
||
{
|
||
if ((pMessage->pBuffers[i].BufferType & (SECBUFFER_READONLY | SECBUFFER_READONLY_WITH_CHECKSUM))
|
||
== (SECBUFFER_READONLY | SECBUFFER_READONLY_WITH_CHECKSUM))
|
||
{
|
||
//
|
||
// FESTER: return invalid token because of data buffers
|
||
//
|
||
|
||
return SEC_E_INVALID_TOKEN;
|
||
}
|
||
|
||
//
|
||
// retain the "read-only" semantics for NTLMv1
|
||
//
|
||
|
||
if (pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
// decrypt (before checksum...)
|
||
if ( (Op == eUnseal)
|
||
&& !(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM) )
|
||
{
|
||
SspEncryptBuffer(
|
||
pContext,
|
||
pRc4Sched,
|
||
pMessage->pBuffers[i].cbBuffer,
|
||
pMessage->pBuffers[i].pvBuffer
|
||
);
|
||
}
|
||
|
||
SspGenCheckSum(&pMessage->pBuffers[i], &Sig);
|
||
|
||
// Encrypt
|
||
if ( (Op == eSeal)
|
||
&& !(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM) )
|
||
{
|
||
SspEncryptBuffer(
|
||
pContext,
|
||
pRc4Sched,
|
||
pMessage->pBuffers[i].cbBuffer,
|
||
pMessage->pBuffers[i].pvBuffer
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Required by CRC-32 algorithm
|
||
//
|
||
|
||
Sig.CheckSum ^= 0xffffffff;
|
||
|
||
// when we encrypt 0, we will get the cipher stream for the nonce!
|
||
Sig.Nonce = 0;
|
||
|
||
SspEncryptBuffer(
|
||
pContext,
|
||
pRc4Sched,
|
||
sizeof(NTLMSSP_MESSAGE_SIGNATURE) - sizeof(ULONG),
|
||
&Sig.RandomPad
|
||
);
|
||
|
||
|
||
if ((pContext->NegotiateFlags & NTLMSSP_APP_SEQ) == 0)
|
||
{
|
||
Sig.Nonce ^= *pNonce; // use our sequence number and encrypt it
|
||
(*pNonce) += 1;
|
||
}
|
||
else if (Op == eSeal || Op == eSign || MessageSeqNo != 0)
|
||
Sig.Nonce ^= MessageSeqNo; // use caller's sequence number and encrypt it
|
||
else
|
||
Sig.Nonce = AlignedSig.Nonce; // use sender's sequence number
|
||
|
||
//
|
||
// for SignMessage calling, does nothing (copies garbage)
|
||
// For VerifyMessage calling, allows it to compare sig block
|
||
// upon return to Verify without knowing whether its MD5 or CRC32
|
||
//
|
||
|
||
Sig.RandomPad = AlignedSig.RandomPad;
|
||
}
|
||
|
||
pMessage->pBuffers[Signature].cbBuffer = sizeof(NTLMSSP_MESSAGE_SIGNATURE);
|
||
|
||
RtlCopyMemory(
|
||
pSig,
|
||
&Sig,
|
||
NTLMSSP_MESSAGE_SIGNATURE_SIZE
|
||
);
|
||
|
||
return(SEC_E_OK);
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmMakeSignature
|
||
//
|
||
// 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: KernelContextHandle - 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: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
|
||
// routine SspHandleSignMessage. It's possible that
|
||
// bugs got copied too
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmMakeSignature(
|
||
IN ULONG_PTR KernelContextHandle,
|
||
IN ULONG fQOP,
|
||
IN PSecBufferDesc pMessage,
|
||
IN ULONG MessageSeqNo
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PNTLM_KERNEL_CONTEXT pContext;
|
||
NTLMSSP_MESSAGE_SIGNATURE Sig;
|
||
NTLMSSP_MESSAGE_SIGNATURE *pSig;
|
||
|
||
MAYBE_PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmMakeSignature\n" ));
|
||
|
||
UNREFERENCED_PARAMETER(fQOP);
|
||
|
||
Status = NtlmReferenceContext( KernelContextHandle, FALSE );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
pContext = (PNTLM_KERNEL_CONTEXT) KernelContextHandle ;
|
||
}
|
||
else
|
||
{
|
||
DebugLog(( DEB_ERROR,
|
||
"Bad kernel context 0x%lx\n", KernelContextHandle));
|
||
goto CleanUp_NoDeref;
|
||
|
||
}
|
||
|
||
|
||
Status = SspSignSealHelper(
|
||
pContext,
|
||
eSign,
|
||
pMessage,
|
||
MessageSeqNo,
|
||
&Sig,
|
||
&pSig
|
||
);
|
||
|
||
|
||
if( !NT_SUCCESS( Status ) )
|
||
{
|
||
DebugLog(( DEB_ERROR, "NtLmMakeSignature, SspSignSealHelper returns %lx\n", Status ));
|
||
goto CleanUp;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
pSig,
|
||
&Sig,
|
||
NTLMSSP_MESSAGE_SIGNATURE_SIZE
|
||
);
|
||
|
||
CleanUp:
|
||
|
||
NtlmDerefContext( pContext );
|
||
|
||
CleanUp_NoDeref:
|
||
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmMakeSignature 0x%lx\n", Status ));
|
||
return(Status);
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmVerifySignature
|
||
//
|
||
// 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: KernelContextHandle - 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: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
|
||
// routine SspHandleVerifyMessage. It's possible that
|
||
// bugs got copied too
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmVerifySignature(
|
||
IN ULONG_PTR KernelContextHandle,
|
||
IN PSecBufferDesc pMessage,
|
||
IN ULONG MessageSeqNo,
|
||
OUT PULONG pfQOP
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PNTLM_KERNEL_CONTEXT pContext;
|
||
|
||
NTLMSSP_MESSAGE_SIGNATURE Sig;
|
||
PNTLMSSP_MESSAGE_SIGNATURE pSig; // pointer to buffer with sig in it
|
||
NTLMSSP_MESSAGE_SIGNATURE AlignedSig; // Aligned sig buffer.
|
||
|
||
MAYBE_PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmVerifySignature\n" ));
|
||
|
||
UNREFERENCED_PARAMETER(pfQOP);
|
||
|
||
Status = NtlmReferenceContext( KernelContextHandle, FALSE );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
pContext = (PNTLM_KERNEL_CONTEXT) KernelContextHandle ;
|
||
}
|
||
else
|
||
{
|
||
DebugLog(( DEB_ERROR,
|
||
"Bad kernel context 0x%lx\n", KernelContextHandle));
|
||
goto CleanUp_NoDeref;
|
||
|
||
}
|
||
|
||
|
||
Status = SspSignSealHelper(
|
||
pContext,
|
||
eVerify,
|
||
pMessage,
|
||
MessageSeqNo,
|
||
&Sig,
|
||
&pSig
|
||
);
|
||
|
||
|
||
if( !NT_SUCCESS( Status ) )
|
||
{
|
||
DebugLog(( DEB_ERROR, "NtLmVerifySignature, SspSignSealHelper returns %lx\n", Status ));
|
||
goto CleanUp;
|
||
}
|
||
|
||
RtlCopyMemory( &AlignedSig, pSig, sizeof( AlignedSig ) );
|
||
|
||
if (AlignedSig.Version != NTLM_SIGN_VERSION) {
|
||
Status = SEC_E_INVALID_TOKEN;
|
||
goto CleanUp;
|
||
}
|
||
|
||
// validate the signature...
|
||
if (AlignedSig.CheckSum != Sig.CheckSum)
|
||
{
|
||
Status = SEC_E_MESSAGE_ALTERED;
|
||
goto CleanUp;
|
||
}
|
||
|
||
// with MD5 sig, this now matters!
|
||
if (AlignedSig.RandomPad != Sig.RandomPad)
|
||
{
|
||
Status = SEC_E_MESSAGE_ALTERED;
|
||
goto CleanUp;
|
||
}
|
||
|
||
if (AlignedSig.Nonce != Sig.Nonce)
|
||
{
|
||
Status = SEC_E_OUT_OF_SEQUENCE;
|
||
goto CleanUp;
|
||
}
|
||
|
||
CleanUp:
|
||
|
||
NtlmDerefContext( pContext );
|
||
|
||
CleanUp_NoDeref:
|
||
|
||
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmVerifySignature 0x%lx\n", Status ));
|
||
return(Status);
|
||
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmSealMessage
|
||
//
|
||
// 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: KernelContextHandle - 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: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
|
||
// routine SspHandleSealMessage. It's possible that
|
||
// bugs got copied too
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmSealMessage(
|
||
IN ULONG_PTR KernelContextHandle,
|
||
IN ULONG fQOP,
|
||
IN PSecBufferDesc pMessage,
|
||
IN ULONG MessageSeqNo
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PNTLM_KERNEL_CONTEXT pContext;
|
||
NTLMSSP_MESSAGE_SIGNATURE Sig;
|
||
PNTLMSSP_MESSAGE_SIGNATURE pSig; // pointer to buffer where sig goes
|
||
|
||
MAYBE_PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmSealMessage\n" ));
|
||
|
||
UNREFERENCED_PARAMETER(fQOP);
|
||
|
||
Status = NtlmReferenceContext( KernelContextHandle, FALSE );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
pContext = (PNTLM_KERNEL_CONTEXT) KernelContextHandle ;
|
||
}
|
||
else
|
||
{
|
||
DebugLog(( DEB_ERROR,
|
||
"Bad kernel context 0x%lx\n", KernelContextHandle));
|
||
goto CleanUp_NoDeref;
|
||
|
||
}
|
||
|
||
|
||
Status = SspSignSealHelper(
|
||
pContext,
|
||
eSeal,
|
||
pMessage,
|
||
MessageSeqNo,
|
||
&Sig,
|
||
&pSig
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
DebugLog(( DEB_ERROR, "SpSealMessage, SspSignSealHelper returns %lx\n", Status ));
|
||
goto CleanUp;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
pSig,
|
||
&Sig,
|
||
NTLMSSP_MESSAGE_SIGNATURE_SIZE
|
||
);
|
||
|
||
|
||
CleanUp:
|
||
|
||
NtlmDerefContext( pContext );
|
||
|
||
CleanUp_NoDeref:
|
||
|
||
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmSealMessage 0x%lx\n", Status ));
|
||
return(Status);
|
||
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmUnsealMessage
|
||
//
|
||
// 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: KernelContextHandle - 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: This was stolen from net\svcdlls\ntlmssp\client\sign.c ,
|
||
// routine SspHandleUnsealMessage. It's possible that
|
||
// bugs got copied too
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmUnsealMessage(
|
||
IN ULONG_PTR KernelContextHandle,
|
||
IN PSecBufferDesc pMessage,
|
||
IN ULONG MessageSeqNo,
|
||
OUT PULONG pfQOP
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PNTLM_KERNEL_CONTEXT pContext;
|
||
|
||
NTLMSSP_MESSAGE_SIGNATURE Sig;
|
||
PNTLMSSP_MESSAGE_SIGNATURE pSig; // pointer to buffer where sig goes
|
||
NTLMSSP_MESSAGE_SIGNATURE AlignedSig; // aligned buffer.
|
||
|
||
MAYBE_PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmUnsealMessage\n" ));
|
||
|
||
UNREFERENCED_PARAMETER(pfQOP);
|
||
|
||
Status = NtlmReferenceContext( KernelContextHandle, FALSE );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
pContext = (PNTLM_KERNEL_CONTEXT) KernelContextHandle ;
|
||
}
|
||
else
|
||
{
|
||
DebugLog(( DEB_ERROR,
|
||
"Bad kernel context 0x%lx\n", KernelContextHandle));
|
||
goto CleanUp_NoDeref;
|
||
|
||
}
|
||
|
||
Status = SspSignSealHelper(
|
||
pContext,
|
||
eUnseal,
|
||
pMessage,
|
||
MessageSeqNo,
|
||
&Sig,
|
||
&pSig
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
DebugLog(( DEB_ERROR, "SpUnsealMessage, SspSignSealHelper returns %lx\n", Status ));
|
||
goto CleanUp;
|
||
}
|
||
|
||
RtlCopyMemory( &AlignedSig, pSig, sizeof(AlignedSig) );
|
||
|
||
if (AlignedSig.Version != NTLM_SIGN_VERSION) {
|
||
Status = SEC_E_INVALID_TOKEN;
|
||
goto CleanUp;
|
||
}
|
||
|
||
// validate the signature...
|
||
if (AlignedSig.CheckSum != Sig.CheckSum)
|
||
{
|
||
Status = SEC_E_MESSAGE_ALTERED;
|
||
goto CleanUp;
|
||
}
|
||
|
||
if (AlignedSig.RandomPad != Sig.RandomPad)
|
||
{
|
||
Status = SEC_E_MESSAGE_ALTERED;
|
||
goto CleanUp;
|
||
}
|
||
|
||
if (AlignedSig.Nonce != Sig.Nonce)
|
||
{
|
||
Status = SEC_E_OUT_OF_SEQUENCE;
|
||
goto CleanUp;
|
||
}
|
||
|
||
|
||
CleanUp:
|
||
|
||
NtlmDerefContext( pContext );
|
||
|
||
CleanUp_NoDeref:
|
||
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmUnsealMessage 0x%lx\n", Status ));
|
||
return (Status);
|
||
}
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmGetContextToken
|
||
//
|
||
// Synopsis: returns a pointer to the token for a server-side context
|
||
//
|
||
// Effects:
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns:
|
||
//
|
||
// Notes:
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmGetContextToken(
|
||
IN ULONG_PTR KernelContextHandle,
|
||
OUT PHANDLE ImpersonationToken,
|
||
OUT OPTIONAL PACCESS_TOKEN *RawToken
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PNTLM_KERNEL_CONTEXT pContext = NULL;
|
||
|
||
MAYBE_PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmGetContextToken\n" ));
|
||
|
||
Status = NtlmReferenceContext( KernelContextHandle, FALSE );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
pContext = (PNTLM_KERNEL_CONTEXT) KernelContextHandle ;
|
||
}
|
||
else
|
||
{
|
||
DebugLog(( DEB_ERROR,
|
||
"Bad kernel context 0x%lx\n", KernelContextHandle));
|
||
goto CleanUp_NoDeref;
|
||
|
||
}
|
||
|
||
if (pContext->ClientTokenHandle == NULL)
|
||
{
|
||
DebugLog(( DEB_ERROR, "Invalid TokenHandle for context 0x%lx\n", pContext ));
|
||
Status= SEC_E_NO_IMPERSONATION;
|
||
goto CleanUp;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(ImpersonationToken))
|
||
{
|
||
*ImpersonationToken = pContext->ClientTokenHandle;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(RawToken))
|
||
{
|
||
if (pContext->ClientTokenHandle != NULL)
|
||
{
|
||
if (pContext->AccessToken == NULL)
|
||
{
|
||
Status = ObReferenceObjectByHandle(
|
||
pContext->ClientTokenHandle,
|
||
TOKEN_IMPERSONATE | TOKEN_QUERY,
|
||
NULL,
|
||
ExGetPreviousMode(),
|
||
(PVOID *) &pContext->AccessToken,
|
||
NULL);
|
||
|
||
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(Status))
|
||
{
|
||
ASSERT(pContext->AccessToken != NULL);
|
||
*RawToken = pContext->AccessToken;
|
||
}
|
||
}
|
||
|
||
|
||
CleanUp:
|
||
|
||
NtlmDerefContext( pContext );
|
||
|
||
CleanUp_NoDeref:
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmGetContextToken 0x%lx\n", Status ));
|
||
return (Status);
|
||
}
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmQueryContextAttributes
|
||
//
|
||
// Synopsis: Querys attributes of the specified context
|
||
// This API allows a customer of the security
|
||
// services to determine certain attributes of
|
||
// the context. These are: sizes, names, and lifespan.
|
||
//
|
||
// Effects:
|
||
//
|
||
// Arguments:
|
||
//
|
||
// ContextHandle - Handle to the context to query.
|
||
//
|
||
// Attribute - Attribute to query.
|
||
//
|
||
// #define SECPKG_ATTR_SIZES 0
|
||
// #define SECPKG_ATTR_NAMES 1
|
||
// #define SECPKG_ATTR_LIFESPAN 2
|
||
//
|
||
// Buffer - Buffer to copy the data into. The buffer must
|
||
// be large enough to fit the queried attribute.
|
||
//
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns:
|
||
//
|
||
// STATUS_SUCCESS - Call completed successfully
|
||
//
|
||
// STATUS_INVALID_HANDLE -- Credential/Context Handle is invalid
|
||
// STATUS_UNSUPPORTED_FUNCTION -- Function code is not supported
|
||
//
|
||
// Notes:
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
|
||
NTSTATUS NTAPI
|
||
NtLmQueryContextAttributes(
|
||
IN ULONG_PTR KernelContextHandle,
|
||
IN ULONG Attribute,
|
||
IN OUT PVOID Buffer
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PSecPkgContext_NamesW ContextNames = NULL;
|
||
PSecPkgContext_DceInfo ContextDceInfo = NULL;
|
||
PSecPkgContext_SessionKey ContextSessionKeyInfo = NULL;
|
||
PSecPkgContext_Sizes ContextSizes = NULL;
|
||
PSecPkgContext_Flags ContextFlags = NULL;
|
||
PSecPkgContext_PasswordExpiry PasswordExpires;
|
||
PSecPkgContext_UserFlags UserFlags;
|
||
PSecPkgContext_PackageInfo PackageInfo = NULL;
|
||
PSecPkgContext_TargetInformation TargetInformation = NULL;
|
||
|
||
PNTLM_KERNEL_CONTEXT pContext = NULL;
|
||
unsigned int Length = 0;
|
||
|
||
MAYBE_PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmQueryContextAttributes\n" ));
|
||
|
||
|
||
|
||
Status = NtlmReferenceContext( KernelContextHandle, FALSE );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
pContext = (PNTLM_KERNEL_CONTEXT) KernelContextHandle ;
|
||
} else {
|
||
|
||
//
|
||
// for PACKAGE_INFO or NEGOTIATION_INFO, don't require a completed
|
||
// context.
|
||
//
|
||
|
||
if( (Attribute != SECPKG_ATTR_PACKAGE_INFO) &&
|
||
(Attribute != SECPKG_ATTR_NEGOTIATION_INFO)
|
||
)
|
||
{
|
||
DebugLog(( DEB_ERROR,
|
||
"Bad kernel context 0x%lx\n", KernelContextHandle));
|
||
goto CleanUp_NoDeref;
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Handle each of the various queried attributes
|
||
//
|
||
|
||
switch ( Attribute ) {
|
||
case SECPKG_ATTR_SIZES:
|
||
|
||
ContextSizes = (PSecPkgContext_Sizes) Buffer;
|
||
ContextSizes->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE;
|
||
|
||
if (pContext->NegotiateFlags & (NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
|
||
NTLMSSP_NEGOTIATE_SIGN |
|
||
NTLMSSP_NEGOTIATE_SEAL) ) {
|
||
ContextSizes->cbMaxSignature = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
||
} else {
|
||
ContextSizes->cbMaxSignature = 0;
|
||
}
|
||
|
||
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL) {
|
||
ContextSizes->cbBlockSize = 1;
|
||
ContextSizes->cbSecurityTrailer = NTLMSSP_MESSAGE_SIGNATURE_SIZE;
|
||
}
|
||
else
|
||
{
|
||
ContextSizes->cbBlockSize = 0;
|
||
ContextSizes->cbSecurityTrailer = 0;
|
||
}
|
||
|
||
break;
|
||
|
||
//
|
||
// No one uses the function so don't go to the overhead of maintaining
|
||
// the username in the context structure.
|
||
//
|
||
|
||
case SECPKG_ATTR_DCE_INFO:
|
||
|
||
ContextDceInfo = (PSecPkgContext_DceInfo) Buffer;
|
||
|
||
if (ContextDceInfo == NULL)
|
||
{
|
||
DebugLog(( DEB_ERROR, "Null buffer SECPKG_ATTR_DCE_INFO.\n" ));
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if (pContext->ContextNames)
|
||
{
|
||
Length = (unsigned int) wcslen(pContext->ContextNames);
|
||
}
|
||
|
||
ContextDceInfo->pPac = (LPWSTR) LsaKernelFunctions->AllocateHeap(
|
||
(Length + 1) * sizeof(WCHAR));
|
||
|
||
if (ContextDceInfo->pPac != NULL)
|
||
{
|
||
RtlCopyMemory(
|
||
ContextDceInfo->pPac,
|
||
pContext->ContextNames,
|
||
Length * sizeof(WCHAR));
|
||
|
||
LPWSTR Temp = (LPWSTR)ContextDceInfo->pPac;
|
||
Temp[Length] = L'\0';
|
||
}
|
||
else
|
||
{
|
||
DebugLog(( DEB_ERROR, "Bad Context->pPac in SECPKG_ATTR_DCE_INFO.\n" ));
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
ContextDceInfo->AuthzSvc = 0;
|
||
|
||
break;
|
||
|
||
case SECPKG_ATTR_NAMES:
|
||
|
||
ContextNames = (PSecPkgContext_Names) Buffer;
|
||
|
||
if (ContextNames == NULL)
|
||
{
|
||
DebugLog(( DEB_ERROR, "Null buffer SECPKG_ATTR_NAMES.\n" ));
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if (pContext->ContextNames)
|
||
{
|
||
Length = (unsigned int) wcslen(pContext->ContextNames);
|
||
DebugLog(( DEB_TRACE, "NtLmQueryContextAttributes: ContextNames length is 0x%lx\n", Length));
|
||
}
|
||
|
||
ContextNames->sUserName = (LPWSTR) LsaKernelFunctions->AllocateHeap(
|
||
(Length + 1) * sizeof(WCHAR));
|
||
|
||
if (ContextNames->sUserName != NULL)
|
||
{
|
||
RtlCopyMemory(
|
||
ContextNames->sUserName,
|
||
pContext->ContextNames,
|
||
Length * sizeof(WCHAR));
|
||
|
||
ContextNames->sUserName[Length] = L'\0';
|
||
}
|
||
else
|
||
{
|
||
DebugLog(( DEB_ERROR, "Bad Context->sUserName in SECPKG_ATTR_NAMES.\n" ));
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
break;
|
||
|
||
case SECPKG_ATTR_TARGET_INFORMATION:
|
||
|
||
TargetInformation = (PSecPkgContext_TargetInformation) Buffer;
|
||
|
||
if (TargetInformation == NULL)
|
||
{
|
||
DebugLog(( DEB_ERROR, "Null buffer SECPKG_ATTR_TARGET_INFORMATION.\n" ));
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
TargetInformation->MarshalledTargetInfo = NULL;
|
||
|
||
if (pContext->pbMarshalledTargetInfo == NULL)
|
||
{
|
||
Status = STATUS_NOT_FOUND;
|
||
goto Cleanup;
|
||
}
|
||
|
||
Length = pContext->cbMarshalledTargetInfo;
|
||
DebugLog(( DEB_TRACE, "NtLmQueryContextAttributes: TargetInformation length is 0x%lx\n", Length));
|
||
|
||
TargetInformation->MarshalledTargetInfo = (PUCHAR) LsaKernelFunctions->AllocateHeap(
|
||
Length
|
||
);
|
||
|
||
if (TargetInformation->MarshalledTargetInfo != NULL)
|
||
{
|
||
RtlCopyMemory(
|
||
TargetInformation->MarshalledTargetInfo,
|
||
pContext->pbMarshalledTargetInfo,
|
||
Length
|
||
);
|
||
|
||
TargetInformation->MarshalledTargetInfoLength = Length;
|
||
}
|
||
else
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
break;
|
||
|
||
case SECPKG_ATTR_SESSION_KEY:
|
||
ContextSessionKeyInfo = (PSecPkgContext_SessionKey) Buffer;
|
||
ContextSessionKeyInfo->SessionKeyLength = MSV1_0_USER_SESSION_KEY_LENGTH;
|
||
ContextSessionKeyInfo->SessionKey =
|
||
(PUCHAR) LsaKernelFunctions->AllocateHeap(
|
||
ContextSessionKeyInfo->SessionKeyLength);
|
||
if (ContextSessionKeyInfo->SessionKey != NULL)
|
||
{
|
||
RtlCopyMemory(
|
||
ContextSessionKeyInfo->SessionKey,
|
||
pContext->SessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
}
|
||
else
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
break;
|
||
|
||
case SECPKG_ATTR_PASSWORD_EXPIRY:
|
||
PasswordExpires = (PSecPkgContext_PasswordExpiry) Buffer;
|
||
if(pContext->PasswordExpiry.QuadPart != 0) {
|
||
PasswordExpires->tsPasswordExpires = pContext->PasswordExpiry;
|
||
} else {
|
||
Status = SEC_E_UNSUPPORTED_FUNCTION;
|
||
}
|
||
break;
|
||
|
||
case SECPKG_ATTR_USER_FLAGS:
|
||
UserFlags = (PSecPkgContext_UserFlags) Buffer;
|
||
UserFlags->UserFlags = pContext->UserFlags;
|
||
break;
|
||
|
||
case SECPKG_ATTR_FLAGS:
|
||
{
|
||
BOOLEAN Client = (pContext->ClientTokenHandle == 0);
|
||
ULONG Flags = 0;
|
||
|
||
ContextFlags = (PSecPkgContext_Flags) Buffer;
|
||
ContextFlags->Flags = 0;
|
||
|
||
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_SEAL) {
|
||
if( Client )
|
||
{
|
||
Flags |= ISC_RET_CONFIDENTIALITY;
|
||
} else {
|
||
Flags |= ASC_RET_CONFIDENTIALITY;
|
||
}
|
||
}
|
||
|
||
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) {
|
||
if( Client )
|
||
{
|
||
Flags |= ISC_RET_SEQUENCE_DETECT | ISC_RET_REPLAY_DETECT | ISC_RET_INTEGRITY;
|
||
} else {
|
||
Flags |= ASC_RET_SEQUENCE_DETECT | ASC_RET_REPLAY_DETECT | ASC_RET_INTEGRITY;
|
||
}
|
||
}
|
||
|
||
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NULL_SESSION) {
|
||
if( Client )
|
||
{
|
||
Flags |= ISC_RET_NULL_SESSION;
|
||
} else {
|
||
Flags |= ASC_RET_NULL_SESSION;
|
||
}
|
||
}
|
||
|
||
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) {
|
||
if( Client )
|
||
{
|
||
Flags |= ISC_RET_DATAGRAM;
|
||
} else {
|
||
Flags |= ASC_RET_DATAGRAM;
|
||
}
|
||
}
|
||
|
||
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_IDENTIFY) {
|
||
if( Client )
|
||
{
|
||
Flags |= ISC_RET_IDENTIFY;
|
||
} else {
|
||
Flags |= ASC_RET_IDENTIFY;
|
||
}
|
||
}
|
||
|
||
ContextFlags->Flags |= Flags;
|
||
|
||
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 = (PSecPkgInfoW) LsaKernelFunctions->AllocateHeap(
|
||
sizeof(SecPkgInfoW) +
|
||
sizeof(NTLMSP_NAME) +
|
||
sizeof(NTLMSP_COMMENT)
|
||
);
|
||
|
||
if (PackageInfo->PackageInfo == NULL)
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
PackageInfo->PackageInfo->Name = (LPWSTR) (PackageInfo->PackageInfo + 1);
|
||
PackageInfo->PackageInfo->Comment = (LPWSTR) ((((PCHAR) PackageInfo->PackageInfo->Name)) + sizeof(NTLMSP_NAME));
|
||
wcscpy(
|
||
PackageInfo->PackageInfo->Name,
|
||
NTLMSP_NAME
|
||
);
|
||
|
||
wcscpy(
|
||
PackageInfo->PackageInfo->Comment,
|
||
NTLMSP_COMMENT
|
||
);
|
||
PackageInfo->PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION;
|
||
PackageInfo->PackageInfo->wRPCID = NTLMSP_RPCID;
|
||
PackageInfo->PackageInfo->fCapabilities = NTLMSP_CAPS;
|
||
PackageInfo->PackageInfo->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE;
|
||
|
||
if ( Attribute == SECPKG_ATTR_NEGOTIATION_INFO )
|
||
{
|
||
PSecPkgContext_NegotiationInfo NegInfo ;
|
||
|
||
NegInfo = (PSecPkgContext_NegotiationInfo) PackageInfo ;
|
||
if( pContext ) {
|
||
NegInfo->NegotiationState = SECPKG_NEGOTIATION_COMPLETE ;
|
||
} else {
|
||
NegInfo->NegotiationState = 0;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case SECPKG_ATTR_LIFESPAN:
|
||
default:
|
||
|
||
Status = STATUS_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
|
||
Cleanup:
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
switch ( Attribute) {
|
||
|
||
case SECPKG_ATTR_NAMES:
|
||
|
||
if (ContextNames && ContextNames->sUserName)
|
||
{
|
||
LsaKernelFunctions->FreeHeap(ContextNames->sUserName);
|
||
ContextNames->sUserName = NULL;
|
||
}
|
||
break;
|
||
|
||
case SECPKG_ATTR_DCE_INFO:
|
||
|
||
if (ContextDceInfo && ContextDceInfo->pPac)
|
||
{
|
||
LsaKernelFunctions->FreeHeap(ContextDceInfo->pPac);
|
||
ContextDceInfo->pPac = NULL;
|
||
}
|
||
break;
|
||
|
||
case SECPKG_ATTR_SESSION_KEY:
|
||
|
||
if(ContextSessionKeyInfo && ContextSessionKeyInfo->SessionKey)
|
||
{
|
||
LsaKernelFunctions->FreeHeap(ContextSessionKeyInfo->SessionKey);
|
||
ContextSessionKeyInfo->SessionKey = NULL;
|
||
}
|
||
break;
|
||
|
||
case SECPKG_ATTR_NEGOTIATION_INFO:
|
||
|
||
if(PackageInfo && PackageInfo->PackageInfo)
|
||
{
|
||
LsaKernelFunctions->FreeHeap(PackageInfo->PackageInfo);
|
||
PackageInfo->PackageInfo = NULL;
|
||
}
|
||
break;
|
||
|
||
}
|
||
}
|
||
|
||
if( pContext ) {
|
||
NtlmDerefContext( pContext );
|
||
}
|
||
|
||
CleanUp_NoDeref:
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmQueryContextAttributes 0x%lx\n", Status ));
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmCompleteToken
|
||
//
|
||
// Synopsis: Completes a context
|
||
//
|
||
// Effects:
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns:
|
||
//
|
||
// Notes:
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
NTSTATUS NTAPI
|
||
NtLmCompleteToken(
|
||
IN ULONG_PTR ContextHandle,
|
||
IN PSecBufferDesc InputBuffer
|
||
)
|
||
{
|
||
UNREFERENCED_PARAMETER (ContextHandle);
|
||
UNREFERENCED_PARAMETER (InputBuffer);
|
||
PAGED_CODE();
|
||
DebugLog(( DEB_TRACE, "Entering NtLmCompleteToken\n" ));
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmCompleteToken\n" ));
|
||
return(STATUS_NOT_SUPPORTED);
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmMakePackedContext
|
||
//
|
||
// Synopsis: Maps a context to the caller's address space
|
||
//
|
||
// Effects:
|
||
//
|
||
// Arguments: Context - The context to map
|
||
// MappedContext - Set to TRUE on success
|
||
// ContextData - Receives a buffer in the caller's address space
|
||
// with the mapped context.
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns:
|
||
//
|
||
// Notes:
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
|
||
NTSTATUS
|
||
NtLmMakePackedContext(
|
||
IN PNTLM_KERNEL_CONTEXT Context,
|
||
OUT PBOOLEAN MappedContext,
|
||
OUT PSecBuffer ContextData,
|
||
IN ULONG Flags
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PNTLM_PACKED_CONTEXT PackedContext = NULL;
|
||
ULONG ContextSize, ContextNameSize = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
if (Context->ContextNames)
|
||
{
|
||
ContextNameSize = (ULONG) wcslen(Context->ContextNames);
|
||
}
|
||
|
||
|
||
ContextSize = sizeof(NTLM_PACKED_CONTEXT) +
|
||
ContextNameSize * sizeof(WCHAR);
|
||
|
||
PackedContext = (PNTLM_PACKED_CONTEXT) NtLmAllocate(ContextSize);
|
||
|
||
if (PackedContext == NULL)
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlZeroMemory( PackedContext, ContextSize );
|
||
|
||
#if 0
|
||
// Copy all fields of the old context
|
||
|
||
|
||
RtlCopyMemory(
|
||
PackedContext,
|
||
Context,
|
||
sizeof(NTLM_KERNEL_CONTEXT)
|
||
);
|
||
|
||
|
||
if (ContextNameSize > 0)
|
||
{
|
||
PackedContext->ContextNames = (LPWSTR) sizeof(NTLM_PACKED_CONTEXT);
|
||
|
||
RtlCopyMemory(
|
||
PackedContext+1,
|
||
Context->ContextNames,
|
||
ContextNameSize * sizeof(WCHAR));
|
||
}
|
||
else
|
||
{
|
||
PackedContext->ContextNames=NULL;
|
||
}
|
||
|
||
|
||
|
||
// Replace some fields
|
||
|
||
//
|
||
// Token will be returned by the caller of this routine
|
||
//
|
||
|
||
PackedContext->ClientTokenHandle = NULL;
|
||
|
||
PackedContext->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXPORTED_CONTEXT;
|
||
|
||
if ((Flags & SECPKG_CONTEXT_EXPORT_RESET_NEW) != 0)
|
||
{
|
||
PackedContext->SendNonce = (ULONG) -1;
|
||
PackedContext->RecvNonce = (ULONG) -1;
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
&PackedContext->SessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH
|
||
);
|
||
#endif
|
||
|
||
|
||
|
||
|
||
PackedContext->Tag = NTLM_PACKED_CONTEXT_MAP ;
|
||
PackedContext->NegotiateFlags = Context->NegotiateFlags ;
|
||
PackedContext->SendNonce = Context->SendNonce ;
|
||
PackedContext->RecvNonce = Context->RecvNonce ;
|
||
RtlCopyMemory(
|
||
PackedContext->SessionKey,
|
||
Context->SessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH );
|
||
|
||
PackedContext->ContextSignature = Context->ContextSignature ;
|
||
PackedContext->PasswordExpiry = Context->PasswordExpiry ;
|
||
PackedContext->UserFlags = Context->UserFlags ;
|
||
if ( ContextNameSize )
|
||
{
|
||
PackedContext->ContextNames = sizeof( NTLM_PACKED_CONTEXT );
|
||
PackedContext->ContextNameLength = (ContextNameSize + 1) * sizeof( WCHAR ) ;
|
||
|
||
RtlCopyMemory(
|
||
(PackedContext + 1),
|
||
Context->ContextNames,
|
||
PackedContext->ContextNameLength );
|
||
|
||
}
|
||
else
|
||
{
|
||
PackedContext->ContextNames = 0 ;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
PackedContext->SignSessionKey,
|
||
Context->SignSessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH );
|
||
|
||
RtlCopyMemory(
|
||
PackedContext->VerifySessionKey,
|
||
Context->VerifySessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH );
|
||
|
||
RtlCopyMemory(
|
||
PackedContext->SealSessionKey,
|
||
Context->SealSessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH );
|
||
|
||
RtlCopyMemory(
|
||
PackedContext->UnsealSessionKey,
|
||
Context->SealSessionKey,
|
||
MSV1_0_USER_SESSION_KEY_LENGTH );
|
||
|
||
RtlCopyMemory(
|
||
&PackedContext->SealRc4Sched,
|
||
&Context->SealRc4Sched,
|
||
sizeof( struct RC4_KEYSTRUCT ) );
|
||
|
||
RtlCopyMemory(
|
||
&PackedContext->UnsealRc4Sched,
|
||
&Context->UnsealRc4Sched,
|
||
sizeof( struct RC4_KEYSTRUCT ) );
|
||
|
||
|
||
// Replace some fields
|
||
|
||
|
||
//
|
||
// Token will be returned by the caller of this routine
|
||
//
|
||
|
||
PackedContext->ClientTokenHandle = 0 ;
|
||
|
||
// Save the fact that it's exported
|
||
PackedContext->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXPORTED_CONTEXT;
|
||
|
||
if ((Flags & SECPKG_CONTEXT_EXPORT_RESET_NEW) != 0)
|
||
{
|
||
PackedContext->SendNonce = (ULONG) -1;
|
||
PackedContext->RecvNonce = (ULONG) -1;
|
||
}
|
||
|
||
|
||
|
||
ContextData->pvBuffer = PackedContext;
|
||
ContextData->cbBuffer = ContextSize;
|
||
|
||
|
||
|
||
*MappedContext = TRUE;
|
||
|
||
Cleanup:
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
if (PackedContext != NULL)
|
||
{
|
||
NtLmFree(PackedContext);
|
||
}
|
||
}
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function:
|
||
//
|
||
// Synopsis:
|
||
//
|
||
// Effects:
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns:
|
||
//
|
||
// Notes:
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
NtLmExportSecurityContext(
|
||
IN ULONG_PTR ContextHandle,
|
||
IN ULONG Flags,
|
||
OUT PSecBuffer PackedContext,
|
||
IN OUT PHANDLE TokenHandle
|
||
)
|
||
{
|
||
PNTLM_KERNEL_CONTEXT Context = NULL;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
BOOLEAN MappedContext = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugLog(( DEB_TRACE, "Entering NtLmExportSecurityContext\n" ));
|
||
|
||
if (ARGUMENT_PRESENT(TokenHandle))
|
||
{
|
||
*TokenHandle = NULL;
|
||
}
|
||
|
||
PackedContext->pvBuffer = NULL;
|
||
PackedContext->cbBuffer = 0;
|
||
PackedContext->BufferType = 0;
|
||
|
||
Status = NtlmReferenceContext( ContextHandle, FALSE );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
Context = (PNTLM_KERNEL_CONTEXT) ContextHandle ;
|
||
}
|
||
else
|
||
{
|
||
goto Cleanup_NoDeref ;
|
||
}
|
||
|
||
Status = NtLmMakePackedContext(
|
||
Context,
|
||
&MappedContext,
|
||
PackedContext,
|
||
Flags
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
ASSERT(MappedContext);
|
||
|
||
//
|
||
// Now either duplicate the token or copy it.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(TokenHandle))
|
||
{
|
||
if ((Flags & SECPKG_CONTEXT_EXPORT_DELETE_OLD) != 0)
|
||
{
|
||
*TokenHandle = Context->ClientTokenHandle;
|
||
Context->ClientTokenHandle = NULL;
|
||
}
|
||
else
|
||
{
|
||
Status = NtDuplicateObject(
|
||
NtCurrentProcess(),
|
||
Context->ClientTokenHandle,
|
||
NULL,
|
||
TokenHandle,
|
||
0, // no new access
|
||
0, // no handle attributes
|
||
DUPLICATE_SAME_ACCESS
|
||
);
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
|
||
}
|
||
|
||
Cleanup:
|
||
|
||
NtlmDerefContext( Context );
|
||
|
||
Cleanup_NoDeref:
|
||
|
||
return (Status);
|
||
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NtLmCreateKernelModeContext
|
||
//
|
||
// Synopsis: Creates a kernel-mode context to support impersonation and
|
||
// message integrity and privacy
|
||
//
|
||
// Effects:
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns:
|
||
//
|
||
// Notes:
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
NtLmCreateKernelModeContext(
|
||
IN ULONG_PTR ContextHandle,
|
||
IN OPTIONAL HANDLE TokenHandle,
|
||
IN PSecBuffer MarshalledContext,
|
||
OUT PNTLM_KERNEL_CONTEXT * NewContext
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PNTLM_KERNEL_CONTEXT Context = NULL;
|
||
PNTLM_PACKED_CONTEXT PackedContext;
|
||
unsigned int Length = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (MarshalledContext->cbBuffer < sizeof(NTLM_PACKED_CONTEXT))
|
||
{
|
||
DebugLog((DEB_ERROR,"NtLmCreateKernelModeContext: Invalid buffer size for marshalled context: was 0x%x, needed 0x%x\n",
|
||
MarshalledContext->cbBuffer, sizeof(NTLM_PACKED_CONTEXT)));
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
PackedContext = (PNTLM_PACKED_CONTEXT) MarshalledContext->pvBuffer;
|
||
|
||
Context = (PNTLM_KERNEL_CONTEXT) NtLmAllocate( sizeof(NTLM_KERNEL_CONTEXT));
|
||
if (!Context)
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
DebugLog((DEB_ERROR,"NtLmCreateKernelModeContext: Allocation error for Context\n"));
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
Context,
|
||
sizeof(NTLM_KERNEL_CONTEXT));
|
||
|
||
// Copy contenets of PackedContext->pvBuffer to Context
|
||
//// *Context = *PackedContext;
|
||
|
||
Context->NegotiateFlags = PackedContext->NegotiateFlags;
|
||
//// Context->ClientTokenHandle = (HANDLE)PackedContext->ClientTokenHandle;
|
||
Context->ContextSignature = PackedContext->ContextSignature;
|
||
|
||
|
||
RtlCopyMemory( Context->SessionKey, PackedContext->SessionKey, sizeof(PackedContext->SessionKey) );
|
||
|
||
RtlCopyMemory( Context->SignSessionKey, PackedContext->SignSessionKey, sizeof(PackedContext->SignSessionKey) );
|
||
RtlCopyMemory( Context->VerifySessionKey, PackedContext->VerifySessionKey, sizeof(PackedContext->VerifySessionKey) );
|
||
RtlCopyMemory( Context->SealSessionKey, PackedContext->SealSessionKey, sizeof(PackedContext->SealSessionKey) );
|
||
RtlCopyMemory( Context->UnsealSessionKey, PackedContext->UnsealSessionKey, sizeof(PackedContext->UnsealSessionKey) );
|
||
|
||
RtlCopyMemory( &Context->SealRc4Sched, &PackedContext->SealRc4Sched, sizeof(PackedContext->SealRc4Sched) );
|
||
RtlCopyMemory( &Context->UnsealRc4Sched, &PackedContext->UnsealRc4Sched, sizeof(PackedContext->UnsealRc4Sched) );
|
||
|
||
|
||
KsecInitializeListEntry( &Context->List, NTLM_CONTEXT_SIGNATURE );
|
||
|
||
|
||
// These need to be changed
|
||
|
||
Context->ClientTokenHandle = TokenHandle;
|
||
|
||
if (Context->SendNonce == (ULONG) -1)
|
||
{
|
||
// The context was exported with the reset flag
|
||
Context->SendNonce = 0;
|
||
}
|
||
|
||
if (Context->RecvNonce == (ULONG) -1)
|
||
{
|
||
// The context was exported with the reset flag
|
||
Context->RecvNonce = 0;
|
||
}
|
||
|
||
if( Context->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2 ) {
|
||
|
||
Context->pSealRc4Sched = &Context->SealRc4Sched;
|
||
Context->pUnsealRc4Sched = &Context->UnsealRc4Sched;
|
||
Context->pSendNonce = &Context->SendNonce;
|
||
Context->pRecvNonce = &Context->RecvNonce;
|
||
} else {
|
||
|
||
Context->pSealRc4Sched = &Context->SealRc4Sched;
|
||
Context->pUnsealRc4Sched = &Context->SealRc4Sched;
|
||
Context->pSendNonce = &Context->SendNonce;
|
||
Context->pRecvNonce = &Context->SendNonce;
|
||
}
|
||
|
||
|
||
Context->ContextNames = NULL;
|
||
|
||
Length = (MarshalledContext->cbBuffer - sizeof(NTLM_PACKED_CONTEXT));
|
||
if (Length > 0)
|
||
{
|
||
Context->ContextNames = (LPWSTR) NtLmAllocate(Length + sizeof(WCHAR));
|
||
if (!Context->ContextNames)
|
||
{
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
RtlCopyMemory(Context->ContextNames, PackedContext + 1, Length);
|
||
// null terminate the string
|
||
*(Context->ContextNames + (Length/2)) = UNICODE_NULL;
|
||
}
|
||
|
||
Context->PasswordExpiry = PackedContext->PasswordExpiry;
|
||
Context->UserFlags = PackedContext->UserFlags;
|
||
|
||
KSecInsertListEntry(
|
||
NtlmActiveList,
|
||
&Context->List );
|
||
|
||
*NewContext = Context;
|
||
|
||
Cleanup:
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
if (Context != NULL)
|
||
{
|
||
NtlmFreeKernelContext(Context);
|
||
}
|
||
}
|
||
|
||
DebugLog(( DEB_TRACE, "Leaving NtLmCreateKernelContext 0x%lx\n", Status ));
|
||
return(Status);
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function:
|
||
//
|
||
// Synopsis:
|
||
//
|
||
// Effects:
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns:
|
||
//
|
||
// Notes:
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
|
||
NTSTATUS
|
||
NtLmImportSecurityContext(
|
||
IN PSecBuffer PackedContext,
|
||
IN OPTIONAL HANDLE TokenHandle,
|
||
OUT PULONG_PTR ContextHandle
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
PNTLM_KERNEL_CONTEXT Context = NULL;
|
||
|
||
PAGED_CODE();
|
||
DebugLog((DEB_TRACE,"Entering NtLmImportSecurityContext\n"));
|
||
|
||
Status = NtLmCreateKernelModeContext(
|
||
0, // LsaContextHandle not present
|
||
TokenHandle,
|
||
PackedContext,
|
||
&Context
|
||
);
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
DebugLog((DEB_ERROR,"NtLmImportSecurityContext: Failed to create kernel mode context: 0x%x\n",
|
||
Status));
|
||
goto Cleanup;
|
||
}
|
||
|
||
*ContextHandle = (ULONG_PTR) Context;
|
||
|
||
Cleanup:
|
||
if (Context != NULL)
|
||
{
|
||
NtlmDerefContext( Context );
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
//+---------------------------------------------------------------------------
|
||
//
|
||
// Function: NtlmSetPagingMode
|
||
//
|
||
// Synopsis: Switch the paging mode for cluster support
|
||
//
|
||
// Arguments: [Pagable] --
|
||
//
|
||
// History: 7-07-98 RichardW Created
|
||
//
|
||
// Notes:
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
NTSTATUS
|
||
NtlmSetPagingMode(
|
||
BOOLEAN Pagable
|
||
)
|
||
{
|
||
if ( Pagable )
|
||
{
|
||
NtlmPoolType = PagedPool ;
|
||
NtlmActiveList = NtlmPagedList ;
|
||
}
|
||
else
|
||
{
|
||
if ( NtlmNonPagedList == NULL )
|
||
{
|
||
NtlmNonPagedList = LsaKernelFunctions->CreateContextList( KSecNonPaged );
|
||
if ( NtlmNonPagedList == NULL )
|
||
{
|
||
return STATUS_NO_MEMORY ;
|
||
}
|
||
}
|
||
|
||
NtlmActiveList = NtlmNonPagedList ;
|
||
|
||
NtlmPoolType = NonPagedPool ;
|
||
}
|
||
return STATUS_SUCCESS ;
|
||
|
||
}
|