You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3003 lines
83 KiB
3003 lines
83 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// 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 ;
|
|
|
|
}
|